Tado Integration - Put Offset

@Noorbertt yep I had success… Wrote the code, my PR has been accepted and merged. Assume that means it will come in next HA release. Feel free to install as a custom_component now to test out:

1 Like

Unfortunately I don’t know how to integrate this. I have to wait for updates.
Thank you your work:) !

@Noorbertt you just ad a folder called ‘custom_components’ under /config and drop the Tado folder from git in there and restart HA.

HA will look in custom_components before its own components. Means you can try new features like this. Then when you want to revert just delete the folder and restart HA

If not, just wait, it will be there soon I expect based on the fact its merged

Thank you! It’s just working nice :slight_smile:

Could you check my automation please?

alias: Tado bedroom offset
description: ''
trigger:
  - platform: state
    entity_id:
      - sensor.ble_temperature_bedroom_xiaomi
      - sensor.bedroom_temperature
condition:
  - condition: template
    value_template: >
      {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
      room_temp = states('sensor.ble_temperature_bedroom_xiaomi')|float %} {{
      (tado_temp - room_temp) > 0.5 }}
action:
  - service: tado.set_climate_temperature_offset
    data:
      entity_id: climate.bedroom
      offset: >
        {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
        room_temp = states('sensor.ble_temperature_bedroom_xiaomi')|float %} {%
        set current_offset = state_attr('climate.bedroom', 'offset_celsius') %}
        {{ (-(tado_temp - room_temp) + current_offset)|round }}
mode: single

The tado sensor always show less temperature.
But if I change the condition like this: tado_temp - room_temp) < 0.5 the offset is triggered very often.

Yeah I must admit that example template I put in the docs isn’t the best, it only looks to change the temp offset if the tado thermostat is higher, meaning you always get the warmer temp. I’ll update the docs when I do my next pr. for now try this as your base:

# Example automation to set temprature offset based on another thermostat value
automation:
    # Trigger if the state of either thermostat changes
    trigger:
    - platform: state
      entity_id:
        - sensor.temp_sensor_room
        - sensor.tado_temperature
    
    # Check if the room temp is more than 0.5 different than the tado thermostat reading
    condition:
    - condition: template
      value_template: >
        {% set tado_temp = states('sensor.tado_temperature')|float %}
        {% set room_temp = states('sensor.temp_sensor_room')|float %}
        {{ (tado_temp - room_temp)|abs > 0.5 }}
    
    # Work out what the new offset should be (room temp less the tado temp but add the current offset value)
    action:
    - service: tado.set_climate_temperature_offset
      data:
        entity_id: climate.tado
        offset: >
          {% set tado_temp = states('sensor.tado_temperature')|float %}
          {% set room_temp = states('sensor.temp_sensor_room')|float %}
          {% set current_offset = state_attr('climate.tado', 'offset_celsius') %}
          {{ ((room_temp - tado_temp ) + current_offset)|round(1) }}

so four changes:

1 add ‘|abs’ to condition template (meaning we are checking the absolute variance, not just if higher)
2 remove the minus(-) in offset template
3 swap room and tado temp round in the offset template
4 add (1) to round, as may as well get better precision

I think that should work, let me know. I have a more complex one for doing every room in one automation but is dependant on a standard naming convention. Will share are some point

@Noorbertt so your templatee

Would become

alias: Tado bedroom offset
description: ''
trigger:
  - platform: state
    entity_id:
      - sensor.ble_temperature_bedroom_xiaomi
      - sensor.bedroom_temperature
condition:
  - condition: template
    value_template: >
      {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
      room_temp = states('sensor.ble_temperature_bedroom_xiaomi')|float %} {{
      (tado_temp - room_temp)|abs > 0.5 }}
action:
  - service: tado.set_climate_temperature_offset
    data:
      entity_id: climate.bedroom
      offset: >
        {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
        room_temp = states('sensor.ble_temperature_bedroom_xiaomi')|float %} {%
        set current_offset = state_attr('climate.bedroom', 'offset_celsius') %}
        {{ ((room_temp - tado_temp) + current_offset)|round(1) }}
mode: single

With your automation the bedroom’s tado went crazy (I had 30C and so :smiley: )

But with this looks okay for now:

alias: Tado bedroom offset
description: ''
trigger:
  - platform: state
    entity_id:
      - sensor.ble_temperature_bedroom_xiaomi
      - sensor.bedroom_temperature
condition:
  - condition: template
    value_template: >
      {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
      room_temp = states('sensor.ble_temperature_bedroom_xiaomi')|float %} {{
      (tado_temp - room_temp)|abs > 0.5 }}
action:
  - service: tado.set_climate_temperature_offset
    data:
      entity_id: climate.bedroom
      offset: >
        {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
        room_temp = states('sensor.ble_temperature_bedroom_xiaomi')|float %} {%
        set current_offset = state_attr('climate.bedroom', 'offset_celsius') %}
        {{ (-(tado_temp - room_temp) + current_offset)|round(1) }}
mode: single

Will see after a longest test

@Noorbertt that’s odd, but possibly cause I forgot to pipe current_offset to float… maybe with the minus sign it forces jinja2 to calculate properly… I dunno

Anyhow, I think I have the generic automation to handle all rooms working, looking at my dashboard they all seem to be staying in sync. I’ll do a bit more testing and share if interested?

2 Likes

@north3221 please do share. I have to confess that your initial python script works so well I have not change it to full home assistant automation yet :sweat_smile: However, I plan to change it when the integrations is officially updated!

Regards,

Here you go. Its pretty long but I did that to make it more readable and easier to maintain. Slightly annoying is needing to repeat variable setting. I don’t know of a way to use variables across templates in same script, so think I will look at helpers as global variables, but for now this will make it easier for people to copy and replicate. Remembered something about variables in the bday release, read it, turns out they are awesome… Updated the automation using variables, so much better!

Take a look at the note at the top:

# Regulate tado heating equipment based on external sensors. For this to work you need to use a standard naming convention for every tado/external thermostat pairing
# So each of them must be names the same i.e. 'bedroom'
# Then that should be followed by a consistent name space. I use the standard '_temperature' for tado and '_temperature_measurement' for external thermostat
# NB If you use something else you need to update the name space variables below
# NB It seems every time the offset is updated the device calls for heat (even if not needed so will then close again), this will drain the batteries faster
#    the tolerance is set at 0.2 here, which is quite low and will mean more calls to update offset, you may want to allow higher tolerance to help battery life
  - id: automation.regulate_heating
    alias: Regulate Heating
    mode: queued
    max: 25
    # List of all sensors to use to trigger (tado temp sensor and external temp sensor for each room)
    trigger:
    - platform: state
      entity_id:
        - sensor.house_temperature
        - sensor.house_temperature_measurement
        - sensor.bedroom_temperature
        - sensor.bedroom_temperature_measurement
        - sensor.guest_bedroom_temperature
        - sensor.guest_bedroom_temperature_measurement
        - sensor.back_bedroom_temperature
        - sensor.back_bedroom_temperature_measurement        
    variables:
        climate_ns: _temperature
        sensor_ns: _temperature_measurement
        tolerance: 0.2
        object_id: >
            {% set object_id = trigger.to_state.object_id %}
            {% if object_id.endswith(climate_ns) %}{% set object_id = object_id[:-(climate_ns|length)] %}
            {% elif object_id.endswith(sensor_ns) %}{% set object_id = object_id[:-(sensor_ns|length)] %}{% endif %}
            {{ object_id }}
        climate_id: "{{ 'climate.' + object_id }}"
        climate_sensor_id: "{{ 'sensor.' + object_id + climate_ns }}"
        sensor_id: "{{ 'sensor.' + object_id + sensor_ns }}"
        offset: "{{ ((states(sensor_id)|float - states(climate_sensor_id)|float) +state_attr(climate_id, 'offset_celsius')|float)|round(1) }}"
    condition:
    # Only run if temp states are a number
    - condition: template
      value_template: "{{ states(sensor_id)|int != 0 or states(sensor_id) == '0' }}"
    - condition: template
      value_template: "{{ states(climate_sensor_id)|int != 0 or states(climate_sensor_id) == '0' }}"
    # Only run if the climate device is set to 'auto' and preset is 'home'
    - condition: template
      value_template: >
        {{ states(climate_id) == 'auto' and state_attr(climate_id, 'preset_mode') == 'home' }}
    # Only run if there is a difference in temperature greater than the tolerance
    - condition: template
      value_template: > 
        {{ (states(sensor_id)|float - states(climate_sensor_id)|float)|abs > tolerance }}
        
    action:
    # Optional add it to logbook
    - service: logbook.log
      data:
          name: Regulate Heating
          message: >    
            {{ trigger.to_state.name }} temp was {{ states(climate_sensor_id) }} but room is {{ states(sensor_id) }}
            changing offset from {{ state_attr(climate_id, 'offset_celsius') }} to {{ offset }}
          entity_id: >
            {{ climate_id }}
          domain: climate
    # Set the offset
    - service: tado.set_climate_temperature_offset
      data:
        entity_id: "{{ climate_id}}"
        offset: "{{ offset }}"
    # Stop re triggers for a period allowing Tado Integration to refresh
    - delay: 20

Love your work, thank you!

One question,

Only run if the climate device is set to ‘auto’ and preset is ‘home’

Why? I don’t see the use for this condition.

Yeah remove if you like, I just don’t see the point in syncing if I am out or I turned the heating off. As I mentioned in the the radiator valves open and close every time the offset is updated, so figure may as well save battery when the temp being out of sync doesn’t matter

I was playing with this and I found a small issue.
If the external sensor going offline for some reason the automation can do crazy things:

There is a way to add some sort of safety switch?

@Noorbertt ahh not a problem I have had, good catch. Yeah makes sense to add some safety around that. Funny when I set it up I did toy with the idea of using numeric_state as the trigger instead of state, the issue is you have to also say above or below, so you could use numeric state above 0 as the trigger.

However, I prefer to keep that part as-is for now, so I added a condition to the original post checking that the state is numeric. That’s the best way I could come up with to check in the template, if someone has a better idea of validating its a number please shout up

Give it a go

1 Like

just for a feedback, with this:

# Regulate tado heating equipment based on external sensors. For this to work you need to use a standard naming convention for every tado/external thermostat pairing
# So each of them must be names the same i.e. 'bedroom'
# Then that should be followed by a consistent name space. I use the standard '_temperature' for tado and '_temperature_measurement' for external thermostat
# NB If you use something else you need to update the name space variables below
# NB It seems every time the offset is updated the device calls for heat (even if not needed so will then close again), this will drain the batteries faster
#    the tolerance is set at 0.2 here, which is quite low and will mean more calls to update offset, you may want to allow higher tolerance to help battery life
- id: automation.tado_offset
  alias: Tado offset
  mode: queued
  max: 25
  # List of all sensors to use to trigger (tado temp sensor and external temp sensor for each room)
  trigger:
    - platform: state
      entity_id:
        - sensor.bedroom_temperature
        - sensor.bedroom_temperature_measurement
  variables:
    climate_ns: _temperature
    sensor_ns: _temperature_measurement
    tolerance: 0.5
    object_id: >
      {% set object_id = trigger.to_state.object_id %}
      {% if object_id.endswith(climate_ns) %}{% set object_id = object_id[:-(climate_ns|length)] %}
      {% elif object_id.endswith(sensor_ns) %}{% set object_id = object_id[:-(sensor_ns|length)] %}{% endif %}
      {{ object_id }}
    climate_id: "{{ 'climate.' + object_id }}"
    climate_sensor_id: "{{ 'sensor.' + object_id + climate_ns }}"
    sensor_id: "{{ 'sensor.' + object_id + sensor_ns }}"
    offset: "{{ ((states(sensor_id)|float - states(climate_sensor_id)|float) +state_attr(climate_id, 'offset_celsius')|float)|round(1) }}"
  condition:
    # Only run if temp states are a number
    - condition: template
      value_template: "{{ states(sensor_id)|int != 0 or states(sensor_id) == '0' }}"
    - condition: template
      value_template: "{{ states(climate_sensor_id)|int != 0 or states(climate_sensor_id) == '0' }}"
    # Only run if the climate device is set to 'auto' and preset is 'home'
    - condition: template
      value_template: >
        {{ states(climate_id) == 'auto' and state_attr(climate_id, 'preset_mode') == 'home' }}
    # Only run if there is a difference in temperature greater than the tolerance
    - condition: template
      value_template: >
        {{ (states(sensor_id)|float - states(climate_sensor_id)|float)|abs > tolerance }}

  action:
    # Optional add it to logbook
    - service: logbook.log
      data:
        name: Tado offset
        message: >
          {{ trigger.to_state.name }} temp was {{ states(climate_sensor_id) }} but room is {{ states(sensor_id) }}
          changing offset from {{ state_attr(climate_id, 'offset_celsius') }} to {{ offset }}
        entity_id: >
          {{ climate_id }}
        domain: climate
    # Set the offset
    - service: tado.set_climate_temperature_offset
      data:
        entity_id: "{{ climate_id}}"
        offset: "{{ offset }}"
    # Stop re triggers for a period allowing Tado Integration to refresh
    - delay: 00:01:40

I recive this in the “long” term:
Screenshot 2021-02-03 04.12.58

So I have been playing about with this and there is a slight issue. I have it set to queue and a delay before each run to allow Tado integration to sync. However, from reading the docs and some testing, the only part that queues is the action. So the variables and conditions for run 2 happens before its put on the queue, which is no good.

Anyhow, I’ve created a more flexible automation for this, allowing you to choose settings per device etc and also I’ve updated my Tado integration code to track offset changes (will raise another PR), this allows for a back-off option (only run if not updated for x seconds kinda thing). I’ve shifted stuff to action, so it calculates at the appropriate time. Bit more testing and will share. I’ll see if I can do it as a blog post or something, make it a little easier to consume.

Cant wait, thank you! :star_struck:

in the meantime this is work okkay for me at the momet:

alias: Tado bedroom offset
description: ''
trigger:
  - platform: state
    entity_id:
      - sensor.bedroom_temperature_measurement
      - sensor.bedroom_temperature
condition:
  - condition: template
    value_template: >
      {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
      room_temp = states('sensor.bedroom_temperature_measurement')|float %} {{
      (tado_temp - room_temp)|abs > 0.3 }}
  - condition: state
    entity_id: climate.bedroom
    state: home
    attribute: preset_mode
action:
  - service: tado.set_climate_temperature_offset
    data:
      entity_id: climate.bedroom
      offset: >
        {% set tado_temp = states('sensor.bedroom_temperature')|float %} {% set
        room_temp = states('sensor.bedroom_temperature_measurement')|float %} {%
        set current_offset = state_attr('climate.bedroom', 'offset_celsius') %}
        {{ (-(tado_temp - room_temp) + current_offset)|round(1) }}
  - delay:
      hours: 0
      minutes: 5
      seconds: 0
      milliseconds: 0
mode: single

I’m new to all this, but could is number do the job?
I.E.

    # Only run if temp states are a number
    - condition: template
      value_template: "{{ states(sensor_id) is number }}"