Here are the scripts I used to undo changes of key settings by users of the home assistant app.
There are two separate scripts. This is because I needed separate behaviour: switches and inputs need to be turned back off if they are turned on. And scripts, automations and schedules need to be turned back on if they are turned off. I didn’t bother to factor them together, so they are 2 separate scripts with ‘off’ and ‘on’ basically reversed.
I wasn’t able to completely prevent the switch, so the approach I took was to monitor for changes in state, and then immediately revert the change. Several times… to counteract attempts to brute force the system. Gotta give them credit for being persistent at trying to break it …
I also send a message if attempts are made to change the settings.
If you want to use these, replace the ‘parent’ and ‘childX’ with the actual user names.
Switch monitor
- id: '1670954893000'
alias: Switch sentry - disallow turning on by certain users
description: ''
trigger:
- platform: state
entity_id:
- input_boolean.boolean1
- input_boolean.boolean2
- input_select.select1
- switch.switch1
to: 'on'
action:
- variables:
user_id: '{{ trigger.to_state.context.user_id }}'
entity: '{{ trigger.entity_id }}'
from_state: '{{ trigger.from_state.state }}'
to_state: '{{ trigger.to_state.state }}'
changed_by_user: "{% if trigger.to_state.context.user_id is defined %}\n {{
states.person|selectattr(\n \"attributes.user_id\", \"==\", trigger.to_state.context.user_id\n
\ )|map(attribute=\"attributes.friendly_name\")|first\n }}\n{% endif %}
\ \n"
- alias: Stop if no user caused the trigger
condition:
- ' {{ user_id != None }} '
- alias: Get user who changed
variables:
changed_by_user: "{{ states.person|selectattr(\n \"attributes.user_id\",
\"==\", trigger.to_state.context.user_id\n )|map(attribute=\"attributes.friendly_name\")|first\n}}\n"
- alias: Determine if change was allowed
variables:
forbidden: '{{ changed_by_user in disallowed_users }}
'
- service: system_log.write
data:
message: 'User id who triggered: {{ user_id }}
User who triggered: {{ changed_by_user }}
Forbidden: {{ forbidden }}
'
level: error
- if:
- ' {{ forbidden }} '
then:
- service: system_log.write
data:
level: info
message: 'User who triggered: {{ trigger.to_state.context.user_id }}
Disallowed? {{ (trigger.to_state.context.user_id != None and states.person|selectattr("attributes.user_id",
"==", trigger.to_state.context.user_id)|map(attribute="attributes.friendly_name")|first
in disallowed_users) }}
'
- service: script.notify_people
data:
person: parent
title: Forbidden change made by {{ changed_by_user }}
message: '{{ entity }} {{ from_state }} -> {{ to_state }}
'
- service: homeassistant.turn_off
target:
entity_id: '{{ entity }}'
- wait_template: '{{ is_state(entity, ''on'') }}'
alias: Wait just in case the switch turn on was triggered
timeout: 00:00:30
continue_on_timeout: false
- service: homeassistant.turn_off
target:
entity_id: '{{ entity }}'
- wait_template: '{{ is_state(entity, ''on'') }}'
alias: Wait just in case the switch turn on was triggered
timeout: 00:00:30
continue_on_timeout: false
- service: homeassistant.turn_off
target:
entity_id: '{{ entity }}'
variables:
disallowed_users:
- child1
- child2
mode: parallel
max: 20
Second script for automations, scripts and schedules, where I replaced off with on:
- id: '1670954893001'
alias: Script monitor - disallow turning off
description: ''
trigger:
- platform: state
entity_id:
- script.script1
- automation.automation1
- automation.automation2
- switch.schedule_d2cdb8
to: 'off'
condition: []
action:
- variables:
user_id: '{{ trigger.to_state.context.user_id }}'
entity: '{{ trigger.entity_id }}'
from_state: '{{ trigger.from_state.state }}'
to_state: '{{ trigger.to_state.state }}'
changed_by_user: "{% if trigger.to_state.context.user_id is defined %}\n {{
states.person|selectattr(\n \"attributes.user_id\", \"==\", trigger.to_state.context.user_id\n
\ )|map(attribute=\"attributes.friendly_name\")|first\n }}\n{% endif %}
\ \n"
- alias: Stop if no user caused the trigger
condition:
- ' {{ user_id != None }} '
- alias: Get user who changed
variables:
changed_by_user: "{{ states.person|selectattr(\n \"attributes.user_id\",
\"==\", trigger.to_state.context.user_id\n )|map(attribute=\"attributes.friendly_name\")|first\n}}\n"
- alias: Determine if change was allowed
variables:
forbidden: '{{ changed_by_user in disallowed_users }}
'
- service: system_log.write
data:
message: 'User id who triggered: {{ user_id }}
User who triggered: {{ changed_by_user }}
Forbidden: {{ forbidden }}
'
level: error
- if:
- ' {{ forbidden }} '
then:
- service: system_log.write
data:
level: info
message: 'User who triggered: {{ trigger.to_state.context.user_id }}
Disallowed? {{ (trigger.to_state.context.user_id != None and states.person|selectattr("attributes.user_id",
"==", trigger.to_state.context.user_id)|map(attribute="attributes.friendly_name")|first
in disallowed_users) }}
'
- service: script.notify_people
data:
person: parent
title: Forbidden change made by {{ changed_by_user }}
message: '{{ entity }} {{ from_state }} -> {{ to_state }}
'
- service: homeassistant.turn_on
target:
entity_id: '{{ entity }}'
- wait_template: '{{ is_state(entity, ''off'') }}'
alias: Wait just in case the switch turn off was triggered
timeout: 00:00:30
continue_on_timeout: false
- service: homeassistant.turn_on
target:
entity_id: '{{ entity }}'
- wait_template: '{{ is_state(entity, ''off'') }}'
alias: Wait just in case the switch turn off was triggered
timeout: 00:00:30
continue_on_timeout: false
- service: homeassistant.turn_on
target:
entity_id: '{{ entity }}'
mode: parallel
variables:
disallowed_users:
- child1
- child2
max: 20
The notify_people
script is a script I use to send notifications to phones etc., but not if the system is set to quiet mode. It looks like this:
alias: Notify people
mode: parallel
icon: mdi:message-alert
max: 30
description: Send notification to the given people or group
fields:
person:
description: Who should receive the notification
selector:
select:
options:
- parent1
- parent2
- parents
- bedrooms
- tv
- all
title:
description: The title of the notification
example: State change
selector:
text: null
message:
description: The message content
example: The light is on!
selector:
text:
multiline: true
sequence:
- condition: state
entity_id: input_boolean.quiet_mode
state: "off"
- if:
- "{{ person in ['all'] }}"
then:
- service: notify.mobile_app_parent1
data:
title: "{{ title }}"
message: "{{ message }}"
- service: notify.mobile_app_parent2
data:
title: "{{ title }}"
message: "{{ message }}"
- if:
- "{{ person in ['all', 'tv'] }}"
then:
- service: notify.tv1
data:
title: "{{ title }}"
message: "{{ message }}"