Electric Savings with Solar and Load Control

There are a number of great Energy projects in Home Assistant. I wish to share my project for controlling my heat pumps to limit grid demand and cost. My target is to save > 50% from my historical energy bills.

I. Introduction
Utility-scale management with distributed (solar) power sources is a complex issue. Utility cost, particularly for delivery of peak-power, is a major concern. My utility, Salt River Project (Phoenix, AZ USA) has responded with residential tariff for solar customers with low “volumetric” energy (kWh) rates but high (escalating) charges for on-peak power demand (kW). A detailed explanation with analysis may be found here:

Unfortunately, this pricing plan makes residential solar an unattractive investment for most customers. However, I decided to try to find a way to realize a ~10-year payback period for solar by writing my own “load controller” in HA. (I decided not to install a commercial load controller due to cost and to concerns about cycling appliance power off and on.)

Here I will outline the approach I am taking for demand/load control and home energy management.

This applies to home cooling during summer months.

II. Approach and equipment

My largest electrical loads during the summer are from two air-to-air split heat pumps. We defer other large loads to off-peak (hot water, laundry, oven etc.). Summer peak-tariff periods are from 14:00-20:00 weekdays. The automations control the use of the heat pumps. Rather than turn the HP’s on and off, the automation sets low or high temperature setpoints. This allows each HP to ramp power as designed.

Summary

I use a Sense Home Energy Monitor (Solar model), and I have two inverter heat pumps controlled by two Ecobee thermostats. The larger heat pump is for the downstairs (rated at 5 tons or 17.5 kW), and the smaller for the upstairs (rated at 3 tons or 10.5 kW). Typical power draw is much less than the rated power. The Ecobees are controlled by a Hubitat hub (I started in home automation with Hubitat), and I created two virtual switches that are shared with Home Assistant.
In Home Assistant I am using the Node-Red add-on for automation. I think Node Red greatly facilitated writing the main automation.

III. Automations

a. “Supercool” This is a simple automation that pre-cools the home from 08:00 – 14:00 (off-peak) to a setpoint adjusted based on the high-temperature forecast for the day. For hot days the setpoint is around 70 F (21 C). My wife complains about the “cold,” but pre-cooling is necessary to limit maximum temperature in the home later in the day.

b. Thermostat control. This is an automation that controls Ecobee temperature setpoints based on the state of the two virtual switches. If the “Upstairs Heatpump” switch is “on,” the setpoint on the corresponding Ecobee is set to 70 F (21 C). If it’s off the setpoint is set to 85 F (29.4 C). The same applies to the “Downstairs Thermostat.” This automation runs every 6 minutes to avoid excessive heat pump cycling but allow responsiveness to loss of solar power or to high grid demand. This automation is constrained to run only during peak-rate periods on weekdays.

c. Primary “Switch Automation” in Node Red. In NR, I decided not to create complex objects or arrays containing all the information required to set the switches, opting instead to use message payloads. I created a few global variables that are used several times in the flow. I also created a couple of sub-flows to tidy up the look a bit.

Note that this is my very first Node Red automation, and it’s likely there are more efficient ways of accomplishing the same goals.


Trigger (30 s) and set Global Variables. The automation uses Net Power production (Solar – total power use, minimum 0), the setpoints for the upstairs and downstairs thermostats, and reads in a Demand Limit from a helper. The two change nodes for thermostat setpoints pull the setpoints from the message object and set corresponding global variables. FYI, global variables are used since they are passed to sub-flows.


Main Automation. The green nodes are debug nodes. The blue call-service nodes simply set the states of the two virtual switches. For example:
image
Call service node that turns on both virtual switches.

The logic is set by several switch nodes.

First Power Switch:

This switch takes the rolling-average Net Power Production and evaluates whether there is enough excess power to start both heat pumps (output 1), enough excess power to start one of the two heat pumps (output 2), enough excess power to “do nothing” (if one or both heat pumps are running this is ok), or zero Net Power Production (which requires evaluating how much power is being pulled from the grid).

From here there are several possible combinations of Heat Pump states and grid export/import power levels that are used to set the virtual switches. Explaining the rest would require a very long (and very dull) post. But the idea is to set the switches in such a way as to keep the demand below a threshold (demand_limit).

IV. Performance

So, how well does it work? Here is an example from yesterday.

Power and temperature plots for yesterday. On the power plot, you can see that grid demand fell at 14:00 (the start of the peak-tariff period). Around 16:00 it appeared that the automation was not working – you can see that power was being exported to the grid. I found that the Ecobee thermostats were in “eco+ mode,” and at that time my thermostats were ignoring the automation setpoints. Around 16:54 I turned eco+ off, but it was too late to prevent the home from becoming uncomfortably warm. Lesson learned!
(Apologies for using the Fahrenheit temperature scale – my wife would be unhappy if I set up the thermostats in Celsius.)

Yesterday, my power company reported that my peak demand was 1.2 kW, which occurred after sunset between 19:30 and 20:00. Maximum demand between 14:00 and 18:30 was 1.0 kW, a little below the automation’s demand_limit.

My June electric bill ran around 30% of historical values, and if I stay on-track with demand, July will be about the same. It’s early days, but so far, I am on-track with my goal to pay-back the solar panel investment in < 10 years.

3 Likes

Did you share the code as well?
Would like to have a look of I could use some of it.

HI @jhrath,

Just saw your request. I will be on travel for the next 10 days. I’ll return to your request then.

The primary code as mentioned sets virtual switches for turning heap pumps “on” or “off” (III.c). I’m running thermostat control with another hub (Hubitat), simply because I started with Hubitat and it works well. It would be easy to read the “pseudocode” and port to Home Assistant.

Over the course of a record-hot summer here, I’m saving over 50% on my power bill compared to previous years, and I was aggressive about deferring power use in past years during peak time.

Hello @jhrath,

  1. SuperCool

“Supercool” is the first of the automations. I’m using a simple formula to calculate the setpoint that uses the forecast high temperature. Note that all temperatures here are in Fahrenheit.

This is the first time I am sharing node red code, I hope you can read/import this.

[{"id":"99d206213eaa9807","type":"tab","label":"Supercool","disabled":false,"info":"","env":[]},{"id":"ac0d09d55d98dfde","type":"inject","z":"99d206213eaa9807","name":"Trigger 5:00 PM daily","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 17 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":100,"wires":[["8954b9aa2084b424"]]},{"id":"e998a1e7e8892098","type":"api-call-service","z":"99d206213eaa9807","name":"Supercool Setpoint","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"climate","service":"set_temperature","areaId":[],"deviceId":["f657170607213beece37307b2cbb6f4f"],"entityId":["sensor.supercool_setpoint_temperature"],"data":"{\"temperature\": msg.payload}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":230,"y":180,"wires":[["6d477df34c4da067"]]},{"id":"6d477df34c4da067","type":"debug","z":"99d206213eaa9807","name":"Supercool SP","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":600,"y":180,"wires":[]},{"id":"8954b9aa2084b424","type":"api-current-state","z":"99d206213eaa9807","name":"Forecast High","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.weather_tomorrow_max_temp","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"parts","propertyType":"msg","value":"","valueType":"num"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":340,"y":100,"wires":[["e9a4d8b59b33b8fe"]]},{"id":"e9a4d8b59b33b8fe","type":"function","z":"99d206213eaa9807","name":"Adjust Supercool SP","func":"\n// Extract the temperature from the input payload\nvar T = msg.payload;\n\n// Perform the temperature conversion\nvar output = 74 - (T - 94) / 4;\n\n// Set the converted temperature as the output payload\nmsg.payload = output;\n\n// Return the modified message\nreturn msg;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":580,"y":100,"wires":[["e998a1e7e8892098"]]},{"id":"95cc2380.4ef81","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

I started this project before I began using Home Assistant, and the direct control of the Ecobee Thermostats is with Hubitat Elevation. There is an Ecobee integration for Home Assistant, and If I were starting over I’d simply do everything in Home Assistant. However, I think it’s easy to read Hubitat Automations, so you should have no trouble making equivalent automations in Home Assistant.

Convert the Supercool Setpoint to a Hubitat Local Variable (including this just for completeness):

SuperCool Automation

  1. Thermostat Control (uses settings from the Virtual Switches).
    Virtual Switches were created in Hubitat and shared with Home Assistant.

  1. Heat Pump Switch Automation
    This is the primary automation that sets virtual switches in Node Red (plugin).

(see next message… there is a character limit on posts)

Heat Pump Automation

[{"id":"c51d38339c64c7c7","type":"subflow","name":"Subflow 6","info":"","in":[{"x":40,"y":160,"wires":[{"id":"49c63435c5b13e78"}]}],"out":[{"x":680,"y":80,"wires":[{"id":"44eb78d8c040adb5","port":0}]},{"x":680,"y":80,"wires":[{"id":"44eb78d8c040adb5","port":1}]},{"x":680,"y":240,"wires":[{"id":"0de37c8770e49835","port":0}]},{"x":680,"y":240,"wires":[{"id":"0de37c8770e49835","port":1}]}]},{"id":"49c63435c5b13e78","type":"change","z":"c51d38339c64c7c7","name":"Upstairs SP","rules":[{"t":"set","p":"payload","pt":"msg","to":"Upstairs_SP","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":190,"y":160,"wires":[["b3c7b08dd019d626"]]},{"id":"b3c7b08dd019d626","type":"switch","z":"c51d38339c64c7c7","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lte","v":"75.0","vt":"num"},{"t":"gt","v":"75.0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":330,"y":160,"wires":[["0bc06ab2f127ce68"],["ad82477ff8c114ae"]]},{"id":"0bc06ab2f127ce68","type":"change","z":"c51d38339c64c7c7","name":"Downstairs SP","rules":[{"t":"set","p":"payload","pt":"msg","to":"Downstairs_SP","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":80,"wires":[["44eb78d8c040adb5"]]},{"id":"ad82477ff8c114ae","type":"change","z":"c51d38339c64c7c7","name":"Downstairs SP","rules":[{"t":"set","p":"payload","pt":"msg","to":"Downstairs_SP","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":240,"wires":[["0de37c8770e49835"]]},{"id":"44eb78d8c040adb5","type":"switch","z":"c51d38339c64c7c7","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lte","v":"75.0","vt":"num"},{"t":"gt","v":"75.0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":550,"y":80,"wires":[[],[]]},{"id":"0de37c8770e49835","type":"switch","z":"c51d38339c64c7c7","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lte","v":"75.0","vt":"num"},{"t":"gt","v":"75.0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":550,"y":240,"wires":[[],[]]},{"id":"2c29ba9c617d20a5","type":"tab","label":"Heat Pump Switch Control","disabled":false,"info":"","env":[]},{"id":"03a26ff333137812","type":"inject","z":"2c29ba9c617d20a5","name":"30 s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"30","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":90,"y":460,"wires":[["22f1ee23a157eb92","a0d7ced3a3422ee9","566c827b3db5d018","9e472044423f83d5"]]},{"id":"22f1ee23a157eb92","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Net Power Production","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.grid_export","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":180,"y":360,"wires":[["61ad75c805a65727"]]},{"id":"61ad75c805a65727","type":"function","z":"2c29ba9c617d20a5","name":"Average Last 5 Readings","func":"let values = context.get(\"values\") || [];\n\nvalues.push(msg.payload);\n\nif (values.length > 5) {\n    values.shift();\n}\n\nlet sum = values.reduce((acc, curr) => acc + curr, 0);\nlet avg = sum / values.length;\n\ncontext.set(\"values\", values);\n\nmsg.payload = avg;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":210,"y":240,"wires":[["e51b9e29fd504fcc","ca0482e75f9981fc"]]},{"id":"9d480c95dbd7d080","type":"debug","z":"2c29ba9c617d20a5","name":"Avg_Net_Power","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":380,"y":160,"wires":[]},{"id":"e51b9e29fd504fcc","type":"change","z":"2c29ba9c617d20a5","name":"Set Global var","rules":[{"t":"set","p":"Avg_Net_Power","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":200,"y":160,"wires":[["9d480c95dbd7d080"]]},{"id":"a0d7ced3a3422ee9","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Upstairs Thermostat","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"climate.upstairs","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":300,"y":440,"wires":[["85d11aa01bf528c9"]]},{"id":"566c827b3db5d018","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Downstairs Thermostat","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"climate.downstairs","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":310,"y":520,"wires":[["9ad981d54fe56d52"]]},{"id":"9ad981d54fe56d52","type":"change","z":"2c29ba9c617d20a5","name":"Downstairs SP","rules":[{"t":"set","p":"Downstairs_SP","pt":"global","to":"data.attributes.temperature","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"Downstairs_SP","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":520,"wires":[[]]},{"id":"85d11aa01bf528c9","type":"change","z":"2c29ba9c617d20a5","name":"Upstairs SP","rules":[{"t":"set","p":"Upstairs_SP","pt":"global","to":"data.attributes.temperature","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"Upstairs_SP","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":440,"wires":[[]]},{"id":"fd3c17071f287c2e","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"4500","vt":"str"},{"t":"btwn","v":"1000","vt":"num","v2":"4500","v2t":"num"},{"t":"btwn","v":"1","vt":"num","v2":"1000","v2t":"num"},{"t":"lte","v":"1","vt":"num"}],"checkall":"false","repair":false,"outputs":4,"x":710,"y":240,"wires":[["8acd95e8c1e71433"],["3eb4b81076e4b50b"],["355b6c8888271f7c"],["7c6d510aea9edc94"]]},{"id":"8acd95e8c1e71433","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.downstairs_ac_switch","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":680,"y":100,"wires":[["7def42454fc74d14"]]},{"id":"7def42454fc74d14","type":"debug","z":"2c29ba9c617d20a5","name":"Turn Both ON","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":840,"y":100,"wires":[]},{"id":"355b6c8888271f7c","type":"debug","z":"2c29ba9c617d20a5","name":"Do nothing 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":990,"y":340,"wires":[]},{"id":"65c15703725b50e6","type":"debug","z":"2c29ba9c617d20a5","name":"Leave Both ON 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1330,"y":20,"wires":[]},{"id":"6e1ba578343538b1","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.downstairs_ac_switch","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1160,"y":20,"wires":[["65c15703725b50e6"]]},{"id":"ea9200101714bd0d","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"1600","vt":"str"},{"t":"lt","v":"1600","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1370,"y":200,"wires":[["a37ee96d98d36c96"],["0c7c392a49746204"]]},{"id":"a39066b187e3fac0","type":"debug","z":"2c29ba9c617d20a5","name":"Leave Upstaris OFF 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1980,"y":220,"wires":[]},{"id":"4e5a7af12709a0fd","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"2000","vt":"str"},{"t":"lt","v":"2000","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":1390,"y":100,"wires":[["071e3996aa243a5c"],["48bd626aab1225f3"]]},{"id":"071e3996aa243a5c","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1570,"y":60,"wires":[["01d216239bf0cb49"]]},{"id":"a6774881f123b7e9","type":"debug","z":"2c29ba9c617d20a5","name":"Leave Upstairs ON 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1980,"y":120,"wires":[]},{"id":"9e0878971e6bae93","type":"debug","z":"2c29ba9c617d20a5","name":"Downstairs (both) ON 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1970,"y":60,"wires":[]},{"id":"d9e9e18df92d2f3a","type":"debug","z":"2c29ba9c617d20a5","name":"Upstairs (both) ON 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1740,"y":180,"wires":[]},{"id":"691ae91051ed72d6","type":"change","z":"2c29ba9c617d20a5","name":"Avg Net Power","rules":[{"t":"set","p":"payload","pt":"msg","to":"Avg_Net_Power","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":1220,"y":200,"wires":[["ea9200101714bd0d"]]},{"id":"b31d9832cf480553","type":"change","z":"2c29ba9c617d20a5","name":"Avg Net Power","rules":[{"t":"set","p":"payload","pt":"msg","to":"Avg_Net_Power","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":1240,"y":100,"wires":[["4e5a7af12709a0fd"]]},{"id":"13e95fe4dd288fc1","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1590,"y":280,"wires":[["d9c024b468254015"]]},{"id":"f84454f6749ecf02","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1580,"y":320,"wires":[["5c35b91d7844278a"]]},{"id":"cf9d42a62a8209c6","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"3500","vt":"str"},{"t":"btwn","v":"1650","vt":"num","v2":"3500","v2t":"num"},{"t":"lt","v":"1650","vt":"num"}],"checkall":"true","repair":false,"outputs":3,"x":1390,"y":300,"wires":[["13e95fe4dd288fc1"],["f84454f6749ecf02"],["81da7da2fe074563"]]},{"id":"fbca3f4b52eb5f10","type":"debug","z":"2c29ba9c617d20a5","name":"Downstairs ON 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1990,"y":280,"wires":[]},{"id":"48632030c817c6a1","type":"debug","z":"2c29ba9c617d20a5","name":"Upstairs ON 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1980,"y":320,"wires":[]},{"id":"8a69ccbd437b9d90","type":"change","z":"2c29ba9c617d20a5","name":"Avg Net Power","rules":[{"t":"set","p":"payload","pt":"msg","to":"Avg_Net_Power","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":1220,"y":300,"wires":[["cf9d42a62a8209c6"]]},{"id":"7f9e1cf59728308b","type":"debug","z":"2c29ba9c617d20a5","name":"Do nothing (both off) 9","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1140,"y":860,"wires":[]},{"id":"6ba90db2a08f9343","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":440,"wires":[["3f2d597056358ade"]]},{"id":"7fef0a14ccbdf74f","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both AC Units OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1470,"y":380,"wires":[["1264ce4a29e712b0"]]},{"id":"1264ce4a29e712b0","type":"debug","z":"2c29ba9c617d20a5","name":"Both OFF 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1690,"y":380,"wires":[]},{"id":"7bf581b202a40bb8","type":"debug","z":"2c29ba9c617d20a5","name":"Downstairs OFF 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1890,"y":440,"wires":[]},{"id":"44c2deb6dda0eb08","type":"debug","z":"2c29ba9c617d20a5","name":"Upstairs OFF 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1960,"y":500,"wires":[]},{"id":"2be285145ba12967","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1530,"y":500,"wires":[["4ceadd8f67081671"]]},{"id":"f0d030d404740919","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"4000","vt":"str"},{"t":"btwn","v":"3000","vt":"num","v2":"4000","v2t":"num"},{"t":"btwn","v":"demand_limit","vt":"global","v2":"3000","v2t":"num"},{"t":"lte","v":"demand_limit","vt":"global"}],"checkall":"true","repair":false,"outputs":4,"x":1250,"y":440,"wires":[["7fef0a14ccbdf74f"],["6ba90db2a08f9343"],["2be285145ba12967"],["1fa7d6c7eb10cc8a"]]},{"id":"dfb7c9b25bf4a3c4","type":"debug","z":"2c29ba9c617d20a5","name":"Do nothing (both on) 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1500,"y":560,"wires":[]},{"id":"e8c602b4718e40df","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Net Power Import","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.grid_import","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1050,"y":440,"wires":[["f0d030d404740919"]]},{"id":"65c0de97787a29c1","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Net Power Import","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.grid_import","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1070,"y":760,"wires":[["8a354a3078acd45c"]]},{"id":"1c96af46192f7518","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Net Power Import","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.grid_import","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1090,"y":680,"wires":[["945fb22e09973617"]]},{"id":"945fb22e09973617","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"demand_limit","vt":"global"},{"t":"lt","v":"demand_limit","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":1270,"y":680,"wires":[["c7b939ad3362e161"],["8bbfbd9a88fb9b7e"]]},{"id":"c7b939ad3362e161","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1090,"y":620,"wires":[["2adbce5bc007ef85"]]},{"id":"be8bbb4264a6dd6a","type":"debug","z":"2c29ba9c617d20a5","name":"Do nothing (Upstairs on) 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1870,"y":680,"wires":[]},{"id":"95a628eacad485a3","type":"debug","z":"2c29ba9c617d20a5","name":"Do nothing (Downstairs on) 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1840,"y":800,"wires":[]},{"id":"37bb734d6fc40d50","type":"debug","z":"2c29ba9c617d20a5","name":"Upstairs OFF 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1500,"y":620,"wires":[]},{"id":"ca0482e75f9981fc","type":"link out","z":"2c29ba9c617d20a5","name":"link out 1","mode":"link","links":["b5f9d1c3c1f1eafb"],"x":385,"y":280,"wires":[]},{"id":"b5f9d1c3c1f1eafb","type":"link in","z":"2c29ba9c617d20a5","name":"link in 1","links":["ca0482e75f9981fc"],"x":485,"y":260,"wires":[["fd3c17071f287c2e"]]},{"id":"01d216239bf0cb49","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1760,"y":60,"wires":[["9e0878971e6bae93"]]},{"id":"48bd626aab1225f3","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1560,"y":120,"wires":[["9af1b64691a53dd4"]]},{"id":"9af1b64691a53dd4","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1760,"y":120,"wires":[["a6774881f123b7e9"]]},{"id":"0c7c392a49746204","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1570,"y":220,"wires":[["208ee7f495d9991e"]]},{"id":"208ee7f495d9991e","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1770,"y":220,"wires":[["a39066b187e3fac0"]]},{"id":"d9c024b468254015","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1790,"y":280,"wires":[["fbca3f4b52eb5f10"]]},{"id":"5c35b91d7844278a","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1780,"y":320,"wires":[["48632030c817c6a1"]]},{"id":"3f2d597056358ade","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1680,"y":440,"wires":[["7bf581b202a40bb8"]]},{"id":"4ceadd8f67081671","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1750,"y":500,"wires":[["44c2deb6dda0eb08"]]},{"id":"a37ee96d98d36c96","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.downstairs_ac_switch","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1540,"y":180,"wires":[["d9e9e18df92d2f3a"]]},{"id":"2adbce5bc007ef85","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1290,"y":620,"wires":[["37bb734d6fc40d50"]]},{"id":"8bbfbd9a88fb9b7e","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1440,"y":680,"wires":[["ea0323494992ddde"]]},{"id":"ea0323494992ddde","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1640,"y":680,"wires":[["be8bbb4264a6dd6a"]]},{"id":"8a354a3078acd45c","type":"switch","z":"2c29ba9c617d20a5","name":"Power","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"demand_limit","vt":"global"},{"t":"lt","v":"demand_limit","vt":"global"}],"checkall":"true","repair":false,"outputs":2,"x":1250,"y":760,"wires":[["fa55d538da2edc42"],["62b9ac1c4936d6a7"]]},{"id":"fa55d538da2edc42","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both AC Units OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1430,"y":740,"wires":[["23d4a79f74811771"]]},{"id":"62b9ac1c4936d6a7","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Downstairs AC ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1410,"y":800,"wires":[["38cd841d87749c5b"]]},{"id":"38cd841d87749c5b","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Upstairs AC OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.upstairs_ac_switch"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1610,"y":800,"wires":[["95a628eacad485a3"]]},{"id":"9265ac10fb3d8ecf","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both AC Units OFF","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.hubitat_vdownstairs_ac","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":910,"y":860,"wires":[["7f9e1cf59728308b"]]},{"id":"3eb4b81076e4b50b","type":"subflow:c51d38339c64c7c7","z":"2c29ba9c617d20a5","name":"HP states","x":960,"y":200,"wires":[["6e1ba578343538b1"],["b31d9832cf480553"],["691ae91051ed72d6"],["8a69ccbd437b9d90"]]},{"id":"7c6d510aea9edc94","type":"subflow:c51d38339c64c7c7","z":"2c29ba9c617d20a5","name":"HP states","x":740,"y":580,"wires":[["e8c602b4718e40df"],["1c96af46192f7518"],["65c0de97787a29c1"],["9265ac10fb3d8ecf"]]},{"id":"acdbff5bfd5659a8","type":"comment","z":"2c29ba9c617d20a5","name":"ON/OFF states","info":"HP states: Upstairs-Downstairs\n1. ON-ON\n2. ON-OFF\n3. OFF-ON\n4. OFF-OFF","x":980,"y":260,"wires":[]},{"id":"43edc27a54494ec6","type":"comment","z":"2c29ba9c617d20a5","name":"ON/OFF states","info":"HP states: Upstairs-Downstairs\n1. ON-ON\n2. ON-OFF\n3. OFF-ON\n4. OFF-OFF","x":720,"y":660,"wires":[]},{"id":"1fa7d6c7eb10cc8a","type":"api-call-service","z":"2c29ba9c617d20a5","name":"Both ON","server":"95cc2380.4ef81","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.downstairs_ac_switch","switch.upstairs_ac"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1320,"y":560,"wires":[["dfb7c9b25bf4a3c4"]]},{"id":"23d4a79f74811771","type":"debug","z":"2c29ba9c617d20a5","name":"Both OFF 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1630,"y":740,"wires":[]},{"id":"9e472044423f83d5","type":"api-current-state","z":"2c29ba9c617d20a5","name":"Demand Limit (W)","server":"95cc2380.4ef81","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_number.demand_limit_setpoint","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":250,"y":600,"wires":[["3e571b961c5013b9"]]},{"id":"3e571b961c5013b9","type":"change","z":"2c29ba9c617d20a5","name":"demand_limit","rules":[{"t":"set","p":"demand_limit","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":600,"wires":[[]]},{"id":"81da7da2fe074563","type":"debug","z":"2c29ba9c617d20a5","name":"Do nothing 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":360,"wires":[]},{"id":"95cc2380.4ef81","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

I use a couple of subflows to simplify the display. Let me know if you have any trouble importing this.

Best wishes - I hope this is helpful.