One button - one bulb - on/off/brightness

Linkind's Zigbee one-button sends 5 different messages. on first short press (on), second (off), holding button (brightness_move_up), next long hold (brightness_move_down) and by releasing the button (brightness_stop).
I managed to switch it on and off, but I didn't have any success with the brightness control and I have no idea how to realize it.
I am grateful for a hint!

What you want to do is not easy.

It requires a lot of messing around with codes, and making a special button.

Buttons only send one message - as is. "I'm pressed".
This isn't enough for what you want/need.

You need buttons that send TWO messages. "I'm pressed" and "I'm released".

You then have to send those messages into more code to determine how long the button is pressed.

If shot, do the simple on/off function.

If long pressed, then you have to either dim or brighten the bulb.

How good at you with CSS and Javascript?

No, the button already sends this 5 messages by itself.
I need a way (in this case a suitable node) that send a brightness command to my bulbs and this repeats and increased until the stop message goes in by releasing the button.

You are going to have to share a lot more information if you want help.

I don't have a Zigbee button.

Is this a physical button or a node in Node-red?

It's a physical button from Linkind (https://www.amazon.de/-/en/1-Button-Wireless-Installation-Dimmable-Required/dp/B084MCT7LG?th=1).

Now I use a trigger node. With this the message stops when I release the button.
Ext step, increasing the brightness level...

Interesting product (nice price).

I coded something you might need in my system last weekend. It is tied into my system so not easy to transfer and will not work out of the box (Warning! mostly javascript!)

For altering the brightness you need to keep the last known state to increment or decrement from. (unless your devices have a brightness up /down parameter - then perhaps you need only a timer to send up/down commands until the button is released)

I keep track of all my devices in one object in global context

image

var systemData = global.get("systemData", "file") || {};

image
You will need the "brightness slide" part.

The config looks like this. You will need to change the max to 254 (I use 10bit in my setup as I have some dimmers with 10bit resolution). Time is the update time in ms. To short will saturate the mqtt loop especialy when QoS is something es then 0 (otherwise messages are queued ...

{
    "time": 300,
    "min": 0,
    "max": 1031,
    "loop": false
}

You can even use a simple ramp (here for my hue values) (loop = true because hue is a circle )

{
    "time": 500,
    "min": 0,
    "max": 360,
    "loop": true,
    "speedMax": 36,
    "speedIncrement": 2
}
  • The increment / scene number is defined by msg.payload
  • Targets can be a string or an array of strings (to control multiple devices)
  • Property is the property to alter
  • Action is the mode (only Auto, Loop and Slide is implemented yet)
  • Scenes is an array of objects of different payloards
  • Use set to add msg.set = true
  • options as described above (too lazy to configure more properties)

Perhaps a Starting point.

[{"id":"78f4e1b531ab2143","type":"subflow","name":"alter values","info":"* The increment / scene number is defined by `msg.payload`\r\n* Targets can be a string or an array of strings (to control multiple devices)\r\n* Property is the property to alter\r\n* Action is the mode (only Auto, Loop and Slide is implemented yet)\r\n* Scenes is an array of objects of different payloards\r\n* Use set to add `msg.set = true`\r\n* options (too lazy to configure more properties)\r\n\r\n```\r\n{\r\n    \"time\": 300,\r\n    \"min\": 0,\r\n    \"max\": 1031,\r\n    \"loop\": false\r\n}\r\n```\r\n\r\nYou can even use a simple ramp (here for my hue values)\r\n```\r\n{\r\n    \"time\": 500,\r\n    \"min\": 0,\r\n    \"max\": 360,\r\n    \"loop\": true,\r\n    \"speedMax\": 36,\r\n    \"speedIncrement\": 2\r\n}\r\n```","category":"communication bus","in":[{"x":71,"y":68,"wires":[{"id":"2a122321aa0b150a"}]}],"out":[{"x":360,"y":102,"wires":[{"id":"2a122321aa0b150a","port":1}]}],"env":[{"name":"targets","type":"json","value":"[\"array of targets\"]","ui":{"icon":"font-awesome/fa-location-arrow","label":{"de":"Targets","en-US":"Targets"},"type":"input","opts":{"types":["str","json"]}}},{"name":"property","type":"str","value":"","ui":{"icon":"font-awesome/fa-bolt","label":{"en-US":"Property"},"type":"input","opts":{"types":["str","json"]}}},{"name":"action","type":"str","value":"auto","ui":{"icon":"font-awesome/fa-bolt","label":{"en-US":"Action"},"type":"select","opts":{"opts":[{"l":{"en-US":"Auto"},"v":"auto"},{"l":{"en-US":"Add"},"v":"add"},{"l":{"en-US":"Toggle"},"v":"toggle"},{"l":{"en-US":"Loop"},"v":"loop"},{"l":{"en-US":"Slide"},"v":"slide"}]}}},{"name":"scenes","type":"json","value":"[{\"color_mode\":\"color_temp\",\"color_temp\":\"coolest\"},{\"color_mode\":\"color_temp\",\"color_temp\":\"cool\"},{\"color_mode\":\"color_temp\",\"color_temp\":\"neutral\"},{\"color_mode\":\"color_temp\",\"color_temp\":\"warmest\"},{\"color_mode\":\"color_temp\",\"color_temp\":\"coolest\"},{\"color_mode\":\"color_hs\",\"color\":{\"hue\":360,\"saturation\":100},\"state\":\"ON\"}]","ui":{"icon":"font-awesome/fa-list-ol","label":{"en-US":"Scenes"},"type":"input","opts":{"types":["json"]}}},{"name":"set","type":"bool","value":"true","ui":{"icon":"font-awesome/fa-bolt","label":{"en-US":"Set"},"type":"checkbox"}},{"name":"options","type":"json","value":"{\"time\":100,\"min\":0,\"max\":1023}","ui":{"icon":"font-awesome/fa-clock-o","label":{"en-US":"Options"},"type":"input","opts":{"types":["json"]}}}],"meta":{"module":"prepare-value","version":"0.0.1","author":"cmeinert@gmx.net","desc":"prepare value for one or many targets","license":"Apache-2.0"},"color":"#A6BBCF","icon":"font-awesome/fa-bolt","status":{"x":360,"y":34,"wires":[{"id":"2a122321aa0b150a","port":0}]}},{"id":"2a122321aa0b150a","type":"function","z":"78f4e1b531ab2143","name":"","func":"var property = env.get(\"property\");\nvar targets = env.get(\"targets\");\nvar action = env.get(\"action\");\nvar scenes = env.get(\"scenes\") || [];\nvar options = env.get(\"options\") || { time: 100, min: 0, max: 1023 };\nif (targets === '') targets = msg.target;\nif (!Array.isArray(targets)) targets = [targets];\n\nvar systemData = global.get(\"systemData\", \"file\") || {};\n\nvar msgOut = {\n    topic: '',\n    payload: {},\n    set: env.get(\"set\")\n}\n\nlet errorCount = 0;\nlet successCount = 0;\n\nvar loopPos = context.get('loopPos') || 0;\nvar timers = context.get('timers') || {};\nif (action === 'loop') {\n    //node.warn(loopPos);\n    loopPos = loopPos + msg.payload;\n    if (loopPos >= scenes.length) loopPos = 0;\n    if (loopPos < 0) loopPos = scenes.length - 1;\n}\n\ntargets.forEach(target => {\n    if (systemData.hasOwnProperty(target)) {\n        if (systemData[target].hasOwnProperty(property)) {\n            msgOut.topic = target;\n            switch (action) {\n                case 'slide':\n                    if (msg.payload !== 0) {\n                        node.warn(\"timerstarted\")\n                        if (!timers[target]) timers[target] = {}\n                        if (timers[target].id) clearInterval(timers[target].id);\n                        timers[target].speed = 1;\n                        timers[target].value = systemData[target][property].value;\n                        timers[target].id = setInterval((node, topic, property, increment) => {\n                            timers[topic].value += increment * timers[target].speed;\n                            if (timers[topic].value > options.max) {\n                                if (options.loop) {\n                                    timers[topic].value = options.min;\n                                } else {\n                                    timers[topic].value = options.max;\n                                    if (increment>0) {\n                                        clearInterval(timers[topic].id);\n                                        delete timers[topic].id;\n                                    }\n                                }\n                            }\n                            if (timers[topic].value < options.min) {\n                                if  (options.loop) {\n                                    timers[topic].value = options.max;\n                                } else {\n                                    timers[topic].value = options.min;\n                                    if (increment<0) {\n                                        clearInterval(timers[topic].id);\n                                        delete timers[topic].id;\n                                    }\n                                }\n                            }\n                            if (options.speedMax) {\n                                timers[target].speed += options.speedIncrement;\n                                if (timers[target].speed > options.speedMax) {\n                                    timers[target].speed = options.speedMax;\n                                }\n                            }\n                            msgOut.payload[property] = timers[topic].value;\n                            console.log(topic, increment, timers[topic].value)\n                            node.send([null, msgOut]);\n                        }, options.time, node, target, property, msg.payload)\n                    } else {\n                        node.warn(\"timer stop\");\n                        clearInterval(timers[target].id);\n                        delete timers[target].id;\n                        return;\n                    }\n                    context.set(\"timers\", timers)\n                    break;\n                case \"loop\":\n                    msgOut.payload = scenes[loopPos];\n                    break;\n                case \"auto\":\n                    switch (typeof systemData[target][property].value) {\n                        case 'boolean':\n                            // node.warn({ property, old: systemData[target][property].value});\n                            if (!msg.hasOwnProperty('payload')) {\n                                msgOut.payload[property] = !systemData[target][property].value;\n                            } else {\n                                msgOut.payload[property] = msg.payload;\n                            }\n                            break;\n                        case 'number':\n                            msgOut.payload[property] = systemData[target][property].value + msg.payload;\n                            break;\n                    }\n            }\n            node.send([null, msgOut]);\n            successCount++;\n        } else {\n            node.warn(`${target} has no ${property} property!`);\n            errorCount++;\n        }\n    } else {\n        node.warn(`${target} unknown!`);\n        errorCount++;\n    }\n});\n\ncontext.set(\"loopPos\", loopPos)\nreturn [{ payload: { fill: (errorCount > 0) ? \"red\" : \"green\", shape: \"dot\", text: `updated ${successCount} (${errorCount} errors)` } }, null];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":179,"y":68,"wires":[[],[]]},{"id":"fec569b65f60bbb7","type":"switch","z":"c4bd7d52.9b899","name":"action","property":"payload.action","propertyType":"msg","rules":[{"t":"eq","v":"brightness_up_click","vt":"str"},{"t":"eq","v":"brightness_down_click","vt":"str"},{"t":"eq","v":"toggle","vt":"str"},{"t":"eq","v":"arrow_left_click","vt":"str"},{"t":"eq","v":"arrow_right_click","vt":"str"},{"t":"eq","v":"arrow_right_hold","vt":"str"},{"t":"eq","v":"arrow_right_release","vt":"str"},{"t":"eq","v":"arrow_left_hold","vt":"str"},{"t":"eq","v":"arrow_left_release","vt":"str"},{"t":"eq","v":"brightness_up_hold","vt":"str"},{"t":"eq","v":"brightness_up_release","vt":"str"},{"t":"eq","v":"brightness_down_hold","vt":"str"},{"t":"eq","v":"brightness_down_release","vt":"str"}],"checkall":"false","repair":false,"outputs":13,"x":526,"y":1241,"wires":[["84741c72b7a5eabe"],["f078bf39d32368d2"],["789177a38c7168d5"],["390662aa494e9159"],["0142a923a5d36dbe"],["197fe00e204c88a4"],["5cb974b04ac1ae66"],["3e56c291017c7cf8"],["5cb974b04ac1ae66"],["ba914f94407d4f3e"],["e8dedea7daf472c8"],["17fb66e424aa1732"],["e8dedea7daf472c8"]]},{"id":"029e0260145554cb","type":"subflow:78f4e1b531ab2143","z":"c4bd7d52.9b899","name":"alter Brightness","env":[{"name":"targets","value":"zigbee/Bad-Decke-RGBW","type":"str"},{"name":"property","value":"brightness","type":"str"}],"x":981,"y":1020,"wires":[["8faa680255d4a1a1"]]},{"id":"84741c72b7a5eabe","type":"change","z":"c4bd7d52.9b899","name":"+100","rules":[{"t":"set","p":"payload","pt":"msg","to":"100","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1003,"wires":[["029e0260145554cb"]]},{"id":"f078bf39d32368d2","type":"change","z":"c4bd7d52.9b899","name":"-100","rules":[{"t":"set","p":"payload","pt":"msg","to":"-100","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":746,"y":1037,"wires":[["029e0260145554cb"]]},{"id":"067fc5d4e969e761","type":"subflow:78f4e1b531ab2143","z":"c4bd7d52.9b899","name":"state","env":[{"name":"targets","value":"zigbee/Bad-Decke-RGBW","type":"str"},{"name":"property","value":"state","type":"str"}],"x":951,"y":1088,"wires":[["8faa680255d4a1a1"]]},{"id":"789177a38c7168d5","type":"change","z":"c4bd7d52.9b899","name":"toggle","rules":[{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1088,"wires":[["067fc5d4e969e761"]]},{"id":"2bc3dce22dadc14f","type":"subflow:78f4e1b531ab2143","z":"c4bd7d52.9b899","name":"scenes","env":[{"name":"targets","value":"zigbee/Bad-Decke-RGBW","type":"str"},{"name":"property","value":"state","type":"str"},{"name":"action","value":"loop","type":"str"},{"name":"scenes","value":"[{\"color_temp\":\"cool\"},{\"color_temp\":\"neutral\"},{\"color_temp\":\"warm\"},{\"color\":{\"hex\":\"#ff0000\"}}]","type":"json"}],"x":961,"y":1156,"wires":[["c79e3af08909151d"]]},{"id":"0142a923a5d36dbe","type":"change","z":"c4bd7d52.9b899","name":"+1","rules":[{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1173,"wires":[["2bc3dce22dadc14f"]]},{"id":"390662aa494e9159","type":"change","z":"c4bd7d52.9b899","name":"-1","rules":[{"t":"set","p":"payload","pt":"msg","to":"-1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1139,"wires":[["2bc3dce22dadc14f"]]},{"id":"17154dde1605cf45","type":"subflow:78f4e1b531ab2143","z":"c4bd7d52.9b899","name":"brightness slide","env":[{"name":"targets","value":"zigbee/Bad-Decke-RGBW","type":"str"},{"name":"property","value":"brightness","type":"str"},{"name":"action","value":"slide","type":"str"},{"name":"scenes","value":"[]","type":"json"},{"name":"options","value":"{\"time\":300,\"min\":0,\"max\":1031,\"loop\":false}","type":"json"}],"x":981,"y":1377,"wires":[["663a14de11492c13","8faa680255d4a1a1"]]},{"id":"ba914f94407d4f3e","type":"change","z":"c4bd7d52.9b899","name":"10","rules":[{"t":"set","p":"payload","pt":"msg","to":"20","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1343,"wires":[["17154dde1605cf45"]]},{"id":"e8dedea7daf472c8","type":"change","z":"c4bd7d52.9b899","name":"0","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1377,"wires":[["17154dde1605cf45"]]},{"id":"17fb66e424aa1732","type":"change","z":"c4bd7d52.9b899","name":"-10","rules":[{"t":"set","p":"payload","pt":"msg","to":"-20","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1411,"wires":[["17154dde1605cf45"]]},{"id":"9d36b72b9b461bbe","type":"subflow:78f4e1b531ab2143","z":"c4bd7d52.9b899","name":"hue slide","env":[{"name":"targets","value":"zigbee/Bad-Decke-RGBW","type":"str"},{"name":"property","value":"hue","type":"str"},{"name":"action","value":"slide","type":"str"},{"name":"scenes","value":"[]","type":"json"},{"name":"options","value":"{\"time\":500,\"min\":0,\"max\":360,\"loop\":true,\"speedMax\":36,\"speedIncrement\":2}","type":"json"}],"x":961,"y":1258,"wires":[["39145fedb8f79528","8faa680255d4a1a1"]]},{"id":"197fe00e204c88a4","type":"change","z":"c4bd7d52.9b899","name":"up","rules":[{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1224,"wires":[["9d36b72b9b461bbe"]]},{"id":"5cb974b04ac1ae66","type":"change","z":"c4bd7d52.9b899","name":"stop","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1258,"wires":[["9d36b72b9b461bbe"]]},{"id":"3e56c291017c7cf8","type":"change","z":"c4bd7d52.9b899","name":"down","rules":[{"t":"set","p":"payload","pt":"msg","to":"-1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":747,"y":1292,"wires":[["9d36b72b9b461bbe"]]}]

So do you have this currently talking to NR ? If so use each of the button states and show us some debug nodes so we can see what they do.

It is going to be complex to do and probably going to have lots of false readings but would be worth seeing if a series of trigger nodes could do what you want

Craig

As far as I can tell there is no brightness increase or decrease command. It will receive a value between 0-254.
In my imagination (and I really have no idea) that's how I imagine it.
I read out the current value or have the value saved before.
Now the message "brightness move up" comes from my trigger node (every 300ms) and an additional function node sends the command to increase previous brightness value plus 10% to the light.

...or, regardless of whether the "brightness move up or down", I always start at zero and then go up in steps of 10%. A little sloppy, but the effect is similar.

I will send my existing flow this evening. May be this shows my thoughts a bit cleary.

1 Like

here is my try so far....

[{"id":"0399a4b7d5e72e43","type":"zigbee2mqtt-in","z":"e12aea8ac8d3d2e3","name":"1button","server":"0ef37b328885a4f0","friendly_name":"onebutton_01","device_id":"0x588e81fffe204550","state":"0","outputAtStartup":false,"x":70,"y":100,"wires":[["70b101bfd18fe5ef"]]},{"id":"70b101bfd18fe5ef","type":"switch","z":"e12aea8ac8d3d2e3","name":"OneButton","property":"payload.action","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"brightness_move_up","vt":"str"},{"t":"eq","v":"brightness_move_down","vt":"str"},{"t":"eq","v":"brightness_stop","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":170,"y":200,"wires":[["74514d8e6851d6fa"],["6e92902acce1424a"],["c2abcb94508d17c0"],["c2abcb94508d17c0"],["9b9f804978495a48"]]},{"id":"74514d8e6851d6fa","type":"api-call-service","z":"e12aea8ac8d3d2e3","name":"Light 1 On","server":"877222d8.588fc","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.bulb_rgb_01","data":"{ \"color_temp\":\"450\" }","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":370,"y":100,"wires":[[]]},{"id":"9b9f804978495a48","type":"function","z":"e12aea8ac8d3d2e3","name":"","func":"msg.payload= \"stop\" ;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":240,"wires":[["c2abcb94508d17c0"]]},{"id":"c2abcb94508d17c0","type":"trigger","z":"e12aea8ac8d3d2e3","name":"","op1":"","op2":"0","op1type":"pay","op2type":"str","duration":"-200","extend":false,"overrideDelay":false,"units":"ms","reset":"stop","bytopic":"all","topic":"topic","outputs":1,"x":570,"y":180,"wires":[["27939616f31d2f98"]]},{"id":"6e92902acce1424a","type":"api-call-service","z":"e12aea8ac8d3d2e3","name":"Light 1 Off","server":"877222d8.588fc","version":3,"debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"light.bulb_rgb_01","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":610,"y":100,"wires":[[]]},{"id":"27939616f31d2f98","type":"debug","z":"e12aea8ac8d3d2e3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":690,"y":240,"wires":[]},{"id":"0ef37b328885a4f0","type":"zigbee2mqtt-server","name":"zigbee2mqtt","host":"localhost","mqtt_port":"1883","mqtt_username":"mqtt_user","mqtt_password":"mqtt1212","tls":"","usetls":false,"base_topic":"zigbee2mqtt"},{"id":"877222d8.588fc","type":"server","name":"homeassistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":""}]```

Hi

never used the "trigger" node before ... so a nice exercise :wink: I don't use the zigbee2mqtt node (any more) so I hope it works for you

The store Data subflow stores all payloads in flow context by topic. Connect this to your zigbee input of the bulb. Make sure everything is in one flow. if not this must be changed to global context.

image

The trigger nodes replays the received msg. The +/- function gets the last value or the stored value (by topic) adds or subtracts 10 checks the limits and send it further. If the limit is reached msg.payload = "stop" is sent to the second output (and the node context is deleted).

The value is then stored (in the node context) and sent to your bulb

Make sure that the topic of your button is changed to the topic of the target bulb. Then you can reuse all nodes to dim different bulbs.

This flow expect that the bulb confirms the new brightness (and then stored via store data).
The extra node context is necessary to eliminate the risk of overlapping confirmations potentially getting the flow stuck (i.e. start at 50 increments +10 two times = 70 ... then receive the 60 confirmed increments this = 70 again .... ) or other hiccups. So in this test it always starts at 50 in real world it should always start at the last confirmed value.

var brightness = context.get("brightness") || flow.get("zigbee")[msg.topic].brightness;
var stopMsg = null;

switch (msg.payload.action) {
    case "brightness_move_up":
        brightness += 10;
        if (brightness >= 254) {
            brightness = 254;
            stopMsg = {payload:"stop"};
        }
        break;
    case "brightness_move_down":
        brightness -= 10;
        if (brightness <= 0) {
            brightness = 0;
            stopMsg = {payload:"stop"};
        }
        break;
    case "brightness_stop":
        stopMsg = { payload: "stop" };
        break;
    default:
        return;
}

context.set("brightness", (stopMsg === null) ? brightness : undefined)
msg.payload={brightness}

return [msg,stopMsg];

The +/- function node could be put into a subflow and reused for many bulbs and buttons

Sorry again a JavaScript "heavy" flow ... but hopefully readable (no "low code" today :crazy_face:)
Hope this helps a bit

[{"id":"aef28cac67285654","type":"subflow","name":"Store Data","info":"","category":"","in":[{"x":51,"y":85,"wires":[{"id":"6d83796d373c491b"}]}],"out":[{"x":377,"y":119,"wires":[{"id":"6d83796d373c491b","port":1}]}],"env":[],"meta":{},"color":"#FFCC66","icon":"font-awesome/fa-database","status":{"x":377,"y":68,"wires":[{"id":"6d83796d373c491b","port":0}]}},{"id":"6d83796d373c491b","type":"function","z":"aef28cac67285654","name":"store all data","func":"var zigbee = flow.get(\"$parent.zigbee\") || {}; // get the data object\nif (!zigbee.hasOwnProperty(msg.topic)) zigbee[msg.topic]={} // initialize if nessesary\n\n// merge all properties of msg.payload into the data object\nObject.keys(msg.payload).forEach(element => {\n    zigbee[msg.topic][element] = msg.payload[element];\n});\n\n// store the data\nflow.set('$parent.zigbee',zigbee)\n\nreturn [{payload:{fill:\"green\",shape:\"dot\",text:msg.topic}},msg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":206,"y":85,"wires":[[],[]]},{"id":"70b101bfd18fe5ef","type":"switch","z":"c4bd7d52.9b899","name":"OneButton","property":"payload.action","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"brightness_move_up","vt":"str"},{"t":"eq","v":"brightness_move_down","vt":"str"},{"t":"eq","v":"brightness_stop","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":1464,"y":374,"wires":[[],[],["e06a06ae556d32a7"],["e06a06ae556d32a7"],["979b98b04c431880"]]},{"id":"27939616f31d2f98","type":"debug","z":"c4bd7d52.9b899","name":"to zigbee","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"msg","x":2236,"y":391,"wires":[]},{"id":"7729994f612f6b0c","type":"inject","z":"c4bd7d52.9b899","name":"","props":[{"p":"payload.brightness","v":"50","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/bulb","x":1287,"y":221,"wires":[["79e8f2572419e66a"]]},{"id":"79e8f2572419e66a","type":"subflow:aef28cac67285654","z":"c4bd7d52.9b899","name":"","x":1506,"y":221,"wires":[[]]},{"id":"e06a06ae556d32a7","type":"trigger","z":"c4bd7d52.9b899","name":"","op1":"","op2":"0","op1type":"pay","op2type":"str","duration":"-200","extend":false,"overrideDelay":false,"units":"ms","reset":"stop","bytopic":"all","topic":"topic","outputs":1,"x":1834,"y":323,"wires":[["979b98b04c431880"]]},{"id":"979b98b04c431880","type":"function","z":"c4bd7d52.9b899","name":"+/-","func":"var brightness = context.get(\"brightness\") || flow.get(\"zigbee\")[msg.topic].brightness;\nvar stopMsg = null;\n\nswitch (msg.payload.action) {\n    case \"brightness_move_up\":\n        brightness += 10;\n        if (brightness >= 254) {\n            brightness = 254;\n            stopMsg = {payload:\"stop\"};\n        }\n        break;\n    case \"brightness_move_down\":\n        brightness -= 10;\n        if (brightness <= 0) {\n            brightness = 0;\n            stopMsg = {payload:\"stop\"};\n        }\n        break;\n    case \"brightness_stop\":\n        stopMsg = { payload: \"stop\" };\n        break;\n    default:\n        return;\n}\n\ncontext.set(\"brightness\", (stopMsg === null) ? brightness : undefined)\nmsg.payload={brightness}\n\nreturn [msg,stopMsg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2039,"y":408,"wires":[["27939616f31d2f98"],["e06a06ae556d32a7"]]},{"id":"dd639489145029b9","type":"inject","z":"c4bd7d52.9b899","name":"up","props":[{"p":"payload.action","v":"brightness_move_up","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/bulb","x":1257,"y":323,"wires":[["70b101bfd18fe5ef"]]},{"id":"0bc85bc78ef049b0","type":"inject","z":"c4bd7d52.9b899","name":"down","props":[{"p":"payload.action","v":"brightness_move_down","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/bulb","x":1257,"y":374,"wires":[["70b101bfd18fe5ef"]]},{"id":"81dce11b52d52fa7","type":"inject","z":"c4bd7d52.9b899","name":"stop","props":[{"p":"payload.action","v":"brightness_stop","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/bulb","x":1257,"y":425,"wires":[["70b101bfd18fe5ef"]]}]
1 Like

Thanks! This looks great, unfortunately I do not understand what happend in the store section.
... and surely I am not in the situation to adopt this for my configuration.
Maybe some days of investigation of your code will give me a clearly sight.

That isn't so complicated . Simply stick a mqtt in node to it and it will store all the data as it arrives (or all devices or parameters you configured as retainedin zigbee2mqtt immediate as the node connects)

image

set the topic to yourBaseTopic/# and Output to parsed JSON. My base topic is zigbee (but I think the dafault is zigbee2mqtt so I used zigbee2mqtt in the flow above.

image
If you press refresh on the contextdata tab you see all your data
image

I didn't included that because every broker and zigbee2mqtt setup differs

For this flow the base topic does not matter. If your button comes with zigbee2mqtt/buttonand your bulb with ´zigbee2mqtt/bulb` you only have to change the topic on the input with a change node

image

Then add /set to the messages you send to mqtt and it should work (no real need for the zigbee2mqtt node, as long as you know the topics and parameters) And your setup can be done with a single mqtt in and out node:

[{"id":"aef28cac67285654","type":"subflow","name":"Store Data","info":"","category":"","in":[{"x":51,"y":85,"wires":[{"id":"6d83796d373c491b"}]}],"out":[{"x":377,"y":119,"wires":[{"id":"6d83796d373c491b","port":1}]}],"env":[],"meta":{},"color":"#FFCC66","icon":"font-awesome/fa-database","status":{"x":377,"y":68,"wires":[{"id":"6d83796d373c491b","port":0}]}},{"id":"6d83796d373c491b","type":"function","z":"aef28cac67285654","name":"store all data","func":"var zigbee = flow.get(\"$parent.zigbee\") || {}; // get the data object\nif (!zigbee.hasOwnProperty(msg.topic)) zigbee[msg.topic]={} // initialize if nessesary\n\n// merge all properties of msg.payload into the data object\nObject.keys(msg.payload).forEach(element => {\n    zigbee[msg.topic][element] = msg.payload[element];\n});\n\n// store the data\nflow.set('$parent.zigbee',zigbee)\n\nreturn [{payload:{fill:\"green\",shape:\"dot\",text:msg.topic}},msg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":206,"y":85,"wires":[[],[]]},{"id":"70b101bfd18fe5ef","type":"switch","z":"c4bd7d52.9b899","name":"OneButton","property":"payload.action","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"brightness_move_up","vt":"str"},{"t":"eq","v":"brightness_move_down","vt":"str"},{"t":"eq","v":"brightness_stop","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":1685,"y":255,"wires":[[],[],["e06a06ae556d32a7"],["e06a06ae556d32a7"],["979b98b04c431880"]]},{"id":"27939616f31d2f98","type":"debug","z":"c4bd7d52.9b899","name":"to zigbee","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"msg","x":2423,"y":255,"wires":[]},{"id":"7729994f612f6b0c","type":"inject","z":"c4bd7d52.9b899","name":"","props":[{"p":"payload.brightness","v":"50","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/bulb","x":1270,"y":34,"wires":[["79e8f2572419e66a"]]},{"id":"79e8f2572419e66a","type":"subflow:aef28cac67285654","z":"c4bd7d52.9b899","name":"","x":1591,"y":102,"wires":[[]]},{"id":"e06a06ae556d32a7","type":"trigger","z":"c4bd7d52.9b899","name":"","op1":"","op2":"0","op1type":"pay","op2type":"str","duration":"-200","extend":false,"overrideDelay":false,"units":"ms","reset":"stop","bytopic":"all","topic":"topic","outputs":1,"x":1919,"y":204,"wires":[["979b98b04c431880"]]},{"id":"979b98b04c431880","type":"function","z":"c4bd7d52.9b899","name":"+/-","func":"var brightness = context.get(\"brightness\") || flow.get(\"zigbee\")[msg.topic].brightness;\nvar stopMsg = null;\n\nswitch (msg.payload.action) {\n    case \"brightness_move_up\":\n        brightness += 10;\n        if (brightness >= 254) {\n            brightness = 254;\n            stopMsg = {payload:\"stop\"};\n        }\n        break;\n    case \"brightness_move_down\":\n        brightness -= 10;\n        if (brightness <= 0) {\n            brightness = 0;\n            stopMsg = {payload:\"stop\"};\n        }\n        break;\n    case \"brightness_stop\":\n        stopMsg = { payload: \"stop\" };\n        break;\n    default:\n        return;\n}\n\ncontext.set(\"brightness\", (stopMsg === null) ? brightness : undefined)\nmsg.payload={brightness}\n\nreturn [msg,stopMsg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2124,"y":289,"wires":[["9bd7d52fc27854e8"],["e06a06ae556d32a7"]]},{"id":"dd639489145029b9","type":"inject","z":"c4bd7d52.9b899","name":"up","props":[{"p":"payload.action","v":"brightness_move_up","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/button","x":1240,"y":204,"wires":[["4acf37f925bbb165"]]},{"id":"0bc85bc78ef049b0","type":"inject","z":"c4bd7d52.9b899","name":"down","props":[{"p":"payload.action","v":"brightness_move_down","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/button","x":1240,"y":255,"wires":[["4acf37f925bbb165"]]},{"id":"81dce11b52d52fa7","type":"inject","z":"c4bd7d52.9b899","name":"stop","props":[{"p":"payload.action","v":"brightness_stop","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"zigbee2mqtt/button","x":1240,"y":306,"wires":[["4acf37f925bbb165"]]},{"id":"f6406bb6ed5a4f85","type":"mqtt in","z":"c4bd7d52.9b899","name":"zigbee@home","topic":"zigbee/#","qos":"2","datatype":"json","broker":"65208b9c.8c8714","nl":false,"rap":true,"rh":0,"inputs":0,"x":1226,"y":102,"wires":[["79e8f2572419e66a","38c95a9a6e0872ca"]]},{"id":"4acf37f925bbb165","type":"change","z":"c4bd7d52.9b899","name":"zigbee2mqtt/bulb","rules":[{"t":"set","p":"topic","pt":"msg","to":"zigbee2mqtt/bulb","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1501,"y":255,"wires":[["70b101bfd18fe5ef"]]},{"id":"9bd7d52fc27854e8","type":"function","z":"c4bd7d52.9b899","name":"/set","func":"msg.topic = msg.topic + '/set';\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2277,"y":289,"wires":[["27939616f31d2f98","f90509bb27aca67d"]]},{"id":"f90509bb27aca67d","type":"mqtt out","z":"c4bd7d52.9b899","name":"zigbee@home","topic":"","qos":"2","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"65208b9c.8c8714","x":2443,"y":323,"wires":[]},{"id":"38c95a9a6e0872ca","type":"switch","z":"c4bd7d52.9b899","name":"by Topic","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"zigbee2mqtt/button","vt":"str"},{"t":"eq","v":"zigbee2mqtt/something else","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1403,"y":170,"wires":[["4acf37f925bbb165"],[]]},{"id":"65208b9c.8c8714","type":"mqtt-broker","name":"mosquitto@home","broker":"192.168.2.14","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]

Thank you Christian. this helps...

But the data storage does not seem to be correct in my case.
A value of 255 remains and the brightness increase does not work.

[{"id":"aef28cac67285654","type":"subflow","name":"Store Data","info":"","category":"","in":[{"x":50,"y":30,"wires":[{"id":"6d83796d373c491b"}]}],"out":[{"x":377,"y":119,"wires":[{"id":"6d83796d373c491b","port":1}]}],"env":[],"meta":{},"color":"#FFCC66","icon":"font-awesome/fa-database","status":{"x":377,"y":68,"wires":[{"id":"6d83796d373c491b","port":0}]}},{"id":"6d83796d373c491b","type":"function","z":"aef28cac67285654","name":"store all data","func":"var zigbee = flow.get(\"$parent.zigbee\") || {}; // get the data object\nif (!zigbee.hasOwnProperty(msg.topic)) zigbee[msg.topic]={} // initialize if nessesary\n\n// merge all properties of msg.payload into the data object\nObject.keys(msg.payload).forEach(element => {\n    zigbee[msg.topic][element] = msg.payload[element];\n});\n\n// store the data\nflow.set('$parent.zigbee',zigbee)\n\nreturn [{payload:{fill:\"green\",shape:\"dot\",text:msg.topic}},msg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":206,"y":85,"wires":[[],[]]},{"id":"e12aea8ac8d3d2e3","type":"tab","label":"Linkind Button","disabled":false,"info":""},{"id":"70b101bfd18fe5ef","type":"switch","z":"e12aea8ac8d3d2e3","name":"switch","property":"payload.action","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"},{"t":"eq","v":"brightness_move_up","vt":"str"},{"t":"eq","v":"brightness_move_down","vt":"str"},{"t":"eq","v":"brightness_stop","vt":"str"}],"checkall":"false","repair":false,"outputs":5,"x":170,"y":340,"wires":[["74514d8e6851d6fa"],["6e92902acce1424a"],["c2abcb94508d17c0"],["c2abcb94508d17c0"],["eae70ea50743dc1a"]]},{"id":"74514d8e6851d6fa","type":"api-call-service","z":"e12aea8ac8d3d2e3","name":"Light 1 On","server":"877222d8.588fc","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.bulb_rgb_01","data":"{ \"color_temp\":\"450\" }","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":330,"y":200,"wires":[[]]},{"id":"6e92902acce1424a","type":"api-call-service","z":"e12aea8ac8d3d2e3","name":"Light 1 Off","server":"877222d8.588fc","version":3,"debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"light.bulb_rgb_01","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":410,"y":260,"wires":[[]]},{"id":"27939616f31d2f98","type":"debug","z":"e12aea8ac8d3d2e3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":400,"wires":[]},{"id":"c2abcb94508d17c0","type":"trigger","z":"e12aea8ac8d3d2e3","name":"","op1":"","op2":"0","op1type":"pay","op2type":"str","duration":"-200","extend":false,"overrideDelay":false,"units":"ms","reset":"stop","bytopic":"all","topic":"topic","outputs":1,"x":490,"y":320,"wires":[["eae70ea50743dc1a"]]},{"id":"f65a5786c0ffeb07","type":"mqtt in","z":"e12aea8ac8d3d2e3","name":"onebutton_01","topic":"zigbee2mqtt/onebutton_01","qos":"2","datatype":"json","broker":"4803db9febf3ecc7","nl":false,"rap":true,"rh":0,"inputs":0,"x":90,"y":220,"wires":[["70b101bfd18fe5ef","588fcccb68202e29"]]},{"id":"588fcccb68202e29","type":"subflow:aef28cac67285654","z":"e12aea8ac8d3d2e3","name":"","x":240,"y":120,"wires":[[]]},{"id":"eae70ea50743dc1a","type":"function","z":"e12aea8ac8d3d2e3","name":"+/-","func":"var brightness = context.get(\"brightness\") || flow.get(\"zigbee\")[msg.topic].brightness;\nvar stopMsg = null;\n\nswitch (msg.payload.action) {\n    case \"brightness_move_up\":\n        brightness += 10;\n        if (brightness >= 254) {\n            brightness = 254;\n            stopMsg = {payload:\"stop\"};\n        }\n        break;\n    case \"brightness_move_down\":\n        brightness -= 10;\n        if (brightness <= 0) {\n            brightness = 0;\n            stopMsg = {payload:\"stop\"};\n        }\n        break;\n    case \"brightness_stop\":\n        stopMsg = { payload: \"stop\" };\n        break;\n    default:\n        return;\n}\n\ncontext.set(\"brightness\", (stopMsg === null) ? brightness : undefined)\nmsg.payload={brightness}\n\nreturn [msg,stopMsg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":480,"wires":[["5b6413991048e885"],["c2abcb94508d17c0"]]},{"id":"5b6413991048e885","type":"function","z":"e12aea8ac8d3d2e3","name":"/set","func":"msg.topic = msg.topic + '/set';\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":480,"wires":[["27939616f31d2f98"]]},{"id":"877222d8.588fc","type":"server","name":"homeassistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":""},{"id":"4803db9febf3ecc7","type":"mqtt-broker","name":"MQTT","broker":"localhost","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]

From 254 there should not be a further increase (see limit in the code above). If your limit os higher (like some of mine the max is 1023 (10bit)

What do you see in the flow context inspector panel? Remember the value only appears when it was received / acknowledged by the bulb. (Perhaps put a debug node behind the mqtt in node and see if you receive/ see anything)

Allways good to use mqtt explorer to see what is going on on the mqtt broker at all). Perhaps send a screenshot of the bulb and button part. As you used the zigbee2mqtt node you likely use the legacy api (otherwise zigbee2mqtt don’t work in the current version)

Check if all topics match your setup.

I have a config Object for all my devices which has conversion functions to/from device value to system value. So for example: a device brightness value always gets converted to 0 - 100%, that way I do not have to worry about different device values for different properties.

Thats reasonable. But unless you work with floating point you loose a lot of resolution. I build my hole system around a common set of types and units as I interface with at lease 5 different systems and (zigbee / homekit / homematic / the dashboard and own devices following the homie convention) so all slightly different. All interconnected with a kind of bus flow where every system can grab a value if the bus detects a match and do the conversion automatically.

Here I would be happy if @migsun receive any value to start with.

Perhaps this is the problem. The bulb only accept the on/off. An altering of the brightness did not happen. In context inspector panel ist folgendes zu sehen...

My zigbee lamp turns on automatically when I send a brightness command. Connect an inject node to an MQTT Out node with these settings;

msg.payload: {"brightness": 200}
msg.topic: zigbee2mqtt/bulb_RGB_01/set

This should turn on the lamp & set the brightness. If this works try changing the number in msg.payload

Note: the MQTT Out node should be connected to your server but have no topic entry

nothing happen.
the lamp does not come on and no brightness value is entered.

Can you control the bulb with the zigbee2mqtt dashboard? This is the first test I do to test the functions of a new device

And if so do you get these updates into node-red?