Dimming using single push button

Hi,
I am a newbie to Node Red and loving it. I am trying to achieve a flow for Tuya Dimmers flashed with Tasmota and working with Homeassistant. I am wanting to use another single pushbutton on a seperate Sonoff device also running Tasmota with a switchmode1 0 with a momentary pushbutton to toggle light on/off with a short press and cycle between dim up to 100% and down to about 10% with long press. The flow I have kind of works (it is a mix of several flows i have found online) but I know there must be a more elegant way of deciding if short press (less than x milliseconds)then send toggle, else pass press push and subsequent release of button to process dimming. Any help would be greatly appreciated.

Thanks

43%20pm|690x275

[{"id":"6060c8b6.1977e","type":"api-current-state","z":"8fc67949.33607","name":"Office Brightness","server":"f33ae2cd.0fa4","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":true,"entity_id":"light.office_light","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":150,"y":500,"wires":[["83302ba.965fdd8"]]},{"id":"69e8c99.022afb8","type":"change","z":"8fc67949.33607","name":"Get Brightness","rules":[{"t":"set","p":"payload","pt":"msg","to":"data.attributes.brightness","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":560,"wires":[["d4743cd1.548d"]]},{"id":"49cf06fb.b1e638","type":"function","z":"8fc67949.33607","name":"Add Fib","func":"var iCurr = context.flow.get('iCurr') || 2;\nvar iPrev = context.flow.get('iPrev') || 1;\nvar iFib = context.flow.get('iFib') || 2;\n\nmsg.payload = Math.min(100, msg.payload + iFib);\n\niCurr = iFib;\niFib = iFib + iPrev;\niPrev = iCurr;\n\ncontext.flow.set('iCurr',iCurr);\ncontext.flow.set('iPrev',iPrev);\ncontext.flow.set('iFib',iFib);\n\nif (iCurr == 2){\n    context.flow.set('dCurr',null);\n    context.flow.set('dPrev',null);\n    context.flow.set('dFib',null);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":920,"y":360,"wires":[["58176016.a7ef9","c293ba8f.64b658"]]},{"id":"d2213fcb.659ac8","type":"server-state-changed","z":"8fc67949.33607","name":"Office Switch State","server":"f33ae2cd.0fa4","version":1,"entityidfilter":"binary_sensor.office_switch","entityidfiltertype":"substring","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":false,"x":110,"y":220,"wires":[["fdcd66b3.9e41a","d4de75bd.11a56"]]},{"id":"d4de75bd.11a56","type":"switch","z":"8fc67949.33607","name":"Hold State","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"nnull"}],"checkall":"true","repair":false,"outputs":2,"x":150,"y":300,"wires":[["6060c8b6.1977e"],["b2f3b0b3.cc8f2","b1580762.edfe5"]]},{"id":"c5848476.d0628","type":"function","z":"8fc67949.33607","name":"Subtract Fib","func":"var dCurr = context.flow.get('dCurr') || 2;\nvar dPrev = context.flow.get('dPrev') || 1;\nvar dFib = context.flow.get('dFib') || 2;\n\nmsg.payload = Math.max(10, msg.payload - dFib);\n\ndCurr = dFib;\ndFib = dFib + dPrev;\ndPrev = dCurr;\n\ncontext.flow.set('dCurr',dCurr);\ncontext.flow.set('dPrev',dPrev);\ncontext.flow.set('dFib',dFib);\n\nif (dCurr == 2){\n    context.flow.set('iCurr',null);\n    context.flow.set('iPrev',null);\n    context.flow.set('iFib',null);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":890,"y":300,"wires":[["3ed7284c.f01ff","c293ba8f.64b658"]]},{"id":"ac48a0d5.87cf1","type":"msg-router","z":"8fc67949.33607","routerType":"roundrobin","topicDependent":false,"counterReset":true,"msgKeyField":"payload","undefinedHash":false,"outputsInfo":[{"active":true,"clone":false,"delay":"0","weight":"0"},{"active":true,"clone":false,"delay":"0","weight":"0"}],"name":"Round Robin Router","delaying":"unrelated","msgControl":false,"outputs":2,"x":740,"y":560,"wires":[["49cf06fb.b1e638"],["c5848476.d0628"]]},{"id":"aa22df38.988778","type":"api-call-service","z":"8fc67949.33607","name":"Office Light","server":"f33ae2cd.0fa4","service_domain":"light","service":"turn_on","data":"{\"entity_id\":\"light.office_light\"}","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":1330,"y":360,"wires":[[]],"icon":"node-red/light.png"},{"id":"b2f3b0b3.cc8f2","type":"traffic","z":"8fc67949.33607","name":"Break when Stop","property_allow":"payload","filter_allow":"on","ignore_case_allow":false,"negate_allow":false,"send_allow":false,"property_stop":"payload","filter_stop":"off","ignore_case_stop":false,"negate_stop":false,"send_stop":false,"default_start":true,"differ":false,"x":670,"y":260,"wires":[["c5848476.d0628"]]},{"id":"3ed7284c.f01ff","type":"delay","z":"8fc67949.33607","name":"","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":670,"y":200,"wires":[["b2f3b0b3.cc8f2"]]},{"id":"b1580762.edfe5","type":"traffic","z":"8fc67949.33607","name":"Break when Stop","property_allow":"payload","filter_allow":"on","ignore_case_allow":false,"negate_allow":false,"send_allow":false,"property_stop":"payload","filter_stop":"off","ignore_case_stop":false,"negate_stop":false,"send_stop":false,"default_start":true,"differ":false,"x":670,"y":360,"wires":[["49cf06fb.b1e638"]]},{"id":"58176016.a7ef9","type":"delay","z":"8fc67949.33607","name":"","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":650,"y":420,"wires":[["b1580762.edfe5"]]},{"id":"d4743cd1.548d","type":"range","z":"8fc67949.33607","minin":"1","maxin":"255","minout":"1","maxout":"100","action":"clamp","round":true,"property":"payload","name":"Brightness %","x":520,"y":560,"wires":[["ac48a0d5.87cf1"]]},{"id":"83302ba.965fdd8","type":"switch","z":"8fc67949.33607","name":"Light On/Off","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":150,"y":560,"wires":[["69e8c99.022afb8"],["e9b0771a.56668"]]},{"id":"e9b0771a.56668","type":"change","z":"8fc67949.33607","name":"Set Brightness","rules":[{"t":"set","p":"payload","pt":"msg","to":"10","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":640,"wires":[["3f9bbda3.c02cca"]]},{"id":"3f9bbda3.c02cca","type":"function","z":"8fc67949.33607","name":"Reset Context","func":"context.flow.set('iCurr',null);\ncontext.flow.set('iPrev',null);\ncontext.flow.set('iFib',null);\n\nreturn msg;","outputs":1,"noerr":0,"x":520,"y":640,"wires":[["49cf06fb.b1e638","68a8e294.84651c"]]},{"id":"68a8e294.84651c","type":"change","z":"8fc67949.33607","name":"Reset Router","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":690,"y":640,"wires":[["ac48a0d5.87cf1"]]},{"id":"c293ba8f.64b658","type":"function","z":"8fc67949.33607","name":"Brightness Data","func":"newmsg = {};\n\nbrightness = msg.payload;\n\nnewmsg.payload = { data: {\"brightness_pct\":brightness, \"transition\":0.1 }};\n\nreturn newmsg;","outputs":1,"noerr":0,"x":1160,"y":360,"wires":[["aa22df38.988778"]]},{"id":"bdecf56c.911b78","type":"comment","z":"8fc67949.33607","name":"Why not events?","info":"You must use state because \"hold\" is\ncommunicated by the switch being \"on\".\n\nWe capture the release by the state being \"off\".\n\nThe events node lets you capture a\n\"long click\", but not a hold.","x":100,"y":180,"wires":[]},{"id":"55b063ca.037e94","type":"comment","z":"8fc67949.33607","name":"What's happening here?","info":"If the light is on then we capture its\nbrightness attribute thanks to the\noverride payload earlier. Now we set payload\nto brightness.\n\nIf it's off then we set payload (brightness)\nto 1.","x":150,"y":460,"wires":[]},{"id":"ce214ee1.0cb6f8","type":"comment","z":"8fc67949.33607","name":"Reset?","info":"In Add Fib we have variables that allow us to\nincrease in fibonacci increments.\n\nThese variables are set in the node context\nwhich only ever go back to null if we redeploy\nor explicitly say so.\n\nLet's say we just brightened the light, then\nturned it off. The variables will still be set.\n(They are reset when you go through the other\ndirection).\nWe want to start brightening our light with\nsmall increments again, so we nullify the\ncontext.\n\nWe also need to reset the router because if we\ndimmed, then turned off light, it will want to\nbrighten next time. But we *are* brightening\nnow (from off)! So we reset it because next time\nit should dim.","x":530,"y":700,"wires":[]},{"id":"eb4abda1.de66b","type":"comment","z":"8fc67949.33607","name":"Router?","info":"Round Robin.\n\nWe brighten.\nWe dim.\nWe brighten.\nWe dim.\netc.","x":730,"y":520,"wires":[]},{"id":"49eff3d2.ae64fc","type":"comment","z":"8fc67949.33607","name":"Loops?","info":"Both loops work the same.\n\nWe receive the payload with brightness % and then\nstart adding/subtracting fibonacci increments.\n\nThe traffic node allows this loop to continue\nas long as the hold state is \"on\". Once the hold\nstate changes to \"off\" the loop is broken.\n\nEvery 500ms a light.turn_on with a brightness\nand transition option is sent to the light\nspecified, hopefully gracefully brightening\nthe light.\n\nThere is a little Math in the functions to\ninsure that we don't go below 0% or over 100%.","x":670,"y":300,"wires":[]},{"id":"c1670d07.441d88","type":"debug","z":"8fc67949.33607","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1510,"y":260,"wires":[]},{"id":"b4dea629.3cf7c","type":"switch","z":"8fc67949.33607","name":"Short toggle","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"short","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":790,"y":80,"wires":[["2870c693.ebfbaa"]]},{"id":"2870c693.ebfbaa","type":"change","z":"8fc67949.33607","name":"Toggle","rules":[{"t":"set","p":"payload","pt":"msg","to":"toggle","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":930,"y":80,"wires":[["b1a73ca2.6c77a8"]]},{"id":"fdcd66b3.9e41a","type":"change","z":"8fc67949.33607","name":"Add timestamp","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t    'state': payload,\t    'timestamp': $millis()\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":140,"y":80,"wires":[["74932ec.17155d"]]},{"id":"74932ec.17155d","type":"join","z":"8fc67949.33607","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"payload.state","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":290,"y":80,"wires":[["e596acd1.f23bb"]]},{"id":"e1c5e484.53c948","type":"change","z":"8fc67949.33607","name":"Process delta","rules":[{"t":"set","p":"threshold","pt":"msg","to":"400","tot":"num"},{"t":"set","p":"delta","pt":"msg","to":"$lookup(payload, 'off').timestamp - $lookup(payload, 'on').timestamp","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"delta < threshold ? 'short' : 'long'","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":80,"wires":[["b4dea629.3cf7c"]]},{"id":"e596acd1.f23bb","type":"switch","z":"8fc67949.33607","name":"Filter negative","property":"$lookup(payload, 'off').timestamp - $lookup(payload, 'on').timestamp","propertyType":"jsonata","rules":[{"t":"nnull"}],"checkall":"true","repair":false,"outputs":1,"x":440,"y":80,"wires":[["e1c5e484.53c948"]]},{"id":"8f4d0d9a.3086c","type":"api-call-service","z":"8fc67949.33607","name":"Office Light","server":"f33ae2cd.0fa4","service_domain":"light","service":"toggle","data":"{\"entity_id\":\"light.office_light\"}","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":1250,"y":80,"wires":[[]],"icon":"node-red/light.png"},{"id":"b1a73ca2.6c77a8","type":"change","z":"8fc67949.33607","name":"Set Brightness","rules":[{"t":"set","p":"payload","pt":"msg","to":"10","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":80,"wires":[["8f4d0d9a.3086c"]]},{"id":"f33ae2cd.0fa4","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":false}]

Hi brettig,
Did you solve this? I am in a kind of similar situation and I cannot make this work the way I want it to..
I have my pushbuttons connected to a PLC and I have programmed this to send up one pulse for single click which then triggers light toggle function, this works fine so I am now trying to get push and hold to dim the lights up and down.

I am using IKEA Trådfri lightbulbs and zigbee2mqtt in home assistant to control these.

First I created a ramp function in the PLC which is starting at the current brightness and then ramping up to 255 and then back down again. I then used a function block in node red to pass that ramp as the brightness, like this:

msg.payload = {
domain: 'light',
service: 'turn_on',
data: { entity_id: 'light.bedroom_bed_right', brightness: msg.payload }
};
return msg;

That was not stable at all, I guess that was because the IKEA lights, the zigbee2mqtt bridge or home assistant was not able to process so many commands so I then tried to send a boolean from the PLC instead and attempted to ramp up and down in node red. I could not get this to work either, I noticed that the

data.new_state.attributes.brightness

Immediately gets set to the new brightness command, not the actual state of the lightbulb. So I cannot use this value to decide when to ramp back down for example.
The way I have this working now is like this:
I get the current brightness from home assistant and send that to the PLC.
Once someone activates the push&hold function I use that value, add (or subract) a number from that and send to node red. I then get the current brightness in node red, add a delay of one second to that and send it back to the PLC. When the PLC sees that the actual brightness = brightness command AND push&hold is still activated I add another number to the actual value and send that as a new command.
And if I reach 255 or 1 I reverse the direction.
And if the push&hold is released at any time the last command is kept.
But this is also unstable, quite often the lightbulb does not want to play along and also the light is not at all smoothly dimmed up and down.
I would very much like to have the actual brightness of the lightbulb as a variable that can be used, I mean a value that is ramped up and down according to the actual light. Not just immediately getting the new setpoint.

Hi,

I kinda got things working ok. My prefered solution that I will be going with is to use a WiFi dimmer controller flashed with Tasmota. There may be other more compact tasmota compatible options out there. This is the one I used

I am using standard dimmable 240v LED Downlights. I am tucking the dimmer controller in behind the wall as I want more compact switch plates with multiple controllers on one plate and prefer the appearance of the switch plates I already have. For the wall controller, I have another sonoff device this time flashed with ESP Easy and using a Rotary encoder (for dimmer operations) with the encoder pushbutton (for toggle on/of).

This works good enough for what I need. The only thing that is not perfect is that ESP Easy does not allow writing the current dimmer value to the encoder. This is only a small issue ie the actual light dimmer value may not match what is stored for the encoder. This quickly rights itself when the dimmer knob is turned and the correct value is always shown for the slider in Homeassistant. It could potentially mean that if the light is on 20% and the dimmer knob has 100% stored turning the dimer knob would see the light then jump to 100% before then following the knob.

I would have prefered to stick with a Tasmota only solution but it seemed like getting Rotary Encoder support going in Tasmota was going to take a steeper learning curve in the source code than I have the time for ATM. If Encoder support was available in Tasmota this could then eliminate the need for the second Sonoff device although to use an encuder with pushbutton requires 3 spare GPIO's. I wanted Tem/Humidity in each room so Im just using Sonoff TH10's with the Sonoff Tem/humidity sensor and there are 3 GPIO's available with a bit of soldering.

I did have a single pushbutton working as a dim up down but I have some legacy visitors at times (my mother) that would require too much of my time to educate in their use I decided to stick with something that is absolutely clear at first glance.

Hope this helps

Brett