This is a relatively small one but works better than I expected. The basic idea is: I have two 1500W heaters in two separate rooms but on the same puny 15A circuit. If they’re both running at the same time, there’s about six seconds or so before the breaker cuts out. I could theoretically just set them both to “low”, but with the cold winter that just makes neither of them keep up.
The idea, then, is to use Hass-controlled plugs to coordinate them, so only one is running at a time. To make things easier (and nicer), both have their temperature dials set full-tilt and I use the generic thermostat to actually regulate temperatures. I opted for Aeotec Smart Switch 6 plugs for the power monitoring and encrypted communication, and Iris contact sensors for temperature sensing, because they’re cheap and I’ve had good results overall.
So, first of all, I have two input_booleans that represent whether that room wants heat:
input_boolean:
ne_bedroom_request_heat:
initial: 'off'
nw_bedroom_request_heat:
initial: 'off'
And then the virtual thermostats:
climate ne_bedroom:
platform: generic_thermostat
name: NE Bedroom
heater: switch.ne_bedroom_request_heat
target_sensor: sensor.centralite_3320l_0e261180_1
target_temp: 60
ac_mode: no
min_temp: 35
max_temp: 90
climate nw_bedroom:
platform: generic_thermostat
name: NW Bedroom
heater: switch.nw_bedroom_request_heat
target_sensor: sensor.centralite_3320l_0ded19ff_1
target_temp: 60
ac_mode: no
min_temp: 35
max_temp: 90
You might’ve noticed the heater entity_ids above. Well, I didn’t find out until today that generic_thermostat has been fixed to work with any boolean entity. I’ll play with that later, but for now here’s the old workaround:
switch:
- platform: template
switches:
ne_bedroom_request_heat:
value_template: "{{ is_state('input_boolean.ne_bedroom_request_heat', 'on') }}"
turn_on:
service: input_boolean.turn_on
entity_id: input_boolean.ne_bedroom_request_heat
turn_off:
service: input_boolean.turn_off
entity_id: input_boolean.ne_bedroom_request_heat
- platform: template
switches:
nw_bedroom_request_heat:
value_template: "{{ is_state('input_boolean.nw_bedroom_request_heat', 'on') }}"
turn_on:
service: input_boolean.turn_on
entity_id: input_boolean.nw_bedroom_request_heat
turn_off:
service: input_boolean.turn_off
entity_id: input_boolean.nw_bedroom_request_heat
So, that’s the virtual thermostats and the input_boolean flags they twiddle. Now how does this get tied to the actual switches? Through a series of automations, of course.
I wanted the NW bedroom to be “dominant” as I actually sleep in there. So it overrides the NE bedroom when needed.
First, a safety measure, so I’m hopefully not totally dependent on the circuit breaker defusing an unsafe situation:
automation heater_coex_safety:
alias: Heater coexistence -- safety
trigger:
- platform: state
entity_id: switch.ne_bedroom_heater_switch
- platform: state
entity_id: switch.nw_bedroom_heater_switch
condition:
- condition: state
entity_id: switch.nw_bedroom_heater_switch
state: 'on'
- condition: state
entity_id: switch.ne_bedroom_heater_switch
state: 'on'
action:
service: switch.turn_off
entity_id: switch.ne_bedroom_heater_switch
Yes, no from/to in the trigger. I wanted it as greedy as possible, so that the condition gets checked at any remote possibility they’ve both wound up on simultaneously.
Let’s turn some heaters on:
automation heater_coex_nw_on:
alias: Heater coexistence -- NW on
trigger:
- platform: state
entity_id: input_boolean.nw_bedroom_request_heat
from: 'off'
to: 'on'
for:
seconds: 5
action:
service: switch.turn_on
entity_id: switch.nw_bedroom_heater_switch
Through the automations themselves, I’m building in a 5 second delay. Good to have that little buffer in case of lag on the Z-Wave network. Again, since NW preempts NE, it doesn’t check whether NE is running. Instead, NE checks if NW is running.
automation heater_coex_ne_on:
alias: Heater coexistence -- NE on
trigger:
- platform: state
entity_id: input_boolean.ne_bedroom_request_heat
from: 'off'
to: 'on'
for:
seconds: 5
- platform: state
entity_id: switch.nw_bedroom_heater_switch
from: 'on'
to: 'off'
for:
seconds: 5
condition:
- condition: state
entity_id: input_boolean.ne_bedroom_request_heat
state: 'on'
- condition: state
entity_id: input_boolean.nw_bedroom_request_heat
state: 'off'
action:
service: switch.turn_on
entity_id: switch.ne_bedroom_heater_switch
I decided it makes more sense to check if the NW bedroom wants heat when deciding whether to turn the NE heater on as opposed to checking the switch itself, since due to the 5 second delay, the NW bedroom heater may not have turned on just yet.
And turning heaters off:
automation heater_coex_nw_off:
alias: Heater coexistence -- NW off
trigger:
- platform: state
entity_id: input_boolean.nw_bedroom_request_heat
from: 'on'
to: 'off'
action:
service: switch.turn_off
entity_id: switch.nw_bedroom_heater_switch
When you look at this automation you might think, why not connect the thermostat directly to the switch? Well, I want that 5 second delay when it comes on, but immediate when it turns off. This seemed like the most straightforward way to do it.
automation heater_coex_ne_off:
alias: Heater coexistence -- NE off
trigger:
- platform: state
entity_id: input_boolean.ne_bedroom_request_heat
from: 'on'
to: 'off'
- platform: state
entity_id: input_boolean.nw_bedroom_request_heat
from: 'off'
to: 'on'
action:
service: switch.turn_off
entity_id: switch.ne_bedroom_heater_switch
Straightforward. NW heater coming on? Turn off. No longer need heat? Turn off.
So that’s pretty much it. Heaters turn on with a 5 second delay, turn off immediately. When both rooms want heat, the NW heater is on. When only the NW room wants heat, the NW heater is on. When only the NE room wants heat, the NE heater is on. When neither want heat, both are off. Breaker doesn’t pop, and the rooms stay warm.
I discovered an interesting race condition in this. If the NW heater turns off less than 5 seconds after the NE room requests heat, the NE heater won’t wait the full 5 second delay. I decided it’s too harmless to try to fix.