@RolandLT @johnBoy @swifty @Angelo_Santagata and for anyone else that has been looking to use the iTRVs in ‘passive mode’ i.e. not call for heat and only come on when other radiators are heating, basically behaving like a normal non-smart TRV. I have come up with an automation to emulate this behavior as closely as possible.
It’s not perfect and the ‘Passive TRVs’ do still call for heat while they’re on, but will only come on if one or more of the ‘Active TRVs’ are calling for heat. Anyway, it does what I need it to do for a few rarely used rooms and I suspect will be good enough for people looking to emulate this behaviour. It’s been asked what the benefit of having a ‘passive mode’ for an iTRV over a non-smart TRV is, as that is what it is emulating. Well, for me at least, I still have automated control over it for other things so I can make changes without needing to be physically present to adjust the TRVs manually.
To set this up you’ll need to copy (and modify to suit your use case) both the automation and the template sensors. Only binary_sensor.active_trvs_heating_demand
is essential, but I do recommend you create both the binary_sensor.passive_trvs_heating_demand
and the binary_sensor.wiser_hub_status
as well. The ‘Passive TRVs’ is useful to pop onto a dashboard along with the ‘Active TRVs’ to keep an eye on things and that the automation is behaving itself, while the ‘Wiser Hub Status’ is being used in all my Wiser automations to make sure that requests are delivered to the hub in case it’s offline. If you don’t create the ‘Wiser Hub Status’ template entity, you’ll need to remove both conditional references to it where it is being used with wait_for_tigger
from the automation (they are enclosed by if-then
statements).
So, firstly the template entities:
- binary_sensor:
# Wiser Hub Status binary sensor
- name: Wiser Hub Status
device_class: problem
icon: >-
{%- if is_state('binary_sensor.wiser_hub_status', 'on') -%}
mdi:router-wireless-off
{%- else -%}
mdi:router-wireless
{%- endif -%}
state: >-
{%- if is_state_attr('sensor.wiser_hub_heating_operation_mode', 'last_update_status', 'Success') -%}
off
{%- else -%}
on
{%- endif -%}
# Active TRVs Heating Demand binary sensor
- name: Active TRVs Heating Demand
icon: >-
{%- if is_state('binary_sensor.active_trvs_heating_demand', 'on') -%}
mdi:radiator
{%- else -%}
mdi:radiator-disabled
{%- endif -%}
state: >-
{%- if is_state('sensor.hallway_climate_heating_demand', '0')
and is_state('sensor.living_room_climate_heating_demand', '0')
and is_state('sensor.kitchen_climate_heating_demand', '0')
and is_state('sensor.bedroom_climate_heating_demand', '0')
and is_state('sensor.office_climate_heating_demand', '0') -%}
off
{%- else -%}
on
{%- endif -%}
# Passive TRVs Heating Demand binary sensor
- name: Passive TRVs Heating Demand
icon: >-
{%- if is_state('binary_sensor.passive_trvs_heating_demand', 'on') -%}
mdi:radiator
{%- else -%}
mdi:radiator-disabled
{%- endif -%}
state: >-
{%- if is_state('sensor.spare_room_climate_heating_demand', '0')
and is_state('sensor.garage_climate_heating_demand', '0') -%}
off
{%- else -%}
on
{%- endif -%}
And, secondly the automation:
# Wiser passive TRV emulation 1.4
- id: C70E4094-B538-43C3-9CCD-59F908475DBE
alias: "Wiser: Passive TRV emulation"
mode: restart
trigger:
# Active TRVs Heating Demand turns On
- platform: state
entity_id: binary_sensor.active_trvs_heating_demand
from: 'off'
to: 'on'
id: active_trvs_on
# Active TRVs Heating Demand turns Off
- platform: state
entity_id: binary_sensor.active_trvs_heating_demand
from: 'on'
to: 'off'
id: active_trvs_off
action:
- choose:
- conditions:
# Active TRVs Heating Demand turns On
- condition: trigger
id: active_trvs_on
sequence:
- repeat:
while:
# Check Active TRVs Heating Demand is On
- condition: state
entity_id: binary_sensor.active_trvs_heating_demand
state: 'on'
sequence:
- if:
# Check Wiser Hub is not offline
- condition: state
entity_id: binary_sensor.wiser_hub_status
state: 'on'
then:
# Wait for Wiser Hub to come back online
- wait_for_trigger:
- platform: state
entity_id: binary_sensor.wiser_hub_status
to: 'off'
- if:
# Check Away Mode is Off
- condition: state
entity_id: switch.wiser_hub_away_mode
state: 'off'
then:
# Spare Room
# Set thermostat to Heat; easier to identify it's in passive heating mode and stops it being reset by Cancel Heating Overrides
- service: climate.set_hvac_mode
target:
entity_id: climate.spare_room_climate
data:
hvac_mode: heat
- if:
# Check Spare Room target temperature hasn't hit the limit temperature already
# AND Spare Room current temperature is at least 0.4°C below limit temperature; to prevent increase beyond limit temperature
- condition: template
value_template: >-
{{ (state_attr('climate.spare_room_climate', 'temperature') | float) < (states('input_number.passive_trv_limit_temperature') | float)
and (state_attr('climate.spare_room_climate', 'current_temperature') | float) < (states('input_number.passive_trv_limit_temperature') | float - 0.4) }}
then:
# Set Spare Room target temperature to current temperature plus 0.5°C; to prevent high demand
- service: climate.set_temperature
target:
entity_id: climate.spare_room_climate
data:
temperature: "{{ state_attr('climate.spare_room_climate', 'current_temperature') | float + 0.5 }}"
else:
# Set Spare Room target temperature to limit temperature; in case limit temperature has been lowered or current temperature is less than 0.4°C below limit temperature
- service: climate.set_temperature
target:
entity_id: climate.spare_room_climate
data:
temperature: "{{ states('input_number.passive_trv_limit_temperature') | float }}"
# Garage
# Set thermostat to Heat; easier to identify it's in passive heating mode and stops it being reset by Cancel Heating Overrides
- service: climate.set_hvac_mode
target:
entity_id: climate.garage_climate
data:
hvac_mode: heat
- if:
# Check Garage target temperature hasn't hit the limit temperature already
# AND Garage current temperature is at least 0.4°C below limit temperature; to prevent increase beyond limit temperature
- condition: template
value_template: >-
{{ (state_attr('climate.garage_climate', 'temperature') | float) < (states('input_number.passive_trv_limit_temperature_garage') | float)
and (state_attr('climate.garage_climate', 'current_temperature') | float) < (states('input_number.passive_trv_limit_temperature_garage') | float - 0.4) }}
then:
# Set Garage target temperature to current temperature plus 0.5°C; to prevent high demand
- service: climate.set_temperature
target:
entity_id: climate.garage_climate
data:
temperature: "{{ state_attr('climate.garage_climate', 'current_temperature') | float + 0.5 }}"
else:
# Set Garage target temperature to limit temperature; in case limit temperature has been lowered or current temperature is less than 0.4°C below limit temperature
- service: climate.set_temperature
target:
entity_id: climate.garage_climate
data:
temperature: "{{ states('input_number.passive_trv_limit_temperature_garage') | float }}"
# Update target temperature every 15 minutes
- delay: 00:15:00
- conditions:
# Active TRVs Heating Demand turns Off
- condition: trigger
id: active_trvs_off
sequence:
- if:
# Check Wiser Hub is not offline
- condition: state
entity_id: binary_sensor.wiser_hub_status
state: 'on'
then:
# Wait for Wiser Hub to come back online
- wait_for_trigger:
- platform: state
entity_id: binary_sensor.wiser_hub_status
to: 'off'
# Reset to thermostats to Auto
- service: climate.set_hvac_mode
target:
entity_id:
- climate.spare_room_climate
- climate.garage_climate
data:
hvac_mode: auto
Hopefully this is useful to a few people and easy to understand, especially as I’ve commented everything.
Just swap in your own sensors and limit temperature variables etc and change the delay in the while loop that sets how often it checks and updates the target temperatures while maintaining a low heating demand to something suitable for your heating system. I imagine 15 minutes is probably OK for most people, but if you’ve got a really high flow temperature set on your boiler and your rooms heat up really quickly, you may want to reduce this, or vice-versa.
Note, you can probably create the ‘Active TRVs’ and ‘Passive TRVs’ as Groups instead. I just prefer using template sensors, as you can set dynamic icons for them (which I don’t think you can do with Groups, at least not within the UI anyway).
EDIT: I’ve updated the automation to use template
conditions, as I discovered that the numeric_state
conditions I was using at the outset and had tested with do not work with variables, even when trying to convert to a float
. I’ve also updated it to work with two input_number
helpers, created as below. For the second one, use the same settings, but append Garage (or whatever you want to call it) to the name:
This means you can change either input_number
as you wish, either via the dashboard, or another automation using the input_number.set_value
service to set it on a schedule to suits your needs. Consquently, the variables referenced elsewhere have also been removed, as they are no longer needed and would have prevented changes to the limit temperatures while the automation is running.
EDIT: I’ve updated the automation again to include some else
statements to take into account that now input_numbers
are being used, there are some additional considerations like if the limit temperature is lowered while the automation is running and if it’s less than 0.4 degrees below the limit temperature when it’s triggered. All tested and seems to be working properly for me. If anyone else uses this and finds any oddities (bugs), please let me know.
EDIT: Another update to the automation to take into account whether Away Mode is on (I don’t want this bumping the unheated rooms up when Away Mode sets everything to 10 degrees) and to set the hvac_mode
to heat
in order to prevent it being reset if you use the Cancel All Heating Overrides button, plus make it easier to identify that it’s in ‘passive heating mode’. This is optional and can be removed if you don’t care about whether it can be reset by the Cancel All Heating Overrides button and don’t like the change of colour of the thermostat dial to orange. I’ve also added a version number to the comment at the top for easy identification of whether any further updates have been added since this update. It’s currently at version 1.4.