Decagrog
Decagrog

Reputation: 97

Passing a parameter to the paho mqtt callback

I'm new to Python and I'm trying to run a control to pulse an RGB LED, every time I receive a MQTT message/topic. So I have created an object threadObj in order to run a thread and cycling the pulse animation.

The problem I'm facing is that I don't know how to pass the object instance to the on_message mqtt callback. I was looking for the partial function application but right now I'm not sure is the right way to do it.

This is the main script:

from P9813 import P9813
from safeGPIO import safeGPIO as GPIO
import json
from math import ceil
import os
import threading
import queue
import sys
import subprocess
import threading
import time
from functools import partial
from ledPulse import ledPulse
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print("Connected to {0} with result code {1}".format(HOST, rc))
    # Subscribe to any hotword topic --- old code:   # client.subscribe("hermes/hotword/default/detected")
    client.subscribe("hermes/hotword/#")

    # Subscribe to any topic starting with 'hermes/intent/'
    client.subscribe('hermes/intent/#')

def on_message(client, userdata, msg):

    print("- Message received on topic {0}: {1}".format(msg.topic, msg.payload))
    print('* ledPulse instance count {0} nameid {1}'.format(ledPulse.instance_count, threadObj))

    if msg.topic == 'hermes/hotword/default/detected':
        print("Wakeword detected! Wakeword light!")
        #Wakeword light

        print('ledPulseThread.Status -> ON and status {0}'.format(threadObj.status))
        threadObj.Status = "ON"  # T.StreamV could be modified to run at creation and wait for this to change to "ON"
        threadObj.msgTopic = msg.topic
        threadObj.ledDriver = ledDriver
        threadObj.ledColor = LEDS_CYAN
        #threadObj.daemon = True
        threadObj.ledPulseThread.start()  # this calls the T.StreamV function in a separate thread

    if msg.topic == 'hermes/intent/mywai:ShowMarketplace':
        # intent ShowMarketplace light
        print("Intent detected! ShowMarketplace light!")
        ledDriver[0] = LEDS_GREEN
        ledDriver.write()

    if msg.topic == 'hermes/hotword/toggleOn':
        # light for hotword toggled-on
        threadObj.ledColor = LEDS_WARM_WHITE
        threadObj.Status = "OFF"
        print('status ', threadObj.Status)
        print('T.Status -> "OFF"')
        print("Intent detected!  Hotword ended!")
        ledDriver[0] = LEDS_WARM_WHITE
        ledDriver.write()
        #threadObj.ledPulseThread.terminate()  # this calls the T.StreamV function in a separate thread

#########################################################################
# MAIN
#########################################################################
if __name__ == '__main__':
    #main()    
    HOST = 'localhost'
    PORT = 1883
    gpio = GPIO()
    gpio.cleanup()

    # Construct the object
    ledDriver = P9813(32, 33)
    # Create led (R,G,B) list
    leds = [[0, 0, 0]]

    # Define color constants
    #              [R,G,B]
    LED_STRENGTH = 100
    LEDS_OFF = [0, 0, 0]
    LEDS_RED = [LED_STRENGTH, 0, 0]
    LEDS_GREEN = [0, LED_STRENGTH, 0]
    LEDS_BLUE = [0, 0, LED_STRENGTH]
    LEDS_YELLOW = [LED_STRENGTH, LED_STRENGTH, 0]
    LEDS_MAGENTA = [LED_STRENGTH, 0, LED_STRENGTH]
    LEDS_CYAN = [0, LED_STRENGTH, LED_STRENGTH]
    LEDS_WHITE = [LED_STRENGTH, LED_STRENGTH, LED_STRENGTH]
    LEDS_WARM_WHITE = [210, 155, 35]

    # Default light is warm white
    #ledDriver[0] = LEDS_WARM_WHITE
    #ledDriver.write()

    # t1 = threading.Thread(target=loop_test, args=(deca,))
    # t1.start()

    threadObj = ledPulse(ledDriver)  # our thread is actually created here, but is hasn't started yet

    client = mqtt.Client()
    client.on_connect = on_connect
    #client.on_message = on_message
    client.on_message = partial(on_message, threadObj) #https://stackoverflow.com/questions/15331726/how-does-the-functools-partial-work-in-python

    client.connect(HOST, PORT, 60)
    client.loop_forever()

    # Ask the user repeatedly for LED brightness setting
    try:
        while (True):
            str = raw_input("Input R, G, B [Enter] or Ctrl-C to quit. R, G, B range from 0 - 255: ")
            leds[0] = list(map(int, str.split(",")))
            print(leds[0])
            ledDriver[0] = leds[0]
            ledDriver.write()
    except KeyboardInterrupt:
        print("\r")
    except:
        print(str)

    # Turn off LEDs before we quit
    leds[0] = [0, 0, 0]
    ledDriver[0] = leds[0]
    ledDriver.write()

The ledPulse Class:

import threading
import time

class ledPulse(object):

    instance_count = 0 #count how many istance of the class are created
    def __init__(self, ledDriver):
        self.status = "OFF"
        self.ledPulseThread = threading.Thread(target=self.ledPulseThread)  # create a thread for our streamV function
        self.ledColor = [30, 40, 50]
        self.ledDriver = ledDriver
        self.msgTopic = ""
        self.counter = 0
        ledPulse.instance_count += 1

    def ledPulseThread(self):
        print("starting thread")
        while self.status == "ON":
            print("nonstop | status = ", self.Status)
            self.ledDriver[0] = [0, 0, 0]
            self.ledDriver.write()
            time.sleep(0.2)
            print("{0} sec | color {2} | driver {3} | instance {4} | deca  {1}".format(self.counter, self.msgTopic, self.ledColor, self.ledDriver.write(), ledPulse.instance_count ))
            self.ledDriver[0] = self.ledColor
            self.ledDriver.write()
            time.sleep(0.2)
            self.counter = self.counter + 1
        print("stopping thread")
        self.counter = 0
        #raise SystemExit

Upvotes: 2

Views: 4090

Answers (1)

hypergamer003
hypergamer003

Reputation: 213

Place the object instance as a value in a dictionary (or any mutable object such as list if you want to change the values in the callback and see the changes reflected in the main code) then pass the object instance as part of the userdata when instantiating an MQTT client. For example:

client_userdata = {'myobject':threadObj}
client = mqtt.Client(userdata=client_userdata)

In your on_message callback, you can access this dictionary in the userdata variable.

userdata['myobject'] <- this is your threadObj

Upvotes: 2

Related Questions