Template "fires" when I didn't expect it to and unexpected "this.state" behavior

I have my garage door mostly working in HA (thanks to many helpful people!). Getting there has shown me that I don’t yet really understand how a template is “triggered” when it has no triggers and I’d like to understand that better. My apologies in advance if there’s a topic out there that addresses this but I couldn’t find it.

Here’s the entire contents of my templates_garage.yaml file. Everything before the first “else” seems to be working fine. Following that is a block of commented out code showing things that didn’t work.

- cover:
  - name: GarageDoor
    unique_id: garage_door
    device_class: garage
    state: >
      {% set garage_door_is_open = is_state( 'binary_sensor.garage_door_control_is_open', 'on') %}
      {% set garage_door_is_closed = is_state( 'binary_sensor.garage_door_control_is_closed', 'on') %}
      {% if garage_door_is_open and not garage_door_is_closed %}
        open
      {% elif not garage_door_is_open and garage_door_is_closed %}
        closed
      {% elif garage_door_is_open and garage_door_is_closed %}
        sensor error
      {% else %}
        {# {{ this }}         logs seem to show that "this" bounces between "open" and "unknown" #}
        {# {{ this.state }}   logs seem to show that "this.state" is always "opening" #}
        {# from the logs, "garage_door" is undefined #}
        {# garage_door.state #}
        {# from the logs, "cover" is undefined #}
        {# cover.garage_door       #}
        {# cover.garage_door.state #}
        {# cover.garagedoor        #}   {# the error logs sure do think this is a valid entity because that's what they use for error messages #}
        {# cover.garagedoor.state  #}
        {# garagedoor   from the logs, "garagedoor" is undefined in a slightly different way #}
        {# {% if ( this.state == 'open' ) %}                {#  1 says opening when it's actually closing #}
        {# {% if ( this.state == "open" ) %}                {#  2 says opening when it's actually closing #}
        {# {% if this.state == "open" %}                    {#  3 says opening when it's actually closing #}
        {# {% if ( garagedoor == 'open' ) %}                {#  4 says opening when it's actually closing #}
        {# {% if ( garagedoor.state == 'open' ) %}          {#  5 says unavailable when closing - I'm guessing this is a coding error #}
        {# {% if ( garage_door == 'open' ) %}               {#  6 says opening when it's actually closing #}
        {# {% if ( cover.garagedoor == 'open' ) %}          {#  7 says unavailable when it's actually closing #}
        {# {% if is_state( 'this.state', 'open' ) %}        {#  8 says opening when it's actually closing #}
        {# {% if is_state( this.state, 'open' ) %}          {#  9 says opening when it's actually closing #}
        {# {% if is_state( 'cover.garagedoor', 'open' ) %}  {# 10 this flashes to closing and then reverts to opening #}
        {# {% if is_state( 'this.state', 'open' ) or is_state( 'this.state', 'closing' ) %}  {#  11 says opening when it's actually closing #}
        {% if is_state( 'cover.garagedoor', 'open' ) or is_state( 'cover.garagedoor', 'closing' ) %}     {#  12  this works #}
          closing
        {% else %}
           opening
        {% endif %}
      {% endif %}
    open_cover:
      action: switch.turn_on
      target:
        entity_id: switch.plusunigarage_output_0
    close_cover:
      action: switch.turn_on
      target:
        entity_id: switch.plusunigarage_output_0
    stop_cover:
      action: switch.turn_on
      target:
        entity_id: switch.plusunigarage_output_0

My first question comes from the line with 10 in the comment. With this line active, the template evaluates to “closing” and then immediately changes to “opening”. I think this means the template is firing again immediately after changing to “closing”. My guess is that the template is firing again based on the fact that “cover.garagedoor” is referenced in the state: block. Can someone tell me if that’s correct?

Second question comes from the line with 11 in the comment. I expected this line to do exactly the same thing that the line 12 did but it doesn’t. What am I missing?

Yes, in a state-based Template entity the template is rendered whenever any of the entities it tracks update their state, so self-referencing can be problematic. That is one of the reasons why the this variable exists. But, this can be tricksy and it’s often better to just use a trigger-based design.


is_state( 'this.state', 'open' ) #Used in #8
is_state( this.state, 'open' ) #Used in #9
is_state( 'this.state', 'open' )   #Used in #11
is_state( 'this.state', 'closing') #Used in #11

Those are all nonsense… The function is_state() needs an entity ID string as the first argument and none of those are an entity ID. The this variable returns the previous state object of the entity; so this.state returns the state value. And, the string “this.state” is just a string that the Jinja engine doesn’t know what to do with because it’s not an entity ID.

The way to test the state value from the variable this is just a simple comparison:

this.state == 'open'

That is not a valid state for a cover entity. You can use the value none, which will cause the cover state to be unknown if it happens. Or, roll the two sensors being “on” into the availability template, which will cause the cover state to be unavailable.


This fix is to use a trigger-based Template cover:

- triggers:
    - id: open
      trigger: state
      entity_id: binary_sensor.garage_door_control_is_open
      to: 'on'
      from: 'off'
    - id: closing
      trigger: state
      entity_id: binary_sensor.garage_door_control_is_open
      from: 'on'
      to: 'off'
    - id: closed
      trigger: state
      entity_id: binary_sensor.garage_door_control_is_closed
      to: 'on'
      from: 'off'
    - id: opening
      trigger: state
      entity_id: binary_sensor.garage_door_control_is_closed
      from: 'on'
      to: 'off'
  cover:
    - name: GarageDoor
      unique_id: garage_door
      device_class: garage
      state: "{{ trigger.id }}"
      open_cover:
        action: switch.turn_on
        target:
          entity_id: switch.plusunigarage_output_0
      close_cover:
        action: switch.turn_on
        target:
          entity_id: switch.plusunigarage_output_0
      stop_cover:
        action: switch.turn_on
        target:
          entity_id: switch.plusunigarage_output_0
      availability: |
        {% set open_ent = 'binary_sensor.garage_door_control_is_open' %}
        {% set closed_ent = 'binary_sensor.garage_door_control_is_closed' %}
        {{ has_value(open_ent) and has_value(closed_ent) and
        not (is_state( open_ent, 'on') and is_state( closed_ent, 'on')) }}

I’d give this answer three thumbs-up if I could! It’s a very insightful reply.

First thumbs-up - switching the code to use triggers on transitions makes so much sense!

Second thumbs-up - I scratched my head and thought “that’s odd” when I saw that the triggers each had an id: that matched the state value that I would want to assign to the template. That sent me off to the documentation to read about trigger id’s.

Third thumbs-up - I laughed out loud when I saw state: “{{ trigger.id }}” and realized that the id: was being used to pass the updated state value from the trigger to the state. That’s really cool and as far as I can see, this use of trigger.id isn’t mentioned in the documentation.

It’s probably a very rare case that the door moves while HA isn’t running but I decided to put in some code to cover that possibility. Sadly, it broke the elegance of the single line state assignment.

Many thanks for the help!

The template documentation got a big overhaul earlier this year, so I would be surprised if it isn’t in there somewhere…

Where it’s appropriate, it’s a great way to create branched logic, without having to draw all the branches; keeping the automation concise and readable. And, the IDs don’t need to be unique like in this example, so you can have a handful of triggers with the ID “on” and a few with the ID “off” and then just use that to define an action like:

action: switch.turn_{{trigger.id}}
target:
  entity_id: switch.example

It’s definitely covered once or twice in the articles in the Cookbook.

Covered here, the very first trigger in the documentation.

And the trigger object information