State condition with 'for' triggering immediately instead of after the set time

Our boiler has been acting up lately so I’m trying to get home assistant to send me a notification when it needs attention. I’m using a Hive thermostat and wrote the following automation so far:

alias: Boiler on but not heating
description: ""
trigger:
  - platform: time_pattern
    minutes: /1
condition:
  - condition: state
    entity_id: climate.thermostat_1
    attribute: hvac_action
    state: heating
    for:
      hours: 1
      minutes: 20
      seconds: 0
action:
  - service: notify.mobile_app_iphone_13_pro
    data:
      message: >-
        Heating hive temp: {{ state_attr('climate.thermostat_1',
        'current_temperature') }}
mode: single

(I’m deliberately using a condition rather than a trigger, as usually the boiler does heat for a bit then stops working.)

The problem is that I get a notification as soon as boiler starts heating. The ‘for’ part of the condition doesn’t seem to do anything.

I’m using 2023.3.1.

The trace doesn’t really seem to give any clues, here’s the relevant bit:

Executed: 5 March 2023 at 17:34:00
Result:
result: true
## entity_id/0
Executed: 5 March 2023 at 17:34:00
Result:
result: true
state: heating
duration: '2023-03-05T16:14:00.368488+00:00'

although I’m not sure what “duration” is meant to show here - it seems to be showing the time the heating would have needed to be on (rather than the time the call for heat happened, which was only a few minutes prior).

Any ideas please?

The state must be continuous for the for time just like the trigger. So your attempt to work around this with a condition will not work.

Does the thermostat stay in a heating state when the boiler stopped heating? Your error condition isn’t very clear to me. Should the boiler be running all the time, or is 1h20 already a long time for it to be on?

Yes, it does - the thermostat stays calling for heat, the boiler just doesn’t actually do any heating as drops into an error code.

A picture probably helps - here you can see an that at the left of the chart the the thermostat as asking for heat, but the boiler isn’t actually heating - until I reset it at around 18:00 and the room starts heating :

The full logic I’m trying to implement is “send a notification if the thermostat has been calling for heat for over minutes and the temperature hasn’t started increasing”.

The yaml I shared only does part of this as I’ve cut it down to a minimal example of the part that’s not working - I’m stuck as the condition for “thermostat has been calling for heat for over minutes” is triggering immediately, resulting in the notification arriving before the boiler has even had a chance to heat the room.

Sorry, I guess I didn’t explain well.

The call for heat is continuous. It’s just the boiler triggers into a fault state and stops actually heating.

I’ve probably explained far too much - TL;DR is:

I setup a condition with a ‘for:’ clause and the condition returns true immediately on a state change, the ‘for’ clause is having no effect.

Post the graph of the history of climate.thermostat_1's hvac_action attribute (you will probably need to create the graph).

Does the graph you posted show that climate.thermostat_1 has been actively heating continuously from 2:00 to about 20:00?

If that’s not representing when hvac_action is heating then post a graph showing it.

Does the graph you posted show that climate.thermostat_1 has been actively heating continuously from 2:00 to about 20:00?

That’s correct. (Well, it’s been calling for heat - as you can see there wasn’t actually any actual increase in the temperature as the boiler needed a fault reseting which I did around 18:00.)

Conditions are immediate. They do not wait. You want wait_for_trigger if you want it to wait after your first trigger.

So wouldn’t that explain why the State Condition is instantly fulfilled? If climate.thermostat_1 has been actively heating for several hours, that would easily fulfill the State Condition’s requirement of “heating for at least 1 hour and 20 minutes”.

The notification happens instantly when the thermostat calls for heat. There’s literally no delay between hvac_action switching to heating and the notification arriving. So in the graph, the notification fired continuously from 01:00-01:20, 02:00-20:30, etc. I only expected it to start firing at around 03:20, 1 hour & 20 minutes after the thermostat starting calling for heat.

Conditions - Home Assistant has an example with a ‘for’ element documented with “optional: Evaluates to true only if state was this for last X time.”, that’s all I’m trying to use.

Right, but it resolves immediately. So if that condition is true (Heat’s been on for x minutes in the past) It will instantly resolve true. Otherwise it will instantly resolve false.

Agreed, what is describe is how I expected it would work.

That’s now how it actually works though - as soon as heat is requested, the condition evaluates to true. i.e. exactly the same behaviour you’d expect if there was no ‘for’. So in the graph shown the condition evaluates to true from when heat is requested around 02:00, rather than at 03:20 when it’s been requested for 1 hour and two minutes.

Right, but was it heating from 00:40 to 02:00? If yes, that’s why it passed.

No; it wasn’t heating from 00:40.

then that may be a bug with condition in combination w/ for when using an attribute.

Regardless, looking at your automation, you should really be using a trigger anyways.

alias: Boiler on but not heating
description: ""
trigger:
  - platform: state
    entity_id: climate.thermostat_1
    attribute: hvac_action
    to: heating
    for:
      hours: 1
      minutes: 20
      seconds: 0
action:
  - service: notify.mobile_app_iphone_13_pro
    data:
      message: >-
        Heating hive temp: {{ state_attr('climate.thermostat_1',
        'current_temperature') }}
mode: single

I think I see a possible issue; if you look at the relevant code:

and in particular:

    if attribute is None:
        value: Any = entity.state
    else:
        value = entity.attributes.get(attribute)

<....>

    duration = dt_util.utcnow() - cast(timedelta, for_period)
    duration_ok = duration > entity.last_changed
    condition_trace_set_result(duration_ok, state=value, duration=duration)
    return duration_ok

The ‘for’ in the condition looks only at entity.last_changed - ie. states.climate.thermostat_1.last_changed - rather than the time the attribute hvac_action changed.

Trying {{states.climate.thermostat_1.last_changed}} in the template editor gives me:

2023-03-05 19:40:33.369574+00:00

which is a few days ago. I think that part is as expected.

So if that’s right, ‘for:’ on a condition only works if you are not using an attribute value?

This was one of the reason I use a template sensor that tracks the hvac_action attribute…

During normal operation, the state of some of my climate entities is essentially static, so last_changed is useless for this purpose.

1 Like

That would be a bug, it should be using last_updated for attributes. But, even with that change, it wouldn’t work the way you expect because last_updated only reflects a change when any attribute updates. So unrelated attribute changes would cause it to fail potentially.

Your best off making this into a template sensor and using that in your condition (What @Didgeridrew is mentioning).

2 Likes

You might want to set up a Trend sensor or Threshold/Derivative combo. Then you can use something like:

trigger:
   - platform: state
     entity_id: sensor.thermo_1_hvac_action
     to: "heating"
     for: "00:30:00"
condition:
  - condition: state
    entity_id: binary_sensor.thermo_1_heating_trend
    state: 'on'
1 Like