Warning, messy post!
So one of the benefits of the hass climate template is I can run custom actions on set_temperature, set_hvac_mode and set_preset_mode climate actions. I use this to set a pre-defined dynamic set point that I get from a template sensor blueprint to my wiser device. This template sensor I created takes into account my settings for the different presets, and then adjusts the temperature for me. I have another automation that updates the wiser deviceās set point when that value changes and the wiser device is in heat/auto mode. I did it this way because I get a sensor I can plot, check up on, but it would probably be better to re-use the calculation with a jinja template and just apply it with the update set point command.
That dynamic sensor has been great so far, I donāt override the radiator to room temperature when itās away, or sleep modes. I mostly use those modes for radiator protection so didnāt see much point heating to the room temperature there, so i save some gas, just make sure the radiator trv is at temp.
Windows open detection is just part of my occupancy blueprint. So when it detects the binary sensor I have created for āVented Modeā and my āWinter Modeā is on, it turns the HVAC mode off and puts the device into a āNoneā preset mode so that my blueprint doesnāt try to control it until āVented Modeā turns off.
My wiser schedules set the background temperatures, the template reads this, and then Iāve got a āTake Over Modeā for each room, this looks at the current set temperature of the wiser schedule, and if itās over 15.5 degreeās it will allow my occupancy to take over and set the occupancy set point values. Those are based on my room occupancy blueprint that gives me āJust Occupiedā, āOccupiedā, āSettled Inā, āJust Unoccupiedā, āUnoccupiedā and āSleepā states.
Iām still building it all out and Iāve not cleaned up much of my testing/still working on and refining. Anyway, the template blueprint for my dynamic set point is below if it helps. An example of usage is below that I import as a package.
The Dynamic Set Point takes into account the following:
- Template sensor preset mode
- Wiser Current Set Point
- Room Temperature
- Radiator (TRV) Temperature
- Eco Mode Input Boolean - My custom mode that looks at how far the current room set point temperature is from the average temperature the floor the room is on, then applies an negative offset up to 1.5 degree. So if my radiator in the livingroom is set to 18Ā°C, and the hallway, livingroom and kitchen are averaged at 19 degree, then it will take a little bit off the rooms set point to account for the additional heat.
- Comfort Mode Input Boolean - Another of my custom modes that applies a specific positive offset to the target when the outdoor temperature is between 1 and -5Ā° C.
blueprint:
name: Room Target Temperature
description: Creates a sensor that adjusts your heater target set point that accounts for the outdoor temperature, indoor average temperature and a trv temperature sensor.
domain: template
#source_url: https://github.com/home-assistant/core/blob/ce0117b2b82cda7900f18d781be2d6a8d0f807ed/homeassistant/components/template/blueprints/inverted_binary_sensor.yaml
input:
template_climate_device:
name: Templated Climate Device
description: ""
selector:
entity:
domain: climate
og_climate_device:
name: Wiser Climate Device
description: ""
selector:
entity:
domain: climate
room_temp:
name: Room Temperature Sensor
description: ""
selector:
entity:
domain: sensor
device_class: temperature
radiator_temp:
name: Radiator Temperature Sensor
description: ""
selector:
entity:
domain: sensor
device_class: temperature
outdoor_temp:
name: Outdoor Temperature Sensor
description: ""
selector:
entity:
domain: sensor
device_class: temperature
indoor_avg_temp:
name: Indoor Average Temperature Sensor
description: ""
selector:
entity:
domain: sensor
device_class: temperature
variables:
template_climate_device_input: !input template_climate_device
og_climate_device_input: !input og_climate_device
room_temp_input: !input room_temp
radiator_temp_input: !input radiator_temp
outdoor_temp_input: !input outdoor_temp
indoor_avg_temp_input: !input indoor_avg_temp
sensor:
state: >
{% set adjust_presets = ["none", "eco", "home", "activity", "comfort", "boost"] %}
{% set wiser_away_presets = ["away", "sleep"] %}
{% set current_preset = state_attr(template_climate_device_input, 'preset_mode') %}
{% set outdoor_temp = states(outdoor_temp_input)|float(0) %}
{% set indoor_avg_temp = states(indoor_avg_temp_input)|float(0) %}
{%- set ns = namespace(current_target=state_attr(template_climate_device_input, 'temperature')|float(0)) %}
{%- if current_preset == "eco" -%}
{%- set ns.current_target = state_attr(og_climate_device_input, "current_schedule_temp")|float(0) -%}
{%- endif -%}
{% set eco_mode = is_state('input_boolean.heating_eco_mode', 'on') %}
{% set eco_adjust_max = 1.5 %}
{% set eco_raw_adjustment = indoor_avg_temp - ns.current_target %}
{% set comfort_mode = is_state('input_boolean.heating_comfort_mode','on') %}
{% set comfort_cold_max = 3 %}
{% set comfort_min = -1 %}
{% set comfort_cold_adjust_max = 0.5 %}
{% set comfort_frozen_max = -5 %}
{% set comfort_frozen_adjust_max = 1 %}
{% set room_temperature = states(room_temp_input)|float(0) %}
{% set radiator_temperature = states(radiator_temp_input)|float(0) %}
{%- if eco_raw_adjustment > 0 and eco_mode -%}
{%- set ratio = eco_raw_adjustment / eco_adjust_max -%}
{%- set ns.current_target = ns.current_target - min((eco_raw_adjustment * ratio), eco_adjust_max) -%}
{% endif %}
{%- if comfort_mode and outdoor_temp >= comfort_min and outdoor_temp <= comfort_cold_max -%}
{%- set adjustment = comfort_cold_adjust_max * (( comfort_cold_max - outdoor_temp ) / comfort_cold_max) -%}
{%- set ns.current_target = ns.current_target + min(adjustment, comfort_cold_adjust_max) -%}
{%- endif -%}
{%- if comfort_mode and outdoor_temp <= comfort_min and outdoor_temp >= comfort_frozen_max -%}
{% set adjustment = comfort_frozen_adjust_max * ( -outdoor_temp / comfort_frozen_max|abs ) %}
{%- set ns.current_target = ns.current_target + comfort_cold_adjust_max + min(adjustment, comfort_frozen_adjust_max) -%}
{%- endif -%}
{%- if current_preset in adjust_presets -%}
{% set radiator_adjusted = ns.current_target - (room_temperature - radiator_temperature) %}
{%- if radiator_adjusted <= 20 and radiator_adjusted >= 5 -%}
{{ radiator_adjusted|round(1) }}
{%- else -%}
{{ ns.current_target|round(1) }}
{%- endif -%}
{%- else -%}
{{ ns.current_target|round(1) }}
{%- endif -%}
device_class: temperature
state_class: measurement
unit_of_measurement: 'Ā°C'
availability: "{{ states(template_climate_device_input) not in ('unknown', 'unavailable') }}"
My Livingroom package for the radiator that uses hass-climate-template:
### Preset and HVAC Modes
input_select:
template_livingroom_climate_preset_modes:
name: Template Livingroom Climate Preset Modes
options:
- Away
- Sleep
- Eco
- Home
- Activity
- Comfort
- Boost
- None
### Target Temperature Input Number
input_number:
template_livingroom_climate_home_temperature:
name: Template Livingroom Climate Home Temperature
unit_of_measurement: Ā°C
min: 5
max: 20
step: 0.1
template_livingroom_climate_activity_temperature:
name: Template Livingroom Climate Activity Temperature
unit_of_measurement: Ā°C
min: 5
max: 20
step: 0.1
template_livingroom_climate_comfort_temperature:
name: Template Livingroom Climate Comfort Temperature
unit_of_measurement: Ā°C
min: 5
max: 20
step: 0.1
template_livingroom_climate_boost_temperature:
name: Template Livingroom Climate Boost Temperature
unit_of_measurement: Ā°C
min: 5
max: 20
step: 0.1
### Dynamic target temperature
template:
- use_blueprint:
path: homeassistant/room_target_temperature_sensor.yaml
input:
template_climate_device: climate.template_livingroom_radiator
og_climate_device: climate.wiser_livingroom
room_temp: sensor.livingroom_thermal_comfort_heat_index
radiator_temp: sensor.wiser_lts_temperature_livingroom
outdoor_temp: sensor.average_outdoor_temperature
indoor_avg_temp: sensor.average_downstairs_temperature
name: Template Livingroom Dynamic Setpoint
unique_id: template_livingroom_dynamic_setpoint
- use_blueprint:
path: homeassistant/room_scheduled_heat_binary_sensor.yaml
input:
og_climate_device: climate.wiser_livingroom
cut_off: input_number.scheduled_temperature_cutoff
name: Template Livingroom Occupancy Take Over
unique_id: template_livingroom_occupancy_takeover
### Climate controller
climate:
- platform: climate_template
## RADIATOR SETTINGS
name: Template Livingroom Radiator
unique_id: template_livingroom_radiator
min_temp: 5
max_temp: 20
temp_step: 0.1
precision: 0.1
mode_action: "restart"
#max_action: 3
hvac_modes:
- "heat" # Occupancy Mode
- "auto" # Wiser Mode
- "off" # Winter Mode Off
preset_modes:
- away
- sleep
- eco
- home
- activity
- comfort
- boost
- none
## RADIATOR STATE -
hvac_action_template: "{{ state_attr('climate.wiser_livingroom', 'hvac_action') }}"
current_temperature_template: "{{ states('sensor.livingroom_thermal_comfort_heat_index')|float(0) }}"
current_humidity_template: "{{ states('sensor.average_livingroom_humidity')|float(0) }}"
#hvac_mode_template: "{{ states('input_select.template_livingroom_climate_hvac_modes')|lower }}"
preset_mode_template: "{{ states('input_select.template_livingroom_climate_preset_modes')|lower }}"
#target_temperature_template: "{{ states('input_number.template_livingroom_climate_target_temperature')|float(0) }}"
availability_template: "{{
not is_state('climate.wiser_livingroom', 'unknown') or
not is_state('climate.wiser_livingroom', 'unavailable')
}}"
### This should reflect the different modes
icon_template: >-
{% set current_state = state_attr('climate.wiser_livingroom', 'hvac_action') %}
{%- if current_state == "heating" -%}
mdi:radiator
{%- elif current_state == "off" or curret_state == "idle" -%}
mdi:radiator-off
{%- else -%}
mdi:radiator-disabled
{%- endif -%}
#1 - presets attributes are changeable via HA
#2 - presets attributes are preserved over HA restarts
#4 - hvac mode is preset attribute
#8 - fan mode is preset attribute
#16 - swing mode is preset attribute
#32 - target temperature is preset attribute
#64 - high/low temperature are presets attribute
#128 - target humidity is preset attribute
## 1 + 2 + 4 + 32
presets_features: 39
presets_template: >-
{{
{
'away': {
'hvac_mode': "auto",
'target_temperature': states('input_number.wiser_holiday_mode_temperature')|float(5) if states('input_boolean.holiday_mode') == "on" else states('input_number.wiser_away_mode_temperature')|float(5),
},
'sleep': {
'hvac_mode': "auto",
'target_temperature': states('input_number.wiser_sleep_mode_temperature')|float(5),
},
'eco': {
'hvac_mode': "auto",
'target_temperature': state_attr('climate.wiser_livingroom', 'current_schedule_temp')|float(5),
},
'home': {
'hvac_mode': "heat",
'target_temperature': states('input_number.template_livingroom_climate_home_temperature')|float(5),
},
'activity': {
'hvac_mode': "heat",
'target_temperature': states('input_number.template_livingroom_climate_activity_temperature')|float(5),
},
'comfort': {
'hvac_mode': "heat",
'target_temperature': states('input_number.template_livingroom_climate_comfort_temperature')|float(5),
},
'boost': {
'hvac_mode': "heat",
'target_temperature': states('input_number.template_livingroom_climate_boost_temperature')|float(5),
}
}
}}
## SCRIPTS
set_temperature:
- variables:
wiser_device: "climate.wiser_livingroom"
passive_mode_switch: "switch.wiser_livingroom_passive_mode"
template_device: "climate.template_livingroom_radiator"
dynamic_set_point: "sensor.template_livingroom_dynamic_setpoint"
- alias: Set temp if passive mode
if:
- condition: template
value_template: "{{ is_state(passive_mode_switch, 'on') }}"
- condition: template
value_template: >-
{{ not is_state(wiser_device, 'off') and not is_state(template_device, 'off') }}
- condition: template
value_template: >-
{% set nearest_5 = (((state_attr(wiser_device, 'target_temp_high')|float(0)/5) | int) +1) * 5 %}
{{ nearest_5 != states(dynamic_set_point)|float(0) }}
then:
- action: climate.set_temperature
target:
entity_id: "{{ wiser_device }}"
data:
target_temp_high: "{{ states(dynamic_set_point)|float(0) }}"
target_temp_low: 10
- alias: Set temp if no passive mode
if:
- condition: template
value_template: "{{ is_state(passive_mode_switch, 'off') }}"
- condition: template
value_template: >-
{{ not is_state(wiser_device, 'off') and not is_state(template_device, 'off') }}
- condition: template
value_template: >-
{% set nearest_5 = (((state_attr(wiser_device, 'temperature')|float(0)/5) | int) +1) * 5 %}
{{ nearest_5 != states(dynamic_set_point)|float(0) }}
then:
- action: climate.set_temperature
target:
entity_id: "{{ wiser_device }}"
data:
temperature: "{{ states(dynamic_set_point)|float(0) }}"
set_hvac_mode:
- alias: Set Wiser HVAC Mode
action: climate.set_hvac_mode
target:
entity_id: "climate.wiser_livingroom"
data:
hvac_mode: "{{ hvac_mode }}"
set_preset_mode:
- variables:
template_device: "climate.template_livingroom_radiator"
device_presets: "{{ state_attr(template_device, 'presets') }}"
dynamic_set_point: "sensor.template_livingroom_dynamic_setpoint"
- if:
- condition: template
value_template: "{{ preset_mode != 'none' }}"
then:
- action: climate.set_hvac_mode
target:
entity_id: "climate.wiser_livingroom"
data:
hvac_mode: "{{ device_presets[preset_mode].hvac_mode }}"
- action: climate.set_temperature
target:
entity_id: "climate.wiser_livingroom"
data:
temperature: "{{ states(dynamic_set_point)|float(0) }}"
- action: input_select.select_option
metadata: {}
data:
option: "{{ preset_mode | title }}"
target:
entity_id: "input_select.template_livingroom_climate_preset_modes"
My goal is to eventually bundle all this into the hass-climate-template, or at least what I can but whilst I figure it out itās just easier to have it in automations/templates. Iāll probably write all this up on my blog over christmas but for now thatās the best I can muster with the state itās currently in. The automations are just triggering on my mode and room changes and constantly applying the correct offset so it survives restarts and any changes. The dynamic one the same pretty much, anytime the dynamic temperature updates, it sets the new target to the wiser device.
This sill enables all the wiser features, inc. passive mode and I can also manually control everything with the thermostat cards on the inteface.