Skip to content

Instantly share code, notes, and snippets.

@clausd84
Forked from sanderma/tado_temp_offset.yaml
Last active September 29, 2025 10:52
Show Gist options
  • Select an option

  • Save clausd84/d9f8d2fcf4f74860f024379edbcaad3c to your computer and use it in GitHub Desktop.

Select an option

Save clausd84/d9f8d2fcf4f74860f024379edbcaad3c to your computer and use it in GitHub Desktop.
Homeassistant blueprint to set Tado offset using separate temperature sensor
blueprint:
name: Tado temperature offset v1.5.2
description: |
Ensure the Tado smart valve aligns with the temperature of a separate sensor.
Adjusts the offset only when the difference exceeds ±0.5°C and incorporates hysteresis.
Changelog:
- v1.4: Initial implementation to adjust the Tado offset based on an external sensor.
- v1.4.1: Adjusted triggers to apply a 5-minute delay for state changes to reduce sensitivity to transient fluctuations.
- v1.4.2: Further ensured the state persists for 5 minutes to reduce unnecessary trigger activations and improve stability.
- v1.4.3: Adjusted triggers to apply a 10-minute delay for state changes to reduce sensitivity to transient fluctuations.
- v1.4.4: Added timestamps to debug logs and optimised time pattern triggers for clarity.
- v1.4.5: Removed time-based trigger and relied solely on state changes for better efficiency.
- v1.4.6: State persists for 10 minutes, reduced to 5 minutes improve temperature stability.
- v1.4.7: Added hysteresis condition to prevent minor adjustment triggers and combined offset adjustment conditions.
- v1.4.8: Prevented redundant offset updates and added additional hysteresis checks to the action block.
- v1.4.9: Made the hysteresis threshold configurable for user flexibility.
- v1.5.0: Added configurable persistence times for both sensors.
- v1.5.1: Replaced single quotes with double to test if template is rendered differently, resulting in logic only triggering service call to tado.set_climate_temperature_offset when conditions met and removing erroneous updates.
- v1.5.2: Cleaned up logging to reduce overhead & reduce duplicate logging.
domain: automation
input:
source_temp_sensor:
name: Source Temperature Sensor
description: This sensor will be used as the source.
selector:
entity:
domain: sensor
device_class: temperature
multiple: false
target_tado:
name: Tado
description: The Tado to set the offset on.
selector:
entity:
domain: climate
multiple: false
hysteresis_threshold:
name: Hysteresis Threshold
description: Minimum difference between calculated and current offset to trigger an adjustment.
default: 0.1
selector:
number:
min: 0.0
max: 1.0
step: 0.01
persistence_time_target:
name: Persistence Time for Target Tado
description: Time in minutes the Tado's current temperature must persist before triggering.
default: 5
selector:
number:
min: 1
max: 60
step: 1
persistence_time_source:
name: Persistence Time for Source Sensor
description: Time in minutes the source sensor's temperature must persist before triggering.
default: 5
selector:
number:
min: 1
max: 60
step: 1
variables:
target_tado: !input target_tado
source_temp_sensor: !input source_temp_sensor
hysteresis_threshold: !input hysteresis_threshold
persistence_time_target: !input persistence_time_target
persistence_time_source: !input persistence_time_source
# Fetch the current temperature from the Tado device, defaulting to None if unavailable
tado_temp: "{{ state_attr(target_tado, 'current_temperature') | float(0) if state_attr(target_tado, 'current_temperature') is not none else 0 }}"
# Fetch the current offset value applied in the Tado device
current_offset: "{{ state_attr(target_tado, 'offset_celsius') | float(0) }}"
# Fetch the actual temperature from the external source sensor
actual_temp: "{{ states(source_temp_sensor) | float(0) }}"
# Calculate the temperature difference between the source sensor and Tado device, rounded to 1 decimal place
offset: "{{ ( actual_temp - tado_temp ) | round(1) }}"
# Determine the new offset by adjusting the current offset based on the temperature difference
calculated_offset: "{{ ( ( actual_temp - tado_temp ) + current_offset ) | round(1) }}"
trigger:
- platform: state
# Trigger when the Tado's current temperature changes and persists for the configured duration
entity_id: !input target_tado
attribute: current_temperature
for:
minutes: !input persistence_time_target
- platform: state
# Trigger when the external sensor's temperature changes and persists for the configured duration
entity_id: !input source_temp_sensor
for:
minutes: !input persistence_time_source
condition:
# Condition 1: Ensure the absolute temperature difference (offset) is significant (>= 0.5°C)
- condition: template
value_template: "{{ offset | abs >= 0.5 }}"
# Condition 2: Ensure the new offset differs meaningfully from the current offset (hysteresis)
- condition: template
value_template: "{{ (calculated_offset - current_offset) | abs >= hysteresis_threshold }}"
# Condition 3: Ensure the source sensor provides a valid temperature (not zero)
- condition: template
value_template: "{{ actual_temp != 0 }}"
action:
- service: system_log.write
data:
# Log when a trigger occurs due to state changes in Tado or the source sensor
message: |
[{{ now() }}] Triggered by Tado temp or source sensor state change.
Tado sensor: {{ target_tado }}
Tado temp: {{ tado_temp }}
Source sensor: {{ source_temp_sensor }}
Actual (source) temp: {{ actual_temp }}
Current offset: {{ current_offset }}
Calculated offset: {{ calculated_offset }}
Offset (difference): {{ offset }}
Hysteresis threshold: {{ hysteresis_threshold }}
Persistence time (target): {{ persistence_time_target }} minutes
Persistence time (source): {{ persistence_time_source }} minutes
level: debug
logger: blueprints.tado.offset
- choose:
- conditions:
# Ensure the offset adjustment is necessary (offset is not zero)
- condition: template
value_template: "{{ offset != 0 }}"
# Condition 1: Ensure the absolute temperature difference (offset) is significant (>= 0.5°C)
- condition: template
value_template: "{{ offset | abs >= 0.5 }}"
# Ensure the calculated offset differs sufficiently from the current offset (hysteresis logic)
- condition: template
value_template: "{{ (calculated_offset - current_offset) | abs >= hysteresis_threshold }}"
# Ensure the calculated offset is not redundant
- condition: template
value_template: "{{ calculated_offset != current_offset }}"
sequence:
- service: system_log.write
data:
# Log that an offset adjustment is being applied
message: >
[{{ now() }}] Applying offset adjustment:
Tado: {{ target_tado }}
Temp difference: {{ offset }}
New offset: {{ calculated_offset }}
Hysteresis threshold: {{ hysteresis_threshold }}
Persistence time (target): {{ persistence_time_target }} minutes
Persistence time (source): {{ persistence_time_source }} minutes
level: info
logger: blueprints.tado.offset
- service: tado.set_climate_temperature_offset
data:
# Apply the calculated offset to the Tado device only if it passes a final validation
offset: >
{{ calculated_offset if (calculated_offset != current_offset and (calculated_offset - current_offset) | abs >= hysteresis_threshold) else current_offset }}
entity_id: "{{ target_tado }}"
default:
- service: system_log.write
data:
# Log when no significant offset adjustment is necessary
message: |
[{{ now() }}] No significant offset adjustment necessary:
Tado: {{ target_tado }}
Offset: {{ offset }}
Current offset: {{ current_offset }}
Hysteresis threshold: {{ hysteresis_threshold }}
Persistence time (target): {{ persistence_time_target }} minutes
Persistence time (source): {{ persistence_time_source }} minutes
level: info
logger: blueprints.tado.offset
- service: system_log.write
data:
# Log the completion of the automation execution
message: |
[{{ now() }}] Automation execution complete.
Tado entity: {{ target_tado }}
Source sensor: {{ source_temp_sensor }}
Hysteresis threshold: {{ hysteresis_threshold }}
Persistence time (target): {{ persistence_time_target }} minutes
Persistence time (source): {{ persistence_time_source }} minutes
level: debug
logger: blueprints.tado.offset
mode: single
@clausd84
Copy link
Author

clausd84 commented Jan 2, 2025

Credit to @sanderma for the base of this blueprint

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment