Skip to content

Instantly share code, notes, and snippets.

@Basanites
Forked from marciogranzotto/sync_lights.yaml
Last active November 14, 2024 13:59
Show Gist options
  • Select an option

  • Save Basanites/094e140c29ead05e92de31aaef5312a6 to your computer and use it in GitHub Desktop.

Select an option

Save Basanites/094e140c29ead05e92de31aaef5312a6 to your computer and use it in GitHub Desktop.
Home Assistant Automation Blueprint for syncing the setpoint of multiple TRVs
blueprint:
name: Sync TRV setpoints
description: Sync the temperature setpoints of two TRVs. If one of them is changed,
the other one gets updated automatically to the new value. Can also be used to
sync the setpoint of a versatile thermostat valve opening entity to its physical
counterpart.
domain: automation
input:
trvs:
name: TRVs + climate
description: TRVs and climate objects to sync the setpoint temperature for
selector:
entity:
filter:
domain: climate
multiple: true
variables:
trvs: !input trvs
trigger:
- platform: state
entity_id: !input trvs
attribute: temperature
condition:
- condition: template
value_template: >
{% set temps = states | selectattr('entity_id', 'in', trvs) | map(attribute='attributes.temperature') | map('float') | list %}
{{ temps | max != temps | min }}
action:
- service: climate.set_temperature
data:
temperature: '{{ trigger.to_state.attributes.temperature | float }}'
target:
entity_id: >
{{ trvs | reject('eq', trigger.entity_id) | list }}
@hilburn
Copy link

hilburn commented Nov 3, 2024

I don't think it should be too bad
When we create the Valve Thermostat just need to go "up" to the device level and search for climate entities for each of the valves and start listening to the set temperature changing on any of them.
On temperature change detection -> propagate to all the other ones, and then up to the Valve Thermostat

@Johnson145
Copy link

Johnson145 commented Nov 13, 2024

I debugged a little further: I think most of my issues are based on the delay configured in the blueprint. I often change the temperature within the delay (default 5s I think) and than the final temperature change doesn't get synced. If I recognized that the numbers are out of sync, but the VT is correct, I tried to sync by applying any change and then reverting that change. The latter is usually again too fast and will just yield another sync problem...

I'm not sure whether I completely understood why you have introduced the delay. I can imagine that initially the two thermostats were triggering each other in an endless loop? If that was the problem, what do you think of replacing the delay with a check that ensures that climate.set_temperature will only get called, if there is actually a difference?

I've tried to modify the blueprint accordingly and at least for me it seems to work much better that way.

It also has a nice side effect: My original climate entity seems to accept only temperature steps of .5 degrees. My VT is configured to allow .1 steps though. That was causing another inconsistency before. With my proposed change, another sync will get triggered automatically to reflect the .5 change back to VT.

Did I miss another problem for which I would need the delay?


This is my current draft:

blueprint:
  name: Sync TRV setpoints
  description: Sync the temperature setpoints of two TRVs. If one of them is changed,
    the other one gets updated automatically to the new value. Can also be used to
    sync the setpoint of a versatile thermostat valve opening entity to its physical
    counterpart.
  domain: automation
  input:
    trvs:
      name: TRVs + climate
      description: TRVs and climate objects to sync the setpoint temperature for
      selector:
        entity:
          filter:
            - domain:
                - climate
          multiple: true
  source_url: https://gist.github.com/Basanites/094e140c29ead05e92de31aaef5312a6
variables:
  trvs: !input trvs
trigger:
  - platform: state
    entity_id: !input trvs
    attribute: temperature
condition:
  - condition: template
    value_template: >
      {% set temps = states | selectattr('entity_id', 'in', trvs) | map(attribute='attributes.temperature') | map('float') | list %}
      {{ temps | max != temps | min }}
action:
  - service: logbook.log
    data:
      name: "Sync TRV setpoints"
      message: >
        Triggered entity: {{ trigger.entity_id }}, Current temperature: {{ state_attr(trigger.entity_id, 'temperature') | float }}, New target temperature: {{ trigger.to_state.attributes.temperature | float }}
  - service: climate.set_temperature
    data:
      temperature: "{{ trigger.to_state.attributes.temperature | float }}"
    target:
      entity_id: "{{ trvs | reject('eq', trigger.entity_id) | list }}"

Added logging for debugging, too.

@Basanites
Copy link
Author

Thanks for looking into this @Johnson145!

I'm not sure whether I completely understood why you have introduced the delay. I can imagine that initially the two thermostats were triggering each other in an endless loop?

Yes, that is about it, they were flip-flopping back and forth until one of them "won" which wasn't necessarily the temperature you were trying to set to.

If that was the problem, what do you think of replacing the delay with a check that ensures that climate.set_temperature will only get called, if there is actually a difference?

I had that in the version before the delay update, but it was not working consistently for some reason. I suspected, that the zigbee state get, returned stale data and because of that triggered another update when there was none to be made. The delay was the quick and dirty fix, but since you encountered other issues, that does not seem like a stable solution either.

Anyways, the current implementation basically does that check anyways, so I will do some testing myself and see if I can reproduce my previous issues. If not, I will update the blueprint accordingly.

Thanks for showing me the logging service by the way! I used to look at the traces all the time, but this might be more convenient.

@Johnson145
Copy link

I had that in the version before the delay update,

Ah interesting, didn't check your older revisions before.

but it was not working consistently for some reason

I may be wrong, but to me it looks like your previous implementation was not using the temperature contained in the triggered automation, but instead requested the current temperature of the device again. I mean not for changing the temperature, but for comparing the two values. I can imagine that approach was open to race conditions because of that?

@Basanites
Copy link
Author

Basanites commented Nov 14, 2024

That definitely sounds plausible.
I would have expected the selectattr | map(attribute='attributes.temperature') to basically be equivalent to state_attr(entity, 'temperature') though, so it would still do the same now, not sure.

Maybe it was also some wonkiness that got introduced from VT and is now fixed 🤷 . Anyways, glad it works consistently now :)

@Johnson145
Copy link

I would have expected the selectattr | map(attribute='attributes.temperature') to basically be equivalent to state_attr(entity, 'temperature') though, so it would still do the same now, not sure.

It should. Now I'm confused, too. I tried quite a few different scripts yesterday. Looks like I shared not the one I wanted to share. My previous version contained a condition like this:

- condition: template
  value_template: >
    {% set current_temp = state_attr(trigger.entity_id, 'temperature') | float %}
    {{ current_temp != trigger.to_state.attributes.temperature | float }}

So my reasoning was that trigger.to_state.attributes.temperature instead of using state_attr twice prevents the race condition.

As I didn't actually include that change in the version I sent to you, I don't have an explanation why it may have fixed the problem... I'll double check which variant I'm actually using again.

Maybe it was also some wonkiness that got introduced from VT and is now fixed 🤷

Maybe it's just that...

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