This is another subflow to use with button
nodes. Though you could use other ones, I am sticking to the basic idea of a button and accidentally pressing it.
The button has two states: ON and OFF.
For me that is used to control a gate
node to stop / allow messages to pass.
Rather than having TWO buttons, I have one.
It has 3 states.
1 - idle / disabled.
2 - on / disabled.
3 - off / disabled.
Though it has a couple of extra intermediate states too.
Starting at Square 1:
It is either ON or OFF.
You want to change it.
You press the button.
It changes colour (background).
Within 3 seconds you press it again and the text and background colour change, indicating the new state.
After 3 seconds it goes back to the original background colour but the text has changed.
Meanwhile a message was sent to control (in my usage case) the state of the gate
node.
It needs to be configured / set up at boot.
This is done by sending a special message with the topic
set to SETUP
.
The structure is described in the documentation of the node.
[{"id":"ce274e69.c6f778","type":"subflow","name":"Button toggle with enable","info":"/**\n* Used to help toggle `button` node state to give a nicer look to the `button`.\n* \n* @version 0.1.0\n* @auther Andrew\n*/\n\n/**\n* Before use the node needs a `setup` message sent to it to configure things.\n* The structure is shown below.\n*/\n\n/**\n* At the end there is a bit on how to use the node.\n*/\n\nStructure (eg):\n```\nmsg = {\n \"topic\":\"SETUP\",\n \"disabledColour\":\"brown\",\n \"colourA\": \"lime\",\n \"txtA\": \"Log\",\n \"txtclrA\": \"black\",\n \"payloadA\": \"GO\",\n \"colourB\": \"green\",\n \"txtB\": \"Stop\",\n \"txtclrB\": \"black\",\n \"payloadB\": \"STOP\",\n \"topicSET\": \"CONTROL\"\n}\nreturn msg;\n```\n\nBreakdown of lines:\n\n```\ndisabledColour - the colour the button is when disabled.\n```\nThis is the background colour of the button which will be seen the most.\nThis is when you have selected the state you want and the button is *disabled*\n\n```\ncolourA - the background colour if condition `A` is selected. (Temporary)\n```\nThis colour is shown at the background colour when option `A` is the one selected.\n\n```\ncolourB - the background colour if condition `B` is selected. (Temporary)\n```\nThis colour is shown at the background colour when option `B` is the one selected.\n\n```\ntxtA - The text to display if condition `A` is selected.\n```\nThis is the text shown when option `A` is selected.\n\n```\ntxtB - The text to display if condition `B` is selected.\n```\nThis is the text shown when option `B` is selected.\n\n```\ntxtclrA - the colour of the text if condition `A` is selected.\n```\nThis is the text colour used when option `A` is selected.\n\n```\ntxtclrB - the colour of the text if condtion `B` is selected.\n```\nThis is the text colour used when option `B` is selected.\n\n```\npayloadA - the text sent to the `gate` if condtion `A` is selected.\n```\nThis is the message output to the flow when option `A` is selected.\n\n```\npayloadB - the text sent to the `gate` if condition `B` is selected.\n```\nThis is the message output to the flow when option `B` is selected.\n\n```\ntopicSET - the message topic which is sent to the `gate`.\n```\nThis is needed if you are sending the message to a `gate` node as they need a\nspecific `topic` to be set to indicate the message is for them.\n\nThe *temporary* meaning is that the button will show that colour for a few seconds\nthen it will turn to the `disabled` colour.\n\nThe active time is `3` seconds to press/toggle the button once initially pressed.\n\n/**\n* **USING THE NODE:**\n* \n* **INPUT**\n* The input is connected to the `button`'s output. (Note: The button must send `X` when pressed, and not send the input to the ouput.)\n* The `button` node must be configured as so:\n* `Label` set to `{{msg.txt}}`\n* `Colour` set to `{{msg.fontclr}}`\n* `Background` set to `{{msg.colour}}`\n* `Payload` set to `X`\n* `if msg arrives on input, emulate a button click` NOT selected.\n* \n* **OUTPUTS**\n* The first one is used to control what is happening in the flow.\n* I use it to control `gate` nodes. (`node-red-contrib-simple-gate`)\n* \n* The second one goes back to the `button` input.\n*/\n","category":"","in":[{"x":190,"y":120,"wires":[{"id":"761936f2.605b3"},{"id":"475a3d05.9bbc34"}]}],"out":[{"x":560,"y":80,"wires":[{"id":"475a3d05.9bbc34","port":0}]},{"x":560,"y":160,"wires":[{"id":"475a3d05.9bbc34","port":1}]}],"env":[],"color":"#3FADB5","outputLabels":["To the `GATE` node. This is what gets things done.","To the `BUTTON` node"],"icon":"node-red-dashboard/ui_switch.png","status":{"x":560,"y":240,"wires":[{"id":"59a2a371.c4ee5c","port":0}]}},{"id":"475a3d05.9bbc34","type":"function","z":"ce274e69.c6f778","name":"Push Button","func":"// 2021 06 08 tidied up a lot of mess. Working now.\n// Rewriting this for subflow usability.\nmsg1 = {};\nvar state = context.get(\"STATE\")||0;\nvar enabled = context.get(\"ENABLED\")||0;\n\n// Use message with `topic` set to `SETUP` to set up values.\nif (msg.topic == \"SETUP\")\n{\n //\n // Background colours first.\n //\n context.set(\"ABGC\", msg.colourA);\n context.set(\"BBGC\", msg.colourB);\n //\n // Disabled button background colour.\n //\n context.set(\"DISABLEDCLR\",msg.disabledColour);\n //\n // Text to be displayed / used.\n //\n context.set(\"Atxt\", msg.txtA);\n context.set(\"Btxt\", msg.txtB);\n //\n // Font colours.\n //\n context.set(\"AFC\",msg.txtclrA);\n context.set(\"BFC\",msg.txtclrB);\n //\n // Payloads. (Used to get things done outside the subflow)\n //\n context.set(\"PayloadA\", msg.payloadA);\n context.set(\"PayloadB\", msg.payloadB);\n //\n // Topic.\n //\n if (msg.topicSET !== null)\n {\n context.set(\"Topic\",msg.topicSET);\n } else\n {\n context.set(\"Topic\",\"~\");\n }\n // To control the state of the `gate` node.\n msg.payload = context.get(\"PayloadA\");\n msg.topic = context.get(\"Topic\");\n // To set the `button` colour/text/etc.\n msg1.txt = context.get(\"Atxt\");\n msg1.colour = context.get(\"DISABLEDCLR\");\n msg1.fontclr = context.get(\"AFC\");\n\n return [msg,msg1];\n}\n\n// Normal operation of the node.\nif (msg.payload == \"X\")\n{\n // Abort if context values are not set.\n if (context.get(\"PayloadA\") == undefined)\n {\n node.status({text:\"Button_Toggle_Enable_Context_Not_Set\"});\n return [null,null];\n }\n //node.warn(\"X detected\");\n // Insert here code to enable other stuff.\n if (enabled === 0)\n {\n if (state === 0)\n {\n //\n // Set things for state 0\n //\n msg.payload = context.get(\"PayloadA\");\n msg.topic = context.get(\"Topic\");\n msg1.colour = context.get(\"ABGC\");\n msg1.txt = context.get(\"Atxt\");\n msg1.fontclr = context.get(\"AFC\");\n }\n else if (state === 1)\n {\n //\n // Set things for state 1\n //\n msg.payload = context.get(\"PayloadB\");\n msg.topic = context.get(\"Topic\");\n msg1.colour = context.get(\"BBGC\");\n msg1.txt = context.get(\"Btxt\");\n msg1.fontclr = context.get(\"BFC\");\n }\n context.set(\"ENABLED\",1);\n return [null,msg1];\n }\n\n state = (state + 1)% 2;\n context.set(\"STATE\",state);\n if (enabled === 1)\n {\n if (state === 0)\n {\n // Condition A\n msg.payload = context.get(\"PayloadA\");\n msg1.colour = context.get(\"ABGC\");\n msg1.txt = context.get(\"Atxt\");\n msg1.fontclr = context.get(\"AFC\");\n } else\n if (state === 1)\n {\n // Condition B\n msg.payload = context.get(\"PayloadB\");\n msg1.colour = context.get(\"BBGC\");\n msg1.txt = context.get(\"Btxt\");\n msg1.fontclr = context.get(\"BFC\");\n }\n }\n \n if (context.get(\"Topic\") == \"~\")\n {\n msg.topic = \"\";\n } else\n {\n msg.topic = context.get(\"Topic\");\n }\n return [msg,msg1];\n}\nif (msg.payload == \"Z\")\n{\n // Abort if context values are not set.\n if (context.get(\"PayloadA\") == undefined)\n {\n return [null,null];\n }\n context.set(\"ENABLED\",0);\n if (state === 0)\n {\n //\n // Set things for state 1\n //\n msg.payload = context.get(\"PayloadA\");\n msg.topic = context.get(\"Topic\");\n msg1.colour = context.get(\"DISABLEDCLR\");\n msg1.txt = context.get(\"Atxt\");\n msg1.fontclr = context.get(\"AFC\");\n }\n else if (state === 1)\n {\n //\n // Set things for state 2\n //\n msg.payload = context.get(\"PayloadB\");\n msg.topic = context.get(\"Topic\");\n msg1.colour = context.get(\"DISABLEDCLR\");\n msg1.txt = context.get(\"Btxt\");\n msg1.fontclr = context.get(\"BFC\");\n }\n return [null,msg1];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","x":380,"y":120,"wires":[[],[]],"outputLabels":["To gate","To button"]},{"id":"4b9bf99.eb5f688","type":"trigger","z":"ce274e69.c6f778","name":"","op1":"","op2":"Z","op1type":"nul","op2type":"str","duration":"3","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":445,"y":170,"wires":[["475a3d05.9bbc34"]],"l":false},{"id":"761936f2.605b3","type":"switch","z":"ce274e69.c6f778","name":"Block SETUP messages","property":"topic","propertyType":"msg","rules":[{"t":"neq","v":"SETUP","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":280,"y":170,"wires":[["4b9bf99.eb5f688"]]},{"id":"59a2a371.c4ee5c","type":"status","z":"ce274e69.c6f778","name":"","scope":["475a3d05.9bbc34"],"x":410,"y":240,"wires":[[]]},{"id":"9f3a3dc4.7e43e8","type":"subflow:ce274e69.c6f778","z":"7879609.4fb06a","name":"","env":[],"x":640,"y":110,"wires":[[],[]]}]