home-assistant / core

:house_with_garden: Open source home automation that puts local control and privacy first.
https://www.home-assistant.io
Apache License 2.0
73.07k stars 30.57k forks source link

Trigger-based template binary sensor incorrectly restarts `delay_on` and `delay_off` #124831

Open mekaneck opened 2 months ago

mekaneck commented 2 months ago

The problem

The delay_on and delay_off options for a trigger-based template binary sensor will restart the delay countdown after each trigger. If the trigger continues to fire after the template renders a new value, the state of the senor does not change until there has been a pause in the triggers long enough to finish the delay.

The image below shows two sensors; a state-based template sensor and a trigger-based template sensor, both configured with delay_on: '0:00:20'. The state of both are set to change to on (after the delay) when the Test entity (graph shown) is greater than zero. The YAML code for the sensors is included in the YAML section further down in this issue.

The state-based sensor turns ON 20 seconds after the value crosses zero. This is the desired behavior. The trigger-based sensor turns ON 20 seconds after the value stops changing, after crossing zero.

image

What version of Home Assistant Core has the issue?

core-2023.8.3

What was the last working version of Home Assistant Core?

No response

What type of installation are you running?

Home Assistant Container

Integration causing the issue

Template

Link to integration documentation on our website

https://www.home-assistant.io/integrations/template/

Diagnostics information

No response

Example YAML snippet

template:
  - trigger:
      - platform: state
        entity_id: input_number.test
    binary_sensor:
      - name: Delay Test with Trigger-Based sensor
        state: "{{ trigger.to_state.state | int(0) > 0 }}"
        delay_on: "0:00:20"
  - binary_sensor:
      - name: Delay Test with State-Based sensor
        state: "{{ states('input_number.test') | int(0) > 0 }}"
        delay_on: "0:00:20"

Anything in the logs that might be useful for us?

No response

Additional information

No response

home-assistant[bot] commented 2 months ago

Hey there @phracturedblue, @tetienne, @home-assistant/core, mind taking a look at this issue as it has been labeled with an integration (template) you are listed as a code owner for? Thanks!

Code owner commands Code owners of `template` can trigger bot actions by commenting: - `@home-assistant close` Closes the issue. - `@home-assistant rename Awesome new title` Renames the issue. - `@home-assistant reopen` Reopen the issue. - `@home-assistant unassign template` Removes the current integration label and assignees on the issue, add the integration domain after the command. - `@home-assistant add-label needs-more-information` Add a label (needs-more-information, problem in dependency, problem in custom component) to the issue. - `@home-assistant remove-label needs-more-information` Remove a label (needs-more-information, problem in dependency, problem in custom component) on the issue.

(message by CodeOwnersMention)


template documentation template source (message by IssueLinks)

mekaneck commented 1 month ago

I noticed that even the comments in the code suggest this bug is present.

For a state-based binary template sensor, the comment is

Cancelled if template result changes

For a trigger-based binary template sensor, the comment is

Cancelled if new trigger received

The trigger-based binary sensor delay should only cancel if the template result changes, just like the state-based version does.

rrooggiieerr commented 1 month ago

In general, the state trigger fires when the state of any of given entities changes. The behavior is as follows:

See: https://www.home-assistant.io/docs/automation/trigger#state-trigger

You probably want to use a numerical state trigger: https://www.home-assistant.io/docs/automation/trigger#numeric-state-trigger

mekaneck commented 1 month ago

I used the state trigger in this example because a trigger-based template sensor configured with a state trigger should behave exactly the same as a state-based template sensor, so the comparison is easy and direct. The delay should not be restarted when a new trigger is fired as long as the template continues to render the same value. This is (or at least is supposed to be) true for both trigger-based and state-based sensors.

I agree that, for the provided example, the bug could be worked around by specifying two numeric triggers (one above and one below). It does make it more complicated but it can be done. However, I'm not trying to solve the problem in the example; I'm trying to illustrate the bug.

All that notwithstanding, I'm happy to demonstrate the same bug exists with a numeric state trigger, though I have to create a new example:

Let's say we want a binary sensor that is true when either input_number.test_a > 0 or input_number.test_b > 0, and we want a turn on "debounce" of 20 seconds. So we can use the following code to create the two different versions of this sensor, the trigger-based one and the state-based one:

  - trigger:
      - platform: numeric_state
        entity_id: input_number.test_a
        above: 0
      - platform: numeric_state
        entity_id: input_number.test_b
        above: 0
      - platform: numeric_state
        entity_id: input_number.test_a
        below: 0.1
      - platform: numeric_state
        entity_id: input_number.test_b
        below: 0.1
    binary_sensor:
      - name: "Test Delay: Numeric Trigger"
        state: "{{ (states('input_number.test_a') | float > 0) or (states('input_number.test_b') | float > 0) }}"
        delay_on: "0:00:20"
  - binary_sensor:
      - name: "Test Delay: State"
        state: "{{ (states('input_number.test_a') | float > 0) or (states('input_number.test_b') | float > 0) }}"
        delay_on: "0:00:20"

This is the result: image

rrooggiieerr commented 1 month ago

The delay should not be restarted when a new trigger is fired as long as the template continues to render the same value.

I'm actually not so sure about this statement. The documentation states:

Whenever the trigger fires, all related entities will re-render

https://www.home-assistant.io/integrations/template/#trigger-based-template-binary-sensors-buttons-images-numbers-selects-and-sensors

I'm not sure if the delay_off is considered a related entity, the documentation is not clear on that, but if it is this would explain the behaviour you're experiencing.

mekaneck commented 1 month ago

I think you're stretching a bit too far on this. It shouldn't matter whether the delays are considered related entities and are rendered again or not, their definition is pretty clear:

delay_off The amount of time the template state must be not met before this sensor will switch to off.

The time at which the template starts being not met starts with the first trigger that causes the template to render false.

I'll pause here because I think we should be able to agree up to this point?

Subsequent re-renders of the template that continue to render false don't change that starting time, per the documentation.

So whether or not the delay is re-calculated, it should be calculated from the same starting time. Hence my statement that it is irrelevant whether it is a related entity that is rendered again.

It should also be quite clear that no difference in behavior regarding the delay should be expected or desired between the trigger-based or state-based template sensors.