Templating help required (checking state changes in seconds)

Hi there.
I need some help for a template sensor.
I have the below garage sensor, which works great.

    sensor:  
    - name: Garage Status
      unique_id: Garage_status
      icon: mdi:garage-variant
      state: >
        {% if (states('binary_sensor.garage_closed') == 'on' and states('binary_sensor.garage_open_debounced') == 'off') %}
          closed      
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_closed') %}
          opening
        {% elif (states('binary_sensor.garage_open_debounced') == 'on' and states('binary_sensor.garage_closed') == 'off') %}
          open
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_open_debounced') %}
          closing
        {% else %}
          unknown
        {% endif %}

Now sometimes, there is something in the way (physically) and the garage wont close of course. It will stop at the position and HA will in this state report closing indefinitely.
Since the binary sensors will both have the state “off” for time it takes for opening/closing (~20 seconds), i wanted to add the condition that a state change for either opening or closing must have had happen in the last 30s, and for the opposite binary sensor, it must be longer than 30s. I’m unable to get this work, as soon as I add that condition, the sensor becomes unavailable when opening or closing. When finally the garage is closed/open, its back to the “normal” state. Has anyone a hint? The portion with the time check does not work…

This is my adapted sensor code:

    sensor:  
    - name: Garage Status
      unique_id: Garage_status
      icon: mdi:garage-variant
      state: >
        {% if (states('binary_sensor.garage_closed') == 'on' and states('binary_sensor.garage_open_debounced') == 'off') %}
          closed      
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_closed' and (as_timestamp(now()) - as_timestamp(states.binary_sensor.garage_closed.last_changed)) < 30 and (as_timestamp(now()) - as_timestamp(states.binary_sensor.garage_open_debounced.last_changed)) > 30) %}
          opening
        {% elif (states('binary_sensor.garage_open_debounced') == 'on' and states('binary_sensor.garage_closed') == 'off') %}
          open
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_open_debounced' and (as_timestamp(now()) - as_timestamp(states.binary_sensor.garage_closed.last_changed)) > 30 and (as_timestamp(now()) - as_timestamp(states.binary_sensor.garage_open_debounced.last_changed)) < 30) %}
          closing 
        {% else %}
          unknown
        {% endif %}

Thank you so much!

Without digging too deep in the conditions themselves:

You are not posting the full code. As you use a trigger variable, I assume it is a trigger based template sensor? If it is, the templates will only ever be evaluated when the trigger happens, at the time it happens. You will maybe be able to test when the trigger happens how long the previous state has been, but not after a state has held for some time. So the ‘longer’ will never happen because there is no trigger. If the trigger is time, the entity id will not be set.

But last but not least, by adding extra conditions, but no or and no extra elifs, you will likely have multiple conditions where the last else will trigger. Because due to the extra conditions, fewer times you’ll have defined states.

Thanks for the hints and advice, I appreciate it
.
Yes thats right, its a trigger based sensor:

- trigger:
    - platform: state
      entity_id:
        - binary_sensor.garage_closed
        - binary_sensor.garage_open_debounced

Looks like this wont be as easy as I initially thought. My workaround would be to create another sensor, i.e. a switch, which will be turned on as soon as I detect I pressed the button for closing, but the garage never closed. (I already have a push notification for that)
That could then be put into another elif of this sensor.

Are there other ways to check state_changes with time consideration, would you by any chance have a more elegant idea? :slight_smile:

Thank you

I forgot you can also add a trigger with a for clause for the delay. Then you’ll get to the template at the time you want, and you can simply test for the trigger id to know time has passed.

But first of all, I’d use a template cover to emulate the garage door, so you’ll have the full experience. It’s state template is evaluated like any other template sensor, but it would also be hard to do what you do above. So you might want to use your trigger based sensor to prepare the state for it.

I am a bit unclear what you’ll do when the door is stuck though. I would keep it in opening or closing just for the sake of detecting it is stuck. If it is in that state too long, you know there’s something wrong.

As for more elegant solutions, there is an integration or blueprint out there that estimates the percentage ooen based on time, but I don’t remember what it is called. If you search, it might be just what you want.

You are correct, i could simply leave it on “closing” or “opening” and I know something is wrong. Thats how it is working today - I just had the idea to change the state after a certain amount of time for easy of use…

Your approach is interesting.
To get this right, the interesting portion of my sensor is the “closing” part:

{% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_open_debounced') %}
          closing

Did I understand this correctly, you would first check the normal closing and then re-evaluate 30seconds later again? Something like this?

- trigger:
    - platform: state
      entity_id:
        - binary_sensor.garage_open_debounced
        - binary_sensor.garage_open_debounced
          delay_off:
            seconds: 30

delay off is for binary sensors. They will turn off after that time. I was referring to an exttra trigger, besides the one you already had, with a for clause. That will fire if the state is as requested for the given time. So for instance when the state is closing or opening for x time. If you give that trigger an id, you can test for it in the template and set the state to either closed or open as you wish.

I see, so it should be something like this I reckon?

  - trigger:
    - platform: state
      entity_id:
        - binary_sensor.garage_closed
        - binary_sensor.garage_open_debounced
        - binary_sensor.garage_closed
      for:
        seconds: 30
      id: "opening-check"
    sensor:  
    - name: Garage Status
      unique_id: Garage_status
      [config of the rest of the sensor]

As soon as I do this, all 3 triggers only fire after 30 seconds. The for clause cant be only for the last of these three triggers, is that right? I was playing around with indentation but that does not work.

Yes, the for clause would require the state to be stable for 30 secs.My suggestion was to add this in addition to the triggers you already had, not as the sole triggers. You can have multiple triggers.

In fact, I would trigger on the garage status itself being opening or closing, not on the end switches. So, if the state is closing too long, set it closed. If the state is opening too long, set it to open.

  - trigger:
    - platform: state
      entity_id:
        - binary_sensor.garage_closed
        - binary_sensor.garage_open_debounced
    - platform: state
      entity_id: sensor.garage_status
      to: 
        - closing
        - opening
      id: timeout
      for:
        seconds: 30
    sensor:
    - name: Garage Status
      unique_id: Garage_status
      state: |
        {% if trigger.id == 'timeout' %}
         {{ 'closed' if trigger.to_state.state == 'closing' else 'open' }}
        {% elif (states('binary_sensor.garage_closed') == 'on' and states('binary_sensor.garage_open_debounced') == 'off') %}
          closed      
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_closed') %}
          opening
        {% elif (states('binary_sensor.garage_open_debounced') == 'on' and states('binary_sensor.garage_closed') == 'off') %}
          open
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_open_debounced') %}
          closing
        {% else %}
          unknown
        {% endif %}
1 Like

I already have tow triggers and those work:

  - trigger:
    - platform: state
      entity_id:
        - binary_sensor.garage_closed
        - binary_sensor.garage_open_debounced

How could I add a third trigger but only that single trigger should fire after 30s? (The state is stable for 30s when the garage is stuck)

I edited in the full example above while you answered.

You were thinking of adding more entities to the same trigger, I was talking about a whole new trigger.

Thank you so much. I worked it out with your help, awesome.

Here is my final solution (I also needed to reorder the elifs because they fire only on changes):

  - trigger:
    - platform: state
      entity_id:
        - binary_sensor.garage_closed
        - binary_sensor.garage_open_debounced
    - platform: state
      entity_id:
        - binary_sensor.garage_closed
      for:
        seconds: 30
      id: "opening-check"
    - platform: state
      entity_id:
        - binary_sensor.garage_open_debounced
      for:
        seconds: 30
      id: "closing-check"
    sensor:  
    - name: Garage Status
      unique_id: Garage_status
      icon: mdi:garage-variant
      state: >
        {% if (states('binary_sensor.garage_closed') == 'on' and states('binary_sensor.garage_open_debounced') == 'off') %}
          closed
        {% elif (states('binary_sensor.garage_open_debounced') == 'on' and states('binary_sensor.garage_closed') == 'off') %}
          open
        {% elif (trigger.id == 'opening-check' and states('binary_sensor.garage_open_debounced') == 'off') %}
          unknown
        {% elif (trigger.id == 'closing-check' and states('binary_sensor.garage_closed') == 'off') %}
          unknown
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_closed') %}
          opening
        {% elif (trigger.to_state.state == 'off' and trigger.from_state.state == 'on' and trigger.entity_id == 'binary_sensor.garage_open_debounced') %}
          closing
        {% else %}
          unknown
        {% endif %}