Automation for key held down similar to how Hue’s brightness buttons work

Is it possible to create an automation that continues to perform an action (increase the brightness) as long as a button is held down? I want to replicate the behaviour of the brightness buttons on the Hue Dimmer Switches.

My switch (a Z-Wave 4-channel Namron switch) emits a KeyHeldDown event when it detects that the button is being held down, and a KeyReleased event when the button is released. In other words, there are no further events being emitted as long as the button is being held down, so the automation must somehow trigger a loop of continuously increasing the brightness and then use the KeyReleased event to cancel/stop it.

So, to be clear, I can detect these events just fine and create automations based on them, it’s the do-something-repeatedly-until-the-button-is-released part I am stuck on.

Automations are working on triggering based, so you can make an automation to track when the key is pressed first and another one to track key release time. After that, you can calculate the time difference in terms of seconds and make a calculation against a Max time.

Let’s say, if you want to reach from 0 to 100 by 10 seconds and your calculated time is 5 seconds, you should reduce the dim by 50 percent

I don’t know if there is any other easier and better solution to this

Surely there must be a better way even in yaml?

I have never done such an automation in yaml (but I have tried), but in Node red it’s quite easy to have it dim as you hold the button.

Maybe, with one automation to be triggered with key pressed event and repeat until key released event arrived, this might also work

That is the thing we tried in the linked thread.

Yes, that linked thread looks very relevant - they want to achieve exactly the same thing.

@anon63427907 While your solution might work, per se, it wouldn’t be what I’m after. The Hue Dimmer Switch (when working with a Hue Bridge) continuously increases the brightness until the button is released, so you can see with your own eyes when to release the button to achieve the brightness you want.

It looks like the repeat directive should be able to satisfy the requirement that the brightness should keep increasing while the button is held, so the problem boils down to how to stop/cancel the repetition.

Is there really no solution for this? It seems like such a basic thing, seeing as so many buttons/switches support being held down for additional control.

I’m not familiar with the development process for HA (just started transitioning from Hue Bridges), but I would be interested to know if adding support for something like this has been discussed by the developers. I think it’s something that should at least be on some roadmap.

From a bird’s eye perspective, it seems we have all the data we need to support it, and most of the functionality (repeat). What might be missing is a robust and officially supported way to cancel a repeat based on another event (KeyReleased) that also match some criteria to make sure it is a follow-up event from the initiating event (same basic operation, same device etc.).

@Hellis81 It finally dawned on me that you basically spelled out the solution in that other thread - just use Node-RED.

I thought Node-RED was a whole different thing, which would mean I couldn’t use HA, but it turns out it can be used as an addon and it works perfectly!

Here is the flow that I ended up with:

[{"id":"68e6fde03ae709cf","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"fa13c4e391c8fadd","type":"server-events","z":"68e6fde03ae709cf","name":"All HA Events","server":"cd4a2a22.06c838","version":1,"event_type":"zwave_js_value_notification","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":210,"y":60,"wires":[["3981f4f6a1f390dd","06f2510a7778cefa"]]},{"id":"3981f4f6a1f390dd","type":"switch","z":"68e6fde03ae709cf","name":"","property":"payload.event.device_id","propertyType":"msg","rules":[{"t":"eq","v":"5cdf21e8014b0435ccdba59ece42a75d","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":190,"y":140,"wires":[["0e696fc1b59af5e8"]]},{"id":"0e696fc1b59af5e8","type":"switch","z":"68e6fde03ae709cf","name":"","property":"payload.event.value","propertyType":"msg","rules":[{"t":"eq","v":"KeyReleased","vt":"str"},{"t":"eq","v":"KeyHeldDown","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":390,"y":140,"wires":[["66dee43d5705602e"],["63b2023e731ce66e"]]},{"id":"63b2023e731ce66e","type":"change","z":"68e6fde03ae709cf","name":"","rules":[{"t":"set","p":"held_down","pt":"flow","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":230,"y":200,"wires":[["9ab1d4b91429cac0"]]},{"id":"66dee43d5705602e","type":"change","z":"68e6fde03ae709cf","name":"","rules":[{"t":"set","p":"held_down","pt":"flow","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":120,"wires":[[]]},{"id":"9ab1d4b91429cac0","type":"switch","z":"68e6fde03ae709cf","name":"","property":"payload.event.property_key","propertyType":"msg","rules":[{"t":"eq","v":"001","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":190,"y":300,"wires":[["26d4aaa945bbcb90"],["1762482884d3d7a3"]]},{"id":"74d77967d43cdad1","type":"api-call-service","z":"68e6fde03ae709cf","name":"","server":"4bbca37b.1700ec","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.dimmable_light_1","data":"{\t    \"brightness\": msg.data.attributes.brightness+25\t}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":850,"y":260,"wires":[["8b4f10a8a3201758"]]},{"id":"8b4f10a8a3201758","type":"delay","z":"68e6fde03ae709cf","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1050,"y":260,"wires":[["82e07714b58e17da"]]},{"id":"82e07714b58e17da","type":"switch","z":"68e6fde03ae709cf","name":"","property":"held_down","propertyType":"flow","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":810,"y":180,"wires":[["26d4aaa945bbcb90"]]},{"id":"9ee3178e3a4950b7","type":"switch","z":"68e6fde03ae709cf","name":"","property":"held_down","propertyType":"flow","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":810,"y":420,"wires":[["1762482884d3d7a3"]]},{"id":"870762e793857e99","type":"api-call-service","z":"68e6fde03ae709cf","name":"","server":"4bbca37b.1700ec","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.dimmable_light_1","data":"{\t    \"brightness\": msg.data.attributes.brightness-25\t}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":850,"y":340,"wires":[["86f524e63232e6ae"]]},{"id":"86f524e63232e6ae","type":"delay","z":"68e6fde03ae709cf","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1050,"y":340,"wires":[["9ee3178e3a4950b7"]]},{"id":"06f2510a7778cefa","type":"debug","z":"68e6fde03ae709cf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":500,"y":60,"wires":[]},{"id":"1762482884d3d7a3","type":"api-current-state","z":"68e6fde03ae709cf","name":"","server":"4bbca37b.1700ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"light.dimmable_light_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":570,"y":340,"wires":[["870762e793857e99"]]},{"id":"26d4aaa945bbcb90","type":"api-current-state","z":"68e6fde03ae709cf","name":"","server":"4bbca37b.1700ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"light.dimmable_light_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":570,"y":260,"wires":[["74d77967d43cdad1"]]},{"id":"cd4a2a22.06c838","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30},{"id":"4bbca37b.1700ec","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30}]

Glad it works for you… In the other thread I had a very similar request with yours and Hellis also suggested Node Red. Unfortunately, due to the fact that I am not a programmer (perhaps), I don’t understand Node-Red at all. It just doesn’t fit to my mind… Therefore, I am too chicken-shit to try it. Hellis almost had me convinced to try and I never did…

I still believe though that this should be a default functionality for home assistant.

To generalize it a bit, I don’t understand how this whole thing work… If the light support transition HA has a transition in the entities. My light does not support transitions but when I say to Google home “sleep my lights” they transition just fine. With Node Red it is also possible. Why HA doesn’t have it by default?

That’s the deal with Node red. It’s less programming than HA yaml.

Good job.

Nice work if that was your first Node red flow (edit, it’s still good work if it’s not. But you get what I’m saying).
Not much to comment on. The only thing I can see that you could condense is the two first switches in to one function node, but that is not much help and requires more work and makes it harder to read.

Hi Hellis,

I spend about 3 hours on video tutorials and I couldn’t understand it’s logic… Even with no coding at all…

I decided that it’s not for me…

I can totally understand that NR can be daunting to non-programmers (I am one), but I also agree with Hellis81 that once you get it, it can feel quite natural and more drag-and-drop-y than messing with HA’s YAML files.

In case it helps, here’s a quick walkthrough of my solution:

  • (Things flow from left to right in Node-RED)
  • Listen for all zwave_js_value_notification events (basically every event for devices that are connected to my Z-Wave dongle).
  • Filter the events so we just deal with the relevant one (the device we want etc.).
  • If the event is KeyReleased, set a variable for the entire flow stating that the holding-down of the key is done, so that we can check against this elsewhere in the flow, then terminate the flow (the switch is a dead end).
  • If, on the other hand, the event is KeyHeldDown, set the same flow variable as above to true, so that we know the holding-down is still active when we check againt it in the main “loop”.
  • Then check which button is held (property_key “001” is increase, the other is decrease), and route the flow for increase to the top-most loop, and decrease to the bottom loop.
  • In the loop, we basically keep increasing or decreasing until we detect that the holding-down is done (by looking at the variable from earlier).

Hope this helps.

I didn’t find the videos very clear either, but after about an hour of playing around with it, it was quite clear what it was and how to use it.
It’s a thing you must try to understand I believe.

Thanks! I must admit I was pretty pleased once I got it working, but your export in the other thread was very helpful.

One thing I would like to improve would be to have just one loop, and instead use a variable for the brightness adjustment, like 10 for increase and -10 for decrease. I tried doing this with brightness_step_pct , but I couldn’t get it working. The service was called, but it didn’t affect the brightness.

Add a message variable of either 10 or -10 and use that in the call service:

[{"id":"9f0591608cb849d8","type":"switch","z":"a01f7f96f1de4804","name":"","property":"payload.event.value","propertyType":"msg","rules":[{"t":"eq","v":"KeyReleased","vt":"str"},{"t":"eq","v":"KeyHeldDown","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":390,"y":140,"wires":[["f0850d1ed3535e0c"],["8aeebeeaf4994d82"]]},{"id":"19e5103610757ad7","type":"switch","z":"a01f7f96f1de4804","name":"","property":"payload.event.device_id","propertyType":"msg","rules":[{"t":"eq","v":"5cdf21e8014b0435ccdba59ece42a75d","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":190,"y":140,"wires":[["9f0591608cb849d8"]]},{"id":"f0850d1ed3535e0c","type":"change","z":"a01f7f96f1de4804","name":"","rules":[{"t":"set","p":"held_down","pt":"flow","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":120,"wires":[[]]},{"id":"8aeebeeaf4994d82","type":"change","z":"a01f7f96f1de4804","name":"","rules":[{"t":"set","p":"held_down","pt":"flow","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":230,"y":200,"wires":[["2f9f6c39d39ccfa7"]]},{"id":"e6df2560d885ccab","type":"server-events","z":"a01f7f96f1de4804","name":"All HA Events","server":"586b9948bd50a962","version":1,"event_type":"zwave_js_value_notification","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":210,"y":60,"wires":[["19e5103610757ad7","5663b686f5b4c7f8"]]},{"id":"2f9f6c39d39ccfa7","type":"switch","z":"a01f7f96f1de4804","name":"","property":"payload.event.property_key","propertyType":"msg","rules":[{"t":"eq","v":"001","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":170,"y":320,"wires":[["e25919d73833ef90"],["d17d636da29dc362"]]},{"id":"5663b686f5b4c7f8","type":"debug","z":"a01f7f96f1de4804","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":500,"y":60,"wires":[]},{"id":"e25919d73833ef90","type":"change","z":"a01f7f96f1de4804","name":"","rules":[{"t":"set","p":"plus_minus","pt":"msg","to":"10","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":300,"wires":[["ae29f5166b887c41"]]},{"id":"d17d636da29dc362","type":"change","z":"a01f7f96f1de4804","name":"","rules":[{"t":"set","p":"plus_minus","pt":"msg","to":"-10","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":340,"wires":[["ae29f5166b887c41"]]},{"id":"ae29f5166b887c41","type":"api-current-state","z":"a01f7f96f1de4804","name":"","server":"4bbca37b.1700ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"light.dimmable_light_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":710,"y":320,"wires":[["8dd2e4e401aa3e8c"]]},{"id":"617080873964c3eb","type":"switch","z":"a01f7f96f1de4804","name":"","property":"held_down","propertyType":"flow","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":950,"y":240,"wires":[["ae29f5166b887c41"]]},{"id":"8dd2e4e401aa3e8c","type":"api-call-service","z":"a01f7f96f1de4804","name":"","server":"4bbca37b.1700ec","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.dimmable_light_1","data":"{\t    \"brightness\": msg.data.attributes.brightness+msg.plus_minus\t}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":990,"y":320,"wires":[["b8ed2af59326c28f"]]},{"id":"b8ed2af59326c28f","type":"delay","z":"a01f7f96f1de4804","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1190,"y":320,"wires":[["617080873964c3eb"]]},{"id":"586b9948bd50a962","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true},{"id":"4bbca37b.1700ec","type":"server","name":"Home Assistant","version":1,"legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

(it’s untested but I believe it will work)

Yes, excellent, that worked! Thanks a lot :slight_smile:

It could also be done like this:

[{"id":"ae29f5166b887c41","type":"api-current-state","z":"a01f7f96f1de4804","name":"","server":"4bbca37b.1700ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"light.dimmable_light_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":710,"y":320,"wires":[["8dd2e4e401aa3e8c"]]},{"id":"617080873964c3eb","type":"switch","z":"a01f7f96f1de4804","name":"","property":"held_down","propertyType":"flow","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":950,"y":240,"wires":[["ae29f5166b887c41"]]},{"id":"361556e786a77666","type":"function","z":"a01f7f96f1de4804","name":"","func":"if(msg.payload.event.device_id == \"5cdf21e8014b0435ccdba59ece42a75d\"){                \n    if(msg.payload.event.value == \"KeyReleased\"){\n        flow.set(\"held_down\", false);\n        \n    }else if(msg.payload.event.value == \"KeyHeldDown\"){\n        flow.set(\"held_down\", true);\n        \n        if(msg.payload.event.property_key == \"001\"){\n            msg.plus_minus = 10;\n        }else{\n            msg.plus_minus = -10;\n        }\n        return msg;\n    }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":320,"wires":[["ae29f5166b887c41"]]},{"id":"8dd2e4e401aa3e8c","type":"api-call-service","z":"a01f7f96f1de4804","name":"","server":"4bbca37b.1700ec","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.dimmable_light_1","data":"{\t    \"brightness\": msg.data.attributes.brightness+msg.plus_minus\t}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":990,"y":320,"wires":[["b8ed2af59326c28f"]]},{"id":"b8ed2af59326c28f","type":"delay","z":"a01f7f96f1de4804","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1190,"y":320,"wires":[["617080873964c3eb"]]},{"id":"e6df2560d885ccab","type":"server-events","z":"a01f7f96f1de4804","name":"All HA Events","server":"586b9948bd50a962","version":1,"event_type":"zwave_js_value_notification","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":210,"y":280,"wires":[["5663b686f5b4c7f8","361556e786a77666"]]},{"id":"5663b686f5b4c7f8","type":"debug","z":"a01f7f96f1de4804","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":370,"y":240,"wires":[]},{"id":"4bbca37b.1700ec","type":"server","name":"Home Assistant","version":1,"legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true},{"id":"586b9948bd50a962","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

It makes reading the diagram harder, but opening the function node we get in my opinion a better view of what is going on in a quick glance.
But it off course helps if you know how to at least read code.

if(msg.payload.event.device_id == "5cdf21e8014b0435ccdba59ece42a75d"){                
    if(msg.payload.event.value == "KeyReleased"){
        flow.set("held_down", false);
        
    }else if(msg.payload.event.value == "KeyHeldDown"){
        flow.set("held_down", true);
        
        if(msg.payload.event.property_key == "001"){
            msg.plus_minus = 10;
        }else{
            msg.plus_minus = -10;
        }
        return msg;
    }
}

Thanks, that’s interesting. I was going to check out functions shortly, but I wanted to get a feel for the graphical stuff first. Looks like functions might be the way to go when it comes to intermediate logic like this - it feels much more natural and efficient than switches and change nodes. And now that I have figured out that functions can have multiple outputs, things start getting really interesting :slight_smile:

I have now converted the flow to handle everything on the switch (four different button group that correspond to four different lights, with the following logic for each: pressing “1” to turn on, pressing “0” to turn off, holding “1” to brighten, holding “0” to dim).

Functions turned out to be really useful to do basic setup and then route the message to the proper output. I also put the dimming logic in a subflow to keep the main flow clean and easily understandable.

I’m including it all below, in case it can be useful for others.

Full flow export

[{"id":"1764ed308220b866","type":"subflow","name":"BrightenOrDim","info":"","category":"","in":[{"x":100,"y":220,"wires":[{"id":"2a5ce486273beca7"}]}],"out":[],"env":[],"meta":{},"color":"#DDAA99"},{"id":"2a5ce486273beca7","type":"api-current-state","z":"1764ed308220b866","name":"","server":"4bbca37b.1700ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"{{light_id}}","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":330,"y":220,"wires":[["59529845d970e03c"]]},{"id":"89f5472b6a7ebf76","type":"switch","z":"1764ed308220b866","name":"","property":"$flowContext('$parent.held_down')","propertyType":"jsonata","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":550,"y":140,"wires":[["2a5ce486273beca7"]]},{"id":"59529845d970e03c","type":"api-call-service","z":"1764ed308220b866","name":"","server":"a2245b3a359b4c8a","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"{{light_id}}","data":"{ \"brightness\": msg.data.attributes.brightness+msg.plus_minus\t}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":590,"y":220,"wires":[["620ac73e26a99161"]]},{"id":"620ac73e26a99161","type":"delay","z":"1764ed308220b866","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":790,"y":220,"wires":[["89f5472b6a7ebf76"]]},{"id":"4bbca37b.1700ec","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30},{"id":"e853db6505ef2882","type":"tab","label":"Namrom 4-Channel Switch","disabled":false,"info":"","env":[]},{"id":"627d728155b3c136","type":"function","z":"e853db6505ef2882","name":"router","func":"const switch_id = '5cdf21e8014b0435ccdba59ece42a75d'\nconst { event } = msg.payload\n\nif (event.device_id === switch_id) {                \n    const step = 25\n    \n    const on_buttons = [1, 3, 5, 7]\n    \n    const on_button_id_to_group_mappings = {\n        1: 1,\n        3: 2,\n        5: 3,\n        7: 4,\n    }\n    \n    const button_group_to_light_mappings = {\n        1: 'light.dimmable_light_1',\n        2: 'light.dimmable_light_2',\n        3: 'light.dimmable_light_3',\n        4: 'light.dimmable_light_4',\n    }\n    \n    const button_id = parseInt(event.property_key, 10)\n    \n    const on_button_id = on_buttons.includes(button_id)\n        ? button_id\n        : button_id - 1\n    \n    const group = on_button_id_to_group_mappings[on_button_id]\n    const light = button_group_to_light_mappings[group]\n    \n    msg.light_id = light\n\n    if (event.value === 'KeyPressed') {\n        if (on_buttons.includes(button_id)) {\n            return [null, msg, null]\n        } else {\n            return [null, null, msg]\n        }\n    } else {\n        if (event.value === 'KeyReleased') {\n            flow.set('held_down', false)\n        } else if (event.value === 'KeyHeldDown') {\n            flow.set('held_down', true)\n            \n            msg.plus_minus = event.property_key === '001'\n                ? step\n                : -step\n            \n            return [msg, null, null]\n            \n        }\n    }\n}","outputs":3,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":300,"wires":[["10abb1d776943fed"],["15756ca94a3e2f7e"],["e0382650154c4846"]]},{"id":"75bf86c7de985d06","type":"server-events","z":"e853db6505ef2882","name":"zwave events","server":"a2245b3a359b4c8a","version":1,"event_type":"zwave_js_value_notification","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":210,"y":300,"wires":[["627d728155b3c136"]]},{"id":"e0382650154c4846","type":"api-call-service","z":"e853db6505ef2882","name":"turn off","server":"cd4a2a22.06c838","version":3,"debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"{{light_id}}","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":680,"y":360,"wires":[[]]},{"id":"15756ca94a3e2f7e","type":"api-call-service","z":"e853db6505ef2882","name":"turn on","server":"cd4a2a22.06c838","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"{{light_id}}","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":680,"y":300,"wires":[[]]},{"id":"10abb1d776943fed","type":"subflow:1764ed308220b866","z":"e853db6505ef2882","name":"brighten or dim","x":700,"y":240,"wires":[]},{"id":"a2245b3a359b4c8a","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true},{"id":"cd4a2a22.06c838","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30}]

Router function

const switch_id = '5cdf21e8014b0435ccdba59ece42a75d'
const { event } = msg.payload

if (event.device_id === switch_id) {                
    const step = 25
    
    const on_buttons = [1, 3, 5, 7]
    
    const on_button_id_to_group_mappings = {
        1: 1,
        3: 2,
        5: 3,
        7: 4,
    }
    
    const button_group_to_light_mappings = {
        1: 'light.dimmable_light_1',
        2: 'light.dimmable_light_2',
        3: 'light.dimmable_light_3',
        4: 'light.dimmable_light_4',
    }
    
    const button_id = parseInt(event.property_key, 10)
    
    const on_button_id = on_buttons.includes(button_id)
        ? button_id
        : button_id - 1
    
    const group = on_button_id_to_group_mappings[on_button_id]
    const light = button_group_to_light_mappings[group]
    
    msg.light_id = light

    if (event.value === 'KeyPressed') {
        if (on_buttons.includes(button_id)) {
            return [null, msg, null]
        } else {
            return [null, null, msg]
        }
    } else {
        if (event.value === 'KeyReleased') {
            flow.set('held_down', false)
        } else if (event.value === 'KeyHeldDown') {
            flow.set('held_down', true)
            
            msg.plus_minus = event.property_key === '001'
                ? step
                : -step
            
            return [msg, null, null]
            
        }
    }
}
1 Like

I am really curious about the Node Red solution, as I’ve been thinking about utilizing NR for quite some time now.
I ended up putting an automation together using the GUI and yaml for my 4 button aqara opple. It’s not the most feature rich, but basically comes down to using a toggle helper.
I have the hold action turn on my toggle helper, and then the next sequence dims or brightens the lights by 10% with the conditional repeat until, the toggle helper is off.
The release action from the button turns off the toggle, this is all working within a single automation.

button_top_left_hold_in:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.brightness_livingroom
      - repeat:
          until:
            - condition: state
              entity_id: input_boolean.brightness_livingroom
              state: "off"
          sequence:
            - service: light.turn_on
              data:
                transition: 0.5
                brightness_step_pct: 10
              target:
                entity_id: light.living_room_light_group
            - delay:
                hours: 0
                minutes: 0
                seconds: 0.75
                milliseconds: 0
    button_top_left_hold_release:
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.brightness_livingroom

Sorry for the formatting I’m on my phone :wink:

1 Like