5

I'm using Mosquitto and the Python implementation of Paho to try to communicate a couple of programmes. I'm getting some troubles when I use the last will function. My code is this:

Suscriber:

import paho.mqtt.client as mqtt
def on_message(client, userdata, msg):
    print 'Received: ' + msg.payload

client = mqtt.Client()
client.on_message = on_message

client.connect('localhost', 1883)
client.subscribe('hello/#')

client.loop_forever()

Publisher:

import paho.mqtt.client as mqtt

client = mqtt.Client()

client.will_set('hello/will', 'Last will', 0, False)
client.connect('localhost', 1883)

client.publish('hello/world', 'Regular msg', 0, False)
client.disconnect()

The output:

Received: Last will

I should receive only the regular message because I use client.disconnect() to close the connection. If I comment the will_set line, I get the regular message. I also tried publishing both on the same topic and it doesn't work.

Goblinch
  • 517
  • 3
  • 9
  • 21

3 Answers3

13

How to set MQTT last will message properly

The Last will and testament feature requires to follow some rules.

Call client.will_set before you connect

Put call to will_set before client.connect.

client = mqtt.Client()
client.will_set("stack/clientstatus", "LOST_CONNECTION", 0, False)
client.connect(host, port, timeout)

Calling will_set after connect did not work for me.

Only last client.will_set counts

Trying to use will_set multiple times is possible, but only the last one will be effective, previous ones will be ignored.

Let client.disconnect to complete

The client.disconnect is actually request to send out the "DISCONNECT" message to the broker.

As the messages are sent asynchronously, it is possible, that your program ends sooner, then is the "DISCONNECT" message completed.

Use client.loop_*() after client.disconnect This shall allow the client to complete the process of sending the "DISCONNECT" message out.

Following combinations worked for me:

  • loop_start() -> loop_stop()
  • loop_start() -> loop_forever() (it completes after sending DISCONNECT message)

I did not experiment with other combinations.

From those option, the first one seems better to me (keeping loop_start paired with loop_stop). The second option might be simply wrong.

My working code:

import paho.mqtt.client as mqtt

host = "test.mosquitto.org"
port = 1883
timeout = 10

client = mqtt.Client()
client.will_set("stack/clientstatus", "LOST_CONNECTION", 0, False)
client.connect(host, port, timeout)

client.publish("stack/clientstatus", "ON-LINE")

client.loop_start()
for msg in ["msg1", "msg2", "msg3"]:
    client.publish("stack/message", msg)
    print msg

client.publish("stack/clientstatus", "OFF-LINE")
client.disconnect()
client.loop_stop()  # waits, until DISCONNECT message is sent out
print "Now I am done."

Not reliable methods (sleep, adding on_disconnect)

Adding time.sleep might help, but in other situation (not reliable connection) could fail.

Adding client.on_disconnect = on_disconnect did not help in my case as it seems, this callback is processed asynchronously and does not block the client.disconnect call.

Jan Vlcinsky
  • 42,725
  • 12
  • 101
  • 98
  • (20160730) Just a supplement, the [source code](https://github.com/eclipse/paho.mqtt.python/blob/master/src/paho/mqtt/client.py) of `will_set()` said `This must be called before connect() to have any effect.` that is not mentioned in [the document](https://pypi.python.org/pypi/paho-mqtt/1.1#option-functions). – Cychih Jul 30 '16 at 09:45
3

disconnect() has to send a DISCONNECT command to the broker, this is how the broker knows not to send the will. If you just call disconnect() then there is no guarantee this has happened. You need to call loop*() to process outgoing network traffic. You can be sure that the DISCONNECT message has sent by checking for the on_disconnect() callback being called. You could also call loop_forever() after disconnect() which would work the same.

import paho.mqtt.client as mqtt

client = mqtt.Client()

client.will_set('hello/will', 'Last will', 0, False)
client.connect('localhost', 1883)

client.publish('hello/world', 'Regular msg', 0, False)
client.disconnect()
client.loop_forever()

Or use paho.mqtt.publish.single() as @hardillb suggests.

ralight
  • 11,033
  • 3
  • 49
  • 59
  • I understand that but your code doesn't work for me (I keep seen only the will message). Using publish.single instead of publish (and loo_forever at the end) I can see both messages, but I shouldn't see the will one. – Goblinch Feb 23 '15 at 08:10
  • It works when I use on_disconnect to print something, even if I don't use the loop after disconnecting. It also works when I do the loop inside the on_disconnect – Goblinch Feb 23 '15 at 08:18
2

You can try using the single method to publish just one message like this:

import paho.mqtt.publish as publish

publish.single('hello/world', 'Regular msg', 0, False, 'localhost' , 1883, 'publisher', 10, {'topic': 'hello/will', 'payload': 'Will msg', 'qos': 0, 'retain': False})

https://pypi.python.org/pypi/paho-mqtt#single

I would guess the problem is that you are disconnecting before the publish has actually completed which may be why you are seeing the will message.

EDIT - When I run your code with mosquitto_sub -v -t 'hello/#' I see both the normal message and the will being delivered.

EDIT2 -

This works fine for me:

import paho.mqtt.client as mqtt

client = mqtt.Client()

client.will_set('hello/will', 'Last will', 0, False)
client.connect('localhost', 1883)

client.publish('hello/world', 'Regular msg', 0, False)
client.loop();
client.disconnect()
client.loop();
hardillb
  • 54,545
  • 11
  • 67
  • 105
  • Yo have a point there. I tried using a sleep and now I see both messages but I only should see the regular one because I use `client.disconnect()`. I don't want to receive the will message in this case – Goblinch Feb 20 '15 at 07:16
  • Also try adding client.loop() between the publish and discontent. Possibly after the disconnect as well. – hardillb Feb 20 '15 at 08:11
  • I don't understand the loop function but I tried as you say and it doesn't work. I need to disconnect without sending the Will message – Goblinch Feb 20 '15 at 08:43
  • This one did not work for me until I used the timeout parameter of the connect() method! E.g. client.connect(host, port, 30) worked. – Stefan Wegener Nov 23 '17 at 08:10