Open clinta opened 1 year ago
I'm in the same boat! I have 7 zones (different rooms). Here's how I try to deal with this now:
A thermostat for each room:
- platform: thermostat
name: ${zone_1_name} heating
id: heat_01_term
sensor: heat_01_temp
startup_delay: false
default_preset: heat
on_boot_restore_from: default_preset
preset:
- name: heat
default_target_temperature_low: ${default_target_temp}
visual:
min_temperature: 14
max_temperature: 25
temperature_step: 0.1
min_idle_time: 5min
min_heating_off_time: 7min
min_heating_run_time: 7min
heat_deadband: 0.2 °C
heat_overrun: 0.2 °C
heat_action:
- switch.turn_on: heat_01_sw
idle_action:
- switch.turn_off: heat_01_sw
Switch heat_01_sw
is a relay commanding one or more TRV valves allowing hot water to flow in the radiators corresponding to that room. Time needed for a TRV to open is about 2-3 minutes.
There's a separate relay commanding the central heating boiler, heater_sw
:
### Big OR switch between the zones
- platform: template
entity_category: diagnostic
name: "Heat demanded"
icon: mdi:gas-burner
lambda: |-
return (
(id(heat_01_sw).state or
id(heat_02_sw).state or
id(heat_03_sw).state or
id(heat_04_sw).state or
id(heat_05_sw).state or
id(heat_06_sw).state or
id(heat_07_sw).state) or
);
on_state:
- lambda: |-
if (x) {
id(heater_sw).publish_state(true);
} else {
id(heater_sw).publish_state(false);
}
}
### Gas burner debounce+delay, to protect
- platform: template
id: heater_sw
filters:
- delayed_on_off: 20s
on_state:
- lambda: |-
if (x) {
id(heater_relay).turn_on();
} else {
id(heater_relay).turn_off();
}
Last 8 hours:
Topmost is the
heater_relay
, below it the rooms. As you see, 3 of the rooms have the heating turned off.
It would be a little messy, but I could add the functionality I want with automatons if the climate.control
action could dynamically change the heat_deadband
and min_heating_run_time
values.
Or if they were templatable?
Thinking about winter once again, and I discovered I may be able to achieve this with lambdas using set_heat_deadband
and set_heating_minimum_runtime_in_sec
. It'll be complicated, but if I get something working I'll update here.
https://esphome.io/api/classesphome_1_1thermostat_1_1_thermostat_climate
Any luck with resolving the short cycling?
I just finished testing a solution that seems to work well. I set all of the thermostats to have min_heating_run_time: 10min
.
Then I created a binary sensor for the boiler being on, which is just a logical OR for each thermostat:
In that binary sensor I have actions that change the minimum heating runtime and deadband for each thermostat. This works because if a thermostat starts heating, it continues to heat for the existing minimum runtime, changing the min runtime after the thermostat is in heating mode does not affect that heat cycle. Other thermostats can turn on or off during that minimum time with no restrictions. This will ensure the boiler stays on for that minimum time. Changing the deadband causes every thermostat to heat up opportunistically because the boiler is already on. When the boiler turns off, everything goes back to normal.
substitutions:
min_cycle_secs: "600"
heat_deadband: "0.5"
climate:
- &thermostat
name: "Office Thermostat"
id: thermostat_office
sensor: office_temperature
platform: thermostat
on_boot_restore_from: memory
startup_delay: true
visual:
min_temperature: 50 °F
max_temperature: 75 °F
temperature_step: 0.5
min_idle_time: 0s
min_heating_off_time: 0s
min_heating_run_time: ${min_cycle_secs}s
heat_action: {}
idle_action: {}
default_preset: Away
heat_deadband: ${heat_deadband} °C
heat_overrun: 0 °C
- name: "Living Room Thermostat"
sensor: living_room_temperature
id: thermostat_living_room
<<: *thermostat
- name: "Library Thermostat"
sensor: library_temperature
id: thermostat_library
<<: *thermostat
- name: "Master Bedroom Thermostat"
sensor: master_bedroom_temperature
id: thermostat_master_bedroom
<<: *thermostat
- name: "Bedroom 1 Thermostat"
sensor: bedroom_1_temperature
id: thermostat_bedroom_1
<<: *thermostat
- name: "Bedroom 2 Thermostat"
sensor: bedroom_2_temperature
id: thermostat_bedroom_2
<<: *thermostat
- name: "Basement Thermostat Test"
sensor: basement_temperature
id: thermostat_basement
<<: *thermostat
binary_sensor:
- name: "Boiler"
id: boiler_active
icon: "mdi:water-boiler"
platform: template
device_class: running
lambda: |-
if (id(thermostat_office).action == CLIMATE_ACTION_HEATING) { return true; }
if (id(thermostat_living_room).action == CLIMATE_ACTION_HEATING) { return true; }
if (id(thermostat_library).action == CLIMATE_ACTION_HEATING) { return true; }
if (id(thermostat_master_bedroom).action == CLIMATE_ACTION_HEATING) { return true; }
if (id(thermostat_bedroom_1).action == CLIMATE_ACTION_HEATING) { return true; }
if (id(thermostat_bedroom_2).action == CLIMATE_ACTION_HEATING) { return true; }
if (id(thermostat_basement).action == CLIMATE_ACTION_HEATING) { return true; }
return false;
on_press:
- lambda: |-
id(thermostat_office).set_heating_minimum_run_time_in_sec(0);
id(thermostat_office).set_heat_deadband(0);
id(thermostat_living_room).set_heating_minimum_run_time_in_sec(0);
id(thermostat_living_room).set_heat_deadband(0);
id(thermostat_library).set_heating_minimum_run_time_in_sec(0);
id(thermostat_library).set_heat_deadband(0);
id(thermostat_master_bedroom).set_heating_minimum_run_time_in_sec(0);
id(thermostat_master_bedroom).set_heat_deadband(0);
id(thermostat_bedroom_1).set_heating_minimum_run_time_in_sec(0);
id(thermostat_bedroom_1).set_heat_deadband(0);
id(thermostat_bedroom_2).set_heating_minimum_run_time_in_sec(0);
id(thermostat_bedroom_2).set_heat_deadband(0);
id(thermostat_basement).set_heating_minimum_run_time_in_sec(0);
id(thermostat_basement).set_heat_deadband(0);
on_release:
- lambda: |-
id(thermostat_office).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_office).set_heat_deadband(${heat_deadband});
id(thermostat_living_room).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_living_room).set_heat_deadband(${heat_deadband});
id(thermostat_library).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_library).set_heat_deadband(${heat_deadband});
id(thermostat_master_bedroom).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_master_bedroom).set_heat_deadband(${heat_deadband});
id(thermostat_bedroom_1).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_bedroom_1).set_heat_deadband(${heat_deadband});
id(thermostat_bedroom_2).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_bedroom_2).set_heat_deadband(${heat_deadband});
id(thermostat_basement).set_heating_minimum_run_time_in_sec(${min_cycle_secs});
id(thermostat_basement).set_heat_deadband(${heat_deadband});
Describe the problem you have/What new integration you would like
I would like to be able to associate multiple climate thermostats together, so that
run_time
andoff_time
timers are shared between the thermostats, anddeadband
settings are ignored when heating or cooling is active on any one of the thermostats.Please describe your use case for this integration and alternatives you've tried:
When using a multi-zone heating system, it makes sense to have a climate thermostat for each zones. But some settings should apply to the entire heating system, not to the zones. Like
min_heating_run_time
andmin_heating_off_time
, these are to protect the heating system and prevent short cycling. But once the heating system is on, because one zone is on, the as long as all zones don't turn off before min_heating_run_time, individual zones do not need to stay on for that duration. Additionally, once a zone is on,heat_deadband
can be ignored. Once the system is running, it would be beneficial to ignore the deadband and start heating every zone up toheat_overrun
, again to minimize the startup cycles of the heating system and avoid the case of one zone reaching temperature, and shutting off the heater right before another zone passes the deadband and starts calling for heat.Additional context