MQTT Toggle -> JS Transform to Switch Item

I have a zigbee2mqtt MQTT item that provides via JSON a “toggle” message if the Tradfri Remote is pressed. I now would like to toggle a switch item accordingly. Ive checked the forum but all toggle topics are related to rule-based toggling. In my case i need to use JS in order to extract the JSON part - so i would like to do this inside of the JS function. Is there a smart way to pack this into ths JScript? btw using still MQTT1 binding.

item file:

> Switch TradfriRemote01 "TradfriRemote#01" {mqtt="<[mosquitto:/z2m/Tradfri_Remote01:state:JS(getZigbee2Remote.js)]"}

JS function (taken from another JS function that was turning on of ON comes in or OFF if off comes in, i know its not yet working as it should, but any help is appreciated to make it toggle. So here when pressing i only receive a “toggle” message. nothing else…
Anyhow the quesiton if this could be done more simple (mapping???) to save the JS-File?

(function(x){
    var result = "";
    var json = JSON.parse(x)
    if (json.action == 'toggle')
    {
        result="ON";
    }
    if (json.action == 'off')
    {
        result="OFF";
    }
    return result;
})(input)

No. To implement a toggle you need to know the current state of the switch which is not available in the JS transform. You must use a Rule for this.

ah OK, i feared that could be “the” problem - as i was not sure how to bring the state to JS. Thanks a lot.

@rlkoshak i’m not an expert at MQTT2 - but can you tell me if the way to extract the JSON element vis JS is the best i can do - as I need to do this for every button of e.g. Tradfri remote control seperately.
Is there a more convenient way like map? or directly in the item-config lines (which of course would become quite long this way).
(i mean is there any simpler way via MQTT1 or does MQTT2 have some tools to handle this differently?)

Cheers/Thanks a lot Norbert

1 Like

If it’s JSON formatted you can use JSONPATH instead of JS transform. That would be more convenient since it doesn’t require any external files in the transform folder. See https://www.openhab.org/addons/transformations/jsonpath/

This particular issue would be the same for MQTT 1 and MQTT 2.

Thanks a lot Rich,
i already checked this website but for me it lacks good examples that would help me to understand, also tried the community site but could not find anything similar right now.

Yes its JSON…the format from zigbee2mqtt for this device looks something like:
{“battery”:“100.00”,“voltage”:3025,“action”:“on”}.

I mapped this to ON and OFF via js script but how would i do this directly?
(here its the same wording, but what if the wording would be different so i need a mapping)
Switch TradfriRemote01 "TradfriRemote#01" {mqtt="<[mosquitto:/z2m/Tradfri_Remote01:state:JS(getZigbee2Remote.js)]"}

another small question, what does the * stand for in some lines i have seen in the forum?
mqtt="<[broker:topic:state:*:J…

To select the “battery” value you use JSONPATH:$.battery. To select the “action” value use JSONPATH:$action. That’s all there is to it. It’s not hard. You can see https://www.baeldung.com/guide-to-jayway-jsonpath if you need more about JSONPATH.

Switch TradfriRemote01 "TradfriRemote#01" {mqtt="<[mosquitto:/z2m/Tradfri_Remote01:state:JSONPATH($.action)]"}

But that might not work with MQTT1 because the “on” is all lower case. You may need to use a regex filter. See https://www.openhab.org/addons/bindings/mqtt1/#mqtt-binding for that and for the answer to your question about the *.

Even better would be to switch to the MQTT 2.x binding which will be much easier to set up since it has built in the ability to map “on” to “ON”.

Thanks, will give it a try.

What i could imagne is that REGEX may transform on to CAPS letters, but would REGEX at all help if i receive in a JSON path element “action” for example “up, down, right, left and toggle”…or is it best to take this into a string item and handle it in a rule…so if the string is “right” do the following in a rule,…i guess so.

You can only have one transform at a time in MQTT1 (another advantage of MQTT2 as you can chain transformations there).

Even if you move over to a Rule to process the JSON, you should use the JSONPATH transform in the Rule to extract the values. I can’t answer what is easier. If you need to handle actions beyond ON and OFF for a Switch like this you probably need to use a rule.

Thanks Rich, for the remote with multiple string values (more than 5 different buttons that end up as a string in an action JSON element) i started to forward this into a rule.

One more question, one of the Tradfri on off buttons often sends two MQTT messages in a row…
I find it stupid as these (battery level) messages would make sense if they do not occur when the button is pressed, but its just like this…so my JS does only use the first one, cannot find the “click” part and ignores the second message that would have the button pressed state inside…The script is posted above…
Any idea what I could do, becauese its just the way how this Tradfri On Off Switch seems to be implemented…

The result is that often you press the button and Openhab does not realize the state change…as it only acts on the first message that has this valueable content not inside…

15.10.2019 18:45:51 /z2m/Tradfri_Switch01 {“battery”:60,“linkquality”:0}
15.10.2019 18:45:s51 /z2m/Tradfri_Switch01 {“battery”:60,“linkquality”:0,“click”:“off”}

I’m not sure what you are describing is a correct explanation for what is going on. Each message is received and processed individually. The fact that it received one message doesn’t block OH from processing any subsequent messages. It might be possible that the broker is dropping the message because everything is too close together. If that’s the case, then you can probably correct the problem by setting the QOS on the sender to 1 or 2, both of which ensure that the message is delivered at least once.

hm, at least its logg’ed in Mosquitto - which i guess does not say anything about if its dropped or not in the end.

17.10.2019 21:45:25 /z2m/Tradfri_Switch01 {“battery”:87,“linkquality”:0}
17.10.2019 21:45:25 /z2m/Tradfri_Switch01 {“battery”:87,“linkquality”:0,“click”:“off”}

What i see now in the Openhab logs is that for the first message i receive an error message…as my script does not cover a non existing element that the script is searching for…
Any idea how i should cover this in the Jscript?
(but still its strange to me - if the first message does give openhab some bad times, why does it not process the second message correctly ? - as in the double message situation no update happens in the GUI)

2019-10-17 21:46:45.382 [WARN ] [.mqtt.internal.MqttMessageSubscriber] - Error processing MQTT message.
org.openhab.core.transform.TransformationException: Invalid path '$.click' in '{"battery":60,"linkquality":0}'
        at org.openhab.core.transform.TransformationHelper$TransformationServiceDelegate.transform(TransformationHelper.java:71) ~[209:org.openhab.core.compat1x:2.5.0.M3]
        at org.openhab.binding.mqtt.internal.MqttMessageSubscriber.processMessage(MqttMessageSubscriber.java:137) [230:org.openhab.binding.mqtt:1.14.0.M3]
        at org.openhab.io.transport.mqtt.internal.MqttBrokerConnection.messageArrived(MqttBrokerConnection.java:574) [242:org.openhab.io.transport.mqtt:1.14.0.M3]
        at org.eclipse.paho.client.mqttv3.internal.CommsCallback.deliverMessage(CommsCallback.java:513) [283:org.eclipse.paho.client.mqttv3:1.2.1]
        at org.eclipse.paho.client.mqttv3.internal.CommsCallback.handleMessage(CommsCallback.java:416) [283:org.eclipse.paho.client.mqttv3:1.2.1]
        at org.eclipse.paho.client.mqttv3.internal.CommsCallback.run(CommsCallback.java:213) [283:org.eclipse.paho.client.mqttv3:1.2.1]
        at java.lang.Thread.run(Thread.java:745) [?:?]

You cannot rely on the GUI to show you what is going on in real time, especially when debugging errors. You need to use events.log to see the Item states changing and openhab.log to look for error messages and the like.

If you are processing the full JSON string in a Rule so you can handle things like TOGGLE, what is the JS transform doing? And that warning is coming from the JSONPATH transform, not the JS transform.

There shouldn’t be any transform. You can’t implement TOGGLE in a JS transform because you need to know the current state of the Item which is not available in a transform.

The message is a warning and the only way you can suppress it is to change the logging config, or since you are using the old deprecated MQTT1 binding, use the REGEX filter to filter out those messages that don’t contain “click”. See the MQTT1 docs (link above) for details.

Based on the very limited evidence presented, probably because it never received the message in the first place, or you have an error in your code somewhere.

Hello,

i had the same problem with an ikea tradfi switch.
Here is my solution:

My Thing:

Thing mqtt:topic:tradfriswitch1 "IKEA Schalter 1" (mqtt:broker:mqttbroker) @ "Tradfri"{
    Channels:
        Type switch : state  "IKEA Schalter 1" []
        Type string : action "IKEA Schalter 1 action" [ stateTopic="zigbee2mqtt/TradfriSchalter1", transformationPattern="JSONPATH:$.action"]
        Type number : linkquality "IKEA Schalter 1 LinkQuality" [ stateTopic="zigbee2mqtt/TradfriSchalter1",  transformationPattern="JSONPATH:$.linkquality" ]
        Type number : battery "IKEA Schalter 1 Batterie" [ stateTopic="zigbee2mqtt/TradfriSchalter1",  transformationPattern="JSONPATH:$.battery" ]
}

My Item:

Switch Wohnzimmer_Schalter_Switch      "Schalter 1"                    <switch>             (Wohnzimmer) ["Control"] {channel="mqtt:topic:tradfriswitch1:state"}
String Wohnzimmer_Schalter_Action      "Schalter 1 Action "                                 (Wohnzimmer)             { channel="mqtt:topic:tradfriswitch1:action"}
Number Wohnzimmer_Schalter_Link        "Schalter 1 LinkQuality [%d]"   <qualityofservice>   (Wohnzimmer)             { channel="mqtt:topic:tradfriswitch1:linkquality"}
Number Wohnzimmer_Schalter_Batterie    "Schalter 1 Batterie [%d]"      <batterylevel>       (Wohnzimmer)             { channel="mqtt:topic:tradfriswitch1:battery"}

and the rule for toggeling

rule "Tradfri Schalter Toggeln"
when
   Item Wohnzimmer_Schalter_Action received update
then
    if(Wohnzimmer_Schalter_Action.state == "toggle"){
        if(((Wohnzimmer_Schalter_Switch.state == NULL) || (Wohnzimmer_Schalter_Switch.state == OFF))){
            Wohnzimmer_Schalter_Switch.sendCommand(ON)
        }else{
            Wohnzimmer_Schalter_Switch.sendCommand(OFF)
        }
    }
end

And now you can add some rules to control something using the switch. The solution with an additional switch channel enables my things to be controlled via app or IKEA switch.
It works for me and i hope it will help somebody :slightly_smiling_face: