Need help with multi-zone generic thermostat climate configuration

Hi,

I am struggling to create a Home Assistant configuration for the gas central heating in my home and I was wondering if anyone could suggest the best way to configure it.

There are multiple rooms, each with:

  1. One or more wireless Theromostatic Radiator Valve (TRV) which can turn a radiator on and off using MQTT
  2. A wireless thermostat that reports the current temperature using MQTT

There is a gas boiler that has a wireless relay that can be switched on and off using MQTT.

Initially, I want to:

  • set individual target temperatures for each room
  • turn on the TRVs for a room when below the target temperature and turn them off when target temperature reached
  • turn on the boiler if any room is below its target temperature and turn it off when all rooms are at their target temperature

I have a configuration (below) that works for a single room but canā€™t see how to scale to multiple rooms.

It uses a generic thermostat climate component to switch on the boiler when the temperature is below the target temperature.
The TRVs for the room are turned on and off using automations that detect when the state of the climate component changes.

mqtt:
  broker: localhost
  client_id: home-assistant-1
  keepalive: 60
  username: xxxxxxx
  password: xxxxxxx

switch heating:
  name: "Heating"
  platform: mqtt
  state_topic: "/boiler/heating"
  command_topic: "/boiler/heating"
  payload_on: 1
  payload_off: 0
  retain: true

sensor family_room:
  name: "family_room"
  platform: mqtt
  state_topic: "/sensor/ds18b20/f32c45"
  unit_of_measurement: "Ā°C"

switch trv_1a539d:
  name: "TRV_1a539d"
  platform: mqtt
  state_topic: "/trv/1a539d"
  command_topic: "/trv/1a539d"
  payload_on: 1
  payload_off: 0
  retain: true
 
switch trv_1a5647:
  name: "TRV_1a5647"
  platform: mqtt
  state_topic: "/trv/1a5647"
  command_topic: "/trv/1a5647"
  payload_on: 1
  payload_off: 0
  retain: true

climate family:
  platform: generic_thermostat
  name: "Family"
  heater: switch.heating
  target_sensor: sensor.family_room
  min_temp: 5
  max_temp: 30
  target_temp: 19.5
  min_cycle_duration:
    minutes: 10

group:
  family_trvs:
    name: Family TRVs
    entities:
      - switch.trv_1a539d
      - switch.trv_1a5647

automation family_trvs_on:
  alias: "Family TRVs On"
  trigger:
    platform: state
    entity_id: climate.family
    state: 'heat'
  action:
    service: homeassistant.turn_on
    entity_id: group.family_trvs

automation family_trvs_off:
  alias: "Family TRVs Off"
  trigger:
    platform: state
    entity_id: climate.family
    state: 'idle'
  action:
    service: homeassistant.turn_off
    entity_id: group.family_trvs

If I add a 2nd climate component for another room that switches on or off the boiler based on a different target temperature and MQTT temperature sensor, it wonā€™t take into consideration the other climate component, leading to the boiler being turned off when it shouldnā€™t.

So I donā€™t have a boiler or even thermostats/sensors in multiple rooms and Iā€™ve never used MQTT so with all that in mind, I looked over your code and think I understand how you could make this work. Iā€™ve modified and added to your code with an added example of how you might be able to add a ā€œbedroomā€. Hope it helps you. I havenā€™t tested the code but if it doesnā€™t work, it may help lead you to a solution.

mqtt:
  broker: localhost
  client_id: home-assistant-1
  keepalive: 60
  username: xxxxxxx
  password: xxxxxxx

input_boolean:
  family_heater:
    initial: off
  bedroom_heater:
    initial: off

group:
  heat_tigger:
    name: Heat Trigger
    entities:
      - input_boolean.family_heater
      - input_boolean.bedroom_heater

switch heating:
  name: "Heating"
  platform: mqtt
  state_topic: "/boiler/heating"
  command_topic: "/boiler/heating"
  payload_on: 1
  payload_off: 0
  retain: true

sensor family_room:
  name: "family_room"
  platform: mqtt
  state_topic: "/sensor/ds18b20/f32c45"
  unit_of_measurement: "Ā°C"

sensor bedroom:
  name: "bedroom"
  platform: mqtt
  state_topic: "/sensor/ds18b20/f32c45"
  unit_of_measurement: "Ā°C"

switch trv_1a539d:
  name: "TRV_1a539d"
  platform: mqtt
  state_topic: "/trv/1a539d"
  command_topic: "/trv/1a539d"
  payload_on: 1
  payload_off: 0
  retain: true
 
switch trv_1a5647:
  name: "TRV_1a5647"
  platform: mqtt
  state_topic: "/trv/1a5647"
  command_topic: "/trv/1a5647"
  payload_on: 1
  payload_off: 0
  retain: true

switch trv_bedroom1:
  name: "TRV_bedroom1"
  platform: mqtt
  state_topic: "/trv/1a539d"
  command_topic: "/trv/1a539d"
  payload_on: 1
  payload_off: 0
  retain: true
 
switch trv_bedroom2:
  name: "TRV_bedroom2"
  platform: mqtt
  state_topic: "/trv/1a5647"
  command_topic: "/trv/1a5647"
  payload_on: 1
  payload_off: 0
  retain: true

climate family:
  platform: generic_thermostat
  name: "Family"
  heater: input_boolean.family_heater
  target_sensor: sensor.family_room
  min_temp: 5
  max_temp: 30
  target_temp: 19.5
  min_cycle_duration:
    minutes: 10

climate bedroom:
  platform: generic_thermostat
  name: "Bedroom"
  heater: input_boolean.bedroom_heater
  target_sensor: sensor.bedroom
  min_temp: 5
  max_temp: 30
  target_temp: 19.5
  min_cycle_duration:
    minutes: 10

automation climate_control:
  - alias: "Family TRVs Toggle"
    trigger:
      platform: state
      entity_id: climate.family
    action:
      service_template: >
        {% if trigger.to_state.state == "heat" %}
        homeassistant.turn_on
        {% elif trigger.to_state.state == "idle" %}
        homeassistant.turn_off
        {% endif %}
      entity_id:
        - switch.trv_1a539d
        - switch.trv_1a5647

  - alias: "Bedroom TRVs Toggle"
    trigger:
      platform: state
      entity_id: climate.bedroom
    action:
      service_template: >
        {% if trigger.to_state.state == "heat" %}
        homeassistant.turn_on
        {% elif trigger.to_state.state == "idle" %}
        homeassistant.turn_off
        {% endif %}
      entity_id:
        - switch.trv_bedroom1
        - switch.trv_bedroom2

  - alias: "Boiler Switch Toggle"
    trigger:
      platform: state
      entity_id: group.heat_tigger
    action:
      service_template: >
        {% if trigger.to_state.state == "on" %}
        homeassistant.turn_on
        {% elif trigger.to_state.state == "off" %}
        homeassistant.turn_off
        {% endif %}
      entity_id:
        - switch.heating

It would be great to hear any feedback on if this will/wont/does work.

Thanks for the suggestion, it makes sense but unfortunately when I tried it the climate component does not switch to the heat state. I set input_boolean.family_heater as the climateā€™s heater and that does not seem to be set to state ā€˜onā€™. Nothing in the logs to indicate an error but when I changed that back to a switch it worked as before.

The climate component docs state:

heater (Required): entity_id for heater switch, must be a toggle device.

and looking at the code on github, it seems like the heater needs to be a switch (it calls switch.turn_on and switch.turn_off on the heater entity id).

I created a fake MQTT switch and used that instead of an input_boolean and it works. Not exactly ideal but a workaround at least.

I wonder if there is a reason for the requirement of a climate componentā€™s heater being a switch or if there is an easy way for it to be toggle entity instead.

Glad you found a fix. Good question about the necessity of climate calling switch.turn_on and switch.turn_off. Since you found a way to make it work it may not be needed but you could try changing the code to homeassistant.turn_on and see if your change works for you. If it does and youā€™re interested, you could submit a PR. It might open other possibilities for others. Glad you got it though.

I was just trying that and it seems to work. Donā€™t have a huge amount of spare time to get a PR submitted at the moment (Iā€™ve never done it before so I assume there will be a bit of setup involved) but will definitely look at trying to do it.

I have the same problem
how did you make the fake mqtt switch?

Iā€™m actually using a template switch instead of a fake MQTT switch now.

Here is a the appropriate bits of my config that shows two climate generic thermostats and the automations to turn on their TRVs and the heating if the temperature is too cold in either room.

The template switches just map directly to the input booleans.

mqtt:
  broker: mqtt
  client_id: <secret>
  keepalive: 60
  username: <secret>
  password: <secret>

input_boolean:
  family_room_heating:
    initial: off
  guest_one_heating:
    initial: off

sensor family_room:
  name: "family_room"
  platform: mqtt
  state_topic: "/sensor/ds18b20/d0876f"
  unit_of_measurement: "Ā°C"

sensor guest_one:
  name: "guest_one"
  platform: mqtt
  state_topic: "/sensor/ds18b20/d0c232"
  unit_of_measurement: "Ā°C"

switch:
  - platform: template
    switches:
      family_room_heating_workaround:
        value_template: "{{ is_state('input_boolean.family_room_heating', 'on') }}"
        turn_on:
          service: input_boolean.turn_on
          entity_id: input_boolean.family_room_heating
        turn_off:
          service: input_boolean.turn_off
          entity_id: input_boolean.family_room_heating
        entity_id: climate.family
      guest_one_heating_workaround:
        value_template: "{{ is_state('input_boolean.guest_one_heating', 'on') }}"
        turn_on:
          service: input_boolean.turn_on
          entity_id: input_boolean.guest_one_heating
        turn_off:
          service: input_boolean.turn_off
          entity_id: input_boolean.guest_one_heating

switch heating:
  name: "Heating"
  platform: mqtt
  state_topic: "/boiler/heating"
  command_topic: "/boiler/heating"
  payload_on: 1
  payload_off: 0
  retain: true

climate family:
  platform: generic_thermostat
  name: "Family"
  heater: switch.family_room_heating_workaround
  target_sensor: sensor.family_room
  min_temp: 5
  max_temp: 30
  target_temp: 18.5
  tolerance: 0.5

climate guest_one:
  platform: generic_thermostat
  name: "Guest One"
  heater: switch.guest_one_heating_workaround
  target_sensor: sensor.guest_one
  min_temp: 5
  max_temp: 30
  target_temp: 15
  tolerance: 0.5

group:
  family_trvs:
    entities:
      - switch.trv_family1
      - switch.trv_family2
  guest_one_trvs:
    entities:
      - switch.trv_guest_one
  heating_trigger:
    entities:
      - input_boolean.family_room_heating
      - input_boolean.guest_one_heating

automation:
- id: family_trvs_on_id
  alias: Family TRVs on
  trigger:
    platform: state
    entity_id: climate.family
    from: idle
    to: heat
  action:
    service: homeassistant.turn_on
    entity_id: group.family_trvs
- id: guest_one_trvs_on_id
  alias: Guest One TRV on
  trigger:
    platform: state
    entity_id: climate.guest_one
    from: idle
    to: heat
  action:
    service: homeassistant.turn_on
    entity_id: group.guest_one_trvs
- id: bolier_switch_toggle_id
  alias: Boiler Switch Toggle
  trigger:
    platform: state
    entity_id: group.heating_trigger
  action:
    service_template: '{% if trigger.to_state.state == "on" %} homeassistant.turn_on
      {% elif trigger.to_state.state == "off" %} homeassistant.turn_off {% endif %}'
    entity_id:
    - switch.heating
2 Likes

Hey! Can I ask if there is meant to be a dual entity_id in the section above? Iā€™ve been trying to get this very thing working and seem to be coming up against some strange behaviour. No log errors but just doesnā€™t seem to be triggering correctly.

Cheers!

That 2nd entity should not be there. It was a mistake when I copied it in. It should just be:

entity_id: input_boolean.family_room_heating

Just to let you know the problem that required this workaround (not being able to control anything other than a switch with a climate component) was fixed in home assistant 0.59 so I have been able to strip out all this workaround code from my configuration and control the input boolean directly. It works much better and is a lot simpler.

climate family:
  platform: generic_thermostat
  name: "Family"
  heater: input_boolean.family_heating
  target_sensor: sensor.family
  min_temp: 5
  max_temp: 30
  target_temp: 17.5
  hot_tolerance: 0
  cold_tolerance: 0.5

Details on github:
Change generic thermostat - any toggle device as heater switch #10597

1 Like

Awesome!

Thanks for this, do you have your whole configuration available anywhere on github that i can browse over? or would you mind posting your whole updated config?

I want to integrate exactly what you have but Iā€™m getting a lost porting current my config over which is just based on all rooms having the same target temp.

Cheers!

Scrap that! Worked it out from what you had pointed out! by directing the heater to the input boolean! It just took a few re-reads for the info to go in! haha

Great youā€™ve worked it out - I was almost finished writing the following so I will post it anyway:

I donā€™t have my configuration in github unfortunately and it is a bit of a mess so I have included the appropriate bits below (hopefully havenā€™t messed up the copy/paste and minor mods this time).

Iā€™ve ended up doing this a bit differently than in the original post.

I am still controlling the heating (and water) based on multiple inputs but I have time triggered template automations that control the TRVs and check the state every minute to:

  • turn them off if the climate is off or the temperature is greater than target + 1 degree
  • turn them on if the climate is not off and the the temperature is less than target + 1 degree

ā€™

mqtt:
  broker: mqtt
  client_id: secret
  keepalive: 60
  username: secret
  password: secret

input_boolean:
  family_heating:
    initial: off
  guest_one_heating:
    initial: off
  water:
    initial: off

sensor family:
  name: "family"
  platform: mqtt
  state_topic: "/sensor/ds18b20/d0876f"
  unit_of_measurement: "Ā°C"
  expire_after: 300

sensor guest_one:
  name: "guest_one"
  platform: mqtt
  state_topic: "/sensor/ds18b20/d0c232"
  unit_of_measurement: "Ā°C"
  expire_after: 300

sensor water:
  name: "water"
  platform: mqtt
  state_topic: "/sensor/ds18b20/d0187f"
  unit_of_measurement: "Ā°C"
  expire_after: 300
  
switch heating:
  name: "Heating"
  platform: mqtt
  state_topic: "/boiler/heating"
  command_topic: "/boiler/heating"
  payload_on: 1
  payload_off: 0
  retain: true

switch water:
  name: "Water"
  platform: mqtt
  state_topic: "/boiler/water"
  command_topic: "/boiler/water"
  payload_on: 1
  payload_off: 0
  retain: true

switch trv_guest_one:
  name: "trv_guest_one"
  platform: mqtt
  state_topic: "/trv/2c77a6"
  command_topic: "/trv/2c77a6"
  payload_on: 1
  payload_off: 0
  retain: true

climate family:
  platform: generic_thermostat
  name: "Family"
  heater: input_boolean.family_heating
  target_sensor: sensor.family
  min_temp: 5
  max_temp: 30
  target_temp: 17.5
  hot_tolerance: 0
  cold_tolerance: 0.5

climate guest_one:
  platform: generic_thermostat
  name: "Guest One"
  heater: input_boolean.guest_one_heating
  target_sensor: sensor.guest_one
  min_temp: 5
  max_temp: 30
  target_temp: 17
  hot_tolerance: 0
  cold_tolerance: 0.5

climate water:
  platform: generic_thermostat
  name: "Water"
  heater: input_boolean.water
  target_sensor: sensor.water
  min_temp: 5
  max_temp: 60
  target_temp: 40
  hot_tolerance: 0
  cold_tolerance: 2.0

group:
  heating_trigger:
    entities:
      - input_boolean.family_heating
      - input_boolean.guest_one_heating
  water_trigger:
    entities:
      - input_boolean.water
  family_trvs:
    entities:
      - switch.trv_family1
      - switch.trv_family2
  guest_one_trvs:
    entities:
      - switch.trv_guest_one

automation:
  - id: heating_switch_toggle_id
    alias: Heating Switch Toggle
    trigger:
      platform: state
      entity_id: group.heating_trigger
    action:
      service_template: '{% if trigger.to_state.state == "on" %} homeassistant.turn_on
        {% elif trigger.to_state.state == "off" %} homeassistant.turn_off
        {% endif %}'
      entity_id:
      - switch.heating
  - id: water_switch_toggle_id
    alias: Water Switch Toggle
    trigger:
      platform: state
      entity_id: group.water_trigger
    action:
      service_template: '{% if trigger.to_state.state == "on" %} homeassistant.turn_on
        {% elif trigger.to_state.state == "off" %} homeassistant.turn_off
        {% endif %}'
      entity_id:
      - switch.water
  - id: family_trvs_on
    alias: Family TRVs on
    trigger:
      platform: time
      minutes: '/1'
      seconds: 00
    condition:
      condition: template
      value_template: '{{ is_state("group.family_trvs", "off") and not is_state("climate.family", "off") and states.climate.family.attributes.current_temperature < states.climate.family.attributes.temperature + 1 }}'
    action:
      service: homeassistant.turn_on
      data:
        entity_id: group.family_trvs
  - id: family_trvs_off
    alias: Family TRVs off
    trigger:
      platform: time
      minutes: '/1'
      seconds: 00
    condition:
      condition: template
      value_template: '{{ is_state("group.family_trvs", "on") and ( is_state("climate.family", "off") or states.climate.family.attributes.current_temperature > states.climate.family.attributes.temperature + 1 ) }}'
  action:
      service: homeassistant.turn_off
      data:
        entity_id: group.family_trvs
  - id: guest_one_trvs_on
    alias: Guest One TRVs on
    trigger:
      platform: time
      minutes: '/1'
      seconds: 00
    condition:
      condition: template
      value_template: '{{ is_state("group.guest_one_trvs", "off") and not is_state("climate.guest_one", "off") and states.climate.guest_one.attributes.current_temperature < states.climate.guest_one.attributes.temperature + 1 }}'
    action:
      service: homeassistant.turn_on
      data:
        entity_id: group.guest_one_trvs
  - id: guest_one_trvs_off
    alias: Guest One TRVs off
    trigger:
      platform: time
      minutes: '/1'
      seconds: 00
    condition:
      condition: template
      value_template: '{{ is_state("group.guest_one_trvs", "on") and ( is_state("climate.guest_one", "off") or states.climate.guest_one.attributes.current_temperature > states.climate.guest_one.attributes.temperature + 1 ) }}'
    action:
      service: homeassistant.turn_off
      data:
        entity_id: group.guest_one_trvs
2 Likes

Nice! This is still handy for me anyway! And well worth the post for reference for others so thanks so much for putting in the time!

I need to study your trv control as I think youā€™re doing it way better than me. Mine are all based around temps set by the sensors in a little bit of a non-state way so they are a little unstable.

Another quick question, what trvā€™s are you using? DIY or something else?

Thanks again for all of the input and help.

I have gone through a variety of iterations and I am quite happy with the way it works at the moment. With previous versions, I was having problems with the TRVs turning on and off too frequently and the boiler being on too much - heating one room with the other TRVs off and so-on for each room

With the current solution, all rooms are heated and the TRVs only turn off if they get too warm or I specifically turn off the climate control for that room.

There may be better solutions but this is the best I have come up with so far.

I am using DIY TRVs. I modded the following:

http://www.eq-3.com/products/eqiva/radiator-thermostat-model-n.html

I soldered in a ESP8266 wifi chip to the physical controls and wrote some Arduino code that runs MQTT and turns them on and off by directly controlling them electronically.

Nice! This is the exact thing Iā€™m after, Iā€™ve gone completely diy on the TRV with a stepper motor controlling the valve, so the software has been a work in progress since.

Do you have a guide or tutorial with the details of your mod for those you have added a esp?

And particularly, are they battery powered? I am currently cable powered which is limiting the ā€œdistributionā€ of the trvā€™s as a plug has to be in the vicinity.

Would be awesome if you have something detailing your mod.

Also, what was the associated costs? a esp obviously and then just the unit? a quick google returns several versions of the same ā€œRadiator Thermostat Model Nā€, some at Ā£8 and others at Ā£28, Ā£8 would be awesome, 28 not so much! haha.

If you have a guide, please do let me know as Iā€™d be interested in trying your way for sure!

I considered the full DIY motor route, even going as far as buying a couple of worm drive motors, but I was struggling to come up with how to do the enclosure.

I also considered buying wireless TRVs but I couldnā€™t find any that were 1. cheap and 2. had an open, or hacked, protocol.

I havenā€™t written anything up for the mod unfortunately - just kind of winged it. The way I do it actually disconnects the rotary encoder control, and the ESP8266 emulates it in code. It renders the TRV unusable for anything other control by the ESP8266 because I cut the binary encoder pins from the board. There is probably a better way.

I will try writing something up with pictures and the code, but Iā€™m a bit time-limited with two small boys and a full-time job,

The TRVs run on AA batteries but I havenā€™t gone the full step to attempt to power the ESP8266 from the same batteries so I run 4 strands of ribbon cable (2 USB power and 2 data) from the ESP8266 to the TRV and power it from a USB plug somewhere in the room.

My feeling is that the ESP8266 would be a too power hungry to run off a couple of AAs for any length of time so I have accepted the lost socket, and cable run, which I can usually tuck under the skirting as it is an old house. I have thought of experimenting with deep sleep on the ESP8266 to extend battery life but it hasnā€™t had any time yet, as I have an acceptable solution. The commercial products must handle this OK (maybe a WiFi chip with lower power requirements?)

I bought these (no affiliation):

https://www.conrad-electronic.co.uk/ce/en/product/1275959/Thermostat-head----electronical----3-piece-set----5-up-to-295-C----eqiva/?ref=detview1&rt=detview1&rb=1

3 for Ā£28 (maybe this is what you saw and thought it was just for one?) and I think I searched around and got 10% discount from somewhere too.

I bulk ordered the D1 mini ESP8266 chip from China (Aliexpress) so got them for Ā£2 or Ā£3 each.

I use little project boxes to house the D1 mini that cost a couple of quid each I think.

It probably works out at about Ā£15-Ā£18 per TRV.

I also attach a ds18b20 to the ESP8266 as the temperature sensor for the room, although it has to be outside the project box otherwise the heat of the chip skews the temperature reading.

I should definitely write this up.

1 Like

Wow, this is pretty amazing, maybe thereā€™s any source where you share this project, Iā€™d love to do the same with mine, but donā€™t have enough skills to make it myself.

Just ordered my first trv actuator and I am planning to give this a try. Has anyone considered switching the valves on and then off periodically even when the heating is not being used to stop them sticking or is this not an issue?

Probably needs a daily automation to turn each valve on for a few minutes providing the boiler is off/all rooms are up to the required temperature.

Hi, old thread, but Iā€™d like to share my thermostat that I just made. It can be used to handle multiple rooms/zones