daemonize Python script implementing Paho - python-3.x

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

Related

How to read and publish to two different topics at the same time in mqtt

I have created a class Mqtt that contains the definitions of the functions of all the necessary callbacks, and I want to subscribe to two occupancy sensor (actually I have even more) using a single object of the class Mqtt, then based on the sensor that publishes "1", I need to send a PUBLISH message to turn the lights on. However, once I send "1" to testtopic/4 and then to testtopic/5, messages are sent at first t bedroom but then to the kitchen and no more to bedrooms. Here is the main code:
from class_mqtt import Mqtt
import paho.mqtt.client as mqtt
import sys
import threading
import time
from MongoDB import is_circadian_Light
Broker = "broker.mqttdashboard.com"
username = "growthtechnology"
password = "growthtechnology"
PortaBroker = 1883
KeepAliveBroker = 60
client_name = "Local"
light = Mqtt(topic_sub)
# I have two sensors
occupancy_sensor = Mqtt([("testtopic/4", 0), ("testtopic/5", 0)])
tunable_light = Light("deviceID")
# I used test as lights to subscribe and be sure to receive messages
test = Mqtt("testtopic/Kitchen")
try:
print("[STATUS] Initialising MQTT...")
#Initialising mqtt
client = mqtt.Client()
client.username_pw_set(username, password)
client.connect(Broker, PortaBroker, KeepAliveBroker)
# test
client.on_connect = test.connection()
client.on_message = test.message()
client.on_publish = test.publisher()
client.on_disconnect = test.disconnection()
# for lights
client.on_connect = light.connection()
client.on_message = light.message()
client.on_publish = light.publisher()
client.on_disconnect = light.disconnection()
#for sensor
client.on_connect = occupancy_sensor.connection()
client.on_message = occupancy_sensor.message()
client.on_disconnect = occupancy_sensor.disconnection()
def publishing():
client.publish("testtopic/5", 1)
while True:
msg = occupancy_sensor.messages # calling for the variable "messages" to get PUBLISH messages
topic = occupancy_sensor.topic
if msg == "1" and topic == "testtopic/4":
if is_circadian_Light("device2") == 1 :
client.publish("testtopic/Bedroom",tunable_light.circadianLight())
time.sleep(10)
# The problem here is that if someone enters the kitchen I can't send messages to lights in the bedroom
if msg == "1" and topic == "testtopic/5":
if is_circadian_Light("device2") == 1 :
client.publish("testtopic/Kitchen",tunable_light.circadianLight())
time.sleep(10)
pub = threading.Thread(target= publishing)
pub.start()
client.loop_forever()
except KeyboardInterrupt:
print ("\nCtrl+C pressionado, encerrando aplicacao e saindo...")
sys.exit(0)
Here is the on_message callback in the class Mqtt:
def message(self):
def on_message(client, userdata, msg):
Message = str(msg.payload.decode("utf-8"))
self._messages = Message
self._topic = msg.topic
print("[Receiving msg] Topic:" +msg.topic+" / Message: "+Message)
return on_message
I don't know if I need an object for every sensor so I can publish to multiple rooms at the same time, but if there are other solutions it will be better
You can only set the callback functions once for a single client.
You have set them all 3 times, only the last set will be used.
client.on_connect = occupancy_sensor.connection()
client.on_message = occupancy_sensor.message()
client.on_disconnect = occupancy_sensor.disconnection()

How to send data to a Websocket from a faust application

I am working on an usecase at the moment using Kafka and robinhood's faust to process the data coming from Kafka. I have succeeded doing the computation and the results I need are being printed to the console my faust worker is running in.
Now I want to find a way to get my results not only in console but visible in a HTML page. I have taken a look at the websockets library but I can't get it to work in conjunction with faust. The error I get is Crashed reason=RuntimeError('This event loop is already running') I think this is caused because the code is executed for every message that is being processed.
Any help is highly appreciated
This is the code I am using:
import faust, datetime, websockets, asyncio
app = faust.App(
'UseCase',
broker='kafka://localhost:29092',
)
usecase_topic = app.topic('usecase',partitions=8)
usecase_table = app.Table('usecase', default=int)
checkfailure = {}
#app.agent(usecase_topic)
async def process_record(records):
async for record in records:
#count records for each Sensor
print(record)
sensor = record['ext_id']
usecase_table[sensor] += 1
#print(f'Records for Sensor {sensor}: {usecase_table[sensor]}')
#write current timestamp of record and previous timestamp for each sensor to usecase_table dict
currtime_id = record['ext_id']+'c'
prevtime_id = record['ext_id']+'p'
usecase_table[currtime_id] = datetime.datetime.strptime(record['tag_tsp'], "%Y%m%d%H%M%S.%f")
#print current time
print(f'Current time for Sensor {sensor}: {usecase_table[currtime_id]}')
#calculate and print timestamp delta; if no previous value is given print message
if usecase_table[prevtime_id] == 0:
print(f'no previous timestamp for sensor {sensor}')
else:
usecase_table[prevtime_id] = datetime.datetime.strptime(usecase_table[prevtime_id], "%Y%m%d%H%M%S.%f")
print(f'previous time for Sensor {sensor}: {usecase_table[prevtime_id]}')
tsdelta = usecase_table[currtime_id] - usecase_table[prevtime_id]
tsdelta_id = record['ext_id']+'t'
usecase_table[tsdelta_id] = str(tsdelta)
print(f'Sensor: {sensor} timestamp delta: {usecase_table[tsdelta_id]}')
#calculate value delta
currvalue_id = record['ext_id']+'cv'
prevvalue_id = record['ext_id']+'pv'
usecase_table[currvalue_id] = record['tag_value_int']
print(f'current value for Sensor {sensor}: {usecase_table[currvalue_id]}')
if usecase_table[prevvalue_id] == 0:
print(f'no previous record for sensor {sensor}')
else:
print(f'previous value for Sensor {sensor}: {usecase_table[prevvalue_id]}')
vdelta = usecase_table[currvalue_id] - usecase_table[prevvalue_id]
vdelta_id = record['ext_id']+'v'
usecase_table[vdelta_id] = vdelta
print(f'Sensor: {sensor} value delta:{usecase_table[vdelta_id]}')
#calculate cycle time
if usecase_table[prevtime_id] != 0 and usecase_table[prevvalue_id] != 0 and usecase_table[vdelta_id] != 0:
cycletime = tsdelta / usecase_table[vdelta_id]
cyclemsg = f'Sensor {sensor}; Cycletime {cycletime}'
print(cyclemsg)
#add timestamp to checkfailure dict
checkfailure[sensor] = datetime.datetime.strptime(record['tag_tsp'], "%Y%m%d%H%M%S.%f")
#check if newest timestamp for a sensor is older than 10 secs
for key in checkfailure:
if datetime.datetime.now() - checkfailure[key] >= datetime.timedelta(seconds=10):
failuremsg = f'Error: Sensor {key}'
print(failuremsg)
#send results to websocket
async def send_result(websocket,path):
results = cyclemsg + failuremsg
await websockets.send(results)
start_server = websockets.serve(send_result, '127.0.0.1', 5678)
asyncio.get_event_loop().run_until_complete(start_server)
#set previous value and timestamp to current
usecase_table[prevtime_id] = record['tag_tsp']
usecase_table[prevvalue_id] = record['tag_value_int']
It is normal to be confused by this asyncio error message :)
You cannot call loop.run_until_complete from an async def function.
What you need to do is start the websocket server in the background.
That should be easy, and it is using asyncio.ensure_future, but you also want your websocket server to gracefully shut down when your application exits.
For this reason Faust uses "services", and you can define a service for your websocket server:
import faust
import websockets
from mode import Service
from websockets.exceptions import ConnectionClosed
from websockets.server import WebSocketServerProtocol
class App(faust.App):
def on_init(self):
self.websockets = Websockets(self)
async def on_start(self):
await self.add_runtime_dependency(self.websockets)
class Websockets(Service):
def __init__(self, app, bind: str = 'localhost', port: int = 9999, **kwargs):
self.app = app
self.bind = bind
self.port = port
super().__init__(**kwargs)
async def on_message(self, ws, message):
...
async def on_messages(self,
ws: WebSocketServerProtocol,
path: str) -> None:
try:
async for message in ws:
await self.on_message(ws, message)
except ConnectionClosed:
await self.on_close(ws)
except asyncio.CancelledError:
pass
async def on_close(self, ws):
# called when websocket socket is closed.
...
#Service.task
def _background_server(self):
await websockets.serve(self.on_messages, self.bind, self.port)
app = App('UseCase')
# [...]

multiple events in paho mqtt with python

#!/usr/bin/env python
import sys
from arduino.Arduino import Arduino
import paho.mqtt.client as mqtt
import serial
import time
pin = 13
broker_adress = "10.0.2.190"
sys.path.append("/home/hu/Schreibtisch/Arduino_BA_2.0/Probe_Programmierung/Python-Arduino-Proto-API-v2/arduino")
ser = serial.Serial('/dev/ttyACM0', 9600)
b = Arduino('/dev/ttyACM0')
b.output([pin])
b.setLow(pin)
gassensor_value = "no default_value"
sensor_value = [['/Deutschland/Osnabrueck/Coffee-bike-1/Sensor_1',gassensor_value]]
#########################################################################
# Callback_1 for relay
#on_connect1,on_disconnect1,on_subscribe1on_message_1
#########################################################################
def on_connect(mqttrelay, obj, flags, rc):
if rc != 0:
exit(rc)
else:
mqttrelay.subscribe("qos0/test", 0)
def on_disconnect(mqttrelay, obj, rc):
obj = rc
def on_subscribe(mqttrelay, obj, mid, granted_qos):
print(mqttrelay.subscribe("qos0/test", 0))
print("Waiting for the subscribed messages")
def on_message(mqttrelay,userdata, message):
a = str(message.payload.decode("utf-8"))
print(a)
if (a == "1" or a == "0"):
if (a == "1"):
b.setHigh(13)
time.sleep(10)
else:
b.setLow(13)
time.sleep(10)
else:
print("please publish the message 1 or 0")
#########################################################################
# Callback_2 for gassensor
# on_connect2,on_publish2
#########################################################################
def on_publish(mqttgassensor, obj, mid):
print("mid: " + str(mid))
def on_connect(mqttgassensor, userdata, flags, rc):
print("Connected with result code " + str(rc))
#create new instance to subscribe the sitution of relay
mqttrelay = mqtt.Client("relay_K_12", 1)
#create new instance to publish the situation of gassensor
mqttgassensor = mqtt.Cleint("gassensor",1)
#the events and callbacks of instance mqttrelais associate with each other:
mqttrelay.on_message = on_message
mqttrelay.on_connect = on_connect
mqttrelay.on_subscribe = on_subscribe
mqttrelay.on_disconnect = on_disconnect
mqttrelay.connect(broker_adress)
#the events and callbacks of instance gassensor associate with each other:
mqttgassensor.on_connect = on_connect
mqttgassensor.on_publish = on_publish
mqttgassensor.connect(broker_adress)
while True:
mqttrelay.loop_start()
time.sleeps(2)
mqttrelay.loop_stop()
print("relay 開始循環")
mqttgassensor.loop_start()
mqttgassensor.loop()
time.sleep(1)
sensor_value[0][1] = ser.readline()
if (sensor_value[0][1] != "no default_value" or sensor_value[0][1] != b''):
print(sensor_value[0])
mqttgassensor.publish("/Deutschland/Osnabrueck/Coffee-bike-1/Sensor_1", sensor_value[0][1])
mqttgassensor.loop_stop()
Hello, everyone. I want to accomplish two instances in this script.
Through the publish we can get the data from the gassensor. Because at the top of this script i have imported the serial modul with that we can accomplish the communication between the arduino and raspberry pi.
I want to use the subscribe to get the command(1 or 0) from the server. the number 1 can active the Relay and the 0 can deactive the relay.
I have tried to accomplish the two thoughts lonely and successfully. But the Combination gives me no reply.
You only need 1 instance of the MQTT client since there is only 1 broker. You can subscribe and publish to multiple topics from a single client.
You should connect this 1 instance and then start the network loop in the background with client.start_loop()
You can then run your own loop to read from the serial port.

How to Subscribe on Multiple topic using PAHO-MQTT on python

I am trying to subscribe on three different topics using single subscriber client. But with the below mentioned code I am able to get data from only one server.
Please suggest ant modification in my code that can be implemented to get the desired data from the different publisher clients.
# Define Variables
MQTT_BROKER = "10.97.143.44"
MQTT_PORT = 11883
MQTT_TOPIC = [("Server1/kpi1"),("Server2/kpi2"),("Server3/kpi3")
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to broker")
global Connected #Use global variable
Connected = True #Signal connection
else:
print("Connection failed")
def on_message(client, userdata, message):
data = message.payload
receive=data.decode("utf-8")
m_decode = json.loads(receive)
#print(m_decode)
#print (m_decode['Server_name'])
print ("Message received: " + str(m_decode))
Connected = False #global variable for the state of the connection
client = mqttClient.Client("Python") #create new instance
client.on_connect= on_connect #attach function to callback
client.on_message= on_message #attach function to callback
client.connect(MQTT_BROKER,MQTT_PORT) #connect to broker
client.loop_start() #start the loop
while Connected != True: #Wait for connection
time.sleep(0.1)
client.subscribe(MQTT_TOPIC)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print ("exiting")
List item
client.disconnect()
client.loop_stop()
Your MQTT_TOPIC array should contain QOS levels as well as topic names.
From the doc:
String and integer tuple
e.g. subscribe(("my/topic", 1))
topic
a tuple of (topic, qos). Both topic and qos must be present in
the tuple.
qos
not used.
e.g.
MQTT_TOPIC = [("Server1/kpi1",0),("Server2/kpi2",0),("Server3/kpi3",0)]

Add several individual threads to MQTT Python code

this is my code and I need add,
(1) loop(thread1) which can publish data when receive specific data by UART(ser.read())
(2) loop(thread2) which can run report_temp_humidity() with timed delay
so I put thread for test but this runs only one time, not loop.
Is this cause by "mqttclient.loop_forever()" at the end of line?
Need some help on this.
import paho.mqtt.client as mqtt
import Adafruit_DHT
import Adafruit_BBIO.UART as UART
import serial
import threading
import time
# DHT-11 Temperature & Humidity
sensor = Adafruit_DHT.DHT11
sensor_pin = 'P8_11'
# UART
UART.setup("UART1")
ser = serial.Serial(port = "/dev/ttyO1", baudrate=9600)
ser.close()
ser.open()
# Define event callbacks
def on_connect(self, client, userdata, rc):
if rc == 0:
print("Connected successfully.")
else:
print("Connection failed. rc= "+str(rc))
def on_publish(client, userdata, mid):
print("Message "+str(mid)+" published.")
def on_subscribe(client, userdata, mid, granted_qos):
print("Subscribe with mid "+str(mid)+" received.")
def on_message(client, userdata, msg):
print("Message received on topic "+msg.topic+" with QoS "+str(msg.qos)+"and payload "+msg.payload)
if msg.payload == "check_status":
report_temp_humidity()
if msg.payload == "motor_run!":
print "Send command to RF"
ser.write("11.1111[S]")
# Define my function prototypes
def report_temp_humidity():
hum, temp = Adafruit_DHT.read_retry(sensor, sensor_pin)
mqttclient.publish("/my_topic1/", "Temperature "+ str(temp) + "`C", qos=0)
mqttclient.publish("/my_topic1/", "Humidity " + str(hum) + "%", qos=0)
# Define thread(loop) test
def loop_test(delay):
print(loop!)
time.sleep(1)  
t1 = threading.Thread(target=loop_test, args=('1,',))
t1.start()
mqttclient = mqtt.Client()
# Assign event callbacks
mqttclient.on_connect = on_connect
mqttclient.on_publish = on_publish
mqttclient.on_subscribe = on_subscribe
mqttclient.on_message = on_message
# Connect
mqttclient.username_pw_set("dioty_user", "1234567")
mqttclient.connect("mqtt.dioty.co", 1883)
# Start subscription
mqttclient.subscribe("/my_topic1/")
# Publish a message
mqttclient.publish("/my_topic1/", "Hello World Message!")
# Loop
mqttclient.loop_forever()
This has nothing to do with the MQTT library.
Your thread t1 will run the loop_test function, which only has 2 statements in it. It will run these (once) then exit, there is nothing to tell it to continue to do anything else.
If you want that thread to continue to run for ever then you will need to include a loop statement of some sort (e.g. while True).

Resources