Paho MQTT client: how to ignore messages published by myself? - python

My Paho MQTT client does the following:
Subscribe to mytopic/#
Do something
Publish to mytopic/#
Problem:
The published message in step 3 arrives at step 1. I'd like to avoid adding a sender-attribute to the payload.
Is there a proper way of ignoring self-published messages? Something like the following (pseudocode):
def on_message(self, client, userdata, message):
if client.id == message.sender_client_id: # Is there anything like the sender_client_id?
return
Any idea? Thanks!

As of the MQTT v5 spec you can tell the broker not to send your own messages back to you as part of the subscription message.
This removes the need to add the identifier so you can then choose to ignore it.
This does of course rely on both the broker and the MQTT client supporting MQTT v5

This logic should work:
Assign an id to every client
every client publish on mytopic/{id}
every client sub to mytopic/#
ignore messages where message.topic starts with mytopic/{id}

If you are using MQTT v5, you can pass the noLocal option to the paho client when subscribing. This option tells the broker not to send back your own messages.
from paho.mqtt.subscribeoptions import SubscribeOptions
...
options = SubscribeOptions(qos=1, noLocal=True)
client.subscribe('mytopic/#', options=options)

def on_message(self, client, userdata, message):
if client.id == message.sender_client_id: # Is there anything like the sender_client_id?
return
In your pseudocode, you are asking for the client's identity but this is exactly opposite to the MQTT specification. In MQTT, two different clients are unaware of each other's identity, they only communicate via the MQTT broker by subscribing to the topics.

Related

How to use both subscribe and publish at the same time in mqtt python?

I am working with a device that publishes to the topic test/123, where 123 is the name of the device. I need to subscribe to that topic (and processes received messages); in addition I also need to send a word to the same topic (test/123). The device only looks at this topic.
How can I distinguish between incoming and outgoing by content? More precisely, how to send correctly. In the on_message method you need to do this or you need to create another method, but then how to receive incoming messages there. From the incoming messages I need to get the name of the device and then work with it.
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("/test/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
imei = msg.topic.split('test/')[1]
data = msg.payload.decode()
print(imei)
print(data)
publish(imei)
def publish(imei):
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.connect("localhost",1883)
topic = '/test/'+ imei
client.publish(topic,'hello')
print('SEND')
client.disconnect()
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
client.loop_forever()
MQTT does not differentiate between clients in any way, that means if a client subscribes to a given topic it will receive ALL messages on that topic, including the ones it publishes it's self. So with your current design you will always get the message you publish in response to the first message back and this will trigger re-sending that message.
MQTT messages do NOT contain any information about who published the message unless you choose to add it to the payload, so you have no way to identify the incoming message as being the one you just published and this will cause a message loop storm.
The CORRECT solution is to not use the same topic for the 2 messages.
MQTT v5 has a flag that can be passed as part of establishing the connection
which prevents messages being returned to the client that published them. At this time it does not appear that the Paho Python library has a way to set this flag.
If you are using MQTT v3.1.1 and the mosquitto or RSMB MQTT broker then there is an undocumented option (this is not part of the MQTT spec) that can be set which will also prevent messages being returned. The following code will ONLY work with the 2 brokers I have mentioned.
import paho.mqtt.client as mqtt
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("test/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
imei = msg.topic.split('test/')[1]
data = msg.payload.decode()
print(imei)
print(data)
publish(client, imei)
def publish(client,imei):
topic = 'test/'+ imei
client.publish(topic,'hello')
print('SEND')
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.on_connect = on_connect
client.on_message = on_message
client.enable_bridge_mode()
client.connect("localhost", 1883, 60)
client.loop_forever()
p.s. do not start topics with a leading / while legal according to the spec, it will break things like shared subscriptions and adds an extra null to the start of the topic tree.

Reading subscribed MQTT messages after reconnect

I am trying to read messages on a MQTT server. In some cases, the connection is unstable and requires to reconnect. But after reconnect, I am not able to receive any message from the topic that I previously subscribed to. I am using paho's python package to handle MQTT connection. Here is some code I am using
TopicName='some/topic/name'
class Counter:
def __init__(self, mqttClient):
self.messages_recieved = 0
self.mqttClient = mqttClient
self.mqttClient.subscribe(TopicName)
self.mqttClient.on_message = self.on_message
self.mqttClient.on_disconnect = self.on_disconnect
self.mqttClient.loop_start()
def on_message(self, client, userdata, message):
self.messages_received += 1
def on_disconnect(self, client, userdata, rc):
if rc != 0:
print("Trying to reconnect")
while not self.mqttClient.is_connected():
try:
self.mqttClient.reconnect()
except OSError:
pass
If my internet goes down, I am no longer able to receive messages. I have tried to subscribe again to the topic, also I have tried to call loop_start in the on_disconnect method, neither of those worked. Any solution would be helpful. Also to point out messages are being sent, I can see them in the browser on MQTT wall
You have not shown where you are calling connect, but the usual safe pattern is to put the calls to subscribe() in the on_connect() callback attached to the client.
This means that calls to subscribe will
Always wait until the connection has completed
Get called again automatically when a reconnect had happend
Not sure what module you are using, but most will require you to re-subscribe if you disconnect. Add your subscribe() call after your .reconnect() call and you should be good to go. Also, keep in mind that at QOS level 0, any messages that the broker received while you were disconnect, your client will NOT receive...only messages while the client is subscribed will be received by your client. If messages are published with the Retain flag, you client will receive the LAST one received by the broker...even if the client previously received it.

How to get wildcard value from mqtt topic?

Let's say I've several devices each having a temperature. All messages related to device temperature are published on topics device/1/temerature, device/2/temperature, etc. . I handle all messages published on this topic with Python paho-mqtt with a callback function which uses a wildcard expression client.message_on_callback_add("device/+/temperature", ...). Is there a way to get the value of the wildcard expression, here + directly (w.o. need for parsing of msg.topic)?
I believe you are looking to extract the client id from the topic. The callback functions will have clientid as part of the callback. Pleaes check the functions in the below URL.
https://pypi.org/project/paho-mqtt/#callbacks
If you are using the default callback function on the message received, you will be able to get the client as a separate parameter.
No, the callback includes the topic the message was published to.
It is up to you to extract what ever information you need from the topic.
In this example, you can get the subscribed topic when the message arrived. You can split topic to get wildcard characters from subscribed topic.
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
client.subscribe("device/+/temperature")
client.message_callback_add("device/+/temperature", handler("device/+/temperature"))
def handler(subscribed_topic):
wildcard_expression = subscribed_topic
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload), wildcard_expression)
return on_message
client = mqtt.Client()
client.on_connect = on_connect
client.connect(<mqtt broker url>, 1883, 60)
client.loop_forever()

How do I send a response to a MQTT message?

I would like one RaspberryPi-A to send a massage to RaspberryPi-B, and RaspberryPI-B should send a massage back automatically. I know this is a beginners question, but I'm one and I have really struggled all day trying to find an anwser.
This is my client's code
import paho.mqtt.client as mqtt
MQTT_SERVER = "localhost"
MQTT_PATH = "test_channel"
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe(MQTT_PATH)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
# more callbacks, etc
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_SERVER, 1883, 60)
client.loop_forever()
And this is my publisher’s code
import paho.mqtt.publish as publish
MQTT_SERVER = "192.168.1.5"
MQTT_PATH = "test_channel"
publish.single(MQTT_PATH, "Hello World!", hostname=MQTT_SERVER)
How should i write the code differently so my client would return the massage for example: "Hello back"?
There are 2 kinds of messaging models:
Point to Point (one to one) - a message is sent from one application to another application via a queue. There can be more than 1 consuming (receiver) applications but only one of them will receive the message.
Publish / Subscribe - is where a message is published to a topic and multiple consumers (subscribers) will each receive a copy of the message. There can be 1 or more applications publishing messages to the same topic and 1 or more applications consuming (receiving) the messages.
MQTT is built on the Publish / Subscribe messaging model.
Your description sounds like you want Point to Point messaging. Yes, you can bend MQTT to act like Point to Point but be aware that if you have multiple applications publishing messages to the same topic, you may get confused.
MQTT_PATH = "test_channel"
publish.single(MQTT_PATH, "Hello World!", hostname=MQTT_SERVER)
There is no such thing as a "channel" in MQTT. Your code is publishing a message to the topic called: test_channel.
It is better to use a little hierarchy in your topic names.
i.e.
pivk95/food/burgers
pivk95/food/fries
pivk95/food/pizza
pivk95/food/burritos
pivk95/drink/shakes
pivk95/drink/soft_drink
Just remember that any number of applications can publish messages to a given topic and any number of applications can subscribe to a given topic and receive copies of the messages.

Is RabbitMQ capable of passing messages to specific clients? Or must I perform those checks client-side?

I have my software running on a bunch of clients around my network. I've been playing around with RabbitMQ as a solution for passing messages between each client.
My test code is this:
#!/usr/bin/python2
import pika
import time
connection = pika.AsyncoreConnection(pika.ConnectionParameters(
'localhost'))
channel = connection.channel()
def callback(ch, method, properties, body):
# send messages back on certain events
if body == '5':
channel.basic_publish(exchange='',
routing_key='test',
body='works')
print body
channel.queue_declare(queue='test')
channel.basic_consume(callback, queue='test', no_ack=True)
for i in range(0, 8):
channel.basic_publish(exchange='',
routing_key='test',
body='{}'.format(i))
time.sleep(0.5)
channel.close()
Picture this as kind of a 'chat program'. Each client will need to constantly listen for messages. At times, the client will need to send messages back to the server.
This code works, but I've ran into an issue. When the code below sends out the message works, it then retreives that again from the RabbitMQ queue. Is there a way to tell have my client, a producer and a consumer, not receive the message it just sent?
I can't see this functionality built into RabbitMQ so I figured I'd send messages in the form of:
body='{"client_id" : 1, "message" : "this is the message"}'
Then I can parse that string and check the client_id. The client can then ignore all messagess not destined to it.
Is there a better way? Should I look for an alternative to RabbitMQ?
You can have as many queue in RabbitMQ. Why not have a queue for messages to the server as well as a queue for each client?

Categories