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
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.
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:
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.
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.
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:
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.
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
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.