One way:
[{"id":"984c039df8c2b7ef","type":"ui_switch","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"","label":"safety lock","tooltip":"","group":"c7a27394.057878","order":14,"width":4,"height":1,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"x":1770,"y":1480,"wires":[["cc7a0523bdcbe803","9671f89989381e8e"]]},{"id":"e2ef697a54b18e0b","type":"inject","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":1750,"y":1420,"wires":[["984c039df8c2b7ef"]]},{"id":"9671f89989381e8e","type":"trigger","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"","op1":"","op2":"false","op1type":"nul","op2type":"bool","duration":"3","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":1930,"y":1420,"wires":[["984c039df8c2b7ef"]]},{"id":"084f1df60dac2f7a","type":"change","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"disable ","rules":[{"t":"set","p":"payload","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":2240,"y":1430,"wires":[["984c039df8c2b7ef"]]},{"id":"cc7a0523bdcbe803","type":"change","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"","rules":[{"t":"set","p":"enabled","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1950,"y":1480,"wires":[["19d3ed706256be59"]]},{"id":"19d3ed706256be59","type":"ui_button","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"","group":"c7a27394.057878","order":15,"width":5,"height":1,"passthru":false,"label":"Action","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":2110,"y":1480,"wires":[["8e9f451f6117af1d","084f1df60dac2f7a"]]},{"id":"8e9f451f6117af1d","type":"function","z":"7e987ddf260bdf0d","g":"5e617997398223b5","name":"actual action goes on","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2290,"y":1480,"wires":[[]]},{"id":"c7a27394.057878","type":"ui_group","name":"Alternate","tab":"bb73106a674aef89","order":1,"disp":true,"width":"9","collapse":false},{"id":"bb73106a674aef89","type":"ui_tab","name":"Button press examples","icon":"dashboard","order":41,"disabled":false,"hidden":false}]
I don't like that only because it keeps things happening when it isn't being used.
Another way:
[{"id":"f78bdf06ae572875","type":"subflow","name":"Button toggle with enable","info":"Update 2022 02 21\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)\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 - toggle mode or single 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.\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":130,"y":180,"wires":[{"id":"bc8e2ea24946f650"},{"id":"7f6db7e922a9aec4"}]}],"out":[{"x":620,"y":140,"wires":[{"id":"7f6db7e922a9aec4","port":0}]},{"x":620,"y":220,"wires":[{"id":"7f6db7e922a9aec4","port":1}]}],"env":[{"name":"delay","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"mode","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}},{"name":"disabledColour","type":"str","value":""},{"name":"disabledTXT","type":"str","value":""},{"name":"colourA","type":"str","value":""},{"name":"txtA","type":"str","value":""},{"name":"txtclrA","type":"str","value":""},{"name":"colourB","type":"str","value":""},{"name":"txtB","type":"str","value":""},{"name":"txtclrB","type":"str","value":""},{"name":"payloadA","type":"str","value":""},{"name":"payloadB","type":"str","value":""},{"name":"topicSET","type":"str","value":""}],"meta":{},"color":"#3FADB5","outputLabels":["To the `GATE` node","To the `BUTTON` node"],"icon":"node-red-dashboard/ui_switch.png","status":{"x":620,"y":300,"wires":[{"id":"6aaf4e9c71587bfd","port":0}]}},{"id":"7f6db7e922a9aec4","type":"function","z":"f78bdf06ae572875","name":"Push Button","func":"// ---- 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 INPUT = msg.payload;\n//var mode = env.get(\"mode\");\nvar mode = parseInt(env.get(\"mode\"));\nconst Disabledcolour = env.get(\"disabledColour\");\nconst disabledTXT = env.get(\"disabledTXT\");\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 topic = env.get(\"topicSET\");\nif (topic === undefined)\n topic = \"\";\n\n// These are other variables\nlet state = context.get(\"STATE\")||0;\nconst enabled = context.get(\"ENABLED\")||0;\n\nif (msg.topic === \"D\")\n{\n context.set(\"DEBUG\",msg.payload);\n // Show env \n node.warn(\"Disabled colour \" + Disabledcolour);\n node.warn(\"Disabled text \" + disabledTXT);\n node.warn(\"colourA \" + colourA);\n node.warn(\"colourB \" + colourB); \n node.warn(\"txtA \" + txtA);\n node.warn(\"txtB \" + txtB);\n node.warn(\"txtclrA \" + txtclrA);\n node.warn(\"txtclrB \" + txtclrB);\n node.warn(\"payloadA \" + payloadA);\n node.warn(\"payloadB \" + payloadB);\n node.warn(\"topic \" + topic);\n return;\n}\n\nlet debug = context.get(\"DEBUG\") || 0;\n\nif (debug === 1)\n{\n node.warn(\"INPUT \" + INPUT);\n node.warn(\"Disabled colour \" + Disabledcolour);\n node.warn(\"Disabled text \" + disabledTXT);\n node.warn(\"colourA \" + colourA);\n node.warn(\"colourB \" + colourB); \n node.warn(\"txtA \" + txtA);\n node.warn(\"txtB \" + txtB);\n node.warn(\"txtclrA \" + txtclrA);\n node.warn(\"txtclrB \" + txtclrB);\n node.warn(\"payloadA \" + payloadA);\n node.warn(\"payloadB \" + payloadB);\n node.warn(\"topic \" + topic);\n}\n\n// =========================================\n// Main routine below.\n// Set condition if the message is either A or B\nconst msg1 = {};\n\nif (msg.payload === \"A\")\n{\n msg.payload = payloadA;\n msg.topic = topic;\n\n msg1.txt = txtA;\n msg1.colour = Disabledcolour;\n msg1.fontclr = txtclrA;\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 \n context.set(\"STATE\",1);\n\n return [msg,msg1];\n}\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\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 === undefined)\n {\n node.status({text:\"Button_Toggle_Enable_Context_Not_Set\"});\n node.warn(\"Button toggle enable context not set\");\n return [null,null];\n }\n if (enabled === 0)\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 = colourA;\n msg1.txt = txtA;\n msg1.fontclr = txtclrA;\n }\n else \n if (state === 1)\n {\n //\n // Set things for state 1\n //\n// msg.payload = payloadB;\n// msg.topic = topic;\n msg1.colour = colourB;\n msg1.txt = txtB;\n msg1.fontclr = txtclrB;\n }\n context.set(\"ENABLED\",1);\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\n if (mode === 1)\n {\n state = 1;\n mode = 0;\n }\n // Code here for MODE === 0\n if (mode === 0)\n {\n if (state === 0)\n {\n //\n // Condition A\n //\n node.status({fill: \"yellow\",text: \"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: \"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 msg.topic = topic;\n return [msg,msg1];\n}\n\nif (msg.payload === \"Z\")\n{\n //\n ////node.warn(\"Timed out\");\n //\n context.set(\"ENABLED\",0);\n if (mode === 0)\n {\n node.status({text: \"\"});\n if (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 = payloadA;\n msg.topic = topic;\n msg1.colour = Disabledcolour;\n msg1.txt = txtA;\n msg1.fontclr = txtclrA;\n }\n else if (state === 1)\n {\n //\n // Set things for state 2\n //\n msg.payload = payloadB;\n msg.topic = topic;\n msg1.colour = Disabledcolour;\n msg1.txt = txtB;\n msg1.fontclr = txtclrB;\n }\n }\n \n if (mode === 1)\n {\n //\n // Condition A\n //\n node.status({text: \"\"});\n context.set(\"STATE\",0);\n msg1.colour = Disabledcolour;\n msg1.txt = disabledTXT;\n }\n\n return [null,msg1];\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":440,"y":180,"wires":[[],[]],"outputLabels":["To gate","To button"]},{"id":"6aaf4e9c71587bfd","type":"status","z":"f78bdf06ae572875","name":"","scope":["7f6db7e922a9aec4"],"x":460,"y":300,"wires":[[]]},{"id":"d13a8e023b434961","type":"trigger","z":"f78bdf06ae572875","name":"","op1":"","op2":"Z","op1type":"nul","op2type":"str","duration":"3","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":315,"y":150,"wires":[["7f6db7e922a9aec4"]],"l":false},{"id":"bc8e2ea24946f650","type":"switch","z":"f78bdf06ae572875","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"X","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":195,"y":150,"wires":[["69a7bd973e5f8bdd"]],"l":false},{"id":"69a7bd973e5f8bdd","type":"function","z":"f78bdf06ae572875","name":"set msg.delay","func":"msg.delay = env.get(\"delay\") || 1000;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":245,"y":150,"wires":[["d13a8e023b434961"]],"l":false},{"id":"bd1396b75a314127","type":"ui_button","z":"7e987ddf260bdf0d","g":"4254e69c62b12307","name":"Double press","group":"2d3c012e72579d4d","order":0,"width":"3","height":"2","passthru":false,"label":"{{msg.txt}}","tooltip":"","color":"{{msg.fontclr}}","bgcolor":"{{msg.colour}}","icon":"","payload":"X","payloadType":"str","topic":"topic","topicType":"msg","x":1750,"y":1730,"wires":[["6ee44862c31ba778"]]},{"id":"6ee44862c31ba778","type":"subflow:f78bdf06ae572875","z":"7e987ddf260bdf0d","g":"4254e69c62b12307","name":"","env":[{"name":"delay","value":"1500","type":"num"},{"name":"mode","value":"1","type":"num"},{"name":"disabledColour","value":"black","type":"str"},{"name":"disabledTXT","value":"Press to activate","type":"str"},{"name":"colourA","value":"red","type":"str"},{"name":"colourB","value":"blue","type":"str"},{"name":"txtA","value":"Are you sure?","type":"str"},{"name":"txtB","value":"Done!","type":"str"},{"name":"txtclrA","value":"white","type":"str"},{"name":"txtclrB","value":"white","type":"str"},{"name":"payloadA","value":"A","type":"str"},{"name":"payloadB","value":"B","type":"str"},{"name":"topicSET","value":"restore","type":"str"}],"x":1950,"y":1730,"wires":[["300b961c28d814cb"],["bd1396b75a314127"]]},{"id":"300b961c28d814cb","type":"debug","z":"7e987ddf260bdf0d","name":"This gets things done","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":2220,"y":1730,"wires":[]},{"id":"2d3c012e72579d4d","type":"ui_group","name":"Double press","tab":"bb73106a674aef89","order":3,"disp":true,"width":"6","collapse":false},{"id":"bb73106a674aef89","type":"ui_tab","name":"Button press examples","icon":"dashboard","order":41,"disabled":false,"hidden":false}]
You have to double click the button to get things done.