Help with node red flow to prioritize power consumption by devices on a single circuit

I’m setting up a flow in Node-Red that I want to use to prevent devices connected to the same circuit from drawing more current than the circuit capacity and tripping the circuit. It flow is supposed to do that by sensing how much current is being drawn by devices plugged into smart plugs with energy sensing, continuously calculating available current based on sensed current, and preventing a new device from turning on, or turning off a previously on device to keep current consumption below the maximum capacity of the circuit.

I can share more about my thinking if anybody is interested but for now, I am running into a problem. I have it set up so the smart plugs continuously send values of current being drawn to a join node. That’s a screen shot of what I have for now.

The values are joined in the Join node which then sends an object that incudes the name of the smart plug in msg.topic, and the Amps are in msg.payload. The join node sends an object that has all received topics/payloads every time it receives a new value from any smart plug. That’s what the join node is sending out when I have two devices plugged in.

I want to add up all payload values in one object every time it is sent out (or received), and then subtract the sum from the capacity of the circuit in Amps, e.g., 20 Amp circuit, to get a value that represents how much current is available. So for the example above, a node that is receiving these objects from the join node will add up 12.11 + 12.67 (second object from the debug node in the screenshot), subtract that from 20 (20-24.78), and store the resulting value (-4.78) in flow context or send it to another node as “available current”. (In this case, it means i’m drawing more than the max for this circuit)

You will notice that I have a function node between the join node and the debug node. that I tried to set up to do the calculation for me. But it obviously is not doing anything, or it is calculating something but I can’t see it, and it’s simply passing along the object. The code in the function node is this:

I have no coding experience whatsoever. I have been trying to use the function node to do these calculations but nothing has worked. The code you see above is one of a million others I tried. I’m looking for Javascript code online and trying to piece together code that can do what I want, but obviously failing. Any help will be greatly appreciated.

I would avoid using the join node. It is extremely hard to synchronize the flows to the join node, even with just 2 flows and more will just make it expontially more complex.

My best advice is to but a in the state nodes and then use that in a function node to store the value in a flow or global context variable based on that name.
After the function node then go to your calculation node and just use the get command with a ||0 added for all your values.
The ||0 will return 0 if the value does not exist.

The benefit here is that you will be able to update your available current even though the state nodes are not in sync.
The issue might be if a sensor stops responding and just keeps its value, instead of going to unavailable, but it is still better than your current setup that might hang waiting for that value.

Thanks! I will try your suggestion. But it appears to me that the join node is able to keep up (maybe because there are only two flows like you said) no need to synchronize anything. You can see that in the first and second lines in the debug window. Also, when I take one of the plugs offline, the join node keeps sending messages.

Also, using your method, I will still need help with code for summing the values and subtracting from 20. That’s where I was having the most trouble.


I’m guessing this is the to prevent the classic problem of running the microwave and the hairdryer at the same time. Been there many times :slightly_smiling_face:.

Your solution is intriguing but it has some potential problems. Most relate to safety. Your circuit breaker is the ultimate protection but it can wear out over time (think adjusting total load calculations because it trips sooner than expected). Then there are automation component failures (smart plug failures, bad current readings, slow updates or not fast enough scan times, HA is down, etc.). This will add complexity to a safety system (this is being done to prevent the primary safety device, the circuit breaker, from tripping). Electrical circuit safety design is meant to be simple, passive, and reliable.

When I was doing industrial automation, we would never design or implement an automation to manage loads on an electrical circuit to prevent overloads (see reasons above). I know you have a circuit breaker for protection, but if you know the total load on the circuit will be exceeded, you need a bigger capacity circuit (wiring, breaker, etc.), or you need an additional circuit for the additional load, or you keep the additional load off that circuit.

You haven’t shown the configuration of the join node, but it will usually always be affected by missing messages, either by delays due to timeouts or by getting multiple messages from one source to fulfill its numbers, which thereby lowers the output frequency.

Here is an example with a flow variable and 5 devices.

[{"id":"114a7ae5045996a7","type":"inject","z":"be03f9025a1f137a","name":"Device 1 on","props":[{"p":"device","v":"dev1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1275,"wires":[["fa84615b93eb060f"]]},{"id":"398d06976f05284f","type":"inject","z":"be03f9025a1f137a","name":"Device 2 on","props":[{"p":"device","v":"dev2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1320,"wires":[["fa84615b93eb060f"]]},{"id":"3c99b3ef3a437638","type":"inject","z":"be03f9025a1f137a","name":"Device 3 on","props":[{"p":"device","v":"dev3","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1365,"wires":[["fa84615b93eb060f"]]},{"id":"cbdd9d360acb2976","type":"inject","z":"be03f9025a1f137a","name":"Device 4 on","props":[{"p":"device","v":"dev4","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1410,"wires":[["fa84615b93eb060f"]]},{"id":"c94618de9f9e8c2c","type":"inject","z":"be03f9025a1f137a","name":"Device 5 on","props":[{"p":"device","v":"dev5","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1455,"wires":[["fa84615b93eb060f"]]},{"id":"599d95cff1452efa","type":"inject","z":"be03f9025a1f137a","name":"Device 1 off","props":[{"p":"device","v":"dev1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1515,"wires":[["9b08427b7f0ffc15"]]},{"id":"1b89babfb82edacf","type":"inject","z":"be03f9025a1f137a","name":"Device 2 off","props":[{"p":"device","v":"dev2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1560,"wires":[["9b08427b7f0ffc15"]]},{"id":"535070a9f3f24ec7","type":"inject","z":"be03f9025a1f137a","name":"Device 3 off","props":[{"p":"device","v":"dev3","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1605,"wires":[["9b08427b7f0ffc15"]]},{"id":"066c26154394e06f","type":"inject","z":"be03f9025a1f137a","name":"Device 4 off","props":[{"p":"device","v":"dev4","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1650,"wires":[["9b08427b7f0ffc15"]]},{"id":"dc80311b3ceb7b10","type":"inject","z":"be03f9025a1f137a","name":"Device 5 off","props":[{"p":"device","v":"dev5","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":235,"y":1695,"wires":[["9b08427b7f0ffc15"]]},{"id":"f9fdba740aa50451","type":"inject","z":"be03f9025a1f137a","name":"Device 2 unavialable","props":[{"p":"device","v":"dev2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":255,"y":1800,"wires":[["431d6dc6f7e40095"]]},{"id":"55886f1fa55a42e6","type":"inject","z":"be03f9025a1f137a","name":"Device 1 unavialable","props":[{"p":"device","v":"dev1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":255,"y":1755,"wires":[["431d6dc6f7e40095"]]},{"id":"62e0038607921cfc","type":"inject","z":"be03f9025a1f137a","name":"Device 3 unavialable","props":[{"p":"device","v":"dev3","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":255,"y":1845,"wires":[["431d6dc6f7e40095"]]},{"id":"2014c1cfc88fadac","type":"inject","z":"be03f9025a1f137a","name":"Device 4 unavialable","props":[{"p":"device","v":"dev4","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":255,"y":1890,"wires":[["431d6dc6f7e40095"]]},{"id":"718d4d327be3f900","type":"inject","z":"be03f9025a1f137a","name":"Device 5 unavialable","props":[{"p":"device","v":"dev5","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":255,"y":1935,"wires":[["431d6dc6f7e40095"]]},{"id":"fa84615b93eb060f","type":"function","z":"be03f9025a1f137a","name":"Set a random load for a device in a flow variable","func":"flow.set(msg.device, (Math.random() * 7) + 4) // the (Math.random() * 7)+4 just creates a random number between 4 and 11.\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":585,"y":1365,"wires":[["74821bb01ed319bc"]]},{"id":"9b08427b7f0ffc15","type":"function","z":"be03f9025a1f137a","name":"Set a load of 0 for a device in a flow variable","func":"flow.set(msg.device, 0)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":575,"y":1605,"wires":[["74821bb01ed319bc"]]},{"id":"431d6dc6f7e40095","type":"function","z":"be03f9025a1f137a","name":"Set the load to \"unavaialable\" for a device in a flow variable","func":"flow.set(msg.device, \"unavailable\")\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":625,"y":1845,"wires":[["74821bb01ed319bc"]]},{"id":"74821bb01ed319bc","type":"function","z":"be03f9025a1f137a","name":"Calculate load","func":"msg.load1 = flow.get(\"dev1\")||0;\nif (isNaN(msg.load1)) {msg.load1=0;}\nmsg.load2 = flow.get(\"dev2\")||0;\nif (isNaN(msg.load2)) { msg.load2 = 0; }\nmsg.load3 = flow.get(\"dev3\")||0;\nif (isNaN(msg.load3)) { msg.load3 = 0; }\nmsg.load4 = flow.get(\"dev4\")||0;\nif (isNaN(msg.load4)) { msg.load4 = 0; }\nmsg.load5 = flow.get(\"dev5\")||0;\nif (isNaN(msg.load5)) { msg.load5 = 0; }\nmsg.totalLoad = msg.load1 + msg.load2 + msg.load3 + msg.load4 + msg.load5;\nmsg.availableLoad = 20 - msg.totalLoad;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":985,"y":1605,"wires":[["feb29022f5b1f6bc"]]},{"id":"feb29022f5b1f6bc","type":"debug","z":"be03f9025a1f137a","name":"debug 22","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1155,"y":1605,"wires":[]}]

Great point. But it’s really not a solution to improve safety. It’s just for convenience and maybe can be used to save energy. If I stop the breaker from tripping, I don’t have to haul my ass from my office in the attic to the basement every time my wife turns on the microwave and the hairdryer. (How did you guess?). I don’t think it will make it less safe. Right?

To explain the convenience point more. My idea started when I visited family back in Lebanon last year. The country is a mess and people buy electricity from neighborhood generator operators at $100 for 50 Amps because the main electricity is not reliable. Super expensive. So when the main electricity is out, most of the house runs off that 50 Amp circuit breaker. People end up having to turn things on and off to make sure they are not overloading the breaker. Needless to say, the breaker will trip all the time because it’s too hard to manage especially when you have clueless overseas visitors staying with you. You can already see here that safety is out the window at this point. Convenience is key. But i’m sure there are more applications for this outside my specific application.

I’m playing with it now. Works like magic. (I’m a virologist so def. not my field). Very appreciative.

Here’s what I know how to show you about my join node: