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()