Send custom MQTT messages with rules

It`s a general question so the most system informations are unnecessary. What is more relevant is that we are still using the MQTT 1.14.0 binding and have not converted. However, not for the theoretical approach and the question of whether this is possible at all.

I use multiple Raspberry Pi’s with OpenHABian. One “server” and multiple “proxies”. The following is the configuration:

$OPENHAB_SERVER/$OPENHAB_PATH/services/mqtt.cfg

#
# Define your MQTT broker connections here for use in the MQTT Binding or MQTT
# Persistence bundles. Replace <broker> with an ID you choose.
#

# URL to the MQTT broker, e.g. tcp://localhost:1883 or ssl://localhost:8883
MQTTBroker.url=tcp://localhost:1883

# Optional. Client id (max 23 chars) to use when connecting to the broker.
# If not provided a random default is generated.
#<broker>.clientId=<clientId>

# Optional. True or false. If set to true, allows the use of clientId values
# up to 65535 characters long. Defaults to false.
# NOTE: clientId values longer than 23 characters may not be supported by all
# MQTT servers. Check the server documentation.
#<broker>.allowLongerClientIds=false

# Optional. User id to authenticate with the broker.
#MQTTBroker.user=openhabvm

# Optional. Password to authenticate with the broker.
#MQTTBroker.pwd=Ghj%57fD

# Optional. Set the quality of service level for sending messages to this broker.
# Possible values are 0 (Deliver at most once),1 (Deliver at least once) or 2
# (Deliver exactly once). Defaults to 0.
MQTTBroker.qos=0

# Optional. True or false. Defines if the broker should retain the messages sent to
# it. Defaults to false.
#<broker>.retain=<retain>

# Optional. True or false. Defines if messages are published asynchronously or
# synchronously. Defaults to true.
MQTTBroker.async=false

# Optional. Defines the last will and testament that is sent when this client goes offline
# Format: topic:message:qos:retained <br/>
#<broker>.lwt=<last will definition>

$OPENHAB_SERVER/$OPENHAB_PATH/services/mqtt-eventbus.cfg

# Name of the broker as it is defined in the openhab.cfg. If this property is not available, no event bus MQTT binding will be created.
broker=MQTTBroker

# When available, all status updates which occur on the openHAB event bus are published to the provided topic. The message content will
# be the status. The variable ${item} will be replaced during publishing with the item name for which the state was received.
statePublishTopic=/messages/states/${item}

# When available, all commands which occur on the openHAB event bus are published to the provided topic. The message content will be the
# command. The variable ${item} will be replaced during publishing with the item name for which the command was received.
#commandPublishTopic=

# When available, all status updates received on this topic will be posted to the openHAB event bus. The message content is assumed to be
# a string representation of the status. The topic should include the variable ${item} to indicate which part of the topic contains the
# item name which can be used for posting the received value to the event bus.
#stateSubscribeTopic=

# When available, all commands received on this topic will be posted to the openHAB event bus. The message content is assumed to be a
# string representation of the command. The topic should include the variable ${item} to indicate which part of the topic contains the
# item name which can be used for posting the received value to the event bus.
commandSubscribeTopic=/messages/commands/${item}

$OPENHAB_PROXY/$OPENHAB_PATH/services/mqtt.cfg

#
# Define your MQTT broker connections here for use in the MQTT Binding or MQTT
# Persistence bundles. Replace <broker> with an ID you choose.
#

# URL to the MQTT broker, e.g. tcp://localhost:1883 or ssl://localhost:8883
MQTTBroker.url=tcp://<BROKER_IP>:1883

# Optional. Client id (max 23 chars) to use when connecting to the broker.
# If not provided a default one is generated.
#<broker>.clientId=<clientId>

# Optional. User id to authenticate with the broker.
#MQTTBroker.user=openhabvm

# Optional. Password to authenticate with the broker.
#MQTTBroker.pwd=Ghj%57fD

# Optional. Set the quality of service level for sending messages to this broker.
# Possible values are 0 (Deliver at most once),1 (Deliver at least once) or 2
# (Deliver exactly once). Defaults to 0.
#<broker>.qos=<qos>

# Optional. True or false. Defines if the broker should retain the messages sent to
# it. Defaults to false.
#MQTTBroker.retain=false

# Optional. True or false. Defines if messages are published asynchronously or
# synchronously. Defaults to true.
MQTTBroker.async=false

# Optional. Defines the last will and testament that is sent when this client goes offline
# Format: topic:message:qos:retained <br/>
#<broker>.lwt=<last will definition>

$OPENHAB_PROXY/$OPENHAB_PATH/services/mqtt-eventbus.cfg

# Name of the broker as it is defined in the openhab.cfg. If this property is not available, no event bus MQTT binding will be created.
broker=MQTTBroker

# When available, all status updates which occur on the openHAB event bus are published to the provided topic. The message content will 
# be the status. The variable ${item} will be replaced during publishing with the item name for which the state was received.
#statePublishTopic=

# When available, all commands which occur on the openHAB event bus are published to the provided topic. The message content will be the 
# command. The variable ${item} will be replaced during publishing with the item name for which the command was received.
commandPublishTopic=/messages/commands/${item}

# When available, all status updates received on this topic will be posted to the openHAB event bus. The message content is assumed to be 
# a string representation of the status. The topic should include the variable ${item} to indicate which part of the topic contains the 
# item name which can be used for posting the received value to the event bus.
stateSubscribeTopic=/messages/states/${item}

# When available, all commands received on this topic will be posted to the openHAB event bus. The message content is assumed to be a 
# string representation of the command. The topic should include the variable ${item} to indicate which part of the topic contains the 
# item name which can be used for posting the received value to the event bus.
#commandSubscribeTopic=

So to avoid an infinity loop publish and subscribe are logically not present in the same configuration. The server sends out only the status information, receives only the commands and vice versa for the proxies. Otherwise everyone receives what he would send.

Long introduction, but is not the point…
In a Rule, I would then like to think not only about

/messages/states/${item}

but want to use my own topics, like e.g.

/robot1/states/mycustomState1
/robot1/states/mycustomState2
/robot2/states/mycustomState1

For my sake also any calculator or other Raspberry Pi’s etc.

How could I approach this? For this I would not need the MQTT broker configured in mqtt.cfg.

Logically, this is only one approach. The second approach is that I of course send my own system information from each of my Raspberry Pi’s. And especially of course from my “server” and not the “proxies”.

As example:

/path/to/topic

instead of

/messages/states/${item}

So in short, that in addition to what OpenHAB exchanges with each other, I then have access to other devices or from other devices via MQTT. About the sense and nonsense I do not want to discuss, because we want to do a few different experiments here. I just do not know how to configure and apply this correctly.

In a Rule, I sort of tried the following:

rule "mqtt test rule"
when
	Item <MyITEM> received update
then
  val mqttActions = getActions("mqtt","MQTTBroker")
  mqttActions.publishMQTT("/path/to/topic", "TESTED"
  mqttActions.publishMQTT("path/to/topic", "TESTED2"
end

Sadly it does not work. For debugging I am using MQTT.fx. MQTT.fx is using the same Broker. I can subscribe all states and commands from OpenHAB and I can send changes to OpenHAB. So it is off course possible to use this Broker for my idea. (So at this point it would be helpful that you know which MQTT-Binding version I was using.)

What I want to create is a simple Python Code. Off course it works to subscribe states and commands or if I publish with MQTT.fx but I can not subscribe with MQTT.fx or my Python Code anything from my Rule. I mean that it is clear where I have a mistake. I just honestly have no idea what I have to do here and how.

For those who are interested, I still have the simple Python code here: (strictly speaking, it is not part of my problem).

import paho.mqtt.client as mqtt
import time

def on_message(client, userdata, message):
    print("message received " ,str(message.payload.decode("utf-8")))
    print("message topic=",message.topic)
    print("message qos=",message.qos)
    print("message retain flag=",message.retain)

broker_address="192.168.0.5"
broker_port=1883

print("creating new instance")
client = mqtt.Client("P1") #create new instance
client.on_message=on_message #attach function to callback

print("connecting to broker")
client.connect(broker_address) #connect to broker
client.loop_start() #start the loop

print("Subscribing to topic","/messages/states/iKonferenz_DanaLock_Tuerschloss_Batteriestatus")
#client.subscribe("/messages/states/iKonferenz_DanaLock_Tuerschloss_Batteriestatus")
client.subscribe("/messages/states/iKonferenz_DanaLock_Tuerschloss_Batteriestatus")
client.subscribe("/path/to/topic")

time.sleep(400) # wait
client.loop_stop() #stop the loop

From OpenHAB I can receive:

...
('message received ', '96')
('message topic=', u'/messages/states/iKonferenz_DanaLock_Tuerschloss_Batteries tatus')
('message qos=', 0)
('message retain flag=', 0)
...

and from MQTT.fx I can receive:

...
('message received ', 'TEST')
('message topic=', u'/path/to/topic')
('message qos=', 0)
('message retain flag=', 0)
...

So it could be possible if the binding would support it that the configuration and the topic path inside a rule could differ.

Thanks in advance and best regards
Michael

Since you are using the old MQTT binding ( version 1) you should read This.
Note that the MQTT binding together with the MQTT Actions binding are known to “not like each other”, resulting in a non-functional Actions binding. Restarting the MQTT Actions bundle via the Karaf console did help ( I hope to have remembered it correctly).

1 Like

Thank you very much. I tried now following but it does not work:

rule "Irgendetwas rules"
when
    Item iBad_Osram_LEDStreifen_Schalter changed to ON
then
    iMultimedia_Sonos_Lautsprecher_URIspielen.sendCommand("//medialib/Audio/Morgenroutine/WhatIveDone_LinkinPark.mp3")
    iMultimedia_Sonos_Lautsprecher_Lautstaerke.sendCommand(20)
    iApplikation_Alexa_LinkinPark.sendCommand(OFF);

    publish("MQTTBroker", "path/to/topic", "tested")
    publish("MQTTBroker", "/path/to/topic", "tested2")
    //val mqttActions = getActions("mqtt","MQTTBroker")
    //mqttActions.publishMQTT("/path/to/topic", "TESTED")
    //mqttActions.publishMQTT("path/to/topic", "TESTED2")
end

We don’t need to talk about the meaning of the rule, it’s just for testing purposes. I like to listen to Linkin Park and it works. Only MQTT does not send as expected.

I have now times below the code, as I would have to do it with the binding MQTT 2.X.

Also, I am unsure if /path or path. I mean it should be without the /.

However, I cannot receive anything with MQTT.fx on any of the possible topics. Do I have to configure which broker should be used before publish, similar to MQTT 2.X?

So close try this

rule "Irgendetwas rules"
when
    Item iBad_Osram_LEDStreifen_Schalter changed to ON
then
    val mqttActions = getActions("mqtt","mqtt:broker:myMQTTBroker")
    iMultimedia_Sonos_Lautsprecher_URIspielen.sendCommand("//medialib/Audio/Morgenroutine/WhatIveDone_LinkinPark.mp3")
    iMultimedia_Sonos_Lautsprecher_Lautstaerke.sendCommand(20)
    iApplikation_Alexa_LinkinPark.sendCommand(OFF);

    mqttActions.publishMQTT("/path/to/topic", "TESTED")
    mqttActions.publishMQTT("path/to/topic", "TESTED2")
end

Well that’s not what I want to receive. We can ignore the music. It would work if I try it with:

rule "Irgendetwas rules"
when
    Item iBad_Osram_LEDStreifen_Schalter changed to ON
then
    iMultimedia_Sonos_Lautsprecher_URIspielen.sendCommand("//medialib/Audio/Morgenroutine/WhatIveDone_LinkinPark.mp3")
    iMultimedia_Sonos_Lautsprecher_Lautstaerke.sendCommand(20)
    iApplikation_Alexa_LinkinPark.sendCommand(OFF);
end

I am not using the MQTT 2.X Binding so

val mqttActions = getActions("mqtt","mqtt:broker:myMQTTBroker")
mqttActions.publishMQTT("/path/to/topic", "TESTED")

would not work. I will use the MQTT 1.14.0 Binding.

So off course I tried

publish("MQTTBroker", "path/to/topic", "tested")
publish("MQTTBroker", "/path/to/topic", "tested2")

If I would use the MQTT 2.X Binding the equivalent code should be:

val mqttActions = getActions("mqtt","mqtt:broker:MQTTBroker")
mqttActions.publishMQTT("/path/to/topic", "TESTED")

MQTT is not used in any rules but the items of my different Raspberry Pi’s will know the state and the command which is executed according to the mqtt.cfg and mqtt-eventbus.cfg. So at least I have a “master” on which I can controll over other Raspberry Pi’s each conncected device. This works because the “master” send to the slave on which a command should be executed the known state and subscribes the executed command.

What I want to achieve is to publish and subscribe messages without topics like

/messages/commands/${item}
/messages/states/${item}

So the music could be played because of the both config files on my “master” and “slaves”. But I do not want to use this topics inside my rules. I think you admittedly got the point. What I’m saying is, I can put the playing music thing before or after the other MQTT command, it ultimately makes no difference.

What is not possible is that I can create and use a completely different topic. I’ll try to explain it step by step, so that you don’t get confused because of the two MQTT versions. Since I am using the binding MQTT 1.14.0, the actually relevant code should look like this:

rule "Irgendetwas rules"
when
    Item iBad_Osram_LEDStreifen_Schalter changed to ON
then
    publish("MQTTBroker", "path/to/topic", "tested")
end

Because I thought according to this link (thanks to @opus) I have to use

publish(String brokerName, String topic, String message)

If I look inside the mqtt-eventbus.cfg I have broker=MQTTBroker. So I thought I could only add "path/to/topic", "tested" to get publish("MQTTBroker", "path/to/topic", "tested")

But sadly it does not work. So my first suggestion was If I need something like as if I would use the MQTT 2.X Binding that I have to use at first getActions before I could use publishMQTT because inside the link there I could only find publish(String brokerName, String topic, String message).

I have already explained the equivalent between the two bindings and I think I have understood that correctly. But I don’t plan to switch to the newer binding unless it wouldn’t work with the old one, which I plan to do. Otherwise I would have to change the whole system at once. “Never change a running system.”

So the real question is how do I publish to and subscribe from any x topic? I guess the problem is more with the configuration of mqtt.cfg and mqtt-eventbus.cfg. Or with my “network architecture” or how you could title the setup over multiple Raspberry Pi’s.

I am not sure but maybe following could be a solution inside the mqtt-eventbus.cfg file:

...
statePublishTopic=/messages/states/${item}
...
blablablaPublish=${something_which_would_allow_to_publish_any_in_rule_defined_topic}

Or does this program code basically work regardless of the configuration? So the problem here is really the configuration of the binding? Or would the publish command ignore this completely? The broker is running, so I should be able to access the broker. With MQTT.fx and via Mosquitto in a Python code, I also access this broker to be able to publish new topics.

In Python I only used following for accessing this Broker:

broker_address="192.168.0.5"
broker_port=1883

And this also is not the solution:

publish("192.168.0.5:1883", "path/to/topic", "10")
publish("192.168.0.5:1883", "/path/to/topic", "2")

And then the general question. Suppose I had a broker on another device, such as a robot. How would I address this? Topic is clear. But over the network I can also say, over which broker he should provide the messages. I mean MQTT.fx I do not run on the Raspberry Pi, but on my laptop. Okay, the Python script does, but it uses the public IP address and not localhost or 127.0.0.1. And the other Raspberry Pi’s also access it via 192.168.0.5 and share the same broker.

About the music, for example, I had only tested whether the rule is triggered at all and that was not the problem.

Perhaps it is clearer now. I think you have very detailed information here. And in my opinion, it should be possible. Regardless of OpenHAB, I know how to use MQTT otherwise. It fails only at this set screw and I am sure that this is either the wrong call in the rule, which seems to be correct according to the documentation, or the mqtt.cfg and mqtt-eventbus.cfg files.

So what does it do? Any messages in your openhab.log? Do you see the commands in your events.log, so that you can be sure the rule was triggered?

That depends what you want, the Action will do what you tell it.

The publish Action addon that can be used with this binding is separately installable. Make sure you have installed it.

Yes, that is it. The publish Actions relies on you having defined the named Broker in your binding mqtt.cfg file. The real binding though - NOT the eventbus binding.

So, is that working? Have you “ordinary” Items working with MQTT binding okay?

1 Like

long story short: it works. Thanks for your help. The keyword MQTT Action was good. Had it installed on the wrong device.

It works with:

publish("MQTTBroker", "path/to/topic", "10")

But not with

publish("192.168.0.5:1883", "path/to/topic", "10")

or

publish("tcp://192.168.0.5:1883", "path/to/topic", "10")

Since the mqtt.cfg contains the following information

MQTTBroker.url=tcp://192.168.0.5:1883
MQTTBroker.async=false

and in the mqtt-eventbus.cfg the following information

broker=MQTTBroker
commandPublishTopic=/messages/commands/${item}
stateSubscribeTopic=/messages/states/${item}

I strongly assume that this can probably read both information via the name of the broker as an object or at least the URL and you can not directly enter a URL. So you might have to define multiple brokers via mqtt.cfg if you want to use the broker on a robot or something else. Would make sense again only if two separate systems access one and the same system and would then use the broker there. I’m more about the learning effect and what actually means what and how exactly works.

Now I will probably have some fun trying out different ideas and let off steam. Thanks a lot.

Yes, that’s exactly how it works and how it is supposed to work.