How to use a MQTT message in the rules?

Hello,

I’m just starting my first openhab project and took the demo as my template.
I added the mqtt messaging after every switch in order to be able to communicate with an arduino board and vice versa but I would like to get rid of it because it’s a fixed solution and I would like to have it more flexible.
I was wondering if it should be possible to use the incoming messages from the MQTT broker in a rule in order to split the message and dynamically update the corresponding switch from there?
I searched for hours on the internet but can’t find any way to catch the mqtt topic/message in the rules. Is there a way?

Many thanks in advance for your input!

Typically, you would want to define an item that is bound to the MQTT binding like:

String mqttMessage { mqtt="<[broker:/topic:state:default]" }

and then have a rule that is triggered when its value changes:

rule MqttMessageChanged
when
  Item mqttMessage changed
then
  // do stuff with mqttMessage.state, like String.split or anything
  // Xtend allows all manner of string manipulation, like Java
end

Here are some examples of string parsing in Xtend.

Hi watou,

Apologies, I’m coming to this topic a little late, however, I wanted to ask if you could elaborate a little further on the reply you gave Thierry?

I am also new to openHAB and am not that familiar with Java (though have been learning a lot of late) and wanted to achieve something similar, in so far as I wanted to generically extract the payload from an incoming MQTT topic, which I will later use to form an extensible solution.
I have had no trouble binding to incoming floats using the many examples on updating temperatures, humidity etc. even states (with mapping) and ints are fine.
However I am at a loss as to how to extract freeform text in the MQTT payload within a rule.
In this first instance I want to get hold of a 5 byte string representing a logging period in ‘HH:MM’ 24hrs format ie 00…24 for HH, and use this to update the openHAB UI in overall minutes. In effect at start up ‘System started’ I am pinging some remote IoT devices to read a selection of current values such that I can synchronise the UI controls.

I’ve listed out below as far as I’ve got (though suspect some of the rule may be wrong which I’ll correct later), the area I’m have issues with is the line;

var Date dt = formatter.parse(EthernetLoggingPeriodUpdateConfirm.state)

You mention in your reply String.split can be used. I checked out the Xtend Movies example but am still none the wiser.

I would appreciate any help or pointers you could give me.

I copied the code below, apologies for flattened format.

Regards

Steve

.sitemap
Setpoint item=EthernetSetLoggingPeriod minValue=10 maxValue=1440 step=10

.items
Number EthernetSetLoggingPeriod “Logging Period [%d min]” (GC_Config,ConfigGroup,ConfigGroupLoggingPeriodSet)
String EthernetLoggingPeriodUpdateConfirm “[%s]” (GC_Config,ConfigGroup,ConfigGroupLoggingPeriodSet) {mqtt="<[mosquitto:/EthernetDevice/GetLoggingPeriodConfirm:state:default]"}

.rules
rule "Get Current Logging Period"
when
Item EthernetLoggingPeriodUpdateConfirm changed
then
var int hours
var int minutes
var DateFormat formatter = new SimpleDateFormat(“HH:mm”)
var Date dt = formatter.parse(EthernetLoggingPeriodUpdateConfirm.state)
var Calendar cal = Calendar.getInstance()
cal.setTime(dt)
hours = cal.get(Calendar.HOUR)
minutes = cal.get(Calendar.MINUTE)

logDebug(“stevequinnhousehold”, “GetLoggingPeriod ==>”+String::format("%02d", hours)+":"+String::format("%02d", minutes))
EthernetSetLoggingPeriod.sendCommand( (hours * 60) + minutes)
end

You could do this particular job a bit more simply, by using a JavaScript transform. So you would have a single item:

Number EthernetSetLoggingPeriod "Logging Period [%d min]" (GC_Config,ConfigGroup,ConfigGroupLoggingPeriodSet) { mqtt="<[mosquitto:/EthernetDevice/GetLoggingPeriodConfirm:state:JS(hm2m.js)]"}

transform/hm2m.js:

var hm = input.split(":").map(Number);
result = hm[0] * 60 + hm[1];

You could also add a , >[yourbroker:yourtopic:command:*:default] clause to the MQTT binding config string so your setpoint would publish the value to yourtopic on yourbroker.

Hi watou,

Thank you for the quick reply.

I made changes to the .items and .rules then touched hm2m.js in transform (ls - li below)

-rw-r–r-- 1 root root 66 Jun 20 19:00 hm2m.js

I added the following in nano, then saved and exited.

var hm = input.split(":").map(Number);
return hm[0] * 60 + hm[1];

I forced an update by saving the .rules in openHAB Designer. Fragment of tailed log below;
It looks like a beautifully eloquent solution but I find myself out of my depth trying to figure out the issue. Do you think I am missing an addon?

Regards

Steve

2016-06-20 19:01:31.789 [INFO ] [c.internal.ModelRepositoryImpl] - Refreshing model 'stevequinnhousehold.rules’
2016-06-20 19:01:34.221 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/Humd1Status’ : 53.80
2016-06-20 19:01:34.244 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/HeatInd1Status’ : 24.41
2016-06-20 19:01:34.918 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/Barometric1Status’ : 999.31
2016-06-20 19:01:35.563 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Message with id 883 delivered.
2016-06-20 19:01:35.570 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 883 to topic '/WiFiDevice/Button1Status’
2016-06-20 19:01:35.635 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/WiFiDevice/Button1Command’ : Pressed
2016-06-20 19:01:36.293 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Message with id 884 delivered.
2016-06-20 19:01:36.309 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 884 to topic '/EthernetDevice/Button1Status’
2016-06-20 19:01:36.328 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/Button1Command’ : Released
2016-06-20 19:01:37.057 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Message with id 885 delivered.
2016-06-20 19:01:37.064 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 885 to topic '/EthernetDevice/GetLoggingStatus’
2016-06-20 19:01:37.092 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/GetLoggingStatusConfirm’ : 1
2016-06-20 19:01:37.938 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Message with id 886 delivered.
2016-06-20 19:01:37.945 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 886 to topic '/EthernetDevice/GetLoggingPeriodStatus’
2016-06-20 19:01:38.004 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/GetLoggingPeriodConfirm’ : 00:30
2016-06-20 19:01:49.728 [ERROR] [.o.b.m.i.MqttMessageSubscriber] - Error processing MQTT message.
org.openhab.core.transform.TransformationException: An error occured while executing script.
at org.openhab.core.transform.internal.service.JavaScriptTransformationService.transform(JavaScriptTransformationService.java:86) ~[na:na]
at org.openhab.binding.mqtt.internal.MqttMessageSubscriber.processMessage(MqttMessageSubscriber.java:150) ~[na:na]
at org.openhab.io.transport.mqtt.internal.MqttBrokerConnection.messageArrived(MqttBrokerConnection.java:570) [org.openhab.io.transport.mqtt_1.8.3.jar:na]
at org.eclipse.paho.client.mqttv3.internal.CommsCallback.handleMessage(CommsCallback.java:336) [mqtt-client-0.4.0.jar:na]
at org.eclipse.paho.client.mqttv3.internal.CommsCallback.run(CommsCallback.java:148) [mqtt-client-0.4.0.jar:na]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_65]
Caused by: javax.script.ScriptException: :2:0 Invalid return statement
return hm[0] * 60 + hm[1];
^ in at line number 2 at column number 0
at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:467) ~[nashorn.jar:na]
at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:534) ~[nashorn.jar:na]
at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:521) ~[nashorn.jar:na]
at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399) ~[nashorn.jar:na]
at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:150) ~[nashorn.jar:na]
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249) ~[na:1.8.0_65]
at org.openhab.core.transform.internal.service.JavaScriptTransformationService.transform(JavaScriptTransformationService.java:84) ~[na:na]
… 5 common frames omitted
Caused by: jdk.nashorn.internal.runtime.ParserException: :2:0 Invalid return statement
return hm[0] * 60 + hm[1];
^
at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:292) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:277) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.Parser.returnStatement(Parser.java:1547) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.Parser.statement(Parser.java:910) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.Parser.sourceElements(Parser.java:775) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.Parser.program(Parser.java:711) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.Parser.parse(Parser.java:284) ~[nashorn.jar:na]
at jdk.nashorn.internal.parser.Parser.parse(Parser.java:250) ~[nashorn.jar:na]
at jdk.nashorn.internal.runtime.Context.compile(Context.java:1281) ~[nashorn.jar:na]
at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:1248) ~[nashorn.jar:na]
at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:629) ~[nashorn.jar:na]
at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:532) ~[nashorn.jar:na]
… 10 common frames omitted
2016-06-20 19:01:49.748 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/Humd1Status’ : 53.90
2016-06-20 19:01:49.767 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/HeatInd1Status’ : 24.41
2016-06-20 19:01:49.800 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/EthernetDevice/Barometric1Status’ : 999.33
2016-06-20 19:01:49.813 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/WiFiDevice/Temp1Status’ : 20.50
2016-06-20 19:01:49.840 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/WiFiDevice/Humd1Status’ : 71.00
2016-06-20 19:01:49.853 [TRACE] [o.i.t.m.i.MqttBrokerConnection] - Received message on topic ‘/WiFiDevice/HeatInd1Status’ : 20.46

I’ve made a small change to the second line of the JavaScript transform; please let me know if it works or not!

var hm = input.split(":").map(Number);
result = hm[0] * 60 + hm[1];

Hi watou,

I made the delta. It works! Fantastic, now if only I knew what the runic incantations do. :slight_smile:

I did observe some odd behaviour between my iPad (openHAB App), iPhone (safari) and Chrome (Win PC), but I’m happy to live with this. Safari on my mac was the same as chrome.

Thanks for all your help. I appreciate the quick follow up.

On the initial topic, would I be right in assuming the following code to be the correct way to extract the incoming string from the mqtt binding.

var String Str = new String(String::format("%s", EthernetLoggingPeriodUpdateConfirm.state.toString()))

Best Regards

Steve

1 Like

Replacing default in the item definition with JS(hm2m.js) means “instead of just trying to take the raw message value and turn it into an item state, first pass it into a transform that does something with it, and returns the transformed result.” JS means the JavaScript language, and the two lines of that file:

var hm = input.split(":").map(Number);
result = hm[0] * 60 + hm[1];

First turn your HH:mm into an array of two numbers, and the second line sets the result of the transform to be the total minutes it represents.

Every item has a state member (YourItem.state). States can be different classes (UnDefType, DecimalType, StringType, OnOffType, etc.). But every object in Java has a toString method, so you can say YourItem.state.toString in the rule language and it gives you back a Java String object. If you want to have a variable in your rule that holds the string version of the item’s state, you could just say:

var str = EthernetLoggingPeriodUpdateConfirm.state.toString

Empty parentheses in the rule language can be omitted (toString), whereas in Java you need them (toString()).

Hope this helps!

Hi watou,

Yes thanks your description has been a help. I’ve been looking at the xtend documentation to try to get a batter handle on things in general.

I really want to exploit the flexibility of OH but my lack of language knowledge is getting in the way.

I’ll keep plugging away.

Once again thanks for your help

Regards

Steve

1 Like