I'm trying to make a simple "transit schedule changes" program using python MQTT where the publisher can input flight number (will be used as topic) and new flight time, while the transit location will be picked random from given list.
The subscriber will have to input flight number too which will be used as topic. But in my codes, it looks like the subscriber failed to get the message published to the same topic because it keeps on printing Connected Successfully (I'm using client.loop_forever()). Can someone please help me to figure out what's wrong with my code?
This is my first time asking question, so please ask me if something isn't clear from my explanation. Thank you so much :)
Publisher:
import paho.mqtt.client as mqtt
import time
from datetime import datetime, date
import random
def on_connect(client, userdata, flags, rc):
if (rc==0):
global connected
connected = True
#print("Successfully Connected.")
client.on_publish = on_publish
else:
print("Failed to connect.")
def on_publish(client, userdata, mid):
print("Published successfully. MID: "+str(mid))
listTransit = ["Singapura", "Qatar", "Korea Selatan", "Turki", "Republik Tiongkok",
"Amerika Serikat", "Jepang", "Uni Emirat Arab", "Oman", "Islandia"]
broker_address="broker.emqx.io"
client = mqtt.Client("Publisher")
client.on_connect = on_connect
client.connect(broker_address, port=1883)
client.loop_start()
topic = input("Masukkan nomor penerbangan: ")
negaraTujuan = input("negara tujuan: ")
print("Masukkan waktu penerbangan baru (Format: [jam::menit::detik])")
str_time = input()
Date = date.today()
Time = datetime.strptime(str_time, '%H::%M::%S').time()
Location = random.randrange(0,len(listTransit))
if (listTransit[Location] != negaraTujuan):
message = Date.strftime("%Y/%m/%d")+"\nTujuan: "+negaraTujuan+"\nLokasi Transit : "+listTransit[Location]+"\nJam terbang : "+Time.strftime("%H:%M:%S")
client.publish(topic, message)
print("Topic: ",topic)
print(message)
else:
while listTransit[Location] == negaraTujuan:
Location = random.randrange(0,len(listTransit))
message = Date.strftime("%Y/%m/%d")+"\nTujuan: "+negaraTujuan+"\nLokasi Transit : "+listTransit[Location]+"\nJam terbang : "+Time.strftime("%H:%M:%S")
client.publish(topic, message)
print(message)
client.loop_stop()
Subscriber:
import paho.mqtt.client as mqtt
import time
from datetime import datetime, datetime
import re
def on_connect(client, userdata, flags, rc):
if (rc == 0):
print("Connected successfully.")
#global topic
#topic = input("Masukkan nomor penerbangan anda: ")
#client.subscribe(topic)
else:
print("Connection failed.")
def on_message(client, userdata, msg):
print("on_message callback function activated.")
sched = str(msg.payload.decode("utf-8"))
print(sched)
def on_subscribe(client, userdata, mid, granted_qos):
print("Subscribed to "+topic+" successfully")
broker_address="broker.emqx.io"
topic = input("Masukkan nomor penerbangan anda: ")
negaraTujuan = input("negara tujuan: ")
client = mqtt.Client("Subscriber")
client.subscribe(topic)
client.on_connect = on_connect
client.on_message = on_message
client.on_subscribe = on_subscribe
client.connect(broker_address, port=1883)
client.loop_forever()
what I got from running both codes are
Masukkan nomor penerbangan: YT05TA
negara tujuan: Australia
Masukkan waktu penerbangan baru (Format: [jam::menit::detik])
12::50::00
Topic: YT05TA
2023/01/03
Tujuan: Australia
Lokasi Transit : Amerika Serikat
Jam terbang : 12:50:00
Published successfully. MID: 1
from Publisher
and
Masukkan nomor penerbangan anda: YT05TA
negara tujuan: Australia
Connected successfully.
Connected successfully.
Connected successfully.
Connected successfully.
from subscriber. It doesn't even print the print("on_message callback function activated.")
You are using a public broker, so that means it is likely to have LOTS of over clients.
Every client MUST have a UNIQUE Client ID, so using Publisher and Subscriber is very likely to clash with other clients.
The MQTT specification says the broker must disconnect the currently connected client when a new client connects with the same Client ID. As most client libraries will try and reconnect when they are disconnects this leads to a battle between the two clients to stay connected.
Change them both to random values
Related
I've a problem with a simple python & paho.mqtt code, I report below the code that generates problems purged from connections to the db and other secondary functions
import mysql.connector
import datetime
import json
import paho.mqtt.client as mqtt
import aliveUtil
import logging
from logging.handlers import WatchedFileHandler
import dbconn
from signal import signal, SIGINT, SIGTERM
from sys import exit
import ast
import time
def on_connect(client, userdata, flags, rc):
if rc == 0:
mqttcR.subscribe([("/xxx/+/retained",1),("/xxx/status/send/#",1)])
else:
logging.error("Mqtt client retained connection error")
printOutput("Mqtt client retained connect error")
def on_disconnect(client, userdata, rc):
logging.debug("retained returnCode:"+str(rc))
printOutput("Disconnect retained returnCode:"+str(rc))
mqttcR.reconnect()
def on_message(client, obj, msg):
switchMessage(msg)
def on_publish(client, obj, mid):
logging.debug("Payload:" + str(obj.payload) + " Mid:" + str(mid))
def on_subscribe(client, obj, mid, granted_qos):
logging.debug("Mid:" + str(mid) + " GrantedQos:" + str(granted_qos))
def on_log(client, obj, level, string):
logging.debug(string)
def printOutput(stringa):
logging.info(stringa)
def switchMessage(msg):
payload=msg.payload.replace("'", " ")
try:
jsonReceived = json.loads(payload)
except Exception as exception:
logging.error('json.load payload')
logging.error(str(exception))
topic=str(msg.topic)
try:
if topic.find('retained') != -1:
saveRetained(jsonReceived)
elif topic.find('status') != -1:
saveStatus(jsonReceived)
except Exception as exception:
logging.error('Try topic exception')
logging.error(str(exception))
logging.error(str(topic))
#print str(jsonReceived)
def saveRetained(jMessage):
print "do something in saveRetained"
def saveStatus(jMessage):
print "do something in saveRetained"
logging = createWatchedLog('/var/log/xxx/xxx.log')
logging.info('==== START SCRIPT ====')
logging.info('Setup SIGINT signal handler')
signal(SIGINT, get_shutdown_handler('SIGINT received'))
logging.info('Setup SIGTERM signal handler')
signal(SIGTERM, get_shutdown_handler('SIGTERM received'))
logging.info('Setup mqtt client (pyPahoClient)')
unicoID="pythonXXXClient"+str(aliveUtil.randomDigits())
mqttcR = mqtt.Client(client_id=unicoID, clean_session=False)
mqttcR.on_message = on_message
mqttcR.on_connect = on_connect
mqttcR.on_disconnect = on_disconnect
mqttcR.on_publish = on_publish
mqttcR.on_subscribe = on_subscribe
MQTT_Broker = "localhost"
MQTT_Port = 1883
Keep_Alive_Interval = 600
logging.info('Setup mqtt client username e password')
mqttcR.username_pw_set("XXXuser", "xxx")
logging.info('Connect to mqtt broker')
mqttcR.connect(MQTT_Broker, int(MQTT_Port), int(Keep_Alive_Interval))
logging.info('Mqtt loop forever')
mqttcR.loop_forever()
Checking the log file I realized that every 20 minutes the client disconnects and reconnects with the RC 1 message.
2022-01-18 17:14:03,780: INFO: am_alive_mqtt.printOutput():69: Mqtt client connect
2022-01-18 17:14:03,818: DEBUG: am_alive_mqtt.on_subscribe():62: Mid:7 GrantedQos:(1, 1)
2022-01-18 17:34:04,099: DEBUG: am_alive_mqtt.on_disconnect():46: returnCode:1
2022-01-18 17:34:04,101: DEBUG: am_alive_mqtt.on_connect():27: returnCode:0
2022-01-18 17:34:04,101: INFO: am_alive_mqtt.on_connect():30: Mqtt client connect
2022-01-18 17:34:04,145: DEBUG: am_alive_mqtt.on_subscribe():62: Mid:8 GrantedQos:(1, 1)
2022-01-18 17:54:04,412: DEBUG: am_alive_mqtt.on_disconnect():46: returnCode:1
2022-01-18 17:54:04,414: DEBUG: am_alive_mqtt.on_connect():27: returnCode:0
2022-01-18 17:54:04,414: INFO: am_alive_mqtt.on_connect():30: Mqtt client connect
2022-01-18 17:54:04,448: DEBUG: am_alive_mqtt.on_subscribe():62: Mid:9 GrantedQos:(1, 1)
I checked several pages on this site but couldn't find an explanation.
Any suggestions?
Many thanks in advance
UPDATE
After several tests I discovered that the problem occurs when inside the saveRetained function there is a simple query:
query="SELECT * FROM table_mqtt WHERE serialnumber='"+str(serialnumber)+"'"
number_of_rows = cursor.execute(query);
row=cursor.fetchone()
if I comment this part everything works without restarting every 20 min
I wrote code to publish and subscribe to topics. The publication works very well. The subscription also, but my on_message callback function does not display data (print("message.payload")) although I can see the data in protobuf format on node-red.
import paho.mqtt.client as mqtt
from google.protobuf.struct_pb2 import Struct
import json
import pub_message_pb2 as pub_message
from google.protobuf.json_format import Parse
MQTT_HOST = "xxxxxxxxxxx"
CLIENT_ID = "centos"
BROKER_PORT = 1883
CA_CERT = "caCert.pem"
CERTFILE = "ServerCert.pem"
KEYFILE = "ServerKey.pem"
MQTT_KEEPALIVE_INTERVAL = 45
publish_topic = "user/7/device/8CF9572000023509/downlink"
subscribe_topic = "user/7/device/8CF9572000023509/uplink"
token = ""
payload = Parse(json.dumps({
"Token":"OVc0/opKynDuz/DxVzaUcA==",
"Params":{
"FPort":8,
"Data":"BAoAAQ==",
"Confirm":False,
"DevEUI":"8CF9572000023509"
}
}),pub_message.DeviceDownlink())
def on_publish(client, userdata, mid):
print("Published...")
def on_connect(client,userdata,flags,rc):
client.subscribe(subscribe_topic,1)
client.publish(publish_topic,payload.SerializeToString())
def on_message(client,userdata,message):
print(message.topic)
print(message.payload)
#client.disconnect()
client = mqtt.Client(CLIENT_ID,clean_session=False,userdata=None,protocol=mqtt.MQTTv31,transport="tcp")
client.message_callback_add(subscribe_topic,on_message)
client.on_connect = on_connect
client.on_publish = on_publish
client.on_message = on_message
client.tls_set(ca_certs=CA_CERT,certfile=CERTFILE,keyfile=KEYFILE)
client.username_pw_set(username="xxxxxx",password="xxxxxx")
client.connect(MQTT_HOST,BROKER_PORT,MQTT_KEEPALIVE_INTERVAL)
client.loop_forever()
is there something that is not done well? Your suggestions are welcome.
I am writing a script which will log more than one device with different credentials using paho-mqtt. All the client is running in the same address and with the same port. If I change the username and pass then I get the different feeds depending upon the credentials. It's working fine if I write for different devices different script. But I want to log all the devices in on_connet. I have written the script but it's working only for one device. Here is the script:
import paho.mqtt.client as mqtt
import time, json, threading, logging,ssl
clients=[
{"ursername":"username1","password": 'password1'},
{"ursername":"username2","password":'password2'},
{"ursername":"username3","password":'password3'}
]
nclients=len(clients)
run = True
def Create_connections():
for i in range(nclients):
t=int(time.time())
client_id = "client" + str(t)
client = mqtt.Client(client_id)
username = credentials[i]["ursername"]
password = credentials[i]["password"]
client.on_log=on_log
client.on_connect = on_connect
client.on_subscribe=on_subscribe
client.on_message = on_message
client.on_disconnect = on_disconnect
print("connecting to broker")
client.tls_set("CXXXXX.crt", tls_version=ssl.PROTOCOL_TLSv1_2)
client.tls_insecure_set(True)
client.username_pw_set(username, password)
client.loop_start()
client.connect("XXXXX", XXXX, XX)
print("Loop pass ")
def on_log(client, userdata, level, buf):
print("message:" + str(buf))
print("userdata:" + str(userdata))
def on_message(client, userdata, message):
msg="message received",str(message.payload.decode("utf-8"))
print(msg)
def on_connect(client, userdata, flags, rc):
print("Connected with result code:"+str(rc))
client.subscribe('v3/+/devices/+/up')
def on_disconnect(client, userdata, rc):
pass
def on_publish(client, userdata, mid):
print("mid: " + str(mid) + '\n')
def on_subscribe(mosq, obj, mid, granted_qos):
print("Subscribed: " + str(mid) + " " + str(granted_qos))
mqtt.Client.connected_flag=False
no_threads=threading.active_count()
print("current threads =",no_threads)
print("Creating Connections ",nclients," clients")
Create_connections()
Response
current threads = 1
Creating Connections 2 clients
____________________________________________________________________________
client01597648398
<paho.mqtt.client.Client object at 0x7f73c0a5b1d0>
username1
connecting to broker
message:Sending CONNECT (u1, p1, wr0, wq0, wf0, c1, k60) client_id=b'client01597648398'
userdata:None
message:Received CONNACK (0, 0)
userdata:None
Connected with result code:0
message:Sending SUBSCRIBE (d0, m1) [(b'XX/+/devices/+/XX', 0)]
userdata:None
message:Received SUBACK
userdata:None
Subscribed: 1 (0,)
message:Sending PINGREQ
userdata:None
message:Received PINGRESP
userdata:None
Any help will be highly appreciated. Thanks in advance
Your problem lies here:
while run:
client.loop_forever()
loop_forever() is a blocking call which will only return when the associated client is disconnected:
This is a blocking form of the network loop and will not return until the client calls disconnect(). It automatically handles reconnecting. paho-mqtt
So your other clients never get initialized/connected. You might want to use loop_start instead - this will use a separate thread to handle communication for every client:
def Create_connections():
for i in range(nclients):
# ...
client.loop_start()
client.connect("XXXXXXXXXX", XXXX, XX)
# ...
Create_connections()
while run:
pass
This works :
while True:
print('')
command_input = input()
if command_input == 'q':
break
mcp = Mqtt_command_publisher
mcp.publish_command(device_ids, command_input)
But this does not:
class Mqtt_command_bl:
def update_minutes_to_run_at(json):
if not json['device_ids']:
return 'Request must contain device ids'
device_ids = json['device_ids']
minutes_to_run_at = json['minutes_to_run_at']
minutes_to_run_at_command_section = ''
for i in minutes_to_run_at:
m = '"{}",'.format(i)
if i == minutes_to_run_at[len(minutes_to_run_at) - 1]:
m = '"{}"'.format(i)
minutes_to_run_at_command_section += m
#command_input = 'jq \'.+{{minutes_to_run_at:[{}]}}\' /home/pi/hallmonitor_lite/config.json > /home/pi/hallmonitor_lite/tmp.json && mv /home/pi/hallmonitor_lite/tmp.json /home/pi/hallmonitor_lite/new_config.json'.format(minutes_to_run_at_command_section)
command_input = 'mkdir /home/pi/hallmonitor_lite/hello_world'
mcp = Mqtt_command_publisher
mcp.publish_command(device_ids, command_input)
return 'Success'
The class they both call:
class Mqtt_command_publisher:
def publish_command(device_ids, command_input):
mqtt_msg = json.dumps({'device_ids':device_ids,'command':command_input})
print('\n{}'.format(mqtt_msg))
client = mqtt.Client()
client.connect('********', ****, 30)
client.publish('topic/commands', mqtt_msg)
client.disconnect()
Looking at the print statements output from the Mqtt_command_publisher, the output can be the exact same, however, only one of them will execute, and I don't see why one works and the other does not.
I tried this command for testing: mkdir /home/pi/hallmonitor_lite/hello_world
This is the receiving part:
device_id = 0
with open('/home/pi/hallmonitor_lite/config.json') as json_data_file:
data = json.load(json_data_file)
device_id = data['device_id']
def on_connect(client, userdata, flags, rc):
print("Connected with result code: " + str(rc))
client.subscribe("topic/commands")
def on_message(client, userdata, msg):
mqtt_message = msg.payload.decode()
print(mqtt_message)
ids_and_command = json.loads(mqtt_message)
if str(device_id) in ids_and_command['device_ids'] or not ids_and_command['device_ids']:
print(('Executing: {}').format(ids_and_command['command']))
os.system(ids_and_command['command'])
client = mqtt.Client()
client.connect("********", ****, 30)
client.on_connect = on_connect
client.on_message = on_message
client.loop_forever()
Any ideas?
The problem is most likely because the second set of code is creating a message bigger than will fit in a single TCP packet.
This is a problem because you are not running the client network loop so the client.publish command can only send a single packet, the rest of the message would have been sent by the network loop, but even if it was running you are calling disconnect immediately after the publish call.
The client is not meant to be spun up for a single message like that, it is meant to be started and then left running with you just calling the publish method when you want to send a message. If you don't want to do that or can't for some reason there is a specific helper class in the paho python package that will do all the heavy lifting of starting the client, sending the message and then tearing everything down nicely. The docs for the single publish are here.
import paho.mqtt.publish as publish
publish.single("paho/test/single", "payload", hostname="mqtt.eclipse.org")
I am using azure event hub python SDK to send to and receive messages from event hub following this link.https://github.com/Azure/azure-event-hubs-python/tree/develop. I can successfully send and receive messages. But how do i parse the messages and retrieve the data from the event data object. Please find the code below.
import os
import sys
#import logging
from azure.eventhub import EventHubClient, Receiver, Offset
ADDRESS = 'sb://####.servicebus.windows.net/#####'
USER = '##########'
KEY = '##################################'
CONSUMER_GROUP = "$default"
OFFSET = Offset("-1")
PARTITION = "1"
total = 0
last_sn = -1
last_offset = "-1"
try:
if not ADDRESS:
raise ValueError("No EventHubs URL supplied.")
client = EventHubClient(ADDRESS, debug=False, username=USER, password=KEY)
receiver = client.add_receiver(CONSUMER_GROUP, PARTITION, prefetch=5000,
offset=OFFSET)
client.run()
try:
batched_events = receiver.receive(timeout=20)
except:
raise
finally:
client.stop()
for event_data in batched_events:
last_offset = event_data.offset.value
last_sn = event_data.sequence_number
total += 1
print("Partition {}, Received {}, sn={} offset={}".format(
PARTITION,
total,
last_sn,
last_offset))
except KeyboardInterrupt:
pass
if i try to view the event_data received i can see the below message.
event_data
<azure.eventhub.common.EventData at 0xd4f1358>
event_data.message
<uamqp.message.Message at 0xd4f1240>
Any help on the above on how to parse this message to extract the data
As of 1.1.0, there are new utility methods to extract the actual data of the message:
body_as_str
body_as_json
So, what used to be
import json
event_obj = json.loads(next(event_data.body).decode('UTF-8'))
Is now:
event_obj = event_data.body_as_json()
For people using the Event Hub version 5.2.0 -- latest as of today (GitHub, Reference Docs), it's the same as the 1.1.0 version, i.e. use body_as_str() or body_as_json(). But the client has changed -- there's an EventHubProducerClient and an EventHubConsumerClient in the new version. To print the body of an event received:
from azure.eventhub import EventHubConsumerClient
connection_str = '<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>'
consumer_group = '<< CONSUMER GROUP >>'
eventhub_name = '<< NAME OF THE EVENT HUB >>'
client = EventHubConsumerClient.from_connection_string(
connection_str, consumer_group, eventhub_name=eventhub_name
)
def on_event_batch(partition_context, events):
partition_context.update_checkpoint()
for e in events:
print(e.body_as_str())
with client:
client.receive_batch(
on_event_batch=on_event_batch,
starting_position="-1", # "-1" is from the beginning of the partition.
)