Icon Evaluation Precedence Relative to State

Take this sensor that updates its icon depending on its own state:

- name: "Sensor Icon Test"
  state: "{{ now().minute % 3 }}"
  icon: >-
    mdi:numeric-{{ this.state }}

The icon changes in the sequence:

  • State “0” → mdi:numeric-0
  • State “1” → mdi:numeric-1
  • State “2” → mdi:numeric-2

image

Ok so far. However, if the same sensor is defined based on a trigger, the icon follows the previous state instead of the current state:

- trigger:
  - platform: time_pattern
    minutes: /1
  sensor:
    - name: "Trigger Icon Test"
      state: "{{ now().minute % 3 }}"
      icon: >-
        mdi:numeric-{{ this.state }}
  • State “0” → mdi:numeric-2
  • State “1” → mdi:numeric-0
  • State “2” → mdi:numeric-1

image

Apparently in state-based sensors the icon is evaluated post state evaluation, but in trigger-based sensors the icon is chosen before resolving the new state. Is this a documented behavior?

So a question for you.
If I answered yes, or I answered no, how would that change your life?

I mean, you just documented it, feel free to add it to the docs in the appropriate places. That would be a helpful thing to do.

However your logic sounds correct to me, if that is what you are asking.

Not sure where this is going, TBH.

I’d think that this was in the docs or discussed more widely in the forums. I couldn’t find it in either place.

But I just tested a workaround. Define the state in a variables action and use that for the icon instead of looking at the sensor state:

- trigger:
  - platform: time_pattern
    minutes: /1
  action:
    - variables:
        state: "{{ now().minute % 3 }}"
  sensor:
    - name: "Trigger Icon Test"
      state: "{{ state }}"
      icon: >-
        mdi:numeric-{{ state }}

I guess this is a new addition to my notes of HA gotchas.

There is no difference between state-based and trigger-based template sensors when it comes to the rendering order of states, attributes, and icons.

In your example, the state-based template has two triggers (now() which renders once per minute, and the sensor’s own state via this). So in this state-based sensor, everything is rendered twice in rapid succession, every minute. now() will trigger everything to render, and the icon will render with the ‘old’ state. But then immediately after, since the sensor’s state has just changed, it triggers everything to render again. This time, there is no change to the state, but the icon template is rendered with the newer state.

Your example trigger-based template sensor only has one trigger (once per minute via the time pattern).

If you wanted that trigger-based template sensor to behave the same way as the state-based template sensor, you’d need to add a state trigger with the template sensor’s entity ID.

That’s interesting. The trigger-based sensor updates strictly only following the trigger, so I see why it doesn’t re-render on its own state change.

Then maybe the workaround with the action is more efficient than adding a state trigger, since it avoids the dual rendering.

There are scenarios where the self-referencing trigger is desirable, but in your example it is definitely not the most efficient way to get the result your are looking for. I’m not sure the specific problem you’re really trying to solve since the example is contrived, but if you really wanted to create this example sensor, then yes, using a trigger-based sensor without self-referencing would be best.

If you’re going to define the variable based off now() you can just do that in the trigger itself though, instead of using an action block:

- trigger:
  - platform: time_pattern
    minutes: /1
    variables:
      state: "{{ now().minute % 3 }}"
  sensor:
    - name: "Trigger Icon Test"
      state: "{{ state }}"
      icon: >
        mdi:numeric-{{ state }}

But using the action block allows you to grab the trigger variable instead of executing the now() function, which is the more correct and more efficient way to do it:

- trigger:
  - platform: time_pattern
    minutes: /1
  action:
    - variables:
        state: "{{ trigger.now.minute % 3 }}"
  sensor:
    - name: "Trigger Icon Test"
      state: "{{ state }}"
      icon: >
        mdi:numeric-{{ state }}

But if the template is that simple, why even set up the variable in the first place?

- trigger:
  - platform: time_pattern
    minutes: /1
  sensor:
    - name: "Trigger Icon Test"
      state: "{{ trigger.now.minute % 3 }}"
      icon: >
        mdi:numeric-{{ trigger.now.minute % 3 }}
1 Like

My real usage is more complex is it depends on a handful of triggers that do not follow the time, but these examples captured well what the problem was.

Thanks!