daemonize Python script implementing Paho

689 Views Asked by At

I've got a Python script that implements the Paho library to subscribe to and republish MQTT topics coming from a Mosquitto server. My script works fine when I invoke it from the terminal. What is tripping me up is trying to daemonize the script to run unattended. I'm using the daemonize library and while I can get the script to start as daemon, it doesn't actually do anything. When I run the non-daemonized script, I see the MQTT messages received at the endpoint I've specified, but when the same script is running as a daemon, no messages are showing up at my receiving endpoint. I'm including both the non-daemonized and daemonized scripts for reference.

Any ideas as to why the non-daemonized script works but the daemonized script does not?

Non-Daemonized

import paho.mqtt.client as mqtt
import random
import json
import time

#replace [device-id] with your device you created in IoT Hub.
iothubmqtttopic = "devices/MoxieSensorsBHM/messages/events/"

#define on_connect function
def on_connect(client, userdata, flags, rc):
        print("Connected with result code " + str(rc))
        client.subscribe("MW/oneal/Tag/#")

#define on_message function - this function translates the incoming topic
#and publishes to the specified topic for IoT Hub
def on_message(client, userdata, message):
        global iothubmqtttopic
        topic = message.topic
        if(topic != iothubmqtttopic):

                #extract the sensor ID from the topic
                splitTopic = topic.split("/") #split the topic into a list
                sensorID = splitTopic[3] #the 3rd item in the list is the sensor ID
                msgType = splitTopic[4] #the 4th item in the list is the type (e.g. status, UWB)

                #convert the json response to a python dictionary object
                m_decode = str(message.payload.decode("utf-8"))
                m_in = json.loads(m_decode)

                #get current time for timestamp
                ts = time.gmtime()
                tsFormatted = time.strftime("%Y-%m-%d %H:%M:%S", ts)

                #add new elements to the dictionary for topic and sensor ID
                m_in["topic"] = topic
                m_in["sensorID"] = sensorID
                m_in["messageType"] = msgType
                m_in["timestamp"] = tsFormatted

                #set the propertybag for IoT Hub
                propertyBag = "topic=" + topic + "&sensorID=" + sensorID + "&messageType=" + msgType
                print(propertyBag)

                #convert back to JSON
                m_encode = json.dumps(m_in)

                #create an array of all the possible sensor IDs

                #print to screen and publish to IoT Hub
                print("Topic: ", topic)
                print("Message received: ", m_encode)
                print("IoT Hub Topic: ", iothubmqtttopic)
                client.publish(iothubmqtttopic, m_encode)

# replace <broker address> with the FQDN or IP address of your MQTT broker
broker_address="localhost"

#create the client and connect to the broker
print("creating new instance")
client = mqtt.Client("iottopicxlate" + str(random.randrange(1,1000000))) #create new instance
client.on_message=on_message #attach function to callback
print("connecting to broker")
client.connect(broker_address) #connect to broker
client.on_connect = on_connect #attach function to callback

client.loop_forever() #stop the loop

Daemonized

import paho.mqtt.client as mqtt
import random
import json
import time
import os, sys
from daemonize  import Daemonize

def main():
        #replace [device-id] with your device you created in IoT Hub.
        iothubmqtttopic = "devices/MoxieSensorsBHM/messages/events/"

        #define on_connect function
        def on_connect(client, userdata, flags, rc):
                #print("Connected with result code " + str(rc))
                client.subscribe("MW/oneal/Tag/#")

        #define on_message function - this function translates the incoming topic
        #and publishes to the specified topic for IoT Hub
        def on_message(client, userdata, message):
                global iothubmqtttopic
                topic = message.topic
                if(topic != iothubmqtttopic):

                        #extract the sensor ID from the topic
                        splitTopic = topic.split("/") #split the topic into a list
                        sensorID = splitTopic[3] #the 3rd item in the list is the sensor ID
                        msgType = splitTopic[4] #the 4th item in the list is the type (e.g. status, UWB)

                        #convert the json response to a python dictionary object
                        m_decode = str(message.payload.decode("utf-8"))
                        m_in = json.loads(m_decode)

                        #get current time for timestamp
                        ts = time.gmtime()
                        tsFormatted = time.strftime("%Y-%m-%d %H:%M:%S", ts)

                        #add new elements to the dictionary for topic and sensor ID
                        m_in["topic"] = topic
                        m_in["sensorID"] = sensorID
                        m_in["messageType"] = msgType
                        m_in["timestamp"] = tsFormatted

                        #set the propertybag for IoT Hub
                        propertyBag = "topic="+topic+"&sensorID="+sensorID+"&messageType="+msgType
                        #print(propertyBag)

                        #convert back to JSON
                        m_encode = json.dumps(m_in)

                        #print to screen and publish to IoT Hub
                        print("Topic: ", topic + propertyBag)
                        print("Message received: ", m_encode)
                        client.publish(iothubmqtttopic, m_encode)

        # replace <broker address> with the FQDN or IP address of your MQTT broker
        broker_address="localhost"

        #create the client and connect to the broker
        #print("creating new instance")
        client = mqtt.Client("iottopicxlate" + str(random.randrange(1,1000000))) #create new instance
        client.on_message=on_message #attach function to callback
        #print("connecting to broker")
        client.connect(broker_address) #connect to broker
        client.on_connect = on_connect #attach function to callback

        client.loop_forever() #stop the loop

#start the daemon
topictransdpid = os.path.basename(sys.argv[0])
pidfile = "topictransd.pid"
daemon = Daemonize(app = "topictransd", pid = pidfile, action = main)
daemon.start()

Update Vincent, I'm trying to implement your suggestion to write to a file for debug. Please bear with me as I'm learning Python on the fly. I've added the following code snip to the on_message function in the non-daemonized (i.e. working) version of the script and I see the messages written to text files in my "debug" directory. When I implement the same snip in the daemonized version, no files are written. So while my daemon is running, it's not actually doing anything.

                f = open("debug/" + sensorID+tsFormatted + ".txt", "x")
                f.write(m_encode)
                f.close

Any ideas as to what I'm missing?

Update 2 I've implemented a simple logger to write a debug message when the script starts and and another to write a message when daemonize calls the main() function. My log file has an entry for the script starting, but there is not an entry for when main() is called - it's as though daemonize isn't executing the main() function? Here is my updated daemon script with the logger enabled:

import paho.mqtt.client as mqtt
import random
import json
import time
import logging
import os, sys
from daemonize  import Daemonize

def main():
        logger.warning("main has been started") # write a log file entry to indicate main has been called by daemonize
        #replace [device-id] with your device you created in IoT Hub.
        iothubmqtttopic = "devices/MoxieSensorsBHM/messages/events/"

        #define on_connect function
        def on_connect(client, userdata, flags, rc):
                #print("Connected with result code " + str(rc))
                client.subscribe("MW/oneal/Tag/#")

        #define on_message function - this function translates the incoming topic
        #and publishes to the specified topic for IoT Hub
        def on_message(client, userdata, message):
                global iothubmqtttopic
                topic = message.topic
                if(topic != iothubmqtttopic):

                        #extract the sensor ID from the topic
                        splitTopic = topic.split("/") #split the topic into a list
                        sensorID = splitTopic[3] #the 3rd item in the list is the sensor ID
                        msgType = splitTopic[4] #the 4th item in the list is the type (e.g. status, UWB)

                        #convert the json response to a python dictionary object
                        m_decode = str(message.payload.decode("utf-8"))
                        m_in = json.loads(m_decode)

                        #get current time for timestamp
                        ts = time.gmtime()
                        tsFormatted = time.strftime("%Y-%m-%d %H:%M:%S", ts)

                        #add new elements to the dictionary for topic and sensor ID
                        m_in["topic"] = topic
                        m_in["sensorID"] = sensorID
                        m_in["messageType"] = msgType
                        m_in["timestamp"] = tsFormatted

                        #set the propertybag for IoT Hub
                        propertyBag = "topic="+topic+"&sensorID="+sensorID+"&messageType="+msgType
                        #print(propertyBag)

                        #convert back to JSON
                        m_encode = json.dumps(m_in)

                        #print to screen and publish to IoT Hub
                        #print("Topic: ", topic + propertyBag)
                        #print("Message received: ", m_encode)
                        client.publish(iothubmqtttopic, m_encode)

                        #write the message to a debug file
                        f = open("debug/" + sensorID+tsFormatted + ".txt", "w")
                        f.write(m_encode)
                        f.close

        # replace <broker address> with the FQDN or IP address of your MQTT broker
        broker_address="localhost"

        #create the client and connect to the broker
        #print("creating new instance")
        client = mqtt.Client("iottopicxlate" + str(random.randrange(1,1000000))) #create new instance

        #create a logger
        #logging.basicConfig(level=logging.DEBUG, filename="topictransd.log", format="%(asctime)s %(message)s", filemode="w")
        #logger = logging.getLogger()
        #client.enable_logger(logger)

        client.on_message=on_message #attach function to callback
        #print("connecting to broker")
        client.connect(broker_address) #connect to broker
        client.on_connect = on_connect #attach function to callback

        #start the loop
        client.loop_forever()

#start the daemon
logging.basicConfig(level=logging.DEBUG, filename="topictransd.log", format="%(asctime)s %(message)s", filemode="w")
logger = logging.getLogger()

pidfile = "topictransd.pid"
logger.warning("successfully started the script") # write a log file entry to indicate the script has successfully started
daemon = Daemonize(app = "topictransd", pid = pidfile, action = main)
daemon.start()
0

There are 0 best solutions below