Mqtt monitoring / syncing states, need bit of help

yes, it have to be fully symetrical.
Will try this one, thanks!

yeah, I still indeed find my programming languages more friendly as I clearly can see what my code is doing, nodered is bit different approach, but for applications like this I really like it.

Like anything else, Node-RED is a tool. You don't reach for a hammer when you need to unscrew something.

Node-RED is useful for prototyping. More than that, it lets you build quickly so that you can let it do all the boilerplate you've have to write if doing things purely by language. Letting you focus your programming skills on the more interesting and involved bits of logic.

You can still use JavaScript and I will often write function nodes because I find that quicker and easier to write than messing with loads of nodes. The nodes do the setup and generally the basic comms - which are really involved when doing it by code - the functions nodes do the core "business logic".

Best of both worlds! (Assuming you are happy using JavaScript!!)

1 Like

Just a thought, in order that it works immediately on node-red restart the two stat topics should be Retained topics in MQTT.

all my switches are retaining their states, so it should.
will report back when in production :wink:

edit: it works indeed, let's see if there will be some weird rapid switching after some time of using this

@Colin
how logic in Follow node would look like for three topics?

also, is this flow usable for one topic solution?
one topic means two or more switches using same mqtt topic (there is still need to invoke cmnd, as they are broadcasting only /state/ when pressed physically)
Or there is better to use something else?

Thanks!

You said you were a confident programmer so I expect you will be able to understand the existing logic in the function node. Javascript is not much different to C or other similar languages. Put a debug node showing the output of the Join node so you can see what comes out of there (and look at the Info tab for that node for an explanation). Also look in the node red docs at the pages on writing functions and on working with messages. First make sure you understand how the flow I posted works, then if you can't see how to extend it to one or three inputs come back and ask.

well I indeed can understand how that works but in your follow there is comparison between two topic and else

in case three topics logic would be 1 == 2 and 2 == 3 and 1 == 3
which sound bit complicated and full off if - elses, so I was asking basically if that's the way or if its there better one
:wink:

There may well be ways of refactoring the code for the case with three inputs. I will leave that as an exercise for the reader.

:wink:
I've done this below
This code works ok, but for some reason, it triggers cmnd 4times even there is function with 3 ouputs and rbe (its working same without rbe)

[{"id":"18be5b2c.d9e275","type":"mqtt in","z":"69c9331c.66234c","name":"Entrance","topic":"home/entrance/light/stat/POWER2","qos":"0","datatype":"utf8","broker":"e1def315.bf1db","x":420,"y":280,"wires":[["af3a41cb.e2cb3"]]},{"id":"25011ea9.c79b22","type":"mqtt in","z":"69c9331c.66234c","name":"Floor (slave)","topic":"home/hallfloor/light/stat/POWER3","qos":"0","datatype":"utf8","broker":"e1def315.bf1db","x":410,"y":360,"wires":[["af3a41cb.e2cb3"]]},{"id":"e48d33b2.80449","type":"mqtt in","z":"69c9331c.66234c","name":"Entrance (slaves)","topic":"home/entrance/lightslave/stat/POWER","qos":"0","datatype":"utf8","broker":"e1def315.bf1db","x":400,"y":320,"wires":[["af3a41cb.e2cb3"]]},{"id":"d8b23432.a44168","type":"inject","z":"69c9331c.66234c","name":"","topic":"home/entrance/light/stat/POWER2","payload":"ON","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":280,"wires":[["af3a41cb.e2cb3"]]},{"id":"4f8a472.8a830b8","type":"inject","z":"69c9331c.66234c","name":"","topic":"home/entrance/light/stat/POWER2","payload":"OFF","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":230,"y":280,"wires":[["af3a41cb.e2cb3"]]},{"id":"593a370b.885898","type":"inject","z":"69c9331c.66234c","name":"","topic":"home/entrance/lightslave/stat/POWER","payload":"ON","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":320,"wires":[["af3a41cb.e2cb3"]]},{"id":"3e35acc8.afece4","type":"inject","z":"69c9331c.66234c","name":"","topic":"home/entrance/lightslave/stat/POWER","payload":"OFF","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":230,"y":320,"wires":[["af3a41cb.e2cb3"]]},{"id":"3064d4af.a694fc","type":"inject","z":"69c9331c.66234c","name":"","topic":"home/hallfloor/light/stat/POWER3","payload":"ON","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":360,"wires":[["af3a41cb.e2cb3"]]},{"id":"81b25b20.449648","type":"inject","z":"69c9331c.66234c","name":"","topic":"home/hallfloor/light/stat/POWER3","payload":"OFF","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":230,"y":360,"wires":[["af3a41cb.e2cb3"]]},{"id":"a33e1a26.2e5c98","type":"mqtt out","z":"69c9331c.66234c","name":"CMND","topic":"","qos":"","retain":"","broker":"e1def315.bf1db","x":930,"y":520,"wires":[]},{"id":"b269af22.19953","type":"function","z":"69c9331c.66234c","name":"Follow","func":"function areEqual(){\n   var len = arguments.length;\n   for (var i = 1; i< len; i++){\n      if (arguments[i] === null || arguments[i] !== arguments[i-1])\n         return false;\n   }\n   return true;\n}\n\nvar trigger = msg.topic;\nvar state   = msg.payload[msg.topic];\n\nvar ta = \"home/entrance/light/stat/POWER2\";\nvar tb = \"home/entrance/lightslave/stat/POWER\";\nvar tc = \"home/hallfloor/light/stat/POWER3\";\n\nvar a = msg.payload[ta];\nvar b = msg.payload[tb];\nvar c = msg.payload[tc];\n\nif( areEqual(a,b,c) )\n{\n    return false;\n}else{\n    ra = (trigger === ta) ? null : { topic: \"home/entrance/light/cmnd/POWER2\",      payload: state };\n    rb = (trigger === tb) ? null : { topic: \"home/entrance/lightslave/cmnd/POWER\",  payload: state };\n    rc = (trigger === tc) ? null : { topic: \"home/hallfloor/light/cmnd/POWER3\",     payload: state };\n}    \n\nreturn [ra,rb,rc];\n","outputs":3,"noerr":0,"x":710,"y":420,"wires":[["1b00dfeb.39b9c","a33e1a26.2e5c98"],["1b00dfeb.39b9c","a33e1a26.2e5c98"],["1b00dfeb.39b9c","a33e1a26.2e5c98"]]},{"id":"1b00dfeb.39b9c","type":"debug","z":"69c9331c.66234c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":910,"y":300,"wires":[]},{"id":"af3a41cb.e2cb3","type":"rbe","z":"69c9331c.66234c","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":570,"y":320,"wires":[["78ad2b0f.f85654"]]},{"id":"78ad2b0f.f85654","type":"join","z":"69c9331c.66234c","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":690,"y":320,"wires":[["b269af22.19953"]]},{"id":"e1def315.bf1db","type":"mqtt-broker","z":"","name":"MQTT","broker":"10.10.1.10","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]


10/25/2019, 9:55:25 PMnode: 4189398f.c516f8
home/entrance/lightslave/cmnd/POWER : msg : Object
{ topic: "home/entrance/lightslave/cmnd/…", payload: "ON", _msgid: "7bfbb908.70dea8" }
10/25/2019, 9:55:25 PMnode: 4189398f.c516f8
home/hallfloor/light/cmnd/POWER3 : msg : Object
{ topic: "home/hallfloor/light/cmnd/POWE…", payload: "ON", _msgid: "7bfbb908.70dea8" }
10/25/2019, 9:55:25 PMnode: 4189398f.c516f8
home/entrance/light/cmnd/POWER2 : msg : Object
{ topic: "home/entrance/light/cmnd/POWER…", payload: "ON", _msgid: "edfd6e73.23a78" }
10/25/2019, 9:55:25 PMnode: 4189398f.c516f8
home/hallfloor/light/cmnd/POWER3 : msg : Object
{ topic: "home/hallfloor/light/cmnd/POWE…", payload: "ON", _msgid: "edfd6e73.23a78" }

I'm probably overseeing something, but not really sure what, It always triggers home/hallfloor/ twice.

Any idea?

Some initial notes:
The return false should be return null. I have no idea what return false will do. Perhaps it has the same effect as return null but I don't know.
If you do return [[ra,rb,rc]] then it will send all three messages to the first output, so you only need one output.
The Join node should be set to wait for three messages, you still have it set to wait for two.
Does it work ok for you if you disconnect the mqtt in nodes and just use the inject nodes? If so then the problem may be caused by the fact that when you send a command out then soon after there will be a status command back as a result of that change, which may be messing up the algorithm. So possibly something like this happens
Assume all switches are off initially.
Suppose switch A goes on. This will trigger On messages to B and C.
An On status will then come in for B. this will trigger another On for C.
Finally the On status will come in for C. In fact there may be two On status messages for C, it depends whether you get a status message when you send a command to switch it to the state it is already in. That will be mopped up by the RBE node however. I guess the fact that you put the RBE in suggests you have already seen that situation.
Something like that anyway

ah I found somewhere that return [1.2.3] equals to ouput number of the function ...

oh my, thanks

looks like you are right, it works with injects only
but that's where I thought RBE will take a place and basically ignores returning confirmation value of the triggered topic.
But it looks like it's not

Look in the docs page on writing functions for details of how to return multiple messages to one or more outputs. Lots of other useful stuff there, if you haven't read it then do so.
The RBE node is working as it should, this is what could happen

A    B    C
On   On   On       This is the initial state
Off                Switch A Off - flow sends Off to B and C
     Off           Switch B is now off - flow sends another Off to C since status for it has not come in yet
          Off      This is the status from the first Off sent to C
          Off      this is the status from the second off to C, though I am not sure if this will actually come in, it depends on whether the device sends it or not.  This will not get to the function node because of the RBE node.

Note that there are two Off commands to C. The RBE on the front does not stop that. An RBE on the output would stop it.

thanks!
you elaboration makes perfect sense and moving RBE to the end did the trick

Just one word of caution on this. What you have here is a closed loop system. Inputs come in from the Sonoff devices, the flow decides what to do and outputs commands to the Sonoffs, this in turn causes more inputs at the front, which again may cause further outputs, and so on. It is possible that there may be some combination of inputs at just the right times (clicking multiple switches at almost the same time for example) that would cause the system to enter an unstable state with messages running round the loop and the lights switching on and off rapidly. I don't even know if it is possible to calculate whether your system may have any such unstable states.
It would be possible to remove such possibilities if you could determine whether a state change input was as a result of an output sent by your system or whether it was caused by an external event (someone pressing a switch). I don't know whether it is possible to know that or not. If you could tell then the system should be told to ignore inputs that come from commands generated by the flow. This would break the loop and it would not be possible to have an unstable situation.
In practice what you have will very likely be fine but if once in a blue moon when more than one switch gets pressed at almost the same time then it enters such a mode you will know why. Quite possibly in that event clicking the switches again will kick it out of that mode, or possibly not :slight_smile:
It would be worth trying to see if you can make it go unstable by clicking buttons randomly just so you know how likely it is.

that's what I was occasionally experiencing with my first flow.
I hope that this new sophisticated one will not suffer from this behaviour.

Previous issue was kind of reproducable when wifi went off and one of the switches was turned ON while others remained OFF, after wifi regain it started rapid switching.

It's not very common and I tried to simulate it with these new flows and looks like it's working properly.

I was thinking to add some kind of delay into the reading, but then it can cause issue when network will be bit laggy or so and switches would be unsynced. Not really sure how to solve this, maybe some kind of internal delay+gatherer of the states and then continue to the function after 1s or so, what do you think?

update: I found out JOIN to be breaking factor in current flow, as join basically waits 2 or 3 messages till it passes to Function node, which in real world means to press 2 or 3 different switches to actually invoke their sync.

So i've thrown away join node, and inside function node I no longer cares about which topic is the trigger as I (and tasmota) can live with double cmnd to trigger node, eg. tasmota ignores that command if switch is already in requested state.

Now it does look like working reliable.

What is now on the table are some corner cases in case wifi/reboot etc.

You can tell the join node to only wait for 1 message and to send outputs on all subsequent messages so it should have been possible to make it work.

yes, but then it's node which is not really needed as basically you are only passing what you have already, nothing really joining

Not if you are combining them into an object with multiple keys

which in this usecase is not needed as I'm sending cmnd to each topic anyway, so only needed thing here is payload ON/OFF