Template sensor: using this.state in an attribute template

Template sensors w/ triggers resolve at the same time. Itā€™s a bit different than template entities that update based on the template themselves.

E.g. availability does not resolve before state for a triggered template entity.

Dammit. Thatā€™s what you said originally too. :man_facepalming:

(emphasis mine).

Iā€™m basing this off what I remember, I could be wrong. Iā€™m fairly sure it behaves this way. The order of operation is 100% correct, the this object comments may not be correct.

So if it is a triggered template sensor the order you posted above is not valid. We can check that list for untriggered sensors by leaving out the trigger:

    sensor:
      - name: Test
        unique_id: testytestytest
        state: "{{ now() }}" 
        attributes:
          copy_of_state: "{{ this.state }}"

That should update every minute.

1 Like

easy way to test this would beā€¦

- sensor:
  - name: foo
    state: "{{ now() }}"
    attributes:
      test: "{{ this.state }}"

EDIT: You beat me to it

1 Like

Look up :slight_smile:

@DJI would you mind testing either of those ones?

Pedro, That seems to explain the test weā€™ve just run. And just to show that my previous solution has the same fault Iā€™m running this.

  - trigger:
      - platform: time_pattern
        minutes: "/1"
    sensor:
      - name: Test
        unique_id: testytestytest
        state: "{{ now() }}"
        attributes:
          copy_of_this_state: "{{ this.state }}"
          copy_of_states: "{{ states('sensor.test') }}"

with this result:

sensor.test
Test
2023-10-11 17:01:00.152330+01:00	friendly_name: Test
copy_of_this_state: 2023-10-11 17:00:00.159356+01:00
copy_of_states: 2023-10-11 17:00:00.159356+01:00

So Iā€™ve just changed it to an ordnary sensor as suggested.

      unique_id: testytestytest
      state: "{{ now() }}"
      attributes:
          copy_of_this_state: "{{ this.state }}"
          copy_of_states: "{{ states('sensor.test') }}"

It results in

sensor.test
Test
2023-10-11 17:06:00.152369+01:00	friendly_name: Test
copy_of_this_state: 2023-10-11 17:05:00.152527+01:00
copy_of_states: 2023-10-11 17:05:00.152527+01:00

i.e. the same as with a trigger sensor-both reporting the previous value.
Looks like Iā€™ll live with repeating the logic.
Thanls for all your help guys, your time & effort is much appreciated by this newbie!
edited to include the changed code

t

Hang on. Why is the attribute

copy_of_states: "{{ states('sensor.test') }}"

Also a minute behind?

Itā€™s like the attributes are being resolved before the state.

Sorry! I must take more care!

Tom, it does look like it

The state machine always has the previous state, which is currently being calculated. states( grabs from the state machine. That will not be updated until the entire state object is updated.

1 Like

What surprises me is

copy_of_this_state: "{{ this.state }}"

reports 17:00:00 and not 17:01:00.

Hereā€™s my understanding of how it works (which now appears to be incorrect):

  • The state option is evaluated first so this.state, in the state option, refers to the previous value.
  • Attributes are evaluated second so this.state, in an attribute, refers to the newly computed value.

However, the results above imply my understanding is incorrect and this.state always refers to the previous value.

I echo your puzzlement. Surely once the state value is written to the state machine it will register as a change and cause the attribute to be updated based on its new value states('sensor.test'). This doesnā€™t appear to be happening. Does this mean the attributes are not re-evaluated when something they derive from changes? ā€¦but only if something the state value depends on changes?

The state is not written to the state machine until the state object is fully populated. A state object is not just the state of the sensor. Itā€™s an object that contains all information about the entity; last_updated, last_changed, attributes, context, and the state. For template sensors, this is only added to the state machine when all the supplied templates have been resolved (state, availability, attributes, etc).

this just holds the current state object as itā€™s being populated. This behavior may have changed. I personally do not use this functionality, so I cannot confirm or deny that it has changed behavior.

FWIW, Iā€™m fine if it operates differently from the way I thought it did (whether due to my misunderstanding or its behavior changed). My version makes this.state's behavior more complicated to use whereas the example above proves itā€™s far more straightforward; regardless of where this.state is used in the Template Sensor, it always refers to whatā€™s currently in the state machine.

Yes, I agree. Itā€™s come up a bunch in the templating channel on discord. Iā€™d actually use this if it could ensure that it was the object pulled from the state machine prior to the templates execution. But historically, that hasnā€™t been the case. Iā€™m 99% sure it behaved the way you (and I) described it as little as 2 months ago. Itā€™s also possible that it could depend on how fast the system running it is.

EDIT: I want to clarify that Iā€™m specifically talking about the population of the this variable during template execution and not the order of template execution.

As far as I know, for trigger based template sensors it works the same as for automations.
When the trigger occurs, both the this and trigger objects are created.
These are then used in all templates, so also in those for the attributes and availability.

This is purely based on experience, not on evaluation of the code :slight_smile:

I just ran few tests, and the takeaway is that I could not find an example where there was any difference between this.state & this.attributes compared to states() & state_attr() when self-referencing.

What I saw (in 2024.2.2) was the following:

For a trigger-based template sensor where the trigger is not the template sensor itself:

  • Neither this.state nor states('sensor.self-referenced_template_sensor') will update during the rendering of any part of the template sensor.
  • Same comment applies to this.attributes and state_attr()

For a state-based template sensor, and also for a trigger-based template sensor that references its own state as a trigger:

  • The template re-renders after the state is rendered and after each attribute is rendered. Again it makes no difference if you use this or states('sensor.this_template_sensor'). Itā€™s very easy to get into template loops because of this (but HA will catch it if the sensor is state-based, and you will see the loops in your log and the sensor will stop rendering; loops in trigger-based sensors overloaded and crashed my HA instance).
  • Because the entire sensor config re-renders after each (state and/or attribute) update, it makes no difference whether you have the state reference an attribute or if you have an attribute reference the state.

Hereā€™s the basic code I was testing with, but I had various flavors to test what happens if the state references an attribute and vice versa, or if the state was self-referencing, etc.

template:
  - sensor:
      - name: Template-state test using states
        unique_id: 395526fc-87f0-43e0-bd76-f08a94c2851f
        state: >
          {{ states('input_number.test') }}
        attributes:
          first: "{{ states('sensor.template_state_test_using_states') | default(0,1) | int(0) + 1 }}"
          second: "{{ state_attr('sensor.template_state_test_using_states', 'first') | default(0,1) | int(0) + 1 }}"
      - name: Template-state test using this
        unique_id: edf580ae-83c9-4758-a45c-64ef9a0b7ec3
        state: >
          {{ states('input_number.test') }}
        attributes:
          first: "{{ this.state | default(0,1) | int(0) + 1 }}"
          second: "{{ this.attributes.first | default(0,1) | int(0) + 1 }}"
  - trigger:
      - platform: state
        entity_id:
          - input_number.test
    sensor:
      - name: Template-trigger test using states
        unique_id: d6ffbb55-0fe4-4930-90bf-c2afbb507620
        state: >
          {{ states('input_number.test') }}
        attributes:
          first: "{{ states('sensor.template_trigger_test_using_states') | default(0,1) | int(0) + 1 }}"
          second: "{{ state_attr('sensor.template_trigger_test_using_states', 'first') | default(0,1) | int(0) + 1 }}"
      - name: Template-trigger test using this
        unique_id: a8fe416b-7e17-40f2-9467-5fb48084fada
        state: >
          {{ trigger.to_state.state }}
        attributes:
          first: "{{ this.state | default(0,1) | int(0) + 1 }}"
          second: "{{ this.attributes.first | default(0,1) | int(0) + 1 }}"

Results after beginning with all zeros, and changing the input number from 0 to 5 to 10 to 20: