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
So to recap, this logic is not correct:
It’s a chicken-egg scenario. You’re not accessing the context you think you’re accessing.