# A subflow for people who like buttons

**URL:** <https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162>\
**Category:** Share Your Projects\
**Tags:** dashboard-2\
**Created:** [31 May 2026 07:54 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162 "2026-05-31T07:54:05Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [31 May 2026 07:54 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/1 "2026-05-31T07:54:05Z")

</div>

Yeah, I know `subflows` area a bit _Hit and miss_ you either love them or ate them.

I was writing `function` nodes that does SOME of this but not all.  
They were HUGE blocks of text and though it/they were robust, accidental pressing was a problem.

So I _Added_ the option where you have to double click the button.  
Then there is the _click_ and the _toggle_ type.

I've tried to include all the docs, but..... I am not good at that.  
Here's a walk through of how it works.

Input:  
`X` is expected as the usual/normal input. Why? More on that later.  
Outputs: 2.  
1 Is the one `to the GATE`. This is because they were mostly used to control `GATE` nodes.  
2 Loops back to the `button`.  
It has 3 parts to the message:

```auto
msg.txt
msg.fontclr
msg.colour

```

txt is the text to display  
fontclr is what colour the text is.  
colour is the background of the button.

Two _MODES_  
`press` and `toggle`  
Press is basically a button that only sends ONE message. But you have to double click/press it in a given time for the message to be sent.  
(Extras)  
It shows THREE messages on the button's text field.  
(Yes, they can be icons.)  
When _disabled_ - at idle.  
After the first press  
After the second press.

Toggle:  
An `A` and `B` switch.  
Idle - you see what _state_ it is in. (A/B - for simplicity)  
First press - the colour changes to the colour set for that state.  
Second press - the text and colour changes to the other _state_.

The payload then changes.

Why `X`?  
You can send `RELOAD` or `RESET`.  
These are handy if ever you disable the `button` node.  
But sometimes it may be handy to get the state (text) refreshed on the `button`.  
This is where  
`ReSendPayloadOnRELOAD`  
comes in.  
If `0` `RELOAD` only refreshes the `button`'s text.  
If `1`, the payload is also sent.

Anyway, I hope I made the documents readable.

It would be nice if _you_ can have a look and see if you can see any problems.  
It is a bit of a _dog's breakfast_ in the `function` node.  
But I have tried to structure it into blocks.  
And there may be repeats in there toooo. 😉

And it would be nice if people found it useful.

No foreign nodes.

Code:

````auto
[{"id":"e2af3214236cccda","type":"subflow","name":"Button toggle with enable","info":"Update 2026 05 30 17:10\n======\nWasn't getting font colour sent at setup\nDISABLED: explination.\n\n--\nIt is the COLOUR which signals the state\nof the `button`.\nIf `mode 0`:\n disabledTXT is the DISABLED text\n txtA is shown when the button is initially pressed. (Enabled)\n and txtB is the text shown with the second (quick) pressed\n AND `payloadB` is used/sent. NOT `payloadA`!\nIf `mode 1`:\n txtA and txtB toggle depending on the\n output message to be sent.\n disabledTXT is NOT used.\n\nUpdate 2024 09 27\n======\nFixed up mistakes in previous documentation.\n\n\nUpdate 2024 06 08\n======\n\nEdited so you can set the start mode.\nA/B\nHUGE rewrite to get code working correctly.\n\nsend \"RESET\" as `msg.payload` and the `startOn`\nsetting will be reloaded.\nsend \"RELOAD\" as `msg.payload` and the state of\nthe node will be resent to the `button` node.\n(NO output sent)\n\nUpdate 2023 09 03\n------\n\nNow fixed.\n`trigger` node not set to accept `msg.delay`\n\n\nUpdate 2022 02 21\n------\n\nTo initialise the node send a message of `Z` into the input.\n\nIf you want a specific ouptut:\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```\nmode: (0/1) (see below)\nStartOn: either A or B (the two conditions)\ndisabledColour: brown\ndisabledTXT: something\ncolourA: lime\ncolourB: green\ntxtA: Log\ntxtB: Stop\ntxtclrA: black\ntxtclrB: black\npayloadA: GO\npayloadB: STOP\ntopicSET: CONTROL\ndelay: 800\n```\n```\nmode - single mode or toggle mode (0/1).\nThis means in toggle mode you get 2 output messages.\nSingle mode you only get one output message.\n```\n```\ndelay - (optional) millisecond delay for time button is active.\n```\n```\ndisabledColour - the colour the button is when disabled.\n```\n```\ndisabledTXT - This is used when the `mode` is set to `1`. The text is displayed by default.\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. Time determined by `delay` value (ms)\n\nThe active time is `3` seconds (adjustable via the `delay` value) to press/toggle the button once initially pressed.\nNow: if you keep pressing the button, it will *wait* until you finish pressing it.\n\nThe background colours (and text colours) apply to the `button` node (or other) that is pressed.\nThat node must be configured to allow inputs of `{{msg.txt}}`, `{{msg.colour}}` and `{{mdg.fontclr}}`.\n","category":"","in":[{"x":190,"y":260,"wires":[{"id":"0e1b336f6628ae72"},{"id":"751ad8b9c47029b1"}]}],"out":[{"x":930,"y":220,"wires":[{"id":"0e1b336f6628ae72","port":0}]},{"x":950,"y":300,"wires":[{"id":"0e1b336f6628ae72","port":1}]}],"env":[{"name":"delay","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"StartOn","type":"str","value":"","ui":{"label":{"en-US":"StartCond"},"type":"input","opts":{"types":["str"]}}},{"name":"mode","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"disabledColour","type":"str","value":""},{"name":"colourA","type":"str","value":""},{"name":"colourB","type":"str","value":""},{"name":"disabledTXT","type":"str","value":""},{"name":"txtA","type":"str","value":""},{"name":"txtB","type":"str","value":""},{"name":"disabledTXTColour","type":"str","value":""},{"name":"txtclrA","type":"str","value":""},{"name":"txtclrB","type":"str","value":""},{"name":"payloadA","type":"str","value":""},{"name":"payloadB","type":"str","value":""},{"name":"topicSET","type":"str","value":""},{"name":"ResendPayloadOnRELOAD","type":"num","value":"","ui":{"label":{"en-US":"Resend payload on RELOADE"},"type":"input","opts":{"types":["num"]}}}],"meta":{"version":"1.0.0","desc":"Protected button press node"},"color":"#3FADB5","outputLabels":["To the `GATE` node","To the `BUTTON` node"],"icon":"node-red-dashboard/ui_switch.png","status":{"x":860,"y":440,"wires":[{"id":"fa7150113df2bfa1","port":0}]}},{"id":"fa7150113df2bfa1","type":"status","z":"e2af3214236cccda","name":"","scope":["0e1b336f6628ae72"],"x":590,"y":440,"wires":[[]]},{"id":"0e1b336f6628ae72","type":"function","z":"e2af3214236cccda","name":"Push Button","func":"// 2026 05 30 18:44 (working on disabledTXT and how to use/show it.)\n\n// ---- Assign variable names to ENV variables.\n// all future use of variables is by their names below.\n// These are the userdefined variables from ENV\nconst mode = parseInt(env.get(\"mode\")) // Should be `const`?\nconst disabledTXT = env.get(\"disabledTXT\")\nconst disabledColour = env.get(\"disabledColour\")\nconst disabledTXTColour = env.get(\"disabledTXTColour\")\nconst colourA = env.get(\"colourA\")\nconst colourB = env.get(\"colourB\")\nconst txtA = env.get(\"txtA\")\nconst txtB = env.get(\"txtB\")\nconst txtclrA = env.get(\"txtclrA\")\nconst txtclrB = env.get(\"txtclrB\")\nconst payloadA = env.get(\"payloadA\")\nconst payloadB = env.get(\"payloadB\")\nlet ReSend = env.get(\"ResendPayloadOnRELOAD\")\nif (ReSend === undefined)\n ReSend = 0\nlet StartOn = env.get(\"StartOn\")\nlet topic = env.get(\"topicSET\")\nif (topic === undefined)\n topic = \"\"\n\n// These are other variables\nlet state = context.get(\"STATE\")||0\nlet enabled = context.get(\"ENABLED\")||0\nconst msg1 = {}\n\n//--------------------------------------------\n\n// STARTUP\nif (msg.payload == \"START\")\n{\n // This is the first message to set things up.\n //msg.payload = StartOn\n msg.payload = \"Z\"\n}\n\n// Reload desired state\nif (msg.payload == \"RESET\")\n{\n // load startOn value.\n msg.payload = StartOn\n}\n\nif (msg.payload == \"RELOAD\")\n{\n // force redisplay for `button` node.\n if (mode == 1)\n {\n if (state == 0)\n {\n //\n msg.payload = payloadA\n msg.topic = topic\n msg1.txt = txtA\n msg1.colour = disabledColour\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n } else\n if (state == 1)\n {\n //\n msg.payload = payloadB\n msg.topic = topic\n msg1.txt = txtB\n msg1.colour = disabledColour\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n }\n }\n if (mode == 0)\n {\n //\n node.status({ text: \"mode \" + mode + \"\" })\n context.set(\"STATE\", 0)\n msg1.colour = disabledColour\n msg1.txt = disabledTXT\n msg1.fontclr = disabledTXTColour\n //msg = {} // Don't send message on first set up\n msg.payload = payloadB\n msg.topic = topic\n }\n if (ReSend == 0)\n {\n //\n msg.payload = null\n msg.topic = null\n }\n return [msg,msg1]\n}\n\n// =========================================\n// Set condition if the message is either A or B\n\nif (msg.payload === \"A\")\n{\n msg1.payload = null\n if (mode == 1)\n {\n //\n msg1.payload = payloadA\n }\n if (mode == 0)\n {\n //\n msg1.payload = disabledTXT\n msg.payload = payloadB // NOTE THIS IS ONLY FOR `mode == 0`!!\n }\n msg.topic = topic\n \n\n msg1.txt = txtA\n msg1.colour = disabledColour\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n \n context.set(\"STATE\",0)\n\n return [msg,msg1]\n}\n\nif (msg.payload === \"B\")\n{\n msg.payload = payloadB\n msg.topic = topic\n\n msg1.txt = txtB\n msg1.colour = disabledColour\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n \n context.set(\"STATE\",1)\n\n return [msg,msg1]\n}\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n// Main routine below.\n//-------------------------------------\n// Now on to the real stuff.\nif (msg.payload === \"X\")\n{\n //\n // Button pressed.\n //\n node.status({fill: \"green\",text: \"Pressed\"})\n if (enabled === 0) // First time button pressed in this sequence\n {\n //\n context.set(\"ENABLED\", 1)\n if (state === 0) // Depending on `state` depends what now happens.\n {\n //\n // Set things for state 0\n //\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n //node.status({ fill: \"yellow\", text: \"A\" })\n }\n else \n if (state === 1)\n {\n //\n // Set things for state 1\n //\n node.status({ fill: \"blue\", text: \"enabled\" })\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n //node.status({ fill: \"red\", text: \"B\" })\n }\n return [null,msg1]\n }\n // To here `enabled` === 0. Now set to 1.\n //-------------------------------------\n // ENABLED from here down.\n if (enabled === 1)\n {\n\n state = (state + 1)% 2\n context.set(\"STATE\",state)\n\n if (mode === 0)\n {\n if (state == 0)\n {\n //\n // Condition A\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n msg.payload = payloadA\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n }\n else\n if (state == 1)\n {\n //\n // Condition B\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"B\" })\n msg.payload = payloadB\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n }\n } // END FOR MODE === 0\n //-------------------------------------\n // Code here for MODE === 1\n if (mode === 1)\n {\n if (state === 0)\n {\n //\n // Condition A\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\"})\n msg.payload = payloadA\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n } else\n if (state === 1)\n {\n //\n // Condition B\n //\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\"})\n msg.payload = payloadB\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n }\n } // END FOR MODE === 1\n }\n //-------------------------------------\n msg.topic = topic\n return [msg,msg1]\n}\n//-------------------------------------\n\n//-------------------------------------\nif (msg.payload === \"Z\")\n{\n //\n // Timed out\n ////node.warn(\"Timed out\")\n //\n //-------------------------------------\n context.set(\"ENABLED\",0)\n if (mode === 0) \n {\n //\n // Condition A\n //\n node.status({ text: \"mode \" + mode + \"\" })\n context.set(\"STATE\", 0)\n msg1.colour = disabledColour\n msg1.txt = disabledTXT\n msg1.fontclr = disabledTXTColour\n msg = {} // Don't send message on first set up\n }\n //-------------------------------------\n\n //-------------------------------------\n if (mode === 1)\n {\n if (state === 0)\n {\n //\n // Set things for state 0\n //\n msg.payload = payloadA\n msg.topic = topic\n msg1.colour = disabledColour\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n }\n else if (state === 1)\n {\n //\n // Set things for state 1\n //\n msg.payload = payloadB\n msg.topic = topic\n msg1.colour = disabledColour\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n }\n }\n //-------------------------------------\n return [msg,msg1]\n}\n//-------------------------------------\n","outputs":3,"timeout":"","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":600,"y":260,"wires":[[],[],[]],"outputLabels":["Action","To button","Status"]},{"id":"52a1c859d82355af","type":"trigger","z":"e2af3214236cccda","name":"","op1":"","op2":"Z","op1type":"nul","op2type":"str","duration":"3","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":425,"y":370,"wires":[["0e1b336f6628ae72"]],"l":false},{"id":"751ad8b9c47029b1","type":"switch","z":"e2af3214236cccda","name":"payload == `X`","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"X","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":305,"y":370,"wires":[["15caae5aec953df5"]],"l":false},{"id":"15caae5aec953df5","type":"function","z":"e2af3214236cccda","name":"set msg.delay","func":"msg.delay = env.get(\"delay\") || 1000\nreturn msg","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":365,"y":370,"wires":[["52a1c859d82355af"]],"l":false},{"id":"35e044f43ad8f242","type":"inject","z":"e2af3214236cccda","name":"START","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"START","payloadType":"str","x":350,"y":290,"wires":[["0e1b336f6628ae72"]]},{"id":"1daff2c79dbc60c8","type":"subflow:e2af3214236cccda","z":"6cf51e98651df17b","name":"Mode 1 (toggle)","env":[{"name":"delay","value":"500","type":"num"},{"name":"StartOn","value":"A","type":"str"},{"name":"mode","value":"1","type":"num"},{"name":"disabledColour","value":"maroon","type":"str"},{"name":"colourA","value":"yellow","type":"str"},{"name":"colourB","value":"blue","type":"str"},{"name":"disabledTXT","value":"disabled - mode 1","type":"str"},{"name":"txtA","value":"first (1)","type":"str"},{"name":"txtB","value":"second (1)","type":"str"},{"name":"txtclrA","value":"black","type":"str"},{"name":"txtclrB","value":"black","type":"str"},{"name":"payloadA","value":"PayloadA","type":"str"},{"name":"payloadB","value":"PayloadB","type":"str"}],"x":2250,"y":4660,"wires":[["4ca560db24c63940"],["ea0c73f8890f632b","c3e7ff83d992d020"]]}]

````

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [31 May 2026 11:31 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/2 "2026-05-31T11:31:12Z")

</div>

A picture (or 2) is worth a thousand words. 😉

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [31 May 2026 20:14 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/3 "2026-05-31T20:14:54Z")

</div>

The first example of pressing the button.

 ![Screenshot from 2026-06-01 06-06-19](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/b/8/b8ed3908bc85e9abefee428ce32a5258738a7842.png)  
 ![Screenshot from 2026-06-01 06-06-29](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/0/f/0f31d50c56835cd6a0a47abf4086db818004fbd1.png)  
 ![Screenshot from 2026-06-01 06-06-47](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/f/6f7b05788147551fd24d91fb3ef5f2b2be7642b6.png)

Now the toggle one.

 ![Screenshot from 2026-06-01 06-07-15](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/5/c/5cec58d6ebb80670b06fdd38e74d796a00b536bf.png)  
 ![Screenshot from 2026-06-01 06-07-22](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/8/18f6e632adb902061ad75d9a84587baabf99ae85.png)

Here is a couple of example flows.  
(the ones shown here.)

````auto
[{"id":"e2af3214236cccda","type":"subflow","name":"Button toggle with enable","info":"Update 2026 05 30 17:10\n======\nWasn't getting font colour sent at setup\nDISABLED: explination.\n\n--\nIt is the COLOUR which signals the state\nof the `button`.\nIf `mode 0`:\n disabledTXT is the DISABLED text\n txtA is shown when the button is initially pressed. (Enabled)\n and txtB is the text shown with the second (quick) pressed\n AND `payloadB` is used/sent. NOT `payloadA`!\nIf `mode 1`:\n txtA and txtB toggle depending on the\n output message to be sent.\n disabledTXT is NOT used.\n\nUpdate 2024 09 27\n======\nFixed up mistakes in previous documentation.\n\n\nUpdate 2024 06 08\n======\n\nEdited so you can set the start mode.\nA/B\nHUGE rewrite to get code working correctly.\n\nsend \"RESET\" as `msg.payload` and the `startOn`\nsetting will be reloaded.\nsend \"RELOAD\" as `msg.payload` and the state of\nthe node will be resent to the `button` node.\n(NO output sent)\n\nUpdate 2023 09 03\n------\n\nNow fixed.\n`trigger` node not set to accept `msg.delay`\n\n\nUpdate 2022 02 21\n------\n\nTo initialise the node send a message of `Z` into the input.\n\nIf you want a specific ouptut:\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```\nmode: (0/1) (see below)\nStartOn: either A or B (the two conditions)\ndisabledColour: brown\ndisabledTXT: something\ncolourA: lime\ncolourB: green\ntxtA: Log\ntxtB: Stop\ntxtclrA: black\ntxtclrB: black\npayloadA: GO\npayloadB: STOP\ntopicSET: CONTROL\ndelay: 800\n```\n```\nmode - single mode or toggle mode (0/1).\nThis means in toggle mode you get 2 output messages.\nSingle mode you only get one output message.\n```\n```\ndelay - (optional) millisecond delay for time button is active.\n```\n```\ndisabledColour - the colour the button is when disabled.\n```\n```\ndisabledTXT - This is used when the `mode` is set to `1`. The text is displayed by default.\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. Time determined by `delay` value (ms)\n\nThe active time is `3` seconds (adjustable via the `delay` value) to press/toggle the button once initially pressed.\nNow: if you keep pressing the button, it will *wait* until you finish pressing it.\n\nThe background colours (and text colours) apply to the `button` node (or other) that is pressed.\nThat node must be configured to allow inputs of `{{msg.txt}}`, `{{msg.colour}}` and `{{mdg.fontclr}}`.\n","category":"","in":[{"x":190,"y":260,"wires":[{"id":"0e1b336f6628ae72"},{"id":"751ad8b9c47029b1"}]}],"out":[{"x":930,"y":220,"wires":[{"id":"0e1b336f6628ae72","port":0}]},{"x":950,"y":300,"wires":[{"id":"0e1b336f6628ae72","port":1}]}],"env":[{"name":"delay","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"StartOn","type":"str","value":"","ui":{"label":{"en-US":"StartCond"},"type":"input","opts":{"types":["str"]}}},{"name":"mode","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"disabledColour","type":"str","value":""},{"name":"colourA","type":"str","value":""},{"name":"colourB","type":"str","value":""},{"name":"disabledTXT","type":"str","value":""},{"name":"txtA","type":"str","value":""},{"name":"txtB","type":"str","value":""},{"name":"disabledTXTColour","type":"str","value":""},{"name":"txtclrA","type":"str","value":""},{"name":"txtclrB","type":"str","value":""},{"name":"payloadA","type":"str","value":""},{"name":"payloadB","type":"str","value":""},{"name":"topicSET","type":"str","value":""},{"name":"ResendPayloadOnRELOAD","type":"num","value":"","ui":{"label":{"en-US":"Resend payload on RELOAD"},"type":"input","opts":{"types":["num"]}}}],"meta":{"version":"1.0.0","desc":"Protected button press node"},"color":"#3FADB5","outputLabels":["To the `GATE` node","To the `BUTTON` node"],"icon":"node-red-dashboard/ui_switch.png","status":{"x":860,"y":440,"wires":[{"id":"fa7150113df2bfa1","port":0}]}},{"id":"fa7150113df2bfa1","type":"status","z":"e2af3214236cccda","name":"","scope":["0e1b336f6628ae72"],"x":590,"y":440,"wires":[[]]},{"id":"0e1b336f6628ae72","type":"function","z":"e2af3214236cccda","name":"Push Button","func":"// 2026 05 30 18:44 (working on disabledTXT and how to use/show it.)\n\n// ---- Assign variable names to ENV variables.\n// all future use of variables is by their names below.\n// These are the userdefined variables from ENV\nconst mode = parseInt(env.get(\"mode\")) // Should be `const`?\nconst disabledTXT = env.get(\"disabledTXT\")\nconst disabledColour = env.get(\"disabledColour\")\nconst disabledTXTColour = env.get(\"disabledTXTColour\")\nconst colourA = env.get(\"colourA\")\nconst colourB = env.get(\"colourB\")\nconst txtA = env.get(\"txtA\")\nconst txtB = env.get(\"txtB\")\nconst txtclrA = env.get(\"txtclrA\")\nconst txtclrB = env.get(\"txtclrB\")\nconst payloadA = env.get(\"payloadA\")\nconst payloadB = env.get(\"payloadB\")\nlet ReSend = env.get(\"ResendPayloadOnRELOAD\")\nif (ReSend === undefined)\n ReSend = 0\nlet StartOn = env.get(\"StartOn\")\nlet topic = env.get(\"topicSET\")\nif (topic === undefined)\n topic = \"\"\n\n// These are other variables\nlet state = context.get(\"STATE\")||0\nlet enabled = context.get(\"ENABLED\")||0\nconst msg1 = {}\n\n//--------------------------------------------\n\n// STARTUP\nif (msg.payload == \"START\")\n{\n // This is the first message to set things up.\n //msg.payload = StartOn\n msg.payload = \"Z\"\n}\n\n// Reload desired state\nif (msg.payload == \"RESET\")\n{\n // load startOn value.\n msg.payload = StartOn\n}\n\nif (msg.payload == \"RELOAD\")\n{\n // force redisplay for `button` node.\n if (mode == 1)\n {\n if (state == 0)\n {\n //\n msg.payload = payloadA\n msg.topic = topic\n msg1.txt = txtA\n msg1.colour = disabledColour\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n } else\n if (state == 1)\n {\n //\n msg.payload = payloadB\n msg.topic = topic\n msg1.txt = txtB\n msg1.colour = disabledColour\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n }\n }\n if (mode == 0)\n {\n //\n node.status({ text: \"mode \" + mode + \"\" })\n context.set(\"STATE\", 0)\n msg1.colour = disabledColour\n msg1.txt = disabledTXT\n msg1.fontclr = disabledTXTColour\n //msg = {} // Don't send message on first set up\n msg.payload = payloadB\n msg.topic = topic\n }\n if (ReSend == 0)\n {\n //\n msg.payload = null\n msg.topic = null\n }\n return [msg,msg1]\n}\n\n// =========================================\n// Set condition if the message is either A or B\n\nif (msg.payload === \"A\")\n{\n msg1.payload = null\n if (mode == 1)\n {\n //\n msg1.payload = payloadA\n }\n if (mode == 0)\n {\n //\n msg1.payload = disabledTXT\n msg.payload = payloadB // NOTE THIS IS ONLY FOR `mode == 0`!!\n }\n msg.topic = topic\n \n\n msg1.txt = txtA\n msg1.colour = disabledColour\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n \n context.set(\"STATE\",0)\n\n return [msg,msg1]\n}\n\nif (msg.payload === \"B\")\n{\n msg.payload = payloadB\n msg.topic = topic\n\n msg1.txt = txtB\n msg1.colour = disabledColour\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n \n context.set(\"STATE\",1)\n\n return [msg,msg1]\n}\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n// Main routine below.\n//-------------------------------------\n// Now on to the real stuff.\nif (msg.payload === \"X\")\n{\n //\n // Button pressed.\n //\n node.status({fill: \"green\",text: \"Pressed\"})\n if (enabled === 0) // First time button pressed in this sequence\n {\n //\n context.set(\"ENABLED\", 1)\n if (state === 0) // Depending on `state` depends what now happens.\n {\n //\n // Set things for state 0\n //\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n //node.status({ fill: \"yellow\", text: \"A\" })\n }\n else \n if (state === 1)\n {\n //\n // Set things for state 1\n //\n node.status({ fill: \"blue\", text: \"enabled\" })\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n //node.status({ fill: \"red\", text: \"B\" })\n }\n return [null,msg1]\n }\n // To here `enabled` === 0. Now set to 1.\n //-------------------------------------\n // ENABLED from here down.\n if (enabled === 1)\n {\n\n state = (state + 1)% 2\n context.set(\"STATE\",state)\n\n if (mode === 0)\n {\n if (state == 0)\n {\n //\n // Condition A\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n msg.payload = payloadA\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n }\n else\n if (state == 1)\n {\n //\n // Condition B\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"B\" })\n msg.payload = payloadB\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n }\n } // END FOR MODE === 0\n //-------------------------------------\n // Code here for MODE === 1\n if (mode === 1)\n {\n if (state === 0)\n {\n //\n // Condition A\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\"})\n msg.payload = payloadA\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n } else\n if (state === 1)\n {\n //\n // Condition B\n //\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\"})\n msg.payload = payloadB\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n }\n } // END FOR MODE === 1\n }\n //-------------------------------------\n msg.topic = topic\n return [msg,msg1]\n}\n//-------------------------------------\n\n//-------------------------------------\nif (msg.payload === \"Z\")\n{\n //\n // Timed out\n ////node.warn(\"Timed out\")\n //\n //-------------------------------------\n context.set(\"ENABLED\",0)\n if (mode === 0) \n {\n //\n // Condition A\n //\n node.status({ text: \"mode \" + mode + \"\" })\n context.set(\"STATE\", 0)\n msg1.colour = disabledColour\n msg1.txt = disabledTXT\n msg1.fontclr = disabledTXTColour\n msg = {} // Don't send message on first set up\n }\n //-------------------------------------\n\n //-------------------------------------\n if (mode === 1)\n {\n if (state === 0)\n {\n //\n // Set things for state 0\n //\n msg.payload = payloadA\n msg.topic = topic\n msg1.colour = disabledColour\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n }\n else if (state === 1)\n {\n //\n // Set things for state 1\n //\n msg.payload = payloadB\n msg.topic = topic\n msg1.colour = disabledColour\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n }\n }\n //-------------------------------------\n return [msg,msg1]\n}\n//-------------------------------------\n","outputs":3,"timeout":"","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":600,"y":260,"wires":[[],[],[]],"outputLabels":["Action","To button","Status"]},{"id":"52a1c859d82355af","type":"trigger","z":"e2af3214236cccda","name":"","op1":"","op2":"Z","op1type":"nul","op2type":"str","duration":"3","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":425,"y":370,"wires":[["0e1b336f6628ae72"]],"l":false},{"id":"751ad8b9c47029b1","type":"switch","z":"e2af3214236cccda","name":"payload == `X`","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"X","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":305,"y":370,"wires":[["15caae5aec953df5"]],"l":false},{"id":"15caae5aec953df5","type":"function","z":"e2af3214236cccda","name":"set msg.delay","func":"msg.delay = env.get(\"delay\") || 1000\nreturn msg","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":365,"y":370,"wires":[["52a1c859d82355af"]],"l":false},{"id":"35e044f43ad8f242","type":"inject","z":"e2af3214236cccda","name":"START","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"START","payloadType":"str","x":350,"y":290,"wires":[["0e1b336f6628ae72"]]},{"id":"92dad3db1f931790","type":"inject","z":"6cf51e98651df17b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"X","payloadType":"str","x":2080,"y":4330,"wires":[["2543d4e308928e61"]]},{"id":"a373f1291b48ba9b","type":"debug","z":"6cf51e98651df17b","name":"To BUTTON node","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2530,"y":4360,"wires":[]},{"id":"7ea3ca737b21c589","type":"debug","z":"6cf51e98651df17b","name":"ACTION","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":2500,"y":4280,"wires":[]},{"id":"3e3d0449ac4fd03a","type":"inject","z":"6cf51e98651df17b","name":"Start Mode","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.2","topic":"","payload":"RESET","payloadType":"str","x":2070,"y":4270,"wires":[[]]},{"id":"2543d4e308928e61","type":"subflow:e2af3214236cccda","z":"6cf51e98651df17b","name":"Mode 0","env":[{"name":"delay","value":"800","type":"num"},{"name":"StartOn","value":"A","type":"str"},{"name":"mode","value":"0","type":"num"},{"name":"disabledColour","value":"maroon","type":"str"},{"name":"colourA","value":"yellow","type":"str"},{"name":"colourB","value":"blue","type":"str"},{"name":"disabledTXT","value":"disabled text (0)","type":"str"},{"name":"txtA","value":"this (0)","type":"str"},{"name":"txtB","value":"that (0)","type":"str"},{"name":"disabledTXTColour","value":"black","type":"str"},{"name":"txtclrA","value":"black","type":"str"},{"name":"txtclrB","value":"black","type":"str"},{"name":"payloadA","value":"PayloadA","type":"str"},{"name":"payloadB","value":"PayloadB","type":"str"}],"x":2220,"y":4330,"wires":[["7ea3ca737b21c589"],["a373f1291b48ba9b","49fa28f49a3c82f1"]]},{"id":"e97ee7b6f1bb8cc3","type":"inject","z":"6cf51e98651df17b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"RELOAD","payloadType":"str","x":2050,"y":4390,"wires":[["2543d4e308928e61"]]},{"id":"1daff2c79dbc60c8","type":"subflow:e2af3214236cccda","z":"6cf51e98651df17b","name":"Mode 1 (toggle)","env":[{"name":"delay","value":"500","type":"num"},{"name":"StartOn","value":"A","type":"str"},{"name":"mode","value":"1","type":"num"},{"name":"disabledColour","value":"maroon","type":"str"},{"name":"colourA","value":"yellow","type":"str"},{"name":"colourB","value":"blue","type":"str"},{"name":"disabledTXT","value":"disabled - mode 1","type":"str"},{"name":"txtA","value":"first (1)","type":"str"},{"name":"txtB","value":"second (1)","type":"str"},{"name":"txtclrA","value":"black","type":"str"},{"name":"txtclrB","value":"black","type":"str"},{"name":"payloadA","value":"PayloadA","type":"str"},{"name":"payloadB","value":"PayloadB","type":"str"}],"x":2250,"y":4660,"wires":[["4ca560db24c63940"],["ea0c73f8890f632b","c3e7ff83d992d020"]]},{"id":"572e2fb4aaf1f221","type":"inject","z":"6cf51e98651df17b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"X","payloadType":"str","x":2090,"y":4660,"wires":[["1daff2c79dbc60c8"]]},{"id":"199cd5192e5b1f5a","type":"inject","z":"6cf51e98651df17b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"RELOAD","payloadType":"str","x":2060,"y":4720,"wires":[["1daff2c79dbc60c8"]]},{"id":"4ca560db24c63940","type":"debug","z":"6cf51e98651df17b","name":"debug 46","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":2510,"y":4610,"wires":[]},{"id":"ea0c73f8890f632b","type":"debug","z":"6cf51e98651df17b","name":"To BUTTON node","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2540,"y":4690,"wires":[]},{"id":"49fa28f49a3c82f1","type":"ui_button","z":"6cf51e98651df17b","name":"mode 0","group":"091f81e096d98311","order":0,"width":0,"height":0,"passthru":false,"label":"{{msg.txt}}","tooltip":"","color":"{{msg.fontclr}}","bgcolor":"{{msg.colour}}","className":"","icon":"","payload":"X","payloadType":"str","topic":"topic","topicType":"msg","x":2220,"y":4400,"wires":[["2543d4e308928e61"]]},{"id":"c3e7ff83d992d020","type":"ui_button","z":"6cf51e98651df17b","name":"mode 1","group":"091f81e096d98311","order":0,"width":0,"height":0,"passthru":false,"label":"{{msg.txt}}","tooltip":"","color":"{{msg.fontclr}}","bgcolor":"{{msg.colour}}","className":"","icon":"","payload":"X","payloadType":"str","topic":"topic","topicType":"msg","x":2230,"y":4720,"wires":[["1daff2c79dbc60c8"]]},{"id":"03aef5eb78a0fd39","type":"inject","z":"6cf51e98651df17b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"RESET","payloadType":"str","x":2050,"y":4770,"wires":[["1daff2c79dbc60c8"]]},{"id":"091f81e096d98311","type":"ui_group","name":"Subflow testing","tab":"aa487daa.33c1c","order":14,"disp":true,"width":"5","collapse":false,"className":""},{"id":"aa487daa.33c1c","type":"ui_tab","name":"Real_World_Control","icon":"dashboard","order":8,"disabled":false,"hidden":false}]

````

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [31 May 2026 20:39 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/4 "2026-05-31T20:39:36Z")

</div>

Argh! Not _that_ kind of picture! 😃  
 ![MyEyes!MyEyesi.GifGIF](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/7/a/7a9df5cdd8462887b151e5b6acc63db66d563df3.gif)

---

<div class="post-metadata">

**Author:** ![craigcurtin](https://avatars.discourse-cdn.com/v4/letter/c/94ad74/32.png) [@craigcurtin](https://discourse.nodered.org/u/craigcurtin)\
**Post date:** [31 May 2026 22:27 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/5 "2026-05-31T22:27:07Z")

</div>

Andrew,

Feed your code into one of the AI engines and ask it to write a description of what the code does and an overview (as well as analyze it generally) - you will be amazed at the documentation it can produce

Craig

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [1 June 2026 04:33 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/6 "2026-06-01T04:33:12Z")

</div>

Yes, but if anyone can use it.....

Just thought I would share.

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [1 June 2026 04:33 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/7 "2026-06-01T04:33:38Z")

</div>

(What kind of piccies then?)  
🤷

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [1 June 2026 06:53 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/8 "2026-06-01T06:53:09Z")

</div>

I found a bug.

Extract from the code:

```auto
// =========================================
// Set condition if the message is either A or B

if (msg.payload === "A")
{
    msg1.payload = null
    if (mode == 1)
    {
        //
        msg.payload = payloadA // <<==-- This WAS `msg1`. Now correct!
    }
    if (mode == 0)
    {
        //
        msg1.payload = disabledTXT
        msg.payload = payloadB // NOTE THIS IS ONLY FOR `mode == 0`!!
    }
    msg.topic = topic

```

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [1 June 2026 10:29 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/9 "2026-06-01T10:29:14Z")

</div>

> [@Trying\_to\_learn](#):
>
> (What kind of piccies then?)

😃 It was more about the colours. Pretty intense.

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [1 June 2026 10:31 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/10 "2026-06-01T10:31:32Z")

</div>

Yeah, but it is sometimes better to have high contrast colours to better show the state of things than vague similar colours.

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [1 June 2026 10:35 UTC](https://discourse.nodered.org/t/a-subflow-for-people-who-like-buttons/101162/11 "2026-06-01T10:35:26Z")

</div>

Here is the updated code.

~~Forgive me if the comments and _documents_ haven't been updated.~~

# EDIT

(I found another thing I missed)  
I now hope this is a fully working one.

Date has been updated in the docs and the latest fixed mentioned.

````auto
[{"id":"e2af3214236cccda","type":"subflow","name":"Button toggle with enable","info":"Update 2026 06 01 22:00\n======\nWasn't getting font colour sent at setup\nDISABLED: explination.\n\nFixed but with `StartOn` not working\nif `B` is selected.\n\n--\nIt is the COLOUR which signals the state\nof the `button`.\nIf `mode 0`:\n disabledTXT is the DISABLED text\n txtA is shown when the button is initially pressed. (Enabled)\n and txtB is the text shown with the second (quick) pressed\n AND `payloadB` is used/sent. NOT `payloadA`!\nIf `mode 1`:\n txtA and txtB toggle depending on the\n output message to be sent.\n disabledTXT is NOT used.\n\nUpdate 2024 09 27\n======\nFixed up mistakes in previous documentation.\n\n\nUpdate 2024 06 08\n======\n\nEdited so you can set the start mode.\nA/B\nHUGE rewrite to get code working correctly.\n\nsend \"RESET\" as `msg.payload` and the `startOn`\nsetting will be reloaded.\nsend \"RELOAD\" as `msg.payload` and the state of\nthe node will be resent to the `button` node.\n(NO output sent)\n\nUpdate 2023 09 03\n------\n\nNow fixed.\n`trigger` node not set to accept `msg.delay`\n\n\nUpdate 2022 02 21\n------\n\nTo initialise the node send a message of `Z` into the input.\n\nIf you want a specific ouptut:\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```\nmode: (0/1) (see below)\nStartOn: either A or B (the two conditions)\ndisabledColour: brown\ndisabledTXT: something\ncolourA: lime\ncolourB: green\ntxtA: Log\ntxtB: Stop\ntxtclrA: black\ntxtclrB: black\npayloadA: GO\npayloadB: STOP\ntopicSET: CONTROL\ndelay: 800\n```\n```\nmode - single mode or toggle mode (0/1).\nThis means in toggle mode you get 2 output messages.\nSingle mode you only get one output message.\n```\n```\ndelay - (optional) millisecond delay for time button is active.\n```\n```\ndisabledColour - the colour the button is when disabled.\n```\n```\ndisabledTXT - This is used when the `mode` is set to `1`. The text is displayed by default.\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. Time determined by `delay` value (ms)\n\nThe active time is `3` seconds (adjustable via the `delay` value) to press/toggle the button once initially pressed.\nNow: if you keep pressing the button, it will *wait* until you finish pressing it.\n\nThe background colours (and text colours) apply to the `button` node (or other) that is pressed.\nThat node must be configured to allow inputs of `{{msg.txt}}`, `{{msg.colour}}` and `{{mdg.fontclr}}`.\n","category":"","in":[{"x":190,"y":260,"wires":[{"id":"0e1b336f6628ae72"},{"id":"751ad8b9c47029b1"}]}],"out":[{"x":930,"y":220,"wires":[{"id":"0e1b336f6628ae72","port":0}]},{"x":950,"y":300,"wires":[{"id":"0e1b336f6628ae72","port":1}]}],"env":[{"name":"delay","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"StartOn","type":"str","value":"","ui":{"label":{"en-US":"StartCond"},"type":"input","opts":{"types":["str"]}}},{"name":"mode","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"disabledColour","type":"str","value":""},{"name":"colourA","type":"str","value":""},{"name":"colourB","type":"str","value":""},{"name":"disabledTXT","type":"str","value":""},{"name":"txtA","type":"str","value":""},{"name":"txtB","type":"str","value":""},{"name":"disabledTXTColour","type":"str","value":""},{"name":"txtclrA","type":"str","value":""},{"name":"txtclrB","type":"str","value":""},{"name":"payloadA","type":"str","value":""},{"name":"payloadB","type":"str","value":""},{"name":"topicSET","type":"str","value":""},{"name":"ResendPayloadOnRELOAD","type":"num","value":"","ui":{"label":{"en-US":"Resend payload on RELOAD"},"type":"input","opts":{"types":["num"]}}}],"meta":{"version":"1.0.0","desc":"Protected button press node"},"color":"#3FADB5","outputLabels":["To the `GATE` node","To the `BUTTON` node"],"icon":"node-red-dashboard/ui_switch.png","status":{"x":860,"y":440,"wires":[{"id":"fa7150113df2bfa1","port":0}]}},{"id":"fa7150113df2bfa1","type":"status","z":"e2af3214236cccda","name":"","scope":["0e1b336f6628ae72"],"x":590,"y":440,"wires":[[]]},{"id":"0e1b336f6628ae72","type":"function","z":"e2af3214236cccda","name":"Push Button","func":"// 2026 05 30 18:44 (working on disabledTXT and how to use/show it.)\n\n// ---- Assign variable names to ENV variables.\n// all future use of variables is by their names below.\n// These are the userdefined variables from ENV\nconst mode = parseInt(env.get(\"mode\")) // Should be `const`?\nconst disabledTXT = env.get(\"disabledTXT\")\nconst disabledColour = env.get(\"disabledColour\")\nconst disabledTXTColour = env.get(\"disabledTXTColour\")\nconst colourA = env.get(\"colourA\")\nconst colourB = env.get(\"colourB\")\nconst txtA = env.get(\"txtA\")\nconst txtB = env.get(\"txtB\")\nconst txtclrA = env.get(\"txtclrA\")\nconst txtclrB = env.get(\"txtclrB\")\nconst payloadA = env.get(\"payloadA\")\nconst payloadB = env.get(\"payloadB\")\nlet ReSend = env.get(\"ResendPayloadOnRELOAD\")\nif (ReSend === undefined)\n ReSend = 0\nlet StartOn = env.get(\"StartOn\")\nlet topic = env.get(\"topicSET\")\nif (topic === undefined)\n topic = \"\"\n\n// These are other variables\nlet state = context.get(\"STATE\")||0\nlet enabled = context.get(\"ENABLED\")||0\nconst msg1 = {}\n\n//--------------------------------------------\n\n// STARTUP\nif (msg.payload == \"START\")\n{\n // This is the first message to set things up.\n //msg.payload = StartOn\n msg.payload = \"Z\"\n state = (StartOn === \"A\") ? 0 : 1\n}\n\n// Reload desired state\nif (msg.payload == \"RESET\")\n{\n // load startOn value.\n msg.payload = StartOn\n}\n\nif (msg.payload == \"RELOAD\")\n{\n // force redisplay for `button` node.\n if (mode == 1)\n {\n if (state == 0)\n {\n //\n msg.payload = payloadA\n msg.topic = topic\n msg1.txt = txtA\n msg1.colour = disabledColour\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n } else\n if (state == 1)\n {\n //\n msg.payload = payloadB\n msg.topic = topic\n msg1.txt = txtB\n msg1.colour = disabledColour\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n }\n }\n if (mode == 0)\n {\n //\n node.status({ text: \"mode \" + mode + \"\" })\n context.set(\"STATE\", 0)\n msg1.colour = disabledColour\n msg1.txt = disabledTXT\n msg1.fontclr = disabledTXTColour\n //msg = {} // Don't send message on first set up\n msg.payload = payloadB\n msg.topic = topic\n }\n if (ReSend == 0)\n {\n //\n msg.payload = null\n msg.topic = null\n }\n return [msg,msg1]\n}\n\n// =========================================\n// Set condition if the message is either A or B\n\nif (msg.payload === \"A\")\n{\n msg1.payload = null\n if (mode == 1)\n {\n //\n msg.payload = payloadA\n }\n if (mode == 0)\n {\n //\n msg1.payload = disabledTXT\n msg.payload = payloadB // NOTE THIS IS ONLY FOR `mode == 0`!!\n }\n msg.topic = topic\n \n\n msg1.txt = txtA\n msg1.colour = disabledColour\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n \n context.set(\"STATE\",0)\n\n return [msg,msg1]\n}\n\nif (msg.payload === \"B\")\n{\n msg.payload = payloadB\n msg.topic = topic\n\n msg1.txt = txtB\n msg1.colour = disabledColour\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n \n context.set(\"STATE\",1)\n\n return [msg,msg1]\n}\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n// Main routine below.\n//-------------------------------------\n// Now on to the real stuff.\nif (msg.payload === \"X\")\n{\n //\n // Button pressed.\n //\n node.status({fill: \"green\",text: \"Pressed\"})\n if (enabled === 0) // First time button pressed in this sequence\n {\n //\n context.set(\"ENABLED\", 1)\n if (state === 0) // Depending on `state` depends what now happens.\n {\n //\n // Set things for state 0\n //\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n //node.status({ fill: \"yellow\", text: \"A\" })\n }\n else \n if (state === 1)\n {\n //\n // Set things for state 1\n //\n node.status({ fill: \"blue\", text: \"enabled\" })\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n //node.status({ fill: \"red\", text: \"B\" })\n }\n return [null,msg1]\n }\n // To here `enabled` === 0. Now set to 1.\n //-------------------------------------\n // ENABLED from here down.\n if (enabled === 1)\n {\n\n state = (state + 1)% 2\n context.set(\"STATE\",state)\n\n if (mode === 0)\n {\n if (state == 0)\n {\n //\n // Condition A\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n msg.payload = payloadA\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n }\n else\n if (state == 1)\n {\n //\n // Condition B\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"B\" })\n msg.payload = payloadB\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n }\n } // END FOR MODE === 0\n //-------------------------------------\n // Code here for MODE === 1\n if (mode === 1)\n {\n if (state === 0)\n {\n //\n // Condition A\n //\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\"})\n msg.payload = payloadA\n msg1.colour = colourA\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n } else\n if (state === 1)\n {\n //\n // Condition B\n //\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\"})\n msg.payload = payloadB\n msg1.colour = colourB\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n }\n } // END FOR MODE === 1\n }\n //-------------------------------------\n msg.topic = topic\n return [msg,msg1]\n}\n//-------------------------------------\n\n//-------------------------------------\nif (msg.payload === \"Z\")\n{\n //\n // Timed out\n ////node.warn(\"Timed out\")\n //\n //-------------------------------------\n context.set(\"ENABLED\",0)\n if (mode === 0) \n {\n //\n // Condition A\n //\n node.status({ text: \"mode \" + mode + \"\" })\n context.set(\"STATE\", 0)\n msg1.colour = disabledColour\n msg1.txt = disabledTXT\n msg1.fontclr = disabledTXTColour\n msg = {} // Don't send message on first set up\n }\n //-------------------------------------\n\n //-------------------------------------\n if (mode === 1)\n {\n if (state === 0)\n {\n //\n // Set things for state 0\n //\n msg.payload = payloadA\n msg.topic = topic\n msg1.colour = disabledColour\n msg1.txt = txtA\n msg1.fontclr = txtclrA\n node.status({ fill: \"yellow\", text: \"mode \" + mode + \"A\" })\n }\n else if (state === 1)\n {\n //\n // Set things for state 1\n //\n msg.payload = payloadB\n msg.topic = topic\n msg1.colour = disabledColour\n msg1.txt = txtB\n msg1.fontclr = txtclrB\n node.status({ fill: \"red\", text: \"mode \" + mode + \"B\" })\n }\n }\n //-------------------------------------\n return [msg,msg1]\n}\n//-------------------------------------\n","outputs":3,"timeout":"","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":600,"y":260,"wires":[[],[],[]],"outputLabels":["Action","To button","Status"]},{"id":"52a1c859d82355af","type":"trigger","z":"e2af3214236cccda","name":"","op1":"","op2":"Z","op1type":"nul","op2type":"str","duration":"3","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":425,"y":370,"wires":[["0e1b336f6628ae72"]],"l":false},{"id":"751ad8b9c47029b1","type":"switch","z":"e2af3214236cccda","name":"payload == `X`","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"X","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":305,"y":370,"wires":[["15caae5aec953df5"]],"l":false},{"id":"15caae5aec953df5","type":"function","z":"e2af3214236cccda","name":"set msg.delay","func":"msg.delay = env.get(\"delay\") || 1000\nreturn msg","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":365,"y":370,"wires":[["52a1c859d82355af"]],"l":false},{"id":"35e044f43ad8f242","type":"inject","z":"e2af3214236cccda","name":"START","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"START","payloadType":"str","x":350,"y":290,"wires":[["0e1b336f6628ae72"]]},{"id":"940eae8874cd1e5f","type":"subflow:e2af3214236cccda","z":"7085423bf8fdf2c0","name":"","x":890,"y":250,"wires":[[],[]]}]

````
