Is there documentation on what context, id, parent_id and user_id mean in an automation?

I’m experimenting with an automation where I need to do conditions based on what actually triggers a state change, so it looks like context (id, parent_id and user_id) is the most useful for sorting that out. However, I couldn’t find detailed documentation on what those mean.

I created a small automation to try a few things:

alias: TEST self-trigger
mode: queued
trigger:
  - platform: state
    entity_id:
      - light.kitchen_main
action:
  - delay: '00:00:10'
  - service: light.toggle
    target:
      entity_id: light.kitchen_main

For reference, light.kitchen_main is a Zooz ZEN32 switch, and references both the physical button and the actual light (relay).

I triggered the automation in a few different ways and compared the traces, but it doesn’t totally make sense to me.

Raw results

Press physical light switch

this:
  context:
    id: "01GWTTCKR661MQ09AMC3C7NQ9N"
    parent_id: null
    user_id: "9d0e6801adcc465099cce98b282c2be6"
trigger:
  from_state:
    context:
      id: "01GWTPDXQTJDHSZD9R53VWQZ3K"
      parent_id: "01GWTPDXQKBT4M6YBCYK1EFR6H"
      user_id: null
  to_state:
    context:
      id: "01GWTTCYQ8GEKYG3GNDF797JVS"
      parent_id: null
      user_id: null

10 seconds later, automation toggles light, triggering itself to start again

this:
  context:
    id: "01GWTTCYQHHZ4X465GEBHCZE8E"
    parent_id: "01GWTTCYQ8GEKYG3GNDF797JVS"
    user_id: null
trigger:
  from_state:
    context:
      id: "01GWTTCYQ8GEKYG3GNDF797JVS"
      parent_id: null
      user_id: null
  to_state:
    context:
      id: "01GWTTCYQHHZ4X465GEBHCZE8E"
      parent_id: "01GWTTCYQ8GEKYG3GNDF797JVS"
      user_id: null

Created a second automation that toggles the light, “Run” manually

this:
  context:
    id: "01GWTTHNPM2Y2NCQDW3YHTDKNF"
    parent_id: null
    user_id: "9d0e6801adcc465099cce98b282c2be6"
trigger:
  from_state:
    context:
      id: "01GWTTCYQHHZ4X465GEBHCZE8E"
      parent_id: "01GWTTCYQ8GEKYG3GNDF797JVS"
      user_id: null
  to_state:
    context:
      id: "01GWTTJ4NDTJ6WXQG5VRVY75VY"
      parent_id: null
      user_id: null

Second automation, triggered by a fixed time condition

this:
  context:
    id: "01GWTX9YT03EA0P8WWBEMRV11N"
    parent_id: null
    user_id: "9d0e6801adcc465099cce98b282c2be6"
trigger:
  from_state:
    context:
      id: "01GWTTJ4NDTJ6WXQG5VRVY75VY"
      parent_id: null
      user_id: null
  to_state:
    context:
      id: "01GWTXAFJDCZAXC20C6FWEX3T8"
      parent_id: null
      user_id: null

Switch toggled in Lovelace

this:
  context:
    id: "01GWTYGS1K8WEFTMGWHNT9PASF"
    parent_id: null
    user_id: "9d0e6801adcc465099cce98b282c2be6"
trigger:
  from_state:
    context:
      id: "01GWTXAFJDCZAXC20C6FWEX3T8"
      parent_id: null
      user_id: null
  to_state:
    context:
      id: "01GWTYGSG1W3DRFHZMAWN1JWQ5"
      parent_id: null
      user_id: "9d0e6801adcc465099cce98b282c2be6"

I’ll try to summarize my observations here, based on which of id parent_id and user_id have non-null values.

Trigger Action  
this
.context
trigger
.to_state
.context
Notes
Physical switch
or
Another automation
id
user_id
id
 
id: different values
user_id: “owner” user
Lovelace id
user_id
id
user_id
id: different values
user_id: same (logged-in user)
Self-triggered id
parent_id
 
id
parent_id
 
id: same values
parent_id: same values, and equal to to_state.context.id of the original execution
?? id
parent_id
id
 
id: different values
 

I didn’t include from_state.context because it is always the same as the previous to_state.context, (which makes sense)

Note: The last situation I found in another automation, but I haven’t figured out how to trigger it yet. I also can’t find anything with that id in prior traces.

What I’d like to figure out:

  1. Why is there a different context for what I assume is the actual automation (this.context), and the trigger (trigger.to_state.context)? I would have assumed the trigger is the context but clearly it’s not.
  2. What does the context id represent and where does it come from?
    • Is there a way to look up what an id is, either in templates or the log?
  3. In what cases is parent_id populated?
  4. Is my assumption that user_id is the owner user (in case of physical trigger) correct?

The page on state objects includes details on the context

I hadn’t revisited that after looking at this, so thanks, it actually makes a bit more sense now, but I think it’s still missing answers to most of my questions:

Whenever an automation or user interaction causes states to change, a new context is assigned.

I’m guessing the answer to my question number 1 is about a state changing – but what state?

  • It starts with light.kitchen_main state changing off→on. Obviously a state change, so new context.id.
  • This causes the automation to trigger, with a new context.id – so I guess a state changed, but what?
  • The automation runs and changes state of light.kitchen_main from on→off, so I’d expect another new context.id – but this time there isn’t one. Why?

user_id: Will be None if action was not started by a user (ie. started by an automation)

This makes sense for the lovelace UI. It doesn’t seem to make sense for a physical hardware button press, though.

  • In this case, is the user_id the “owner” user or maybe the user that set up the integration?
  • If so, I guess it is best practice to not use that user for any day-to-day use of Lovelace UI, if you care about being able to tell the difference of a trigger based on context.user_id. This seems like a thing most users won’t do – so it isn’t useful for blueprints to rely on it.

I mean, in practice this isn’t really an issue for a couple reasons. Most blueprints and automations don’t really need this particular distinction, and if they do, they use events (ie zwave_js_value_notification) which provide way more detail anyway.

It still feels like the docs are misleading though, it should include something like “Physically pressing buttons (like wall switches) will have the user_id of the user that set up the integration”.

parent_id : Unique identifier of the parent context that started the change, if available. For example, if an automation is triggered, the context of the trigger will be set as parent.

So for the “self-triggered” automation, this makes sense.

However, for when I trigger my automation from another automation, the parent_id is not set – according to this it should be, and intuitively that’s what I was expecting, too.

Other automation used to test this This is pretty simple, and I get the same results whether I let the time condition run or run it manually:
alias: TEST self-trigger trigger
trigger:
  - platform: time
    at: "09:33:00"
action:
  - service: switch.toggle
    target:
      entity_id: switch.kitchen_main

What I still don’t really know is what is the reliable way to tell these apart?

For example, if I want to change my automation so it doesn’t trigger itself, is it as simple as:

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

or do I need to look at all of the context values, like:

condition:
  - condition: template
    value_template:  |
      {{ not (
        this.context.id == trigger.to_state.context.id 
        and this.context.parent_id
        and this.context.parent_id == trigger.to_state.context.parent_id
        and (not this.context.user_id)
        and (not trigger.to_state.context.user_id)
      ) }}

Note: Once I get this sorted out, I think I’ll make a PR to try improving the context documentation.

Impressive investigation! One thought: if you want to avoid having an automation trigger itself, I believe you can set the automation mode to single and then it will not trigger if it is already running.

I had a motion trigger set up with the condition this.context.id != trigger.to_state.context.id which worked for while, but then it started sometimes triggering itself. This happened after the upgrade to HA 2023.6 and whatever Zwave JS UI was out at the same time, but I’m not sure if something changed there or not. It only happened when the trigger was a zwave device, but not consistently – I’m not sure if there was a delay updating the state or something.

Because my automations broke, this is still incompletely documented, and this might have even been relying on something that wasn’t supposed to work this way to begin with, I gave up on this approach for now.

if you want to avoid having an automation trigger itself, I believe you can set the automation mode to single and then it will not trigger if it is already running.

Yes, and this is partly what I switched to, but to get this to work, you need a delay to ensure the script is still running – and wait_for_trigger is great for this:

          - alias: Prevent self-triggering
            if:
              condition: state
              entity_id: !input controlled_entity
              state: 'on'
            then:
              wait_for_trigger:
                - platform: state
                  entity_id: !input controlled_entity 
                  to: "off"
              timeout: { seconds: 3 }

The downside is there are some automations where it doesn’t work:

  • Where single isn’t the right mode (ie: other events should trigger it again)
  • Where there are multiple events to be ignored

In this particular case, I’m using it for a motion-controlled light and off delay, where the delay time is a few minutes. Technically this misses some motion events that would extend the time, but it’s only a 5 second window so not a huge deal.

If the “off delay” was 5 or even 10 seconds, for example, it would probably be turning off a lot more than desired since it would be ignoring motion triggers while the wait_for_trigger action was still running.