How to detect multiple presses on a single button

With reference to this thread:
Single button multiple presses

Have you ever wanted to allow multiple presses from a button do multiple things?

This flow allows that.

One "problem" the use of a flow. variable.

If you are going to use multiple versions on the one flow, you need to edit the name used in the flow.set node and the count presses node.

Oh, and you will/may need to install this node:
node-red-contrib-counter

[{"id":"47f8333.7595f4c","type":"change","z":"710ca09a.8302a8","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"foo","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":1000,"wires":[["baa481f7.8c92f"]]},{"id":"46bb072c.9ab548","type":"inject","z":"710ca09a.8302a8","name":"Reset","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"0.5","x":400,"y":1040,"wires":[["47f8333.7595f4c"]]},{"id":"6419695d.f444a8","type":"debug","z":"710ca09a.8302a8","name":"Four presses","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1170,"y":1120,"wires":[]},{"id":"9a9ece0e.e21968","type":"inject","z":"710ca09a.8302a8","name":"Start","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":403.33331298828125,"y":951.111083984375,"wires":[["baa481f7.8c92f","d4d7fd43.e18b5"]]},{"id":"baa481f7.8c92f","type":"counter","z":"710ca09a.8302a8","name":"","init":"0","step":"1","lower":null,"upper":null,"mode":"increment","outputs":"1","x":590,"y":950,"wires":[["eeab3859.a0a81","1ccfa267.6709d6"]]},{"id":"cdc2f3f7.8cd668","type":"switch","z":"710ca09a.8302a8","name":"count presses","property":"count","propertyType":"flow","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"},{"t":"eq","v":"4","vt":"num"}],"checkall":"true","repair":false,"outputs":4,"x":790,"y":1060,"wires":[["c3a6c0e7.e7824"],["1939c1b3.49b2c6"],["7bfb6bfb.a555f4"],["c75d22c1.43a18"]],"info":"Just expand the flow.count to how ever many\npresses you want/need."},{"id":"d4d7fd43.e18b5","type":"trigger","z":"710ca09a.8302a8","op1":"","op2":"0","op1type":"nul","op2type":"str","duration":"6","extend":false,"units":"s","reset":"","bytopic":"all","name":"","x":410,"y":1000,"wires":[["47f8333.7595f4c"]]},{"id":"1ccfa267.6709d6","type":"change","z":"710ca09a.8302a8","name":"Flow set","rules":[{"t":"set","p":"count","pt":"flow","to":"count","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":770,"y":910,"wires":[[]]},{"id":"d5ec0e6d.65fe68","type":"debug","z":"710ca09a.8302a8","name":"Tripple press","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1170,"y":1080,"wires":[]},{"id":"a4900536.188a4","type":"debug","z":"710ca09a.8302a8","name":"Double press","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1170,"y":1040,"wires":[]},{"id":"f7ea3f92.56f1b","type":"delay","z":"710ca09a.8302a8","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":770,"y":1000,"wires":[["cdc2f3f7.8cd668"]]},{"id":"eaa4f15.17e7a9","type":"debug","z":"710ca09a.8302a8","name":"Single Press","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1170,"y":1000,"wires":[]},{"id":"eeab3859.a0a81","type":"switch","z":"710ca09a.8302a8","name":"","property":"count","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":760,"y":950,"wires":[["f7ea3f92.56f1b"]]},{"id":"c3a6c0e7.e7824","type":"change","z":"710ca09a.8302a8","name":"Single Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Single Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1000,"wires":[["eaa4f15.17e7a9"]]},{"id":"1939c1b3.49b2c6","type":"change","z":"710ca09a.8302a8","name":"Double Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Double Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":1040,"wires":[["a4900536.188a4"]]},{"id":"7bfb6bfb.a555f4","type":"change","z":"710ca09a.8302a8","name":"Tripple Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Tripple Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1080,"wires":[["d5ec0e6d.65fe68"]]},{"id":"c75d22c1.43a18","type":"change","z":"710ca09a.8302a8","name":"Four Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Four Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1120,"wires":[["6419695d.f444a8"]]},{"id":"c0dd4310.43d0a8","type":"comment","z":"710ca09a.8302a8","name":"DELAY - Readme","info":"The DELAY time has to be a bit longer than how\nlong it takes you to press the button the\nmaximum number of times.\nRead information in the SWITCH node too!","x":790,"y":870,"wires":[]},{"id":"8caf97b8.187508","type":"comment","z":"710ca09a.8302a8","name":"Input","info":"","x":400,"y":910,"wires":[]},{"id":"70b7384.c5ce6c8","type":"comment","z":"710ca09a.8302a8","name":"Outputs","info":"Debug nodes are here now, but just connect\nthem to what ever you want.\nThe messages sent are in the nodes just before\nthe debug nodes.\n","x":1150,"y":960,"wires":[]}]
2 Likes

I've re-written the code to optimise how it works and for how long you can be pressing the button.

The old flow limits you to 6 seconds. Granted that is a long time, but it is limited.

I re-wrote how it works which allows unlimited pressing of the button.

Also with the old way you had to wait 6 seconds between sequences, which is also a bit long.

The new one takes that down to 1 second.

This new way the reset signal is kept from being sent because the button has been pressed again with an interval of less than 1 second.

This is 4 examples.
I pressed the inject node 1, 2,3 and 4 times, and got those 4 messages.
Sorry too: I haven't updated an of the comment nodes.

I was a bit over come with excitement that I got the re-write done and wanted to share.
(Yeah, I know. Not really professional.)

[{"id":"1139a03d.d1ca","type":"change","z":"184dc884.7aba5f","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"foo","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":2270,"wires":[["fcb3cb17.693a9"]]},{"id":"900577c3.9deb4","type":"inject","z":"184dc884.7aba5f","name":"Reset","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"0.5","x":430,"y":2230,"wires":[["1139a03d.d1ca"]]},{"id":"820bd696.fbc028","type":"debug","z":"184dc884.7aba5f","name":"Four presses","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1180,"y":2370,"wires":[]},{"id":"525e3ec9.3bb6b8","type":"inject","z":"184dc884.7aba5f","name":"Start","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":273.33331298828125,"y":2201.111083984375,"wires":[["fcb3cb17.693a9","ba72e20d.299b8"]]},{"id":"fcb3cb17.693a9","type":"counter","z":"184dc884.7aba5f","name":"","init":"0","step":"1","lower":null,"upper":null,"mode":"increment","outputs":"1","x":620,"y":2200,"wires":[["9faf97df.4ce4a"]]},{"id":"4189a6d1.44073","type":"switch","z":"184dc884.7aba5f","name":"count presses","property":"count","propertyType":"flow","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"},{"t":"eq","v":"4","vt":"num"}],"checkall":"true","repair":false,"outputs":4,"x":790,"y":2310,"wires":[["83d02113.4743b8"],["fbfe53e5.4fcae8"],["e22efc97.c6b018"],["d9a249bd.1f82a8"]],"info":"Just expand the flow.count to how ever many\npresses you want/need."},{"id":"ba72e20d.299b8","type":"trigger","z":"184dc884.7aba5f","op1":"","op2":"0","op1type":"nul","op2type":"str","duration":"1","extend":true,"units":"s","reset":"","bytopic":"all","name":"","x":440,"y":2310,"wires":[["62860129.ec7bd","4189a6d1.44073"]]},{"id":"9faf97df.4ce4a","type":"change","z":"184dc884.7aba5f","name":"Flow set","rules":[{"t":"set","p":"count","pt":"flow","to":"count","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":770,"y":2200,"wires":[["a018a094.13d14"]]},{"id":"ddd0ab58.0a3e58","type":"debug","z":"184dc884.7aba5f","name":"Tripple press","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1180,"y":2330,"wires":[]},{"id":"ed9739c.0628148","type":"debug","z":"184dc884.7aba5f","name":"Double press","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1180,"y":2290,"wires":[]},{"id":"8082f0.4ba02d1","type":"debug","z":"184dc884.7aba5f","name":"Single Press","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1180,"y":2250,"wires":[]},{"id":"83d02113.4743b8","type":"change","z":"184dc884.7aba5f","name":"Single Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Single Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":2250,"wires":[["8082f0.4ba02d1"]]},{"id":"fbfe53e5.4fcae8","type":"change","z":"184dc884.7aba5f","name":"Double Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Double Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":2290,"wires":[["ed9739c.0628148"]]},{"id":"e22efc97.c6b018","type":"change","z":"184dc884.7aba5f","name":"Tripple Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Tripple Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":2330,"wires":[["ddd0ab58.0a3e58"]]},{"id":"d9a249bd.1f82a8","type":"change","z":"184dc884.7aba5f","name":"Four Press","rules":[{"t":"set","p":"payload","pt":"msg","to":"Four Press","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":2370,"wires":[["820bd696.fbc028"]]},{"id":"a1783324.3f9f","type":"comment","z":"184dc884.7aba5f","name":"DELAY - Readme","info":"The DELAY time has to be a bit longer than how\nlong it takes you to press the button the\nmaximum number of times.\nRead information in the SWITCH node too!","x":820,"y":2120,"wires":[]},{"id":"660d35d0.b41aa4","type":"comment","z":"184dc884.7aba5f","name":"Input","info":"","x":430,"y":2160,"wires":[]},{"id":"f91932b.cacfb5","type":"comment","z":"184dc884.7aba5f","name":"Outputs","info":"Debug nodes are here now, but just connect\nthem to what ever you want.\nThe messages sent are in the nodes just before\nthe debug nodes.\n","x":1160,"y":2210,"wires":[]},{"id":"a018a094.13d14","type":"debug","z":"184dc884.7aba5f","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"count","targetType":"msg","x":1000,"y":2200,"wires":[]},{"id":"62860129.ec7bd","type":"delay","z":"184dc884.7aba5f","name":"Delay","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":430,"y":2270,"wires":[["1139a03d.d1ca"]]}]
2 Likes

This is an excellent flow, I've been looking for something similar for my garage. On two occasions the garage has opened and i wasn't sure why. I feel like if i put the garage switch on a double press it should eliminate that problem.

Makes me want to add secret double press and triple presses to all my room light switches now.

Hi everyone

I'm looking to something very similar as your solution, but would like to detect also long presses for dimming while the button is pressed.

  • sigle cick --> toggle
  • double click / three time click --> special action like activate a szene
  • long press --> dimming while button is pressed

With some google search I got to this article and also found the folowing one.
https://groups.google.com/g/node-red/c/WcHYquUvLjE?pli=1

With the "node-red-contrib-timed-counter" node it looks to me as a very simple solution. But with my sketch and also with the one from the link above I get the folowing error out of the "timed-counter" node.

"Creating handler for ....."

Do you have a Idea what that means and how I can fix it?

I use Node-Red V1.1.2
And the node-red-contrib-timed-counter is the V0.0.4

Thanks for your help.

Hi everyone

First I would like to thank “Trying_to_learn”, because this was very helpful for me too. After some trail and error and a lot of fine tuning I found a solution that works fine for me also with dimming. So I would like to share it with you. My solution builds up on the solution from “Trying_to_learn”. Because of that I share my code in this post.

Function:

My flow is designed to get inputs from a MQTT message that comes from a shelly device and outputs some values for a device that is programmed by my selves. For other uses the flow should be relatively easy to adapt. If not, just ask.

One click, Multiple clicks --> programmable action like toggle a lamp

Long press --> dimming of main output

The clicks are recognized on the falling edges. With that it is possible to detect a long press bevor a single click happens. The single click that would happen when you release the button is suppressed after a dimming action.

Also the time for multiple clicks will be reset after each click. With that the reset delay time counts between the clicks, not fix for all clicks together. This is a bit more useful to be able to click several times, because the delay time gets renewed every click.

Optimization potential:

The dimming holds the value without caring about if the light was turned ON or OFF befor starting dimming with a long press. So if the light is OFF and you hold down the button, it could start with a bright light and then start dimming down.

If someone has ideas how to solve this, I would be thankful.

Used additional nodes:

node-red-contrib-time-based-dimmer

[{"id":"b05aa0d1.fec8b","type":"function","z":"d9cf9e97.6745a","name":"store old states","func":"global.set(msg.topic, msg.payload); // store data in context\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":380,"y":960,"wires":[[]]},{"id":"1c03a867.85dc38","type":"mqtt in","z":"d9cf9e97.6745a","name":"all MQTT topics","topic":"#","qos":"2","datatype":"auto","broker":"b496b411.fe5358","x":140,"y":960,"wires":[["b05aa0d1.fec8b"]]},{"id":"5c465ea2.9fe8a","type":"comment","z":"d9cf9e97.6745a","name":"store states","info":"store states for toggeling outputs","x":150,"y":920,"wires":[]},{"id":"cdd00358.59912","type":"mqtt in","z":"d9cf9e97.6745a","name":"","topic":"shellies/shellyrgbw2-6D11B1/input/0","qos":"2","datatype":"auto","broker":"b496b411.fe5358","x":200,"y":1180,"wires":[["547a44be.7c04bc","e910a7af.637cc8"]]},{"id":"43ff6d40.885d24","type":"counter","z":"d9cf9e97.6745a","name":"button counter","init":"0","step":"1","lower":"","upper":"","mode":"increment","outputs":1,"x":1120,"y":1120,"wires":[["ca30ec30.3ba32"]]},{"id":"91cd0240.3289b","type":"change","z":"d9cf9e97.6745a","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"foo","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":1180,"wires":[["43ff6d40.885d24"]]},{"id":"ca30ec30.3ba32","type":"function","z":"d9cf9e97.6745a","name":"click action","func":"var newMsg = {payload: null, topic: null};\nconst outTopic_main = \"/office/woodlamp4/\"; // topic that should perform a action out of the clicks\nconst outTopic_doubleClick = \"/office/woodlamp3/\";\nconst outTopic_threeClick = \"/office/woodlamp2/\";\n\nconst dimmingTopic = \"dimming\";             // string of topic for detecting dimming\nconst minONValue = 10;                      // button state is set to ON or OFF with this limit\n\nif(msg.topic == dimmingTopic)\n{\n    // dimming action\n    newMsg.topic = outTopic_main;\n    newMsg.payload = msg.payload;\n    \n    // store sate of dimmer for click-action use\n    if(newMsg.payload > minONValue)\n        context.set(dimmingTopic, msg.payload);     \n    else\n        context.set(dimmingTopic, minONValue+1);\n}\nelse\n{\n    // click action\n    switch(msg.count)\n    {\n        case 0: //do nothing after reset the counter to zero\n            break;\n            \n        case 1: // single click action\n            if(global.get(outTopic_main) < minONValue)          // light is OFF and will be turned ON\n            {\n                newMsg.topic = outTopic_main;\n                newMsg.payload = context.get(dimmingTopic);\n            }\n            else                                                // light is ON and will be turned OFF\n            {\n                newMsg.topic = outTopic_main;\n                newMsg.payload = 0;\n            }\n            break;\n        \n        case 2: // double click action\n            if(global.get(outTopic_doubleClick) < minONValue)    // light is OFF and will be turned ON\n            {\n                newMsg.topic = outTopic_doubleClick;\n                newMsg.payload = 200;\n            }\n            else                                                 // light is ON and will be turned OFF\n            {\n                newMsg.topic = outTopic_doubleClick;\n                newMsg.payload = 0;\n            }\n            break;\n        \n        case 3: // three clicks action\n            if(global.get(outTopic_threeClick) < minONValue)    // light is OFF and will be turned ON\n            {\n                newMsg.topic = outTopic_threeClick;\n                newMsg.payload = 200;\n            }\n            else                                                // light is ON and will be turned OFF\n            {\n                newMsg.topic = outTopic_threeClick;\n                newMsg.payload = 0;\n            }\n            break;\n        \n        default: // more clicks action\n            break;\n    }\n}\n\nif(newMsg.topic !== null){          // send if a action should perform\n    return newMsg;\n}","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\nconst dimmingTopic = \"dimming\";\ncontext.set(dimmingTopic, 200); ","finalize":"","x":1310,"y":1120,"wires":[["a5bc6ffc.dd9c"]]},{"id":"a5bc6ffc.dd9c","type":"mqtt out","z":"d9cf9e97.6745a","name":"","topic":"","qos":"","retain":"","broker":"b496b411.fe5358","x":1470,"y":1120,"wires":[]},{"id":"eec4f702.a1c148","type":"comment","z":"d9cf9e97.6745a","name":"Filter dimming","info":"","x":470,"y":1060,"wires":[]},{"id":"372252f9.7ebcae","type":"comment","z":"d9cf9e97.6745a","name":"Reset","info":"Resets the counter of clicks after a fix given time.","x":950,"y":1060,"wires":[]},{"id":"b535aed9.f19a","type":"comment","z":"d9cf9e97.6745a","name":"counter","info":"counts the number of clicks until it is reseted from the reset mechanism.\n\nmsg.count: shows the count value\nmsg.payload: input value of this node","x":1110,"y":1060,"wires":[]},{"id":"32618161.ba173e","type":"comment","z":"d9cf9e97.6745a","name":"click action","info":"defines the action that should be performed when the button is clicked.","x":1300,"y":1060,"wires":[]},{"id":"bf124070.cd469","type":"trigger","z":"d9cf9e97.6745a","name":"delay","op1":"","op2":"reset","op1type":"nul","op2type":"str","duration":"500","extend":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":870,"y":1180,"wires":[["91cd0240.3289b"]],"info":"The delay will be extended with every message / klick. With this behaver it is possible to detect also longer sequences of clicks."},{"id":"81a4be1a.2cce7","type":"comment","z":"d9cf9e97.6745a","name":"Signal Inputs","info":"","x":230,"y":1060,"wires":[]},{"id":"e910a7af.637cc8","type":"trigger","z":"d9cf9e97.6745a","name":"start dimming delay","op1":"","op2":"start dimming","op1type":"nul","op2type":"str","duration":"500","extend":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":410,"y":1240,"wires":[["547a44be.7c04bc"]],"info":"The delay will be extended with every message / klick. With this behaver it is possible to detect also longer sequences of clicks."},{"id":"65418864.bdae38","type":"change","z":"d9cf9e97.6745a","name":"dimm prepair","rules":[{"t":"set","p":"topic","pt":"msg","to":"dimming","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":1240,"wires":[["ca30ec30.3ba32"]]},{"id":"547a44be.7c04bc","type":"function","z":"d9cf9e97.6745a","name":"start dimming?","func":"var newMsg = {payload: null, topic: null};\nconst lastButtonAction = \"lastButtonAction\";\n\n\nif(msg.payload == \"start dimming\")\n{\n    // \"start dimming\" delay is over and we can check if the button is still pressed\n    \n    if(context.get(lastButtonAction) == 1)\n    {\n        // button is still pressed and we can start dimming\n        newMsg.payload = msg.payload;\n        \n    }\n}\nelse\n{\n    // button action that is not \"start dimming\" --> rising edge (1) or falling edge (0)\n    \n    context.set(lastButtonAction, msg.payload); // store if the button was last on a faling (0), rising edge (1) or \"start dimming\"\n    newMsg.payload = msg.payload; // gives button state to the next node\n}\n\n\nif(newMsg.payload !== null){          // send if a action should perform\n    return newMsg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":1180,"wires":[["8fc6262c.8f9868","edf4006c.a22a"]]},{"id":"8fc6262c.8f9868","type":"function","z":"d9cf9e97.6745a","name":"faling edge if not dimming","func":"var newMsg = {payload: null, topic: null};\nconst lastMsg = \"lastMsg\"\n\nif(context.get(lastMsg) != \"start dimming\")\n{\n    // dimming is not active\n    if (msg.payload == \"0\")\n    {\n        newMsg.payload = msg.payload;   // just forward faling edges\n    }\n    \n}\n\ncontext.set(lastMsg, msg.payload);      // store msg as lastMsg\n\nif(newMsg.payload !== null)\n{\n    return newMsg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":770,"y":1120,"wires":[["43ff6d40.885d24","bf124070.cd469"]]},{"id":"edf4006c.a22a","type":"one-button-dimmer","z":"d9cf9e97.6745a","name":"","interval":"20","step":"1","minValue":0,"maxValue":"255","startCommand":"start dimming","stopCommand":"0","x":760,"y":1240,"wires":[["65418864.bdae38"]]},{"id":"b496b411.fe5358","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
2 Likes

That sounds great!

I will see if I can find time to play with it, as this may have a use for me.

I don't have any of those devices. Would it be too much for you to make a flow which emulates them?

That way I can see the messages come in (etc).

So how I am reading it:

  • single/multiple clicks selects the lamp.

  • long clicks adjust the brightness.

But what do you do to simply turn the selected light on/off?

Say, I want to turn on "light 2" (given it is now off)

What do I do?

click, click. pause click?

Shelly to Shelly

Today I have adapted the flow for a flow with just shelly deviced (input and output "shelly"). This could help some more folk, because the shelly devices are quit popular these days.

function clearification

  • Single click: turns on/off a specific lamp, or what ever you programm in the "click action" node e specific light.
  • multible clicks: also free programmable, maybe prefered for light scene activations
  • long clicks: dimms the "single click light" up or down (changes each time if you press the button long). The action of the dimming can be defined also in the "click action" node.

So after copy the flow, you need to adjust the input to your needs (in my case a MQTT node) that sends 1 (pressed) and 0 (released) as a payload. The output behaver you can adjust in the "click action" node. If you do not use MQTT, you can also split the informations afterwards by the topics.

I hope it helps to understand the function. If not, pleas ask.

[{"id":"b05aa0d1.fec8b","type":"function","z":"d9cf9e97.6745a","name":"store old states","func":"global.set(msg.topic, msg.payload); // store data in context\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":380,"y":960,"wires":[[]]},{"id":"1c03a867.85dc38","type":"mqtt in","z":"d9cf9e97.6745a","name":"all MQTT topics","topic":"#","qos":"2","datatype":"auto","broker":"b496b411.fe5358","x":140,"y":960,"wires":[["b05aa0d1.fec8b"]]},{"id":"5c465ea2.9fe8a","type":"comment","z":"d9cf9e97.6745a","name":"store states","info":"store states for toggeling outputs","x":150,"y":920,"wires":[]},{"id":"cdd00358.59912","type":"mqtt in","z":"d9cf9e97.6745a","name":"","topic":"shellies/shellyrgbw2-6D11B1/input/0","qos":"2","datatype":"auto","broker":"b496b411.fe5358","x":200,"y":1180,"wires":[["547a44be.7c04bc","e910a7af.637cc8"]]},{"id":"43ff6d40.885d24","type":"counter","z":"d9cf9e97.6745a","name":"button counter","init":"0","step":"1","lower":"","upper":"","mode":"increment","outputs":1,"x":1120,"y":1120,"wires":[["ca30ec30.3ba32"]]},{"id":"91cd0240.3289b","type":"change","z":"d9cf9e97.6745a","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"foo","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":1180,"wires":[["43ff6d40.885d24"]]},{"id":"ca30ec30.3ba32","type":"function","z":"d9cf9e97.6745a","name":"click action","func":"var newMsg = {payload: null, topic: null};\nconst outTopic_main = \"shellies/shellyrgbw2-6D11B1/white/0/command\"; // topic that should perform a action out of the clicks\nconst outTopic_dim = \"shellies/shellyrgbw2-6D11B1/white/0/set\";\nconst inTopic_main = \"shellies/shellyrgbw2-6D11B1/white/0\";\n\nconst dimmingTopic = \"dimming\";             // string of topic for detecting dimming\nconst minONValue = 10;                      // button state is set to ON or OFF with this limit\n\nif(msg.topic == dimmingTopic)\n{\n    // dimming action\n    newMsg.topic = outTopic_dim;\n    newMsg.payload = {\"brightness\": msg.payload, \"turn\": \"on\"};\n}\nelse\n{\n    // click action\n    switch(msg.count)\n    {\n        case 0: //do nothing after reset the counter to zero\n            break;\n            \n        case 1: // single click action\n            if(global.get(inTopic_main) == \"on\")    // light is OFF and will be turned ON\n            {\n                newMsg.topic = outTopic_main;\n                newMsg.payload = \"off\";\n            }\n            else                                    // light is ON and will be turned OFF\n            {\n                newMsg.topic = outTopic_main;\n                newMsg.payload = \"on\";\n                \n                if(JSON.parse(global.get(outTopic_dim)).brightness < minONValue)\n                {\n                    // shelly is dimmed down and will be set to the min value to be turned ON\n                    newMsg.topic = outTopic_dim;\n                    newMsg.payload = {\"brightness\": minONValue, \"turn\": \"on\"};\n                }\n            }\n            break;\n        \n        case 2: // double click action\n            break;\n        \n        case 3: // three clicks action\n            break;\n        \n        default: // more clicks action\n            break;\n    }\n}\n\nif(newMsg.topic !== null){          // send if a action should perform\n    return newMsg;\n}","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\nconst dimmingTopic = \"dimming\";\ncontext.set(dimmingTopic, 200); ","finalize":"","x":1310,"y":1120,"wires":[["a5bc6ffc.dd9c","1089ac96.5c7613"]]},{"id":"a5bc6ffc.dd9c","type":"mqtt out","z":"d9cf9e97.6745a","name":"","topic":"","qos":"","retain":"","broker":"b496b411.fe5358","x":1470,"y":1120,"wires":[]},{"id":"bf124070.cd469","type":"trigger","z":"d9cf9e97.6745a","name":"delay","op1":"","op2":"reset","op1type":"nul","op2type":"str","duration":"500","extend":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":870,"y":1180,"wires":[["91cd0240.3289b"]],"info":"The delay will be extended with every message / klick. With this behaver it is possible to detect also longer sequences of clicks."},{"id":"e910a7af.637cc8","type":"trigger","z":"d9cf9e97.6745a","name":"start dimming delay","op1":"","op2":"start dimming","op1type":"nul","op2type":"str","duration":"500","extend":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":410,"y":1240,"wires":[["547a44be.7c04bc"]],"info":"The delay will be extended with every message / klick. With this behaver it is possible to detect also longer sequences of clicks."},{"id":"65418864.bdae38","type":"change","z":"d9cf9e97.6745a","name":"dimm prepair","rules":[{"t":"set","p":"topic","pt":"msg","to":"dimming","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":1240,"wires":[["ca30ec30.3ba32"]]},{"id":"547a44be.7c04bc","type":"function","z":"d9cf9e97.6745a","name":"start dimming?","func":"var newMsg = {payload: null, topic: null};\nconst lastButtonAction = \"lastButtonAction\";\n\n\nif(msg.payload == \"start dimming\")\n{\n    // \"start dimming\" delay is over and we can check if the button is still pressed\n    \n    if(context.get(lastButtonAction) == 1)\n    {\n        // button is still pressed and we can start dimming\n        newMsg.payload = msg.payload;\n        \n    }\n}\nelse\n{\n    // button action that is not \"start dimming\" --> rising edge (1) or falling edge (0)\n    \n    context.set(lastButtonAction, msg.payload); // store if the button was last on a faling (0), rising edge (1) or \"start dimming\"\n    newMsg.payload = msg.payload; // gives button state to the next node\n}\n\n\nif(newMsg.payload !== null){          // send if a action should perform\n    return newMsg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":1180,"wires":[["8fc6262c.8f9868","edf4006c.a22a"]]},{"id":"8fc6262c.8f9868","type":"function","z":"d9cf9e97.6745a","name":"faling edge if not dimming","func":"var newMsg = {payload: null, topic: null};\nconst lastMsg = \"lastMsg\"\n\nif(context.get(lastMsg) != \"start dimming\")\n{\n    // dimming is not active\n    if (msg.payload == \"0\")\n    {\n        newMsg.payload = msg.payload;   // just forward faling edges\n    }\n    \n}\n\ncontext.set(lastMsg, msg.payload);      // store msg as lastMsg\n\nif(newMsg.payload !== null)\n{\n    return newMsg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":770,"y":1120,"wires":[["43ff6d40.885d24","bf124070.cd469"]]},{"id":"edf4006c.a22a","type":"one-button-dimmer","z":"d9cf9e97.6745a","name":"","interval":"150","step":"3","minValue":0,"maxValue":"100","startCommand":"start dimming","stopCommand":"0","x":760,"y":1240,"wires":[["65418864.bdae38"]]},{"id":"65554949.9824e8","type":"comment","z":"d9cf9e97.6745a","name":"office LED band","info":"","x":140,"y":1120,"wires":[]},{"id":"eec4f702.a1c148","type":"comment","z":"d9cf9e97.6745a","name":"Filter dimming","info":"","x":470,"y":1060,"wires":[]},{"id":"372252f9.7ebcae","type":"comment","z":"d9cf9e97.6745a","name":"Reset","info":"Resets the counter of clicks after a fix given time.","x":950,"y":1060,"wires":[]},{"id":"b535aed9.f19a","type":"comment","z":"d9cf9e97.6745a","name":"counter","info":"counts the number of clicks until it is reseted from the reset mechanism.\n\nmsg.count: shows the count value\nmsg.payload: input value of this node","x":1110,"y":1060,"wires":[]},{"id":"32618161.ba173e","type":"comment","z":"d9cf9e97.6745a","name":"click action","info":"defines the action that should be performed when the button is clicked.","x":1300,"y":1060,"wires":[]},{"id":"81a4be1a.2cce7","type":"comment","z":"d9cf9e97.6745a","name":"Signal Inputs","info":"","x":230,"y":1060,"wires":[]},{"id":"1089ac96.5c7613","type":"debug","z":"d9cf9e97.6745a","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1450,"y":960,"wires":[]},{"id":"b496b411.fe5358","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

Generic

OK the generic flow is here. For that one I encresed the delay time for dimming and for the button clicks from 500ms to 1s. With that you can be fast enought to press "1" and "0" to simulate the button "press" and "release".

The button actions are now also generic just implemented as node.warn(), so you see the output in the debug window. What it should do for you, is now up to you.

[{"id":"a28d4492.11c098","type":"counter","z":"d9cf9e97.6745a","name":"button counter","init":"0","step":"1","lower":"","upper":"","mode":"increment","outputs":1,"x":1120,"y":2040,"wires":[["bd956e50.475df"]]},{"id":"371a19b8.322e86","type":"change","z":"d9cf9e97.6745a","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"foo","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":2100,"wires":[["a28d4492.11c098"]]},{"id":"bd956e50.475df","type":"function","z":"d9cf9e97.6745a","name":"click action","func":"var newMsg = {payload: null, topic: null};\n\nconst dimmingTopic = \"dimming\";             // string of topic for detecting dimming\nconst minONValue = 10;                      // button state is set to ON or OFF with this limit\n\nif(msg.topic == dimmingTopic)\n{\n    // dimming action\n    node.warn({\"brightness\": msg.payload});\n\n}\nelse\n{\n    // click action\n    switch(msg.count)\n    {\n        case 0: //do nothing after reset the counter to zero\n            break;\n            \n        case 1: // single click action\n            node.warn(\"one click action\");\n            break;\n        \n        case 2: // double click action\n            node.warn(\"two click action\");\n            break;\n        \n        case 3: // three clicks action\n            node.warn(\"three click action\");\n            break;\n        \n        default: // more clicks action\n            break;\n    }\n}\n\nif(newMsg.topic !== null){          // send if a action should perform\n    return newMsg;\n}","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\nconst dimmingTopic = \"dimming\";\ncontext.set(dimmingTopic, 200); ","finalize":"","x":1310,"y":2040,"wires":[[]]},{"id":"e6160ee1.1eff5","type":"trigger","z":"d9cf9e97.6745a","name":"delay","op1":"","op2":"reset","op1type":"nul","op2type":"str","duration":"1000","extend":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":870,"y":2100,"wires":[["371a19b8.322e86"]],"info":"The delay will be extended with every message / klick. With this behaver it is possible to detect also longer sequences of clicks."},{"id":"1b1515ac.eaa45a","type":"trigger","z":"d9cf9e97.6745a","name":"start dimming delay","op1":"","op2":"start dimming","op1type":"nul","op2type":"str","duration":"1000","extend":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":410,"y":2160,"wires":[["cf55bfbe.81adc"]],"info":"The delay will be extended with every message / klick. With this behaver it is possible to detect also longer sequences of clicks."},{"id":"b6823ed.cb241c","type":"change","z":"d9cf9e97.6745a","name":"dimm prepair","rules":[{"t":"set","p":"topic","pt":"msg","to":"dimming","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1110,"y":2160,"wires":[["bd956e50.475df"]]},{"id":"cf55bfbe.81adc","type":"function","z":"d9cf9e97.6745a","name":"start dimming?","func":"var newMsg = {payload: null, topic: null};\nconst lastButtonAction = \"lastButtonAction\";\n\n\nif(msg.payload == \"start dimming\")\n{\n    // \"start dimming\" delay is over and we can check if the button is still pressed\n    \n    if(context.get(lastButtonAction) == 1)\n    {\n        // button is still pressed and we can start dimming\n        newMsg.payload = msg.payload;\n        \n    }\n}\nelse\n{\n    // button action that is not \"start dimming\" --> rising edge (1) or falling edge (0)\n    \n    context.set(lastButtonAction, msg.payload); // store if the button was last on a faling (0), rising edge (1) or \"start dimming\"\n    newMsg.payload = msg.payload; // gives button state to the next node\n}\n\n\nif(newMsg.payload !== null){          // send if a action should perform\n    return newMsg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":2100,"wires":[["7869188.6f595e8","c971a203.7c5f"]]},{"id":"7869188.6f595e8","type":"function","z":"d9cf9e97.6745a","name":"faling edge if not dimming","func":"var newMsg = {payload: null, topic: null};\nconst lastMsg = \"lastMsg\"\n\nif(context.get(lastMsg) != \"start dimming\")\n{\n    // dimming is not active\n    if (msg.payload == \"0\")\n    {\n        newMsg.payload = msg.payload;   // just forward faling edges\n    }\n    \n}\n\ncontext.set(lastMsg, msg.payload);      // store msg as lastMsg\n\nif(newMsg.payload !== null)\n{\n    return newMsg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":770,"y":2040,"wires":[["a28d4492.11c098","e6160ee1.1eff5"]]},{"id":"c971a203.7c5f","type":"one-button-dimmer","z":"d9cf9e97.6745a","name":"","interval":"150","step":"3","minValue":0,"maxValue":"100","startCommand":"start dimming","stopCommand":"0","x":760,"y":2160,"wires":[["b6823ed.cb241c"]]},{"id":"73f928bf.7b97f8","type":"comment","z":"d9cf9e97.6745a","name":"Filter dimming","info":"","x":470,"y":1980,"wires":[]},{"id":"211a729.2d1008e","type":"comment","z":"d9cf9e97.6745a","name":"Reset","info":"Resets the counter of clicks after a fix given time.","x":950,"y":1980,"wires":[]},{"id":"d4c8834a.6b9fd","type":"comment","z":"d9cf9e97.6745a","name":"counter","info":"counts the number of clicks until it is reseted from the reset mechanism.\n\nmsg.count: shows the count value\nmsg.payload: input value of this node","x":1110,"y":1980,"wires":[]},{"id":"7b6bb55b.ea890c","type":"comment","z":"d9cf9e97.6745a","name":"click action","info":"defines the action that should be performed when the button is clicked.","x":1300,"y":1980,"wires":[]},{"id":"f8a12eae.0bfdb","type":"comment","z":"d9cf9e97.6745a","name":"Signal Inputs","info":"","x":230,"y":1980,"wires":[]},{"id":"15c6305b.ddbbb","type":"inject","z":"d9cf9e97.6745a","name":"1 - button pressed","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"test","payload":"1","payloadType":"str","x":170,"y":2120,"wires":[["cf55bfbe.81adc","1b1515ac.eaa45a"]]},{"id":"470401ae.5ef24","type":"inject","z":"d9cf9e97.6745a","name":"0 - button release","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"test","payload":"0","payloadType":"str","x":160,"y":2160,"wires":[["cf55bfbe.81adc","1b1515ac.eaa45a"]]}]

Thanks.

(Dumb question) I see two posts from you.

One with function clarification and then a Generic one.

I'll try to look at them soon. Just now I am up to my chest with Python code and nodes.

one is for shelly devices specifig programmed. And the Generic one does no action right now but has the full logic and has inputs / outputs that makes it easyer for you to see how it works. So maybe if you do not use shelly devicec, you can start with the generic one. It should not be too complex. Then you should be able to implement actons what ever you like.

1 Like