Teach me about context/triggers

What do I need to know about context? The documentation I can find (user, developer) is not exactly exhaustive. Is there some other documentation somewhere, or a thread here that explains its mysteries? (So far only found a single sorta relevant thread through search.)

Reason I am asking is that I am working on a blueprint/automation that can trigger on several attribute changes of a media_player. Here’s the short of it:

- trigger: state
  entity_id: !input media_player
  attribute: media_title
- trigger: state
  entity_id: !input media_player
  attribute: media_artist
- trigger: state
  entity_id: !input media_player
  attribute: media_duration

These may or may not change simultaneously, which unfortunately means that up to three triggers may fire at the same time. For reasons my automation is and has to be in mode: restart.

So I started inspecting the trigger variable in traces. Turns out that for these simultaneous attribute updates, trigger.from_state and trigger.to_state were exactly identical – all the way down to the values of last_* and context. My understanding of context is limited, but my interpretation is that this means they were in fact triggered by one and the same update to this media player’s state object.

Looking at the corresponding values of these automation runs’ respective value for this, those all differ. Not a single value between them are the same.

However upon looking really closely, I eventually noticed something interesting: Sometimes this.context.parent_id was the same as trigger.to_state.context.id, and sometimes it differs. Looking at cases where all three attributes were triggered simultaneously, on two of them these context values were a match, and on one they were not.

So my conclusion is that to prevent the same state object from triggering multiple simultaneous runs of my automation, I should add this condition:

- condition: "{{ this.context.parent_id != trigger.to_state.context.id }}"

But I don’t really know why. And I want to know! How does this.context.parent_id work? Why does its value match trigger.to_state.context.id on two of these simultaneous automation runs, but not on the third? Is the intention actually this specific use-case?!

context in this should come from the state machine. context from the state machine should only have id in it, nothing else (although that may have changed).

parent_id is whatever performed the action (if something performed the action). I.e. it will be the id of a script, automation, template entities script, etc.

user_id is the user’s id if they used a control in the frontend to envoke the change.

Also, in automations, the state objects in the trigger object are the only time you can use context properly before actions are executed. After the automation makes it through conditions, the context object in the automation will be populated.

i.e.

if you’re in variables or conditions, use trigger.<to_state/from_state>.context

in actions you can just use context.

Oh, context isn’t documented as it was never really meant for users.

EDIT: it does have some documentation in developers docs but it doesn’t really describe the properties on the context object.

There was a reasonably popular WTH request asking for the use of context to be simplified for the end user. Hopefully something will come of it.

some changes would need to occur to make it useful in conditions. I.e. the context needs to be updated earlier in automation flow.

Ah ok so it’s not as simple as taking the three contexts and presenting the result to the end user in a nicer way (without them having to use three templates).

Hmm. The thing that gets me is this weird match of this.context.parent_id and trigger.to_state.context.id on two out of three simultaneous runs. That seems so counterintuitive to me? I would’ve expected either:

  1. They all match, because they are all triggered by the same state object.

  2. Only one of them is a match, probably whichever happened to be processed first by the state machine or something like that.

  3. None of them is a match, if the ids refer to entirely different things…

But two out of three? Two?!

You can’t trust this.context.parent_id as it comes from the state machine. With async in play, that object will be updated/different each parallel automation.

The only thing you should ever look at in this.context is this.context.id.

e.g.

if you have 3 things happen at the same time in a parallel automation, whichever one is first will have an empty parent_id. The point in which the other 2 are processed, they may or may not have a parent_id from the state machine. It depends on how fast each trigger was. Some runs you could see none of them with a parent_id, some you could see 2, you’ll never see 3 as the first one trigger is what evokes the update in the state machine.

Do you know if there is any kind of intentional precedence in the order of triggers?

I.e. if I declare three attribute state triggers right after each other like in my first post, and all three attributes are updated by the same state object, should/will the triggers fire in that order?

Looking at my traces, it seems like they do fire in that order. But then again that may be entirely random, or the order in traces not necessarily being the true order anyway, as the only displayed time (in the traces UI itself, not in changed variables) has been rounded off the nearest second and is the same for all runs.

Nope. It’s whatever hits the abstract event loop first.

Just verified, this is populated at the start of each trigger. And it seems like context is now stored in the state machine at some level.

Just keep in mind, you’ll never be able to fully use context parent_id’s because template entities action sections are separate scripts, each with an ID that is not accessible from the state machine.

That’s what the documentation claims should happen, though you never know if it happens in practice. There was some weirdness in another thread recently where this got populated as expected but not this.attributes, can’t quite remember the specifics…

The variable this is the state object of the automation at the moment of triggering the actions.

For this particular automation I am mostly concerned with detecting/preventing these simultaneous runs. The first thing my actions does is download a big chunk of data (RESTful Command), and the automation execution was fast enough that it began doing this before the next trigger overrode the first. This delayed the restart of the automation until the first download was finished, and it also feels quite wasteful to repeat this download multiple times.

Before I found this still experimental context matching I had to add a delay at the beginning of the automation to give the additional triggers some time to initiate the restart of the automation until the last one went through. I hope I don’t have to go back to that but more testing is required before a conclusion can be made.

Make a binary template entity that looks at those 3 attributes and “turns on” when 1 of them is different.

If you don’t want a template entity.

use a state trigger without a state or attribute, then make a template condition looking at those 3 attributes.

Make the automation run in single mode.

Not a bad solution for an automation, but for a blueprint that makes it much less self-contained. At least until template blueprints are ready to be setup from the UI. Would be super neat if in the future combined blueprints were possible, where a single blueprint could be used to setup both automations and its required template entities in a single place…

Now that I had not considered. And I even read that part of the documentation earlier today without reflecting on that being precisely what I needed! That’s probably much more foolproof than this nonsense with contexts. Thanks!

Would’ve been nice if you could specify attributes to monitor in a list…

- trigger: state
  entity_id: !input media_player
  attribute:
    - media_title
    - media_artist
    - media_duration

If run in single mode it would be much harder to detect attribute changes while the automation is active and restart it. As long as video is playing, the automation will stay active through a repeat containing a wait_on_trigger which acts on certain triggers.

You can do restart, put a 1 second delay as your first action so that you make sure you have the “newest” piece of data. Adjust the delay lower until it stops working properly

The documentation is pretty clear:

The variable this is the state object of the automation at the moment of triggering the actions.

Since automation changes state only after it starts running, and not when triggered, this means that this always comes from the previous automation run, not the current one.

So the condition:

"{{ this.context.parent_id == trigger.to_state.context.id }}"

means: “was the previous automation run triggered by the same thing that triggered this run”.

It is therefore obvious that if your automation is triggered 3 times, the condition will be true for the second and third run, and not the first one.

1 Like

That is precisely the kind of technical explanation I was looking for. Huge thanks!

Guess I should continue using that as the technically correct solution for not triggering on multiple attributes of the same state object.

Maybe I wasn’t clear with my responses but the state machine holds the current states, art just rehashed what I said in my first reply.

Also understand that the current state in the state machine doesn’t always come from automations, so it won’t always be the previous automation run.