Just wanted to chime in and say thanks and add a bit more for other people finding this in 2024 and beyond…
this set up so far looks super useful but could probably be updated a bit. So i took & adapted @dujith 's excellent posts above, and slightly updated it for the newer (referred to as “modern” style) template syntax…
for example, what we have above in this thread uses parameters of “friendly_name” and “value_template”, which are now considered “legacy-format”, only found in that part of the docs-section, and is no longer recommended. Instead value_template is referred to just as “state” and “friendly_name” becomes (i believe) just the “name:” parameter (and what was the name can now be referred to as “unique_id”).
Here’s how i imagine it could be updated for the current template-format “style” - put the following in your templates.yaml:
sensor:
- name: "Central Heating Temperature"
unique_id: ch_virtual_temperature
unit_of_measurement: "°C"
state: >
{% if states('climate.trv_bedroom1') == "heat" %}
{% set tempdiff_bedroom1 = state_attr('climate.trv_bedroom1','temperature')
- states('sensor.temperature_bedroom1_temperature') | float %}
{% else %}
{% set tempdiff_bedroom1 = '-99' | float %}
{% endif %}
{% if states('climate.trv_bedroom2') == "heat" %}
{% set tempdiff_bedroom2 = state_attr('climate.trv_bedroom2','temperature')
- states('sensor.temperature_bedroom2_temperature') | float %}
{% else %}
{% set tempdiff_bedroom2 = '-99' | float %}
{% endif %}
{% if states('climate.trv_officegym') == "heat" %}
{% set tempdiff_officegym = state_attr('climate.trv_officegym','temperature')
- states('sensor.temperature_officegym_temperature') | float %}
{% else %}
{% set tempdiff_officegym = '-99' | float %}
{% endif %}
{% if states('climate.trv_hallway') == "heat" %}
{% set tempdiff_hallway = state_attr('climate.trv_hallway','temperature')
- states('sensor.temperature_hallway_lower_temperature') | float %}
{% else %}
{% set tempdiff_hallway = '-99' | float %}
{% endif %}
{% if states('climate.trv_bathroom_lower') == "heat" %}
{% set tempdiff_bathroom_lower = state_attr('climate.trv_bathroom_lower','temperature')
- states('sensor.temperature_bathroom_lower_temperature') | float %}
{% else %}
{% set tempdiff_bathroom_lower = '-99' | float %}
{% endif %}
{% if states('climate.trv_kitchen') == "heat" %}
{% set tempdiff_kitchen = state_attr('climate.trv_kitchen','temperature')
- states('sensor.temperature_livingroom_temperature') | float %}
{% else %}
{% set tempdiff_kitchen = '-99' | float %}
{% endif %}
{% if states('climate.trv_livingroom_center') == "heat" %}
{% set tempdiff_livingroom_center = state_attr('climate.trv_livingroom_center','temperature')
- states('sensor.temperature_livingroom_temperature') | float %}
{% else %}
{% set tempdiff_livingroom_center = '-99' | float %}
{% endif %}
{% if states('climate.trv_livingroom_wall') == "heat" %}
{% set tempdiff_livingroom_wall = state_attr('climate.trv_livingroom_wall','temperature')
- states('sensor.temperature_livingroom_temperature') | float %}
{% else %}
{% set tempdiff_livingroom_wall = '-99' | float %}
{% endif %}
{% set room_list_tempdiffs = [tempdiff_bedroom1,tempdiff_bedroom2,tempdiff_officegym,tempdiff_hallway,tempdiff_bathroom_lower,tempdiff_kitchen,tempdiff_livingroom_center,tempdiff_livingroom_wall] %}
{% set max_tempdiff = (room_list_tempdiffs | max) %}
{% set index_tempdiff = room_list_tempdiffs.index(max_tempdiff) %}
{% if index_tempdiff == 0 %}
{{ (states('sensor.temperature_bedroom1_temperature') | float(18)) }}
{% elif index_tempdiff == 1 %}
{{ (states('sensor.temperature_bedroom2_temperature') | float(18)) }}
{% elif index_tempdiff == 2 %}
{{ (states('sensor.temperature_officegym_temperature') | float(18)) }}
{% elif index_tempdiff == 3 %}
{{ (states('sensor.temperature_hallway_lower_temperature') | float(18)) }}
{% elif index_tempdiff == 4 %}
{{ (states('sensor.temperature_bathroom_lower_temperature') | float(18)) }}
{% else %}
{{ (states('sensor.temperature_livingroom_temperature') | float(18)) }}
{% endif %}
- name: "Central Heating Setpoint"
unique_id: ch_virtual_setpoint
unit_of_measurement: "°C"
state: >
{% if states('climate.trv_bedroom1') == "heat" %}
{% set tempdiff_bedroom1 = state_attr('climate.trv_bedroom1','temperature')
- states('sensor.temperature_bedroom1_temperature') | float %}
{% else %}
{% set tempdiff_bedroom1 = '-99' | float %}
{% endif %}
{% if states('climate.trv_bedroom2') == "heat" %}
{% set tempdiff_bedroom2 = state_attr('climate.trv_bedroom2','temperature')
- states('sensor.temperature_bedroom2_temperature') | float %}
{% else %}
{% set tempdiff_bedroom2 = '-99' | float %}
{% endif %}
{% if states('climate.trv_officegym') == "heat" %}
{% set tempdiff_officegym = state_attr('climate.trv_officegym','temperature')
- states('sensor.temperature_officegym_temperature') | float %}
{% else %}
{% set tempdiff_officegym = '-99' | float %}
{% endif %}
{% if states('climate.trv_hallway') == "heat" %}
{% set tempdiff_hallway = state_attr('climate.trv_hallway','temperature')
- states('sensor.temperature_hallway_lower_temperature') | float %}
{% else %}
{% set tempdiff_hallway = '-99' | float %}
{% endif %}
{% if states('climate.trv_bathroom_lower') == "heat" %}
{% set tempdiff_bathroom_lower = state_attr('climate.trv_bathroom_lower','temperature')
- states('sensor.temperature_bathroom_lower_temperature') | float %}
{% else %}
{% set tempdiff_bathroom_lower = '-99' | float %}
{% endif %}
{% if states('climate.trv_kitchen') == "heat" %}
{% set tempdiff_kitchen = state_attr('climate.trv_kitchen','temperature')
- states('sensor.temperature_livingroom_temperature') | float %}
{% else %}
{% set tempdiff_kitchen = '-99' | float %}
{% endif %}
{% if states('climate.trv_livingroom_center') == "heat" %}
{% set tempdiff_livingroom_center = state_attr('climate.trv_livingroom_center','temperature')
- states('sensor.temperature_livingroom_temperature') | float %}
{% else %}
{% set tempdiff_livingroom_center = '-99' | float %}
{% endif %}
{% if states('climate.trv_livingroom_wall') == "heat" %}
{% set tempdiff_livingroom_wall = state_attr('climate.trv_livingroom_wall','temperature')
- states('sensor.temperature_livingroom_temperature') | float %}
{% else %}
{% set tempdiff_livingroom_wall = '-99' | float %}
{% endif %}
{% set room_list_tempdiffs = [tempdiff_bedroom1,tempdiff_bedroom2,tempdiff_officegym,tempdiff_hallway,tempdiff_bathroom_lower,tempdiff_kitchen,tempdiff_livingroom_center,tempdiff_livingroom_wall] %}
{% set max_tempdiff = (room_list_tempdiffs | max) %}
{% set index_tempdiff = room_list_tempdiffs.index(max_tempdiff) %}
{% if index_tempdiff == 0 %}
{{ state_attr('climate.trv_bedroom1','temperature') | float(18) }}
{% elif index_tempdiff == 1 %}
{{ state_attr('climate.trv_bedroom2','temperature') | float(18) }}
{% elif index_tempdiff == 2 %}
{{ state_attr('climate.trv_officegym','temperature') | float(18) }}
{% elif index_tempdiff == 3 %}
{{ state_attr('climate.trv_hallway','temperature') | float(18) }}
{% elif index_tempdiff == 4 %}
{{ state_attr('climate.trv_bathroom_lower','temperature') | float(18) }}
{% elif index_tempdiff == 5 %}
{{ state_attr('climate.trv_kitchen','temperature') | float(18) }}
{% elif index_tempdiff == 6 %}
{{ state_attr('climate.trv_livingroom_center','temperature') | float(18) }}
{% elif index_tempdiff == 7 %}
{{ state_attr('climate.trv_livingroom_wall','temperature') | float(18) }}
{% endif %}
I’ve also incorporated the idea from @Snelf in the above post, so that when a TRV is not set to heat, it will default to “skipping” it (setting its suggested temp-differential to -99, to effectively prevent it from the choosing (unless we’re in an ice-age perhaps)).
Additionally, things to be aware of about my setup:
- i have only one temperature-sensor (all mine are aqara ones) for the area in the house that includes both the kitchen/living room (as they’re connected, w/o a significant partition between them, so thermally i treat them the-same), but there are in-fact three radiators (and thus TRVs) there - so i check each of them for the temp-diff in both template-sensors, but for the temperature, it’s picked as a “fall-back” in the if-block, and for the setpoint if-block, i actually use the setpoint of the specific TRV).
At the moment, these two values look like this for the temperature:
And like this for the setpoint:
If anyone has any ideas/feedback on my approach, or ideas for improvement, i’im open to them. One thing i’m not sure about which was touched upon in the above thread was whether the boiler will function efficiently if its idea of what the current temperature and the setpoint changes regularly. It seems it wouldn’t be able to “calibrate” its internal PID parameters since each room is slightly different and it won’t be able to converge on the correct parameters if the values/behaviour of the heating keeps shifting between different rooms.
Finally, things that are missing still:
- i need to actually wire-up the thermostat to my boiler still
- come up with automations that take-into-account whether no one is home (i am using the advhanced-heating-control blueprint for that on the individual-TRV basis, but need to incorporate it also telling the boiler to not generate heat if all TRV’s are not needing heat - which at the moment i’m not sure if that can ever happen?).