Help merging 2 Node Red flows in 1

I will have to mess with this when I get home :frowning: I was hoping I could do this remotely.

Aww :neutral_face: so will need 12 different flows for each plug in this case ! :dizzy_face:

Any chance that will change in the future to avoid this ?

This isn’t going to be possible the way it stands now because the node is a two-part system.

  1. On deploy registers the sensor with HA
  2. On input updates the sensor in HA

Not sure your understanding of javascript so I wouldn’t do much explaining of the code below. Ask any questions that you have about it.

Doing it this way you won’t be able to remove the sensors in HA from NR, you’d have to do it from the entity registry.

[{"id":"d5c4ef79.0c564","type":"ha-api","z":"9ce184b.0f5bb78","name":"Register Sensor","protocol":"websocket","method":"get","path":"","data":"{\t   \"type\":\"nodered/discovery\",\t   \"component\": \"sensor\",\t   \"server_id\": serverId,\t   \"node_id\": nodeId,\t   \"config\": {\t       \"name\": name,\t       \"unit_of_measurement\": uom\t   }\t}","dataType":"jsonata","location":"payload","locationType":"msg","responseType":"json","x":768,"y":1216,"wires":[["58a160e0.16fe8"]]},{"id":"a6e1d000.16b7a","type":"function","z":"9ce184b.0f5bb78","name":"Device Ids go here","func":"// List of all device ids, name of switch\nconst deviceIds = [\n    [\"10470280dc4f22ed6311\", \"switch1\"],\n    [\"2\", \"switch2\"],\n    // [\"10470280dc4f22ed6311\", \"switch3\"],\n];\n\ndeviceIds.forEach(d => {\n    [\"power\", \"current\", \"voltage\"].forEach(type => {\n        let unit;        \n        switch(type) {\n            case \"power\":\n                unit = \"W\"\n                break;\n            case \"current\": \n                unit = \"mA\"\n                break;\n            case \"voltage\": \n                unit = \"V\"\n                break;\n        }\n\n        node.send({\n           serverId: d[0],\n           nodeId: type,\n           name: `${d[1]} ${type}`,\n           uom: unit\n        })\n    })\n});\n","outputs":1,"noerr":0,"x":570,"y":1216,"wires":[["d5c4ef79.0c564"]]},{"id":"d872791e.7e3db8","type":"server-events","z":"9ce184b.0f5bb78","name":"","event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"x":184,"y":1216,"wires":[["cab5b48c.7d4408"]]},{"id":"865bb27b.23aaf","type":"inject","z":"9ce184b.0f5bb78","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":1312,"wires":[["58a160e0.16fe8"]]},{"id":"d7ba1479.a00048","type":"function","z":"9ce184b.0f5bb78","name":"fake tuya switch1","func":"msg.data = {\n    name: \"switch_1\",\n    ip: \"192.168.0.112\",\n    id: \"10470280dc4f22ed6311\",\n    available: true\n};\nmsg.payload = {\n    devId: \"10470280dc4f22ed6311\",\n    dps: {\n        1: true,\n        9: 0,\n        18: 5,\n        19: 6,\n        20: 2346,\n        21: 1,\n        22: 591,\n        23: 28555,\n        24: 16349,\n        25: 1275\n    }\n};\nreturn msg;","outputs":1,"noerr":0,"x":506,"y":1312,"wires":[["66028769.695528"]]},{"id":"e7005805.eb98e8","type":"function","z":"9ce184b.0f5bb78","name":"fake tuya switch2","func":"msg.data = {\n    name: \"switch_2\",\n    ip: \"192.168.0.112\",\n    id: \"2\",\n    available: true\n};\nmsg.payload = {\n    devId: \"2\",\n    dps: {\n        1: true,\n        9: 0,\n        18: 1,\n        19: 2,\n        20: 2346,\n        21: 1,\n        22: 591,\n        23: 28555,\n        24: 16349,\n        25: 1275\n    }\n};\nreturn msg;","outputs":1,"noerr":0,"x":508,"y":1360,"wires":[["66028769.695528"]]},{"id":"66028769.695528","type":"function","z":"9ce184b.0f5bb78","name":"parse data","func":"const types = {\n    \"power\": \"18\",\n    \"current\": \"19\",\n    \"voltage\": \"20\"\n}\nfor (const [type, id] of Object.entries(types)) {\n    node.send({\n        serverId: msg.payload.devId,\n        nodeId: type,\n        payload: msg.payload.dps[id] || 0,\n    });\n}","outputs":1,"noerr":0,"x":696,"y":1312,"wires":[["9ddb7a33.f8dad8"]]},{"id":"9ddb7a33.f8dad8","type":"ha-api","z":"9ce184b.0f5bb78","name":"Update Sensor","protocol":"websocket","method":"get","path":"","data":"{\t    \"type\":\"nodered/entity\",\t    \"server_id\": serverId,\t    \"node_id\": nodeId,\t    \"state\": payload    \t}","dataType":"jsonata","location":"payload","locationType":"msg","responseType":"json","x":880,"y":1312,"wires":[[]]},{"id":"3066ba37.c8ef16","type":"comment","z":"9ce184b.0f5bb78","name":"Register Sensors","info":"","x":144,"y":1168,"wires":[]},{"id":"9df1457d.2665c8","type":"comment","z":"9ce184b.0f5bb78","name":"Update Sensors","info":"","x":144,"y":1264,"wires":[]},{"id":"cab5b48c.7d4408","type":"switch","z":"9ce184b.0f5bb78","name":"connected","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"connected","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":390,"y":1216,"wires":[["a6e1d000.16b7a"]]},{"id":"58a160e0.16fe8","type":"switch","z":"9ce184b.0f5bb78","name":"hub","property":"true","propertyType":"jsonata","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":322,"y":1312,"wires":[["d7ba1479.a00048","e7005805.eb98e8"]]},{"id":"fe75725c.0db45","type":"comment","z":"9ce184b.0f5bb78","name":"Update Sensors after registering them","info":"","x":534,"y":1264,"wires":[]}]
1 Like

You will only have 12 nodes for each sensor. But you should be able to have one horizontal line flow if you route them and template properly. You can also consolidate all sensor nodes in one subflow of space is what bothers you. Otherwise have template sensors based on input text, and one call service node

I would like to dynamically create sensors for the brightness of each of my lights so I can then better monitor changes in brightness with NR (I don’t think there is currently a way in NR to monitor attribute changes with the same flexibility as state changes i.e. you can’t have an attribute change trigger a flow only if it has changed for x amount of time). So my solution is to dynamically create a sensor for the brightness of each of my lights so that anytime I add another light to my home, NR will create a sensor for it automatically (via the method in the post I’m replying to) and then NR will also monitor that sensor’s state (which would be the brightness of the light). My question: is this a practical approach? Will the sensors that are dynamically created through this method be updated constantly like a template sensor would?

isn’t attribute a state ‘member’?
I’m asking myself from time to time.
Anyway try to react on state change then compare attribute value with one stored in flow variable.

At this moment I’m not sure NR can create sensors with names generated dynamically in runtime. I guess it cannot.

I don’t think the state node will trigger if an entity’s attribute changes. I want it to trigger only when a light’s brightness or color temperature changes but only once it’s changed for a few seconds. The reason for this is that I can use a trigger-state node to trigger a flow when an attribute changes but it triggers a ridiculous amount of messages for any change to my Hue lights (it also triggers for several entity attribute changes that I don’t care to be listening to). So if I change the brightness and color of a light it triggers my flow several times on the way to its destination brightness/color. The problem is much worse when I change the brightness/color of more than 1 light, which I do way more often

Either option is doable.

I personally would go with the approach of monitoring the attributes and then continuing if x time has elapsed.

I can mock either way up if you want an example.

That would be great if you could do that. Just to be clear though, creating sensors dynamically through node red would not be the same as creating a template sensor in the config file, right? As in the sensor wouldn’t automatically update any time the entity whose attribute the sensor is referencing is updated?

You can make the sensor created in NR update just like a template sensor would. You would just need to listen for the change and update the sensor.

Not really tested all that much but example 1 shows creating real entities in HA and the second shows creating temp ones that will disappear after a HA reset and will only appear again after their state changes.

[{"id":"d5c4ef79.0c564","type":"ha-api","z":"b28195ad.c495e8","name":"Register Sensor","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"","dataType":"json","location":"here","locationType":"msg","responseType":"json","x":912,"y":112,"wires":[["6cba3632.d10ad8"]]},{"id":"d872791e.7e3db8","type":"server-events","z":"b28195ad.c495e8","name":"","event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"x":168,"y":112,"wires":[["cab5b48c.7d4408"]]},{"id":"9ddb7a33.f8dad8","type":"ha-api","z":"b28195ad.c495e8","name":"Update Sensor","debugenabled":true,"protocol":"websocket","method":"get","path":"","data":"","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":912,"y":208,"wires":[[]]},{"id":"3066ba37.c8ef16","type":"comment","z":"b28195ad.c495e8","name":"Register Sensors","info":"","x":128,"y":64,"wires":[]},{"id":"9df1457d.2665c8","type":"comment","z":"b28195ad.c495e8","name":"Update Sensors","info":"","x":128,"y":160,"wires":[]},{"id":"cab5b48c.7d4408","type":"switch","z":"b28195ad.c495e8","name":"running?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"running","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":364,"y":112,"wires":[["28c3215c.c54dce"]]},{"id":"fe75725c.0db45","type":"comment","z":"b28195ad.c495e8","name":"Update Sensors after registering them","info":"","x":518,"y":160,"wires":[]},{"id":"a158770b.9ec218","type":"ha-get-entities","z":"b28195ad.c495e8","name":"get all lights","rules":[{"property":"entity_id","logic":"starts_with","value":"light.","valueType":"str"}],"output_type":"split","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":582,"y":112,"wires":[["cb2f3f76.c36ce"]]},{"id":"cb2f3f76.c36ce","type":"function","z":"b28195ad.c495e8","name":"","func":"const id = msg.payload.entity_id.split(\".\")[1];\nconst node_id = `${id}_brightness`;\nconst name = msg.payload.attributes.friendly_name ? `${msg.payload.attributes.friendly_name} Brightness` : `${id.replace(\"_\", \" \")} brightness`;\n\nmsg.data = msg.payload;\nmsg.payload = {\n    data: {\n        type:\"nodered/discovery\",\n        component: \"sensor\",\n        server_id: \"home\",\n        node_id,\n        config: {\n           name\n        }\n    }\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":748,"y":112,"wires":[["d5c4ef79.0c564"]]},{"id":"28c3215c.c54dce","type":"change","z":"b28195ad.c495e8","name":"","rules":[{"t":"delete","p":"createdEntities","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":463,"y":112,"wires":[["a158770b.9ec218"]],"l":false},{"id":"6cba3632.d10ad8","type":"function","z":"b28195ad.c495e8","name":"","func":"const id = msg.data.entity_id.split(\".\")[1];\nconst node_id = `${id}_brightness`;\nconst name = msg.data.attributes.friendly_name ? `${msg.data.attributes.friendly_name} Brightness` : `${id.replace(\"_\", \" \")} brightness`;\nconst brightness = msg.data.state !== \"on\" ? 0 : (msg.data.attributes.brightness || 0);\n\nmsg.payload = {\n    data: {\n        type:\"nodered/entity\",\n        server_id: \"home\",\t \n        node_id,\n        state: brightness\n    }\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":748,"y":208,"wires":[["9ddb7a33.f8dad8"]]},{"id":"1c0038cc.619de7","type":"server-state-changed","z":"b28195ad.c495e8","name":"","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"^light\\.(?!.*\\_brightness$).*","entityidfiltertype":"regex","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":false,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":208,"y":208,"wires":[["8c2a719c.02828"]]},{"id":"8c2a719c.02828","type":"change","z":"b28195ad.c495e8","name":"","rules":[{"t":"set","p":"data","pt":"msg","to":"msg.data.new_state","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":463,"y":208,"wires":[["6cba3632.d10ad8"]],"l":false}]
[{"id":"a0279b06.8882a8","type":"function","z":"b28195ad.c495e8","name":"","func":"const e = msg.data.new_state;\nconst state = e.state !== \"on\" ? 0 : (e.attributes.brightness || 0);\n\nmsg.payload = {\n    path: `states/${e.entity_id.replace(\"light.\", \"sensor.\")}_brightness`,\n    data: {\n        state\n    }\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":460,"y":416,"wires":[["49dc0d09.cd7074"]]},{"id":"47f2fcc.51cc604","type":"server-state-changed","z":"b28195ad.c495e8","name":"","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"^light\\.(?!.*\\_brightness$).*","entityidfiltertype":"regex","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":false,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":208,"y":416,"wires":[["a0279b06.8882a8"]]},{"id":"49dc0d09.cd7074","type":"ha-api","z":"b28195ad.c495e8","name":"create temp sensor","debugenabled":true,"protocol":"http","method":"post","path":"","data":"","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":666,"y":416,"wires":[[]]}]
2 Likes

Thank you again for the great examples!!
I saw that in the first flow, you are using websocket and HTTP on the second. Any reason why? I thought websocket is preferable…

the second one is using POST /api/states/<entity_id> https://developers.home-assistant.io/docs/api/rest/ because it doesn’t require you to create an HA entity first. It’s just a quick and dirty way to make temporary entities.

Updates or creates a state. You can create any state that you want, it does not have to be backed by an entity in Home Assistant.

Thanks. I am always using the quick and dirty way.
I couldn’t find the documentation to create entities the “nice” way using websocket. That part (AFAIK) is not documented on the websocket api page…
Is there a doc to explain why:

msg.payload = {
    data: {
        type:"nodered/discovery",
        component: "sensor",
        server_id: "home",
        node_id,
        config: {
           name
        }
    }
};

Should be like that?

I have tried to mimic @kermit example and it doesn’t work.

[{"id":"5614f8b6.1a8d08","type":"debug","z":"c07c0ac5.ca6f98","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":990,"y":2640,"wires":[]},{"id":"49c6d3eb.60806c","type":"inject","z":"c07c0ac5.ca6f98","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":500,"y":2640,"wires":[["f81c7076.aca23"]]},{"id":"a08a12f1.8668a","type":"ha-api","z":"c07c0ac5.ca6f98","name":"","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":820,"y":2640,"wires":[["5614f8b6.1a8d08"]]},{"id":"f81c7076.aca23","type":"function","z":"c07c0ac5.ca6f98","name":"","func":"const node_id = 'conso_gaz';\nconst name = 'conso'\nmsg.data = msg.payload;\nmsg.payload = {\n    data: {\n        type:\"nodered/discovery\",\n        component: \"sensor\",\n        server_id: \"home\",\n        node_id,\n        config: {\n           name\n        }\n    }\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":2640,"wires":[["a08a12f1.8668a"]]},{"id":"6b0a6ba0.949944","type":"ha-api","z":"c07c0ac5.ca6f98","name":"Update Sensor","debugenabled":true,"protocol":"websocket","method":"get","path":"","data":"","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":960,"y":2700,"wires":[["5614f8b6.1a8d08"]]},{"id":"664bf095.348d1","type":"function","z":"c07c0ac5.ca6f98","name":"","func":"const node_id = 'conso_gaz';\nconst name = 'conso;'\n\nmsg.payload = {\n    data: {\n        type:\"nodered/entity\",\n        server_id: \"home\",\t \n        node_id,\n        state: 234\n    }\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":796,"y":2700,"wires":[["6b0a6ba0.949944"]]},{"id":"d26c8c4d.65c8e","type":"inject","z":"c07c0ac5.ca6f98","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":620,"y":2700,"wires":[["664bf095.348d1"]]}]

The api node replies “success”… but, I can’t see the supposedly created sensor in HA.

There are no endpoints in HA core that allow you to create entities via a WebSocket. That’s why I created the NR custom integration the creates several endpoints that allows this. There is really no documentation as it was only meant to be used with NR as such everything is driven via the UI.

The closest thing you’ll find to documentation other than just looking through the code is this thread.

https://community.home-assistant.io/t/custom-component-for-node-red-contrib-home-assistant-websocket/150736/7?u=kermit

Your export works for me.

Thanks for your answer.

  • After having restarted HA, I can now see the entity being created in HA. All good (almost)!
  • I have just upgraded to 0.28.0. The flow that was giving me success yesterday, is now complaining with:
"TypeError: Cannot read property 'value' of undefined"

coming out of the API call…

Was there a change between 0.27.9 and 0.28.0 in this respect?

what node is throwing that error?

The “API” Node. Using the same flow copied 2 posts above.
Only difference being the upgrade to 0.28.0.

definitely broke it. accidentally committed a file