I have mentioned that sometimes pressing buttons on a dashboard can be problematic.
You accidentally click it, or ... something else.
This subflow
node allows you to take better control of such buttons.
If you are wanting to, you can use other buttons (CSS type).
I am only using the standard button
node to show its use.
Basic flow overview:
And this shows an example of the button being pressed.
Start:
First click
(Button becomes active)
Second click.
After 3 seconds of inactivity, it then locks and looks like this.
The text in the button and the "output displayed here" are not the same.
You can change them both to what you want.
Code:
(Including the sub-flow node)
(This is a flow unto itself - just to maybe help keep things separate)
[{"id":"f78bdf06ae572875","type":"subflow","name":"Button toggle with enable","info":"Update 2022 02 04\nCode has been changed since initial version.\nSet the variables below.\nTo set the initial value of the node, inject a message of either `A` or `B`.\n\n`A` sets the mode to what the `a` named messages indicate.\n\n`B` sets the mode to what the `b` named messages indicate.\n\nThe output from the `button` node must be `X` to toggle the state, and the button must NOT pass the input onto the output!\n\nYou MUST set the `environment` variables listed below or it won't work.\n(Example)\n```\n\"disabledColour\":\"brown\",\n\"colourA\": \"lime\",\n\"colourB\": \"green\",\n\"txtA\": \"Log\",\n\"txtB\": \"Stop\",\n\"txtclrA\": \"black\",\n\"txtclrB\": \"black\",\n\"payloadA\": \"GO\",\n\"payloadB\": \"STOP\",\n\"topicSET\": \"CONTROL\"\n```\n```\ndisabledColour - the colour the button is when disabled.\n```\n```\ncolourA - the background colour if condition `A` is selected. (Temporary)\n```\n```\ncolourB - the background colour if condition `B` is selected. (Temporary)\n```\n```\ntxtA - The text to display if condition `A` is selected.\n```\n```\ntxtB - The text to display if condition `B` is selected.\n```\n```\ntxtclrA - the colour of the text if condition `A` is selected.\n```\n```\ntxtclrB - the colour of the text if condtion `B` is selected.\n```\n```\npayloadA - the text sent to the output if condtion `A` is selected.\n```\n```\npayloadB - the text sent to the output if condition `B` is selected.\n```\n```\nThese payloads can then be used to set context values or used to control (external node) `gate`\n```\n```\ntopicSET - the message topic which is sent to the `gate`.\n(this is needed if/when used to control the afore mentioned `gate` nodes.)\n```\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.\nThough now, if you keep pressing the button, it will *wait* until you finish pressing it.\n\n","category":"","in":[{"x":190,"y":90,"wires":[{"id":"7f6db7e922a9aec4"},{"id":"1be2ec1de1ce08e1"}]}],"out":[{"x":560,"y":50,"wires":[{"id":"7f6db7e922a9aec4","port":0}]},{"x":560,"y":130,"wires":[{"id":"7f6db7e922a9aec4","port":1}]}],"env":[],"meta":{},"color":"#3FADB5","outputLabels":["To the `GATE` node","To the `BUTTON` node"],"icon":"node-red-dashboard/ui_switch.png","status":{"x":560,"y":210,"wires":[{"id":"6aaf4e9c71587bfd","port":0}]}},{"id":"7f6db7e922a9aec4","type":"function","z":"f78bdf06ae572875","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//node.warn(\"INPUT \" + msg.payload);\n\n\n\n// Look for a message with topic set to SETUP to set values.\n//if (msg.topic == \"SETUP\") //Do this if the message is NOT \"X\"\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\nif (msg.payload == \"A\")\n{\n //\n //\n //\n msg.payload = context.get(\"PayloadA\");\n msg.topic = context.get(\"Topic\");\n\n msg1.txt = context.get(\"Atxt\");\n msg1.colour = context.get(\"DISABLEDCLR\");\n msg1.fontclr = context.get(\"AFC\");\n \n context.set(\"STATE\",0);\n\n return [msg,msg1];\n}\nif (msg.payload == \"B\")\n{\n //\n //\n //\n msg.payload = context.get(\"PayloadB\");\n msg.topic = context.get(\"Topic\");\n\n msg1.txt = context.get(\"Btxt\");\n msg1.colour = context.get(\"DISABLEDCLR\");\n msg1.fontclr = context.get(\"BFC\");\n \n context.set(\"STATE\",1);\n\n return [msg,msg1];\n}\n\n//node.warn(\"payload \" + msg.payload);\n//node.warn(\" start\");\n\n// Now on to the real stuff.\nif (msg.payload == \"X\")\n{\n //\n //node.warn(\"Button press detected\");\n //\n node.status({fill: \"green\",text: \"Pressed\"});\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 //node.warn(\"State = \" + state);\n\n if (state === 0)\n {\n //\n // Set things for state 0\n //\n //node.warn(\"State 0 detected\");\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 //node.warn(\"State 1 detected\");\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// if (context.get(\"ENABLED\") === 1)\n state = (state + 1)% 2;\n //node.warn(state);\n context.set(\"STATE\",state);\n if (enabled === 1)\n {\n if (state === 0)\n {\n // Condition A\n node.status({fill: \"yellow\",text: \"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 node.status({fill: \"red\",text: \"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 //\n ////node.warn(\"Timed out\");\n //\n node.status({text: \"\"});\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":"// Code added here will be run once\n// whenever the node is started.\n\ncontext.set(\"ABGC\", env.get(\"colourA\"));\ncontext.set(\"BBGC\", env.get(\"colourB\"));\n//\n// Disabled button background colour.\n//\ncontext.set(\"DISABLEDCLR\",env.get(\"disabledColour\"));\n//\n// Now do text.\n//\ncontext.set(\"Atxt\", env.get(\"txtA\"));\ncontext.set(\"Btxt\", env.get(\"txtB\"));\n//\n// Font colours.\n//\ncontext.set(\"AFC\",env.get(\"txtclrA\"));\ncontext.set(\"BFC\",env.get(\"txtclrB\"));\n//\n// Payloads.\n//\ncontext.set(\"PayloadA\", env.get(\"payloadA\"));\ncontext.set(\"PayloadB\", env.get(\"payloadB\"));\n\n","finalize":"","libs":[],"x":380,"y":90,"wires":[[],[]],"outputLabels":["To gate","To button"]},{"id":"f9b742ce67c53927","type":"trigger","z":"f78bdf06ae572875","name":"","op1":"","op2":"Z","op1type":"nul","op2type":"str","duration":"3","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":285,"y":140,"wires":[["7f6db7e922a9aec4"]],"l":false},{"id":"6aaf4e9c71587bfd","type":"status","z":"f78bdf06ae572875","name":"","scope":["7f6db7e922a9aec4"],"x":400,"y":210,"wires":[[]]},{"id":"1be2ec1de1ce08e1","type":"switch","z":"f78bdf06ae572875","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"X","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":235,"y":140,"wires":[["f9b742ce67c53927"]],"l":false},{"id":"1283f6a97005461c","type":"tab","label":"Press button with enable example","disabled":false,"info":"","env":[]},{"id":"27872f8e88e28fba","type":"subflow:f78bdf06ae572875","z":"1283f6a97005461c","name":"","env":[{"name":"disabledColour","value":"brown","type":"str"},{"name":"txtA","value":"Go","type":"str"},{"name":"txtB","value":"Stop","type":"str"},{"name":"txtclrA","value":"black","type":"str"},{"name":"txtclrB","value":"black","type":"str"},{"name":"colourA","value":"lime","type":"str"},{"name":"colourB","value":"blue","type":"str"},{"name":"payloadA","value":"go","type":"str"},{"name":"payloadB","value":"stop","type":"str"},{"name":"topicSET","value":"CONTROL","type":"str"}],"x":370,"y":240,"wires":[["1ee1d186296bbfb9","826e751643453061"],["7dfdce3cd2ede505","b08c059a0e560c6d"]]},{"id":"1ee1d186296bbfb9","type":"debug","z":"1283f6a97005461c","name":"This message is what does things.","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":200,"wires":[]},{"id":"7dfdce3cd2ede505","type":"debug","z":"1283f6a97005461c","name":"Loop back to button node","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":370,"y":200,"wires":[]},{"id":"1aa036716db55e3f","type":"inject","z":"1283f6a97005461c","name":"Initial state","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"A","payloadType":"str","x":140,"y":240,"wires":[["27872f8e88e28fba"]]},{"id":"b08c059a0e560c6d","type":"ui_button","z":"1283f6a97005461c","name":"Button example","group":"5b40fa57a34cb695","order":1,"width":0,"height":0,"passthru":false,"label":"{{msg.txt}}","tooltip":"","color":"{{msg.fontclr}}","bgcolor":"{{msg.colour}}","icon":"","payload":"X","payloadType":"str","topic":"topic","topicType":"msg","x":360,"y":300,"wires":[["27872f8e88e28fba"]]},{"id":"3125d7d3bb4a2cb5","type":"comment","z":"1283f6a97005461c","name":"Read me!","info":"Change the noe below to either inject `A` or `B` to select what *mode* you want to be the initial mode.\n","x":120,"y":200,"wires":[]},{"id":"5be4bca2524796a0","type":"comment","z":"1283f6a97005461c","name":"Read me!","info":"The `debug` node below is only there to show you the message.\nIt is not needed.","x":370,"y":160,"wires":[]},{"id":"826e751643453061","type":"ui_text","z":"1283f6a97005461c","group":"5b40fa57a34cb695","order":2,"width":0,"height":0,"name":"","label":"Output displayed here.","format":"{{msg.payload}}","layout":"row-spread","x":650,"y":240,"wires":[]},{"id":"5b40fa57a34cb695","type":"ui_group","name":"Button Press (with enable) example","tab":"bb73106a674aef89","order":1,"disp":true,"width":"6","collapse":false},{"id":"bb73106a674aef89","type":"ui_tab","name":"Tab 42","icon":"dashboard","order":42}]