Context.parent_id

As i understand from the documentation parent id is filled in when triggered by an automation.

I’ve two triggers in my automation. A state trigger and a time_interval trigger.
When the automation is triggered by the state trigger parent_id is not None. However, when the automation is triggered by the time_pattern the parent_id is None. Is this expected behaviour?

I would think all triggers should fill in the parent id. I would like to use this to know if a light has been turned on by the automation or externally from home assistant

Yeah, thats also what i found. However, if my light is changed while the automation is triggered by the time pattern trigger parent_id is None.

So you have long wait delays in your automation?

No, i do not. I’ve added a logbook log statement and i see the following:

As you can see home assistant “knows” it is triggered by a template but parent_id is None…

The trigger when this happens is the time trigger (I’ve made a blueprint for this):

trigger:
  - platform: state
    entity_id: !input light_on_off_template_sensor
  - platform: time_pattern
    seconds: !input trigger_interval

After this time trigger the automation checks if the light should be on/off. If it then changes state another automation checks if it is turned on/off by user or outside ha or ha itself.

In this automation i print to the logbook:

          - service: logbook.log
            data:
              name: Context of manual mode
              message: >
                id:         {{ trigger.to_state.context.id}}
                parent_id:  {{ trigger.to_state.context.parent_id}}
                user_id:    {{ trigger.to_state.context.user_id}}

Which gives me an empty parent_id.

This logbook print is triggered by the following trigger (Note i’ve changed some stuff to make testing easier).

  - alias: When a device has been turned on manually
    platform: template
    value_template: >
      {% set namespace = namespace(manually_triggered=false) %}
      {% set devices = area_entities(area_name) | 
          expand |
          selectattr("domain", "eq", device_domain_name) |
          list
      %}
      {%- for device in devices %}
      {% set device_context = device.context %}
      {% set changed_recently = (as_timestamp(now(), 0) - as_timestamp(device.last_changed, 0)) | int(0) < 1 %}
      {% set device_changed_by_user = device_context.user_id != None %}
      {% set device_changed_outside_ha = device_context.user_id == None and device_context.parent_id == None %}
        {% if changed_recently %}
          {% set namespace.manually_triggered = true %}
          {% break %}
        {% endif %}
      {% endfor %}
      {{ namespace.manually_triggered }}
    id: "device_manually_turned_on"

And this trigger shows no parent_id.

Made another simple automation for testing purposes:

alias: New Automation
description: test
trigger:
  - platform: state
    entity_id:
      - light.living_room_standing
    to: null
condition: []
action:
  - service: logbook.log
    data:
      name: state of light liv
      message: >
        id:         {{ trigger.to_state.context.id}} parent_id:  {{
        trigger.to_state.context.parent_id}} user_id:    {{
        trigger.to_state.context.user_id}}
mode: single

When the light is turned of by another automation where the trigger is the time_pattern. parent id is null:

I’ve found some other related issues:

Apparently it has something to do with time triggers not registering as an event in the database. This way home assistant can’t apply context.

Use a the trace functionality to view all possible context options when automations run. It’ll be in the variables section. You won’t need write these tests all over the place.

FYI, if logbook tells you what changed the light, that info will be in your automation somewhere. And when I say logbook, i’m not referring to your logbook.logs, I’m referring to normal events.

I did this and as expected considering the linked github issues above parent_id is empty:

My workaround now is creating triggered template binary sensor:

    - trigger:
        - platform: time_pattern
          seconds: "/30"
      binary_sensor:
          name: Thirty Second time trigger
          unique_id: thirty_second_time_trigger
          state: >
            {{ not is_state("binary_sensor.thirty_second_time_trigger", "on") }}

And using this binary_sensor as a state trigger in my automation. Then i do have parent_id

You’re looking at the wrong automation. You should be looking at your other automation which should point to the timepattern one. The timepattern one will not have context because it’s a time pattern…

But i would like to know if the light is turned on by the automation or not. So i should look at the light context which is parent == none when turned on/off by a time trigger

You’d make a second automation that does that, you wouldn’t put it in this automation. Your second automation would simply log when the automation changes it, if you want to know that.

The automation provides the context on the light’s state change.

If you want context in that automation without a second automation, make a trigger_id for the timepattern trigger.

I do use two automations.

This automation (blueprint) checks context and based on that enables / disables a input boolan. However, the check doesn’t work because of parent_id == None when light is turned on/ off by the second automation with the time_pattern as trigger.

blueprint:
  name: Handle manual light mode
  description: When manual mode is enabled handle automatic turn off
  domain: automation
  input:
    manual_mode_input_boolean:
      name: Manual mode input boolean
      description: Input boolean which holds the manual mode state
      selector:
        entity:
          domain: input_boolean
    manual_mode_timer:
      name: Manual mode timer
      description: Timer which determines manual mode time out
      selector:
        entity:
          domain: timer
    notify_group:
      name: Notification Group
      description: The name of the notification group to call
      default: ""
    area:
      name: Area name
      description: Area name of the lights
      selector:
        area:
    device_domain:
      name: Device domain
      description: Domain of the device to check for manual mode (e.g. light)
      selector:
        entity:
      default: "light"
    custom_on_state:
      name: Custom on state
      description: "Custom on state off the device. Default: on"
      default: "on"
      selector:
        select:
          options: []
          multiple: false
          custom_value: true
    custom_off_state:
      name: Custom off state
      description: "Custom on state off the device. Default: off"
      default: "off"
      selector:
        select:
          options: []
          multiple: false
          custom_value: true

mode: parallel

variables:
  group: !input notify_group
  group_target: '{{ group | lower | replace(''notify.'', '''') | replace('' '',''_'')}}'
trigger_variables:
  manual_timer: !input manual_mode_timer
  area_name: !input area
  device_domain_name: !input device_domain
  on_state: !input custom_on_state
  off_state: !input custom_off_state

trigger:
  - alias: When manual mode changes
    platform: state
    entity_id: !input manual_mode_input_boolean
    id: "manual_mode_changed"
  - alias: When 5 minutes remaining in timer
    platform: template
    value_template: >
      {% if manual_timer is defined %}
        {% set timer_finishes_at = state_attr(manual_timer, "finishes_at") %}
        {% if timer_finishes_at != None %}
          {{ ((as_timestamp(timer_finishes_at, 0) | int(0)) - (as_timestamp(now(), 0) | int(0))) <= 300 }}
        {% else %}
          {{ false }}
        {% endif %}
      {% else %}
        {{ false }}
      {% endif %}
    id: "timer_almost_done"
  - alias: When delay 1 hour is received
    platform: event
    event_type: mobile_app_notification_action
    event_data:
      action: delay_1h_{{area_name}}_{{device_domain_name}}
    id: "handle_actionable_notification"
  - alias: When delay 2 hours is received
    platform: event
    event_type: mobile_app_notification_action
    event_data:
      action: delay_2h_{{area_name}}_{{device_domain_name}}
    id: "handle_actionable_notification"
  - alias: When delay 4 hours is received
    platform: event
    event_type: mobile_app_notification_action
    event_data:
      action: delay_4h_{{area_name}}_{{device_domain_name}}
    id: "handle_actionable_notification"
  - alias: When turn off is received
    platform: event
    event_type: mobile_app_notification_action
    event_data:
      action: turn_off_manual_mode_{{area_name}}_{{device_domain_name}}
    id: "handle_actionable_notification"
  - alias: When timer is finished
    platform: event
    event_type: "timer.finished"
    event_data:
      entity_id: !input manual_mode_timer
    id: "timer_finished"
  - alias: When a device has been turned on manually
    platform: template
    value_template: >
      {% set namespace = namespace(manually_triggered=false) %}
      {% set devices = area_entities(area_name) | 
          expand |
          selectattr("domain", "eq", device_domain_name) |
          list
      %}
      {%- for device in devices %}
        {% set device_context = device.context %}
        {% set changed_recently = (as_timestamp(now(), 0) - as_timestamp(device.last_changed, 0)) | int(0) < 1 %}
        {% set device_changed_by_user = this.context.user_id != None %}
        {% set device_changed_outside_ha = this.context.user_id == None and this.context.parent_id == None %}
          {% if (device_changed_by_user or device_changed_outside_ha) and changed_recently %}
            {% set namespace.manually_triggered = true %}
            {% break %}
          {% endif %}
      {% endfor %}
      {{ namespace.manually_triggered }}
    id: "device_manually_turned_on"
condition: []
action:
  - choose:
      - conditions:
          - alias: Manual mode changed
            condition: template
            value_template: >
              {{ trigger.id == "manual_mode_changed" }}
        sequence:
          - choose:
              - conditions:
                  - alias: Manual mode turned on
                    condition: template
                    value_template: >
                      {{ trigger.to_state.state == "on" }}
                sequence:
                  - alias: Start timer
                    service: timer.start
                    target: 
                      entity_id: !input manual_mode_timer
                    data:
                      duration: 02:00:00
                  - alias: Notify user
                    service: notify.{{group_target}}
                    data:
                      message: "Manual mode enabled for {{ area_name }} {{ device_domain_name }}"
                      data:
                        tag: manual_mode_{{area_name}}_{{device_domain_name}}
                        sticky: true
                        persistent: true
                        actions:
                          - action: "turn_off_manual_mode_{{area_name}}_{{device_domain_name}}"
                            title: "Turn off"
              - conditions:
                  - alias: Manual mode turned off
                    condition: template
                    value_template: >
                      {{ trigger.to_state.state == "off" }}
                sequence:
                  - alias: Cancel timer
                    service: timer.cancel
                    target: 
                      entity_id: !input manual_mode_timer
      - conditions:
          - alias: Timer almost finished.
            condition: template
            value_template: >
              {{ trigger.id == "timer_almost_done" }}
        sequence:
          - alias: Notify user
            service: notify.{{group_target}}
            data:
              message: "Manual mode almost finished for {{ area_name }} {{ device_domain_name }}"
              data:
                tag: manual_mode_{{area_name}}_{{device_domain_name}}
                sticky: true
                persistent: true
                chronometer: true
                when: "{{ (as_timestamp(now(), 0 ) + (5*60)) | int }}"
                actions:
                  - action: "delay_1h_{{area_name}}_{{device_domain_name}}"
                    title: "Delay 1h"
                  - action: "delay_2h_{{area_name}}_{{device_domain_name}}"
                    title: "Delay 2h"
                  - action: "delay_4h_{{area_name}}_{{device_domain_name}}"
                    title: "Delay 4h"
      - conditions:
          - alias: Actionable notification received
            condition: template
            value_template: >
              {{ trigger.id == "handle_actionable_notification" }}
        sequence:
          - choose:
              - conditions:
                  - alias: Delay for 1 hour
                    condition: template
                    value_template: >
                      {{ trigger.event.data.action == "delay_1h_" + area_name + "_" + device_domain_name }}
                sequence: 
                  - alias: Cancel timer
                    service: timer.cancel
                    target: 
                      entity_id: !input manual_mode_timer
                  - alias: Start timer
                    service: timer.start
                    target: 
                      entity_id: !input manual_mode_timer
                    data:
                      duration: 01:00:00
                  - alias: Clear sticky/ persistent notification
                    service: notify.{{group_target}}
                    data:
                      message: "clear_notification"
                      data:
                        tag: manual_mode_{{area_name}}_{{device_domain_name}}
              - conditions:
                  - alias: Delay for 2 hours
                    condition: template
                    value_template: >
                      {{ trigger.event.data.action == "delay_2h_" + area_name + "_" + device_domain_name }}
                sequence: 
                  - alias: Cancel timer
                    service: timer.cancel
                    target: 
                      entity_id: !input manual_mode_timer
                  - alias: Start timer
                    service: timer.start
                    target: 
                      entity_id: !input manual_mode_timer
                    data:
                      duration: 02:00:00
                  - alias: Clear sticky/ persistent notification
                    service: notify.{{group_target}}
                    data:
                      message: "clear_notification"
                      data:
                        tag: manual_mode_{{area_name}}_{{device_domain_name}}
              - conditions:
                  - alias: Delay for 4 hours
                    condition: template
                    value_template: >
                      {{ trigger.event.data.action == "delay_4h_" + area_name + "_" + device_domain_name }}
                sequence: 
                  - alias: Cancel timer
                    service: timer.cancel
                    target: 
                      entity_id: !input manual_mode_timer
                  - alias: Start timer
                    service: timer.start
                    target: 
                      entity_id: !input manual_mode_timer
                    data:
                      duration: 04:00:00
                  - alias: Clear sticky/ persistent notification
                    service: notify.{{group_target}}
                    data:
                      message: "clear_notification"
                      data:
                        tag: manual_mode_{{area_name}}_{{device_domain_name}}
              - conditions:
                  - alias: Turn off
                    condition: template
                    value_template: >
                      {{ trigger.event.data.action == "turn_off_manual_mode_" + area_name + "_" + device_domain_name }}
                sequence: 
                  - alias: Turn off manual mode
                    service: input_boolean.turn_off
                    entity_id: !input manual_mode_input_boolean
                  - alias: Clear sticky/ persistent notification
                    service: notify.{{group_target}}
                    data:
                      message: "clear_notification"
                      data:
                        tag: manual_mode_{{area_name}}_{{device_domain_name}}
      - conditions:
          - alias: Timer finished
            condition: template
            value_template: >
              {{ trigger.id == "timer_finished" }}
        sequence:
          - alias: Turn off manual mode
            service: input_boolean.turn_off
            entity_id: !input manual_mode_input_boolean
          - alias: Clear sticky/ persistent notification
            service: notify.{{group_target}}
            data:
              message: "clear_notification"
              data:
                tag: manual_mode_{{area_name}}_{{device_domain_name}}
      - conditions:
          - alias: Device turned on/off manually
            condition: template
            value_template: >
              {{ trigger.id == "device_manually_turned_on" }}
        sequence:
          - alias: Turn on manual mode
            service: input_boolean.turn_on
            entity_id: !input manual_mode_input_boolean
blueprint:
  name: Turn light on off
  description: Turn a light on based on a binary sensor
  domain: automation
  input:
    light_on_off_template_sensor:
      name: Light on/off template sensor
      description: Template sensor which determines on/off state off the light
      selector:
        entity:
          domain: binary_sensor
          device_class: occupancy
    target_area:
      name: Target area
      description: Name of the area where the lights should be turned on
      selector:
        target:
    target_lights:
      name: Target lights
      description: Light to target. This overrules the area
      selector:
        target:
          entity:
            domain: light
      default: []
    trigger_interval:
      name: Trigger interval
      description: Periodic check of the light state
      selector:
        number:
          min: 5
          max: 120
          unit_of_measurement: S
          mode: box
    custom_disable:
      name: Custom disabled state
      description: Custom template to disable execution
      selector:
        template:
      default: >
        {{ false }}
    manual_mode:
      name: Manual mode input boolean
      description: Input boolean which determines manual mode
      selector:
        entity:
          domain: input_boolean

mode: queued

variables:
  light_on_off_entity: !input light_on_off_template_sensor
  area: !input target_area
  lights: !input target_lights

trigger:
  - platform: state
    entity_id: !input light_on_off_template_sensor
  - platform: time_pattern
    seconds: !input trigger_interval
  # - platform: state
  #   entity_id: binary_sensor.thirty_second_time_trigger
  #   to: 
  #     - "on"
  #     - "off"

condition:
  - alias: "Light on/off template sensor not is unavailable"
    condition: not
    conditions:
      - condition: state
        entity_id: !input light_on_off_template_sensor
        state: "unavailable"
  - alias: "Custom automation disabled not active"
    condition: not
    conditions:
      - condition: template
        value_template: !input custom_disable
  - alias: "Manual mode not active"
    condition: not
    conditions:
      - condition: state
        entity_id: !input manual_mode
        state: "on"

action:
 - choose:
     - conditions:
        - alias: "Lights should be on"
          condition: template
          value_template: >
              {% from 'light.jinja' import all_lights_of_area_are %}  
              {{ is_state(light_on_off_entity, "on") }}
       sequence:
        - alias: Turn on lights
          service: light.turn_on
          target: >
            {% if lights | count > 0 %}
              {{ lights }}
            {% else %}
              {{ area }}
            {% endif %}
          data:
            transition: 5
     - conditions:
        - alias: Lights should be off
          condition: template
          value_template: >
            {% from 'light.jinja' import all_lights_of_area_are %}
            {{ is_state(light_on_off_entity, "off") }}
       sequence:
        - alias: Turn off lights
          service: light.turn_off
          target: >
            {% if lights | count > 0 %}
              {{ lights }}
            {% else %}
              {{ area }}
            {% endif %}
          data:
            transition: 5

Again, because you’re not looking at the correct context in the second automation. Not to mention, you’re looking at the context in the trigger itself, which is just plain wrong. Context is not populated until the trigger has occurred. I.e. context can only be obtained from the triggers themselves in the variable section or after.

Welp. Due to all the testing i’ve pasted the wrong trigger. The trigger in above automation was initially checking the context of the device that has been changed. Which also doesn’t work.

If i understand you correctly i should check in the action so this is what i do now:

            - conditions:
                - alias: Device turned on/off manually
                  condition: template
                  value_template: >
                    {{ trigger.to_state.context.parent_id == None and (trigger.to_state.context.user_id == None or trigger.to_state.context.user_id != None) }}
              sequence:
                - alias: Turn on manual mode
                  service: input_boolean.turn_on
                  entity_id: !input manual_mode_input_boolean

However, it still doesn’t work. And i think it is still because the light has been turned on by an automation which has been triggered by a time pattern trigger. Which is unfortunate.

You need to watch the trace. That’s it. When you watch it with the trigger you’re looking for, you will see where the context is placed. It will either be in the trigger, in context, or potentialy burried in an object inside the trigger. It’s not in the same place for all triggers. And some triggers do not have context. Well they do, but it won’t be populated due to it’s nature.

Looking at the trace i see the context empty:

this:
  entity_id: automation.living_room_manual_mode
  state: 'on'
  attributes:
    last_triggered: '2023-06-08T12:51:17.990399+00:00'
    mode: parallel
    current: 0
    max: 10
    id: living_room_manual_mode
    friendly_name: Living room - Manual mode
  last_changed: '2023-06-08T12:36:06.409382+00:00'
  last_updated: '2023-06-08T12:51:17.998889+00:00'
  context:
    id: 01H2DGZDB6NY5KQDKKW4XW4E1V
    parent_id: 01H2DGZD9RK4HGV4SC2HECZ6Z3
    user_id: null
trigger:
  platform: template
  entity_id: light.livingroom
  from_state:
    entity_id: light.livingroom
    state: 'on'
    attributes:
      effect_list:
        - blink
        - breathe
        - okay
        - channel_change
        - candle
        - finish_effect
        - stop_effect
        - stop_hue_effect
      supported_color_modes:
        - brightness
      color_mode: brightness
      brightness: 89
      friendly_name: Livingroom
      supported_features: 44
    last_changed: '2023-06-08T12:51:00.452956+00:00'
    last_updated: '2023-06-08T12:51:00.452956+00:00'
    context:
      id: 01H2DGYVXKJ59KB773JKHA8T58
      parent_id: null
      user_id: 3c69db80ae794c8faf95ec06c2af56c0
  to_state:
    entity_id: light.livingroom
    state: 'off'
    attributes:
      effect_list:
        - blink
        - breathe
        - okay
        - channel_change
        - candle
        - finish_effect
        - stop_effect
        - stop_hue_effect
      supported_color_modes:
        - brightness
      friendly_name: Livingroom
      supported_features: 44
    last_changed: '2023-06-08T12:51:30.747682+00:00'
    last_updated: '2023-06-08T12:51:30.747682+00:00'
    context:
      id: 01H2DGZSG0KG8WB6TWD0CCXP7S
      parent_id: null
      user_id: null
  id: device_manually_turned_on
  idx: '7'
  alias: When a device has been turned on manually
  for: null
  description: light.livingroom via template
manual_timer: timer.living_room_manual_mode_time_out
area_name: Living room
device_domain_name: light

The this variable is the current automation that has been triggered and determines the manual mode. As you can see the from_state context has the user_id set which is correct as i turned on the light from the ui. The to_state context has no parent_id and no user_id when the light is turned of by another automation which is triggered by the time_pattern

Post the trigger that determines “manual”

platform: state
entity_id:
  - light.living_room_standing
to: null

So when the light changes state which happens by the time trigger in another automation. This trigger fires which contains no parent_id

Nevermind, I see the trigger above. That trigger is not correct because it’s using this.context in the trigger which is the previous context set in the state machine. And the state machine does not set context at all. You have to determine the “manual intervention” trigger logic inside a variable after the trigger occurs.

i.e. you need a trigger that just looks at the state change of the light to get the context for said light.

- platform: state
  entity_id: light.xyz