Saving button background color while disabled

I have a set of buttons that send different state commands to a controlled device.
I want the button state to reflect the realtime status of the controlled device, so I am using the feedback to set the correct button as "active" and the others as "inactive".
However, I also need to delay how quickly commands can be sent, so I want to disable all buttons for a certain amount of time after pressing one of them.
Reaching either of these goals is easily achievable, but I am having a hard time trying to combine them into a complete system.
I am attaching a basic flow demonstrating the issue. Since I can't include the actual device feedback I added a simple delay on the button press which does the same thing for the purposes of demonstrating the issue.
When I press "State 1", both buttons are disabled. When I get the feedback that the device is in "State 1", the background and text colors change as expected. However, when the timer expires and the buttons are enabled again, they return to the default dashboard universal button state and I lose the state feedback.
Is this not possible without adding a bunch of extra state-tracking logic to restore the correct button state after it is wiped? Shouldn't the enable command only enable the buttons, not modify their other attributes?

[{"id":"dab9bb38.6245e8","type":"ui_button","z":"f1d06a71.51424","name":"State 2","group":"dd64112c.15ad78","order":22,"width":0,"height":0,"passthru":false,"label":"State 2","tooltip":"","color":"{{msg.color}}","bgcolor":"{{msg.background}}","icon":"","payload":"2","payloadType":"str","topic":"","x":300,"y":180,"wires":[["c35c5e66.d510d","e4419de7.65b02"]]},{"id":"6ff77c47.0a405c","type":"ui_button","z":"f1d06a71.51424","name":"State 1","group":"dd64112c.15ad78","order":21,"width":0,"height":0,"passthru":false,"label":"State 1","tooltip":"","color":"{{msg.color}}","bgcolor":"{{msg.background}}","icon":"","payload":"1","payloadType":"str","topic":"","x":300,"y":140,"wires":[["e4419de7.65b02","5feca31a.ea4b8c"]]},{"id":"c35c5e66.d510d","type":"change","z":"f1d06a71.51424","name":"Disable State Buttons","rules":[{"t":"set","p":"enabled","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":180,"wires":[["ea991425.141e38","52f9a2c1.ec5034"]]},{"id":"ea991425.141e38","type":"link out","z":"f1d06a71.51424","name":"Button Control","links":["87de42d.51a28c"],"x":675,"y":240,"wires":[]},{"id":"52f9a2c1.ec5034","type":"delay","z":"f1d06a71.51424","name":"Wait 5 Seconds","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":760,"y":140,"wires":[["7507aec3.a90c48"]]},{"id":"7507aec3.a90c48","type":"change","z":"f1d06a71.51424","name":"Re-Enable State Buttons","rules":[{"t":"set","p":"enabled","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":180,"wires":[["ea991425.141e38"]]},{"id":"87de42d.51a28c","type":"link in","z":"f1d06a71.51424","name":"Button Control","links":["ea991425.141e38"],"x":175,"y":160,"wires":[["6ff77c47.0a405c","dab9bb38.6245e8"]]},{"id":"5feca31a.ea4b8c","type":"change","z":"f1d06a71.51424","name":"Disable State Buttons","rules":[{"t":"set","p":"enabled","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":140,"wires":[["ea991425.141e38","52f9a2c1.ec5034"]]},{"id":"c8ca7726.51e9","type":"function","z":"f1d06a71.51424","name":"Button State","func":"let source = msg.payload\nif (source == \"1\"){\n    msg1 = {background:\"#0094CE\", color:\"white\"}\n    msg2 = {background:\"#036288\", color:\"grey\"}\n}\nelse if (source == \"2\"){\n    msg2 = {background:\"#0094CE\", color:\"white\"}\n    msg1 = {background:\"#036288\", color:\"grey\"}\n}\n\nreturn [msg1, msg2];","outputs":2,"noerr":0,"x":450,"y":240,"wires":[["6ff77c47.0a405c"],["dab9bb38.6245e8"]]},{"id":"e4419de7.65b02","type":"delay","z":"f1d06a71.51424","name":"","pauseType":"delay","timeout":"250","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":290,"y":240,"wires":[["c8ca7726.51e9"]]},{"id":"dd64112c.15ad78","type":"ui_group","z":"","name":"Test","tab":"6d2ffe37.0d7c8","disp":true,"width":"6","collapse":false},{"id":"6d2ffe37.0d7c8","type":"ui_tab","z":"","name":"Test","icon":"dashboard","disabled":false,"hidden":false}]

It is embarrassing to me that I can't answer quicker.

I have lots of buttons very similar to what you are doing.

I guess the short answer is no.

You will need some more nodes and variables to keep track of the state of things.

Another thing which is not good is in the function node the code:

let source = msg.payload
if (source == "1"){
    msg1 = {background:"#0094CE", color:"white"}
    msg2 = {background:"#036288", color:"grey"}
}
else if (source == "2"){
    msg2 = {background:"#0094CE", color:"white"}
    msg1 = {background:"#036288", color:"grey"}
}

return [msg1, msg2];

1 - you should be able to compare msg.payload in the if lines, rather than create a source variable.
2 - msg1 and msg2. Keep them in the same order.
In the first one is is msg1 msg2. In the second one it is msg2 msg1.
This can cause confusion when people are looking at the code.

So it appears that what you are doing is setting the buttons back to enable by a node that only sends the enable command. You have to send the whole command to set background color, set label message, set enable or the button only sets the command you have sent. This means you re-enable the button but since there is no color commands the buttons return to their default colors. Without trying to redo your whole flow I have updated your flow just a bit to show what I mean.

[{"id":"8a07b08d.265e68","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"ba468971.5bb57","type":"ui_button","z":"8a07b08d.265e68","name":"State 2","group":"9a5a0a35.d3a89","order":22,"width":0,"height":0,"passthru":false,"label":"State 2","tooltip":"","color":"{{msg.color}}","bgcolor":"{{msg.background}}","icon":"","payload":"2","payloadType":"str","topic":"","x":260,"y":140,"wires":[["c06617c6.df50c","fe02d7b2.d4739"]]},{"id":"c4837e6f.42848","type":"ui_button","z":"8a07b08d.265e68","name":"State 1","group":"9a5a0a35.d3a89","order":21,"width":0,"height":0,"passthru":false,"label":"State 1","tooltip":"","color":"{{msg.color}}","bgcolor":"{{msg.background}}","icon":"","payload":"1","payloadType":"str","topic":"","x":260,"y":100,"wires":[["fe02d7b2.d4739","686b84a4.16aabc"]]},{"id":"c06617c6.df50c","type":"change","z":"8a07b08d.265e68","name":"Disable State Buttons","rules":[{"t":"set","p":"enabled","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":140,"wires":[["44293c44.55c53c","97adb25d.6056e"]]},{"id":"44293c44.55c53c","type":"link out","z":"8a07b08d.265e68","name":"Button Control","links":["484f2138.de4e8"],"x":875,"y":40,"wires":[]},{"id":"97adb25d.6056e","type":"delay","z":"8a07b08d.265e68","name":"Wait 5 Seconds","pauseType":"delay","timeout":"10","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":620,"y":220,"wires":[["f3c2adf9.310868"]]},{"id":"f3c2adf9.310868","type":"change","z":"8a07b08d.265e68","name":"Re-Enable State Buttons","rules":[{"t":"set","p":"enabled","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":220,"wires":[["961c9b42.bebb6"]]},{"id":"484f2138.de4e8","type":"link in","z":"8a07b08d.265e68","name":"Button Control","links":["44293c44.55c53c"],"x":115,"y":40,"wires":[["c4837e6f.42848","ba468971.5bb57"]]},{"id":"686b84a4.16aabc","type":"change","z":"8a07b08d.265e68","name":"Disable State Buttons","rules":[{"t":"set","p":"enabled","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":100,"wires":[["44293c44.55c53c","97adb25d.6056e"]]},{"id":"961c9b42.bebb6","type":"function","z":"8a07b08d.265e68","name":"Button State","func":"let source = msg.payload\nif (source == \"1\"){\n    msg1 = {\n    background:\"red\", \n    color:\"white\",\n    enabled: true\n    }\n    msg2 = {\n    background:\"green\", \n    color:\"grey\",\n    enabled: true\n    }\n    \n}\nelse if (source == \"2\"){\n    msg2 = {\n    background:\"red\", \n    color:\"white\",\n    enabled: true\n    }\n    msg1 = {\n    background:\"green\",\n    color:\"grey\",\n    enabled: true,   \n    }\n}\n\nreturn [msg1, msg2];","outputs":2,"noerr":0,"x":650,"y":400,"wires":[["c4837e6f.42848"],["ba468971.5bb57"]]},{"id":"fe02d7b2.d4739","type":"delay","z":"8a07b08d.265e68","name":"","pauseType":"delay","timeout":"250","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":210,"y":400,"wires":[["961c9b42.bebb6"]]},{"id":"9a5a0a35.d3a89","type":"ui_group","z":"","name":"Test","tab":"705b670c.f5159","disp":true,"width":"6","collapse":false},{"id":"705b670c.f5159","type":"ui_tab","z":"","name":"Test","icon":"dashboard","disabled":false,"hidden":false}]

Thanks to both of you for your replies!

I eventually found this post:

That got me experimenting along new lines and I came up with this solution, which basically follows what gerry said. I am still new to this whole writing code thing, which is why I love Node-Red because I can use nodes for the bulk of the logic and learn some code in a function node on a very specific task.

It took a bit to figure out how to use node.send with multiple outputs, and then I clearly don't understand exactly what the timeout function is doing because it seems to be backwards compared to how I would think it would go but this code is working for me now.

Thanks for the tips on writing better code!

I still feel like an isolated enable command shouldn't reset the button state, it should just enable it. That feels like a bug to me but I'm not complaining. I'm grateful to have access to Node-Red at all!

[{"id":"dab9bb38.6245e8","type":"ui_button","z":"f1d06a71.51424","name":"State 2","group":"dd64112c.15ad78","order":22,"width":0,"height":0,"passthru":false,"label":"State 2","tooltip":"","color":"{{msg.color}}","bgcolor":"{{msg.background}}","icon":"","payload":"2","payloadType":"str","topic":"","x":300,"y":180,"wires":[["c8ca7726.51e9"]]},{"id":"6ff77c47.0a405c","type":"ui_button","z":"f1d06a71.51424","name":"State 1","group":"dd64112c.15ad78","order":21,"width":0,"height":0,"passthru":false,"label":"State 1","tooltip":"","color":"{{msg.color}}","bgcolor":"{{msg.background}}","icon":"","payload":"1","payloadType":"str","topic":"","x":300,"y":140,"wires":[["c8ca7726.51e9"]]},{"id":"c8ca7726.51e9","type":"function","z":"f1d06a71.51424","name":"Button State","func":"if (msg.payload == \"1\"){\n    setTimeout(function(){\n    msg1 = {enabled:true, background:\"#0094CE\", color:\"white\"};\n    msg2 = {enabled:true, background:\"#036288\", color:\"grey\"};\n    node.send([msg1, msg2]);\n    }, 5000);\n    msg1 = {enabled:false, background:\"#0094CE\", color:\"white\"};\n    msg2 = {enabled:false, background:\"#036288\", color:\"grey\"};\n    node.send([msg1, msg2]);\n}\nelse if (msg.payload == \"2\"){\n    setTimeout(function(){\n    msg1 = {enabled:true, background:\"#036288\", color:\"grey\"};\n    msg2 = {enabled:true, background:\"#0094CE\", color:\"white\"};\n    node.send([msg1, msg2]);\n    }, 5000);\n    msg1 = {enabled:false, background:\"#036288\", color:\"grey\"};\n    msg2 = {enabled:false, background:\"#0094CE\", color:\"white\"};\n    node.send([msg1, msg2]);\n}","outputs":2,"noerr":0,"x":290,"y":240,"wires":[["6ff77c47.0a405c"],["dab9bb38.6245e8"]]},{"id":"dd64112c.15ad78","type":"ui_group","z":"","name":"Test","tab":"6d2ffe37.0d7c8","disp":true,"width":"6","collapse":false},{"id":"6d2ffe37.0d7c8","type":"ui_tab","z":"","name":"Test","icon":"dashboard","disabled":false,"hidden":false}]
1 Like