Today I wanted to share a Node-RED flow that turns on/off a combination of entities with just one button.
A trigger can be a button push - I use an Aqara Wireless Button, but it can be anything really.
Demo
Code in the “Sequence” function node:
const entities = [
'switch.plug2',
'switch.plug3',
'switch.plug4',
].reverse();
const linear = entities.map((e, i) => Math.pow(2, i))
const ha = global.get('homeassistant').homeAssistant;
const binary = entities.map(entity => {
const { state } = ha.states && ha.states[entity]
return state === 'on' ? '1' : '0'
})
let counter = flow.get("counter") || parseInt(binary.join(''), 2) || 0;
this.label = counter;
if (counter < Math.pow(2, entities.length) - 1) {
counter = counter + 1;
} else {
counter = 0;
}
flow.set("counter", counter);
const states = (counter).toString(2).padStart(entities.length, 0)
const collection = entities.map((entity, i) => ({
name: entity,
turn_on: Boolean(Number(states[i]))
}))
const turn_on = collection.length
? collection
.filter(item => item.turn_on)
.map(entity => entity.name)
: []
const turn_off = collection.length
? collection
.filter(item => !item.turn_on)
.map(entity => entity.name)
: []
return {
payload: {
turn_on,
turn_off
}
};
What it does
Code mentioned above makes use of Node-RED’s flow context and Binary-coded decimals.
We can represent every combination of two states (on / off) as a single integer.
How to use it
- Copy and import the following flow to your Node-RED:
[{"id":"2deb91ebdba3a961","type":"function","z":"950348bb2a0562c3","name":"Sequence","func":"const entities = [\n 'switch.plug2',\n 'switch.plug3',\n 'switch.plug4',\n].reverse();\n\nconst linear = entities.map((e, i) => Math.pow(2, i))\n\nconst ha = global.get('homeassistant').homeAssistant;\n\nconst binary = entities.map(entity => {\n const { state } = ha.states && ha.states[entity]\n return state === 'on' ? '1' : '0'\n})\n\nlet counter = flow.get(\"counter\") || parseInt(binary.join(''), 2) || 0;\nthis.label = counter;\n\nif (counter < Math.pow(2, entities.length) - 1) {\n counter = counter + 1;\n} else {\n counter = 0;\n}\n\nflow.set(\"counter\", counter);\n\nconst states = (counter).toString(2).padStart(entities.length, 0)\n\nconst collection = entities.map((entity, i) => ({\n name: entity,\n turn_on: Boolean(Number(states[i]))\n}))\n\nconst turn_on = collection.length\n ? collection\n .filter(item => item.turn_on)\n .map(entity => entity.name)\n : []\n\nconst turn_off = collection.length\n ? collection\n .filter(item => !item.turn_on)\n .map(entity => entity.name)\n : []\n\nreturn {\n payload: {\n turn_on,\n turn_off\n }\n};","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":1600,"wires":[["7534d0c6b1259806","93cc0f6b80bf7d3b"]]},{"id":"977d94a04c986b7b","type":"api-call-service","z":"950348bb2a0562c3","name":"Turn on","server":"e5d3a404.ac0078","version":3,"debugenabled":false,"service_domain":"homeassistant","service":"turn_on","entityId":"","data":"{ \"entity_id\": $join(payload.turn_on, \",\") }","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":760,"y":1600,"wires":[[]]},{"id":"7534d0c6b1259806","type":"switch","z":"950348bb2a0562c3","name":"Is there anything to turn on?","property":"payload.turn_on.length","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":540,"y":1600,"wires":[["977d94a04c986b7b"]]},{"id":"93cc0f6b80bf7d3b","type":"switch","z":"950348bb2a0562c3","name":"Is there anything to turn off?","property":"payload.turn_off.length","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":540,"y":1640,"wires":[["b52b844a3cf7214c"]]},{"id":"700ce69198720e2b","type":"inject","z":"950348bb2a0562c3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":160,"y":1600,"wires":[["2deb91ebdba3a961"]]},{"id":"b52b844a3cf7214c","type":"api-call-service","z":"950348bb2a0562c3","name":"Turn off","server":"e5d3a404.ac0078","version":3,"debugenabled":false,"service_domain":"homeassistant","service":"turn_off","entityId":"","data":"{ \"entity_id\": $join(payload.turn_off, \",\") }","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":760,"y":1660,"wires":[[]]},{"id":"e5d3a404.ac0078","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}]
- In your “Sequence” node modify the
entities
array so that it contains a collection of switches (or other toggleable entities) you’d like to control:
const entities = [
'switch.plug2',
'switch.plug3',
'switch.plug4',
].reverse();
- Add a triggering action right before the “Sequence” node. In my case it’s the Aqara wireless switch
single
click event:
Here’s the whole flow for your convenience:
[{"id":"2deb91ebdba3a961","type":"function","z":"950348bb2a0562c3","name":"Sequence","func":"const entities = [\n 'switch.plug2',\n 'switch.plug3',\n 'switch.plug4',\n].reverse();\n\nconst linear = entities.map((e, i) => Math.pow(2, i))\n\nconst ha = global.get('homeassistant').homeAssistant;\n\nconst binary = entities.map(entity => {\n const { state } = ha.states && ha.states[entity]\n return state === 'on' ? '1' : '0'\n})\n\nlet counter = flow.get(\"counter\") || parseInt(binary.join(''), 2) || 0;\nthis.label = counter;\n\nif (counter < Math.pow(2, entities.length) - 1) {\n counter = counter + 1;\n} else {\n counter = 0;\n}\n\nflow.set(\"counter\", counter);\n\nconst states = (counter).toString(2).padStart(entities.length, 0)\n\nconst collection = entities.map((entity, i) => ({\n name: entity,\n turn_on: Boolean(Number(states[i]))\n}))\n\nconst turn_on = collection.length\n ? collection\n .filter(item => item.turn_on)\n .map(entity => entity.name)\n : []\n\nconst turn_off = collection.length\n ? collection\n .filter(item => !item.turn_on)\n .map(entity => entity.name)\n : []\n\nreturn {\n payload: {\n turn_on,\n turn_off\n }\n};","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":1600,"wires":[["7534d0c6b1259806","93cc0f6b80bf7d3b"]]},{"id":"977d94a04c986b7b","type":"api-call-service","z":"950348bb2a0562c3","name":"Turn on","server":"e5d3a404.ac0078","version":3,"debugenabled":false,"service_domain":"homeassistant","service":"turn_on","entityId":"","data":"{ \"entity_id\": $join(payload.turn_on, \",\") }","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":760,"y":1600,"wires":[[]]},{"id":"7534d0c6b1259806","type":"switch","z":"950348bb2a0562c3","name":"Is there anything to turn on?","property":"payload.turn_on.length","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":540,"y":1600,"wires":[["977d94a04c986b7b"]]},{"id":"93cc0f6b80bf7d3b","type":"switch","z":"950348bb2a0562c3","name":"Is there anything to turn off?","property":"payload.turn_off.length","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":540,"y":1640,"wires":[["b52b844a3cf7214c"]]},{"id":"700ce69198720e2b","type":"inject","z":"950348bb2a0562c3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":160,"y":1600,"wires":[["2deb91ebdba3a961"]]},{"id":"b52b844a3cf7214c","type":"api-call-service","z":"950348bb2a0562c3","name":"Turn off","server":"e5d3a404.ac0078","version":3,"debugenabled":false,"service_domain":"homeassistant","service":"turn_off","entityId":"","data":"{ \"entity_id\": $join(payload.turn_off, \",\") }","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":760,"y":1660,"wires":[[]]},{"id":"1b3bf7f64ed16f86","type":"switch","z":"950348bb2a0562c3","name":"Click Type","property":"payload.event.click_type","propertyType":"msg","rules":[{"t":"eq","v":"single","vt":"str"},{"t":"eq","v":"double","vt":"str"},{"t":"eq","v":"long_click_press","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":610,"y":1800,"wires":[["2deb91ebdba3a961"],[],[]]},{"id":"8f052f4556246d7b","type":"switch","z":"950348bb2a0562c3","name":"Light Switches","property":"payload.entity_id","propertyType":"msg","rules":[{"t":"eq","v":"binary_sensor.switch_158d0001137XXX","vt":"str"},{"t":"eq","v":"binary_sensor.switch_158d00013f9YYY","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":400,"y":1800,"wires":[["1b3bf7f64ed16f86"],["1b3bf7f64ed16f86"]]},{"id":"f4773b8cd5756c13","type":"server-events","z":"950348bb2a0562c3","name":"","server":"e5d3a404.ac0078","version":1,"event_type":"xiaomi_aqara.click","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":190,"y":1800,"wires":[["8f052f4556246d7b"]]},{"id":"e5d3a404.ac0078","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}]
- Deploy
Enjoy