The 'this' State Object - inconsistent and unusable in templates

When using Automation trigger variables the data in the this state object for an automation with a state trigger is as follows:

{
'entity_id': 'automation.my_automation', 
'state': 'on', 
'attributes': {
  'id': 'my_automation', 
  'last_triggered': datetime.datetime(2024, 2, 17, 10, 41, 27, 507745, tzinfo=datetime.timezone.utc), 
  'mode': 'queued', 
  'current': 0, 
  'max': 50, 
  'friendly_name': 'My Automation'
  }, 
'last_changed': '2024-02-17T08:56:11.505558+00:00', 
'last_updated': '2024-02-17T10:41:28.521367+00:00', 
'context': {
  'id': '01HPVAG7XYMW9HW9X', 
  'parent_id': 
  '01HPVAG7LBRTQRGVR', 
  'user_id': None
  }
}

There seems to be no way to make any use of this data in a template because the last_triggered item does not get past the intepreter.
(To see what I mean, simply copy and past it into the template dev tools and try to assign it to a variable).

'last_triggered': datetime.datetime(2024, 2, 17, 10, 41, 27, 507745, tzinfo=datetime.timezone.utc)

What does the datetime.datetime do/mean?

And why is it presented this way in the first place?
The last_changed and last_updated are standard date time format.


Ok, so yes, this post has shameless cross over with this one, but now I think it is more than just me not understanding how to use it (but of course I am happy to be shown I’m wrong).

Last_triggered is a datetime object. What you’re seeing is the string repr of the object. It’s very usable, but I’m assuming you’re doing something like this and expecting it to resolve

variables:
  xyz: "{{ this }}"

That will not work with complex objects. You have to convert all contents of your output to xyz to basic objects: list, dict, str, float, int, bool, None. Anything outside those objects will not resolve properly.

last_changed and last_updated are datetime objects that are converted to strings. The difference is that they aren’t nested deep inside other objects. Because last_triggered is inside attributes, it won’t convert to a string.


I suggest you ask your actual question instead of focusing on this, it requires a full understanding of templates and how things resolve.

If you’re just looking to test things, you can just copy last_changed and paste it’s contents into last_triggered. Or take the values, put dashes between the date, T between the date and time, colon between the times, period between the microseconds, and add the utc timezone. Lastly, use | as_datetime to make them datetime objects. E.g.

{% set this = {
'entity_id': 'automation.my_automation', 
'state': 'on', 
'attributes': {
  'id': 'my_automation', 
  'last_triggered': '2024-02-17T10:41:27.507745+00:00' | as_datetime,
  'mode': 'queued', 
  'current': 0, 
  'max': 50, 
  'friendly_name': 'My Automation'
  }, 
'last_changed': '2024-02-17T08:56:11.505558+00:00' | as_datetime, 
'last_updated': '2024-02-17T10:41:28.521367+00:00' | as_datetime, 
'context': {
  'id': '01HPVAG7XYMW9HW9X', 
  'parent_id': 
  '01HPVAG7LBRTQRGVR', 
  'user_id': None
  }
} %}

You guessed the actual question. I do indeed want to do almost exactly that.

I want to pass the entire this to several different scripts depending on which entity_id triggers the automation and then have the receiving script decide which bits it needs to use.

Bearing in mind that the items in this will be different for every trigger platform, passing every item to every script is not only unnecessary but also open to problems if HA decide to ever change the items in this.

automation:
  alias: my_automation
  trigger:
    - platform: homeassitant
      event: start
      id: homeassistant_start

    - platform: state
      entity_id: sensor.my_entity
      id: state_change

  action:
    - service: >
        script.{{ trigger_id }}
        data:
          this_data: >
            {{ this }}

I get that, I agree!

Are you saying that what I want to do is not possible?
Currently when the script receives it as a string, the templating engine seems to see it as a badly formed dictionary.

You won’t be able to do that because state objects contain objects that are not basic.

You can take the data and convert it into basic values. Or just transfer the information you care about, not the full state object.

Sorry to keep this going but I’d really like to understand this properly (or at least a bit more).

I think being able to pass the entire this variable around would be very useful. I accept it can’t be done but it got me thinking about other state variables so I looked at the trigger variable.

It returned the following for a state trigger.

What is going on with the from_state and to_state?

{
'id': '0',
'idx': '0',
'alias': None,
'platform': 'state',
'entity_id': 'binary_sensor.pir_hall',
'from_state': <state binary_sensor.pir_hall=off; device_class=motion, friendly_name=PIR Hall @ 2024-02-19T17:29:29.374159+00:00>,
'to_state': <state binary_sensor.pir_hall=on; device_class=motion, friendly_name=PIR Hall @ 2024-02-19T18:04:06.192302+00:00>,
'for': None,
'attribute': None,
'description': 'state of binary_sensor.pir_hall'
{

This is the actual output before I ‘beautified’ it.

{'id': '0', 'idx': '0', 'alias': None, 'platform': 'state', 'entity_id': 'binary_sensor.pir_hall', 'from_state': <state binary_sensor.pir_hall=off; device_class=motion, friendly_name=PIR Hall @ 2024-02-19T17:29:29.374159+00:00>, 'to_state': <state binary_sensor.pir_hall=on; device_class=motion, friendly_name=PIR Hall @ 2024-02-19T18:04:06.192302+00:00>, 'for': None, 'attribute': None, 'description': 'state of binary_sensor.pir_hall'}

This is the automation that provided it

automation:

  - alias: Test Pass Trigger Data
    id: test_pass_trigger_data
    mode: queued

    trigger:
      - platform: state
        entity_id: binary_sensor.pir_hall

    action:
      - service: notify.test_file
        data:
          message: >
            {{ trigger }}

Those are state objects, that’s what they serialize as. Just like datetimes from above.

Ok, possibly my last word on this because it is what it is, but…

Couldn’t it be considered an oversight to have a variable available (to the HA programming interface) but which is unusable there?
I admit that the docs specifically only mention the items that can be used but I think the docs infer that the whole dictionary is also available.

But maybe there are very good reasons that I neither know nor understand to explain why some items (eg. from_state and to_state) are first converted from complex objects before they are made usable in templates rather than simply converting the whole dictionary into non-complex objects.

I have to and am happy to assume that is the case.

The trigger objects have actual state objects. Templates, when using expand or states.xxx.xxx don’t return state objects, they return TemplateStates which are objects that act like state objects. Both objects have __gettitem__ overriden to behave like a dictionary when they are not dictionaries. This is getting into high level complex python here, and without knowledge on the subject, it’s going to be very hard to understand.

The take away here that you really just need to know is: Complex objects when serialized (Written to a file), will not show up like dictionaries, lists, strings, floats, bools, ints, or None.

The complex objects need to have a built in method to serialize to make them look like those other objects. This is typically avoided because other things have a cost. Yes, it probably can be done, but I highly doubt this will ever happen. These objects are not meant to be written to disk/files. The template resolver could be updated to allow them to resolve properly in variables though, that would just be a feature request.

2 Likes