Compare commits

...

21 Commits

Author SHA1 Message Date
mike
832a71380f Bugfixes and upgrades:
- Fix bug where initial timeout wouldn't be interruptted by enabled_helper
- Turned scene repeat delay into a variable (non-input for now)
- Fixed enabled_helper==off not repeating the ON scene
- Tried clarifying some logs, aliases, comments
2024-04-26 02:28:23 -07:00
mike
7626c05ad2 hooks? 2024-02-18 22:58:02 -08:00
mike
98849314e3 hooks? 2024-02-18 22:57:30 -08:00
mike
716cf3f1b1 hooks? 2024-02-18 22:55:37 -08:00
mike
b18f09094b hooks? 2024-02-18 22:54:41 -08:00
mike
fa6bb40bc5 hooks? 2024-02-18 22:53:34 -08:00
mike
d2e97b133b hooks? 2024-02-18 22:52:24 -08:00
mike
508e3e37e9 hooks? 2024-02-18 22:51:55 -08:00
mike
09c41900b2 hooks? 2024-02-18 22:51:41 -08:00
mike
0702a851ff hooks? 2024-02-18 22:50:45 -08:00
mike
2277d37989 hooks? 2024-02-18 22:47:08 -08:00
mike
82a3508b87 hooks? 2024-02-18 22:47:04 -08:00
mike
4615be6cee Added disclaimer so I can mirror this to the WAN 2024-02-18 22:33:32 -08:00
mike
9daf98c604 Re-enable old "on before off" code 2024-01-02 02:03:10 -08:00
mike
76d8721a7c When enabled helper goes OFF, reset the countdown timer 2024-01-02 01:58:22 -08:00
mike
610acdc1c4 Add README 2023-12-27 22:22:32 -08:00
mike
1771b154e0 Scene repeating: Make a little zipper via 500ms instead of 1s retries. 2023-12-27 22:07:21 -08:00
mike
65a542484d Switch strategy: No longer "on before off" but instead just retry each scene a configurable number of times. 2023-12-27 21:59:46 -08:00
mike
914b9f7449 Refactor initial debug logging to one action 2023-12-27 21:09:03 -08:00
mike
467fcd30e3 Removing parallel scenes because they didn't help anything 2023-12-27 20:59:20 -08:00
mike
2cab185ac6 Rename yaml file 2023-12-19 23:36:49 -08:00
2 changed files with 248 additions and 146 deletions

37
README.md Normal file
View File

@ -0,0 +1,37 @@
# Mike's Motion Activated Scenes
by Mike Peralta
This is another "motion activated light" automation blueprint for [Home Assistant](https://www.home-assistant.io/).
It uses scenes rather than toggling to allow for robust behavior and attempts to adjust its delay dynamically based on activity.
It features several stages after motion is no longer detected:
* Initial delay (nothing happens)
* Notice: Configurable scene + delay
* Warning: Configurable scene + delay
* Off
The initial delay starts off at a user configured number of seconds. Each time the user interrupts the off-sequence with motion,
the delay gets multiplied by a user configured multiplier, with a user configurable limit.
When the user allows the off-sequence to complete (the off-scene activates), the delay resets to the user configured baseline.
This allows a room to stay on longer when there is frequent activity, but shorter when activity is infrequent.
The automation also obeys a toggle helper to enable/disable motion activation. The user can configure which scene to use when when disabled.
This automation can retry a scene multiple times in a row to help overcome buggy/slow networks or lights that fail to set brightness/color correctly when first turned on.
## Disclaimer
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
By using this software you agree to the disclaimer.

View File

@ -122,6 +122,21 @@ blueprint:
entity:
domain: scene
scene_repeat_count:
name: "Scene repeat/retry count"
description: >
When activating a scene, try this many times in a row.
This is sometimes useful when the network seems buggy or laggy,
or for certain lights that fail to correctly apply brightness/color when first turning on.
default: 1
selector:
number:
min: 1
max: 60
step: 1
mode: box
on_just_before_off_seconds:
name: "\"On\" just before \"Off\""
@ -137,7 +152,7 @@ blueprint:
number:
min: 1
max: 86400
step: 1
step: 0.1
mode: box
@ -145,7 +160,7 @@ blueprint:
name: "Enabled helper"
description: >
Helper variable that will control whether motion activation is enabled or disabled.
This will allow this blueprint to correctly select the "On" or "Off" scene, based on whether motion is currently detected when disabled.
This will allow the automation to correctly select the "On" or "Off" scene, based on whether motion is currently detected when disabled.
An example would be a "Toggle" helper.
default: # Allows no selection
selector:
@ -182,21 +197,23 @@ blueprint:
variables:
motion_sensor: !input motion_sensor
delay_seconds_helper: !input delay_seconds_helper
delay_seconds_default: !input delay_seconds_default
delay_seconds_multiplier: !input delay_seconds_multiplier
delay_seconds_max: !input delay_seconds_max
notice_seconds: !input notice_seconds
warning_seconds: !input warning_seconds
on_just_before_off_seconds: !input on_just_before_off_seconds
enabled_helper: !input enabled_helper
disabled_scene: !input disabled_scene
debug_mode: !input debug_mode
motion_sensor: !input motion_sensor
delay_seconds_helper: !input delay_seconds_helper
delay_seconds_default: !input delay_seconds_default
delay_seconds_multiplier: !input delay_seconds_multiplier
delay_seconds_max: !input delay_seconds_max
notice_seconds: !input notice_seconds
warning_seconds: !input warning_seconds
on_just_before_off_seconds: !input on_just_before_off_seconds
enabled_helper: !input enabled_helper
disabled_scene: !input disabled_scene
debug_mode: !input debug_mode
scene_repeat_count: !input scene_repeat_count
scene_repeat_delay_milliseconds: 500
trigger_variables:
enabled_helper: !input enabled_helper
enabled_helper: !input enabled_helper
############
@ -229,14 +246,16 @@ trigger:
action:
# Action #0
- alias: "Debug log whether the enabled_helper was set"
- alias: "Initial debug logging"
if:
- condition: template
value_template: "{{ debug_mode == true }}"
then:
- if:
- condition: template
value_template: "{{ not enabled_helper == None }}"
- alias: "Debug log whether the enabled_helper was set"
if:
- condition: template
value_template: "{{ not enabled_helper == None }}"
then:
- service: logbook.log
data:
@ -248,45 +267,26 @@ action:
name: "Enabled helper"
message: "Enabled helper is not set."
# Action #1
- alias: "Debug log the enabled_helper name"
if:
- condition: template
value_template: "{{ debug_mode == true }}"
then:
- service: logbook.log
- alias: "Debug log the enabled_helper name"
service: logbook.log
data:
name: "Enabled helper"
message: "Enabled helper name is: {{ enabled_helper }}"
# Action #2
- alias: "Debug log the current delay_seconds value"
if:
- condition: template
value_template: "{{ debug_mode == true }}"
then:
- service: logbook.log
- alias: "Debug log the current delay_seconds value"
service: logbook.log
data:
name: "Delay seconds"
message: "Delay seconds is: {{ states[delay_seconds_helper].state }}"
# Action #3
- alias: "Debug log the on_just_before_off_seconds value"
if:
- condition: template
value_template: "{{ debug_mode == true }}"
then:
service: logbook.log
data:
name: "Debug: On just before off seconds"
message: "On just before off is: {{ on_just_before_off_seconds }} "
- alias: "Debug log the on_just_before_off_seconds value"
service: logbook.log
data:
name: "Debug: On just before off seconds"
message: "On just before off is: {{ on_just_before_off_seconds }} "
# Take actions based on whether the "Enabled helper" was just toggled on or off
# Action #4
# Action #1
- if:
- condition: trigger
id:
@ -295,26 +295,35 @@ action:
then:
# Debug log
- alias: "Debug log - Enabled Helper On/Off"
if:
- condition: template
value_template: "{{ debug_mode == true }}"
- if:
- condition: template
value_template: "{{ debug_mode == true }}"
then:
- service: logbook.log
- alias: "Debug log that the enabled helper is toggled"
service: logbook.log
data:
name: "Debug: Trigger"
message: "Enabled helper was toggled, and has triggered this automation"
- alias: "Debug log - Disabled scene"
if:
- condition: template
value_template: "{{ debug_mode == true }}"
then:
- service: logbook.log
- alias: "Debug log the disabled scene"
service: logbook.log
data:
name: "Debug: Disabled scene"
message: "Disabled scene is currently set to: {{ disabled_scene }}"
# If the enabled helper is OFF, we should start by resetting
# the countdown timer to default
- alias: "If the enabled helper is OFF, reset countdown timer to default"
if:
- condition: trigger
id:
- Enabled Helper is off
then:
- alias: "Reset countdown timer when enabled helper is switched OFF."
service: input_number.set_value
data:
entity_id: !input delay_seconds_helper
value: "{{ delay_seconds_default }}"
# In "auto" mode, activate the ON or OFF scene based on whether motion is currently detected
# Also use "auto" mode if motion activation is ENABLED.
- alias: "auto mode: Activate ON scene, if motion is currently detected, otherwise activate the OFF scene"
@ -327,66 +336,92 @@ action:
entity_id: !input motion_sensor
state: "on"
then:
- parallel:
- alias: "Activate the ON scene"
service: scene.turn_on
target:
entity_id: !input scene_on
- alias: "Activate the ON scene"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_on
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
else:
- parallel:
- alias: "Activate the OFF scene"
service: scene.turn_on
target:
entity_id: !input scene_off
- stop: ""
- alias: "Activate the OFF scene"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_off
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- stop: "Enabled helper was on or auto mode was on; Determined scene to activate based on current motion."
# In ON mode, just activate the ON scene
- alias: "on mode: just activate the ON scene"
# At this point, the enabled helper is assumed to be OFF
# In disabled_scene::ON mode, just activate the ON scene
- alias: "disabled_scene::on mode: just activate the ON scene"
if:
- condition: template
value_template: "{{ disabled_scene == 'on' }}"
then:
- parallel:
- service: scene.turn_on
target:
entity_id: !input scene_on
- stop: ""
- repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_on
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- stop: "Enabled helper is off and disabled scene was ON; Set ON scene"
# In OFF mode, just activate the OF scene
- alias: "off mode: just activate the ON scene"
# In disabled_scene::OFF mode, just activate the OFF scene
- alias: "disabled_scene::off mode: just activate the OFF scene"
if:
- condition: template
value_template: "{{ disabled_scene == 'off' }}"
then:
- parallel:
- service: scene.turn_on
target:
entity_id: !input scene_off
- stop: ""
- repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_off
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- stop: "Enabled helper is off and disabled scene was OFF; Set OFF scene"
# In notice mode, just activate the NOTICE scene
- alias: "notice mode: just activate the NOTICE scene"
# In disabled_scene::notice mode, just activate the NOTICE scene
- alias: "disabled_scene::notice mode: just activate the NOTICE scene"
if:
- condition: template
value_template: "{{ disabled_scene == 'notice' }}"
then:
- parallel:
- service: scene.turn_on
target:
entity_id: !input scene_notice
- stop: ""
- repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_notice
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- stop: "Enabled helper is off and disabled scene was NOTICE; Set NOTICE scene"
# In warning mode, just activate the WARNING scene
- alias: "warning mode: just activate the WARNING scene"
# In disabled_scene::warning mode, just activate the WARNING scene
- alias: "disabed_scene::warning mode: just activate the WARNING scene"
if:
- condition: template
value_template: "{{ disabled_scene == 'warning' }}"
then:
- parallel:
- service: scene.turn_on
target:
entity_id: !input scene_warning
- stop: ""
- repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_warning
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- stop: "Enabled helper is off and disabled scene was WATNING; Set WARNING scene"
# Don't do anything after processing the enabled_helper triggers
- stop: "Done handling enabled helper"
@ -395,6 +430,7 @@ action:
# Take actions based on whether Motion is detected
# Action #2
- alias: "Refuse to do anything if the enabled-helper says we're OFF."
if:
- condition: template
@ -423,16 +459,21 @@ action:
name: "Motion On"
message: "Motion was detected."
- parallel:
- alias: "Activate the ON scene"
service: scene.turn_on
target:
entity_id: !input scene_on
- alias: "Activate the ON scene"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_on
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
# Motion is not detected
else:
# Debug log
- alias: "Debug Log that the motion-off sequence will run, due to motion no longer detected."
if:
- condition: template
@ -443,11 +484,10 @@ action:
name: "Motion Off"
message: "Initiating motion-off sequence, because motion no longer detected."
# Initial period where nothing happens
- alias: "Delay appropriately before doing anything"
wait_template: "{{ states[motion_sensor].state == 'on' }}"
timeout: "{{ states[delay_seconds_helper].state or (enabled_helper != None and is_state(enabled_helper, 'off') ) }}"
wait_template: "{{ states[motion_sensor].state == 'on' or (enabled_helper != None and is_state(enabled_helper, 'off') ) }}"
timeout: "{{ states[delay_seconds_helper].state }}"
- alias: "Quit now if the enabled-helper went OFF"
if:
@ -456,9 +496,10 @@ action:
- condition: template
value_template: "{{ is_state( enabled_helper, 'off' ) }}"
then:
stop: "Quitting early because the enabled-helper went OFF."
stop: "Interrupted initial delay because the enabled helper turned OFF."
- if:
- alias: "Motion detected again during initial delay; Interrupt initial delay"
if:
- condition: state
entity_id: !input motion_sensor
state: "on"
@ -486,11 +527,17 @@ action:
data:
name: "Begin notice period"
message: "Begin notice period; Waiting while motion is still off."
- parallel:
- alias: "Activate Scene: Notice"
service: scene.turn_on
target:
entity_id: !input scene_notice
- alias: "Activate Scene: Notice"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_notice
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- alias: "Delay during the notice period"
wait_template: "{{ states[motion_sensor].state == 'on' or (enabled_helper != None and is_state('enabled_helper', 'off') ) }}"
timeout: "{{ notice_seconds }}"
@ -502,19 +549,21 @@ action:
- condition: template
value_template: "{{ is_state( enabled_helper, 'off' ) }}"
then:
stop: "Quitting early because the enabled-helper went OFF."
stop: "Interrupting NOTICE period because the enabled-helper went OFF."
- if:
- condition: state
entity_id: !input motion_sensor
state: "on"
- alias: "If motion interrupted the NOTICE period, activate the ON scene"
if:
- condition: state
entity_id: !input motion_sensor
state: "on"
then:
- parallel:
- alias: "Activate Scene: On"
service: scene.turn_on
target:
entity_id: !input scene_on
- alias: "Activate Scene: On"
service: scene.turn_on
target:
entity_id: !input scene_on
- alias: "Increase countdown timer when user interrupts the notice period."
service: input_number.set_value
data:
@ -567,16 +616,21 @@ action:
data:
name: "Begin warning period"
message: "Begin warning period; Waiting while motion is still off."
- parallel:
- alias: "Activate Scene: Warning"
service: scene.turn_on
target:
entity_id: !input scene_warning
- alias: "Activate Scene: Warning"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_warning
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- alias: "Delay during the warning period"
wait_template: "{{ states[motion_sensor].state == 'on' or (enabled_helper != None and is_state('enabled_helper', 'off') ) }}"
timeout: "{{ warning_seconds }}"
- alias: "Quit now if the enabled-helper went OFF"
if:
- condition: template
@ -584,25 +638,31 @@ action:
- condition: template
value_template: "{{ is_state( enabled_helper, 'off' ) }}"
then:
stop: "Quitting early because the enabled-helper went OFF."
stop: "Interrupting the WARNING sequence because the enabled-helper went OFF."
- if:
- condition: state
entity_id: !input motion_sensor
state: "on"
- alias: "If motion interrupts the WARNING sequence, just activate the ON scene"
if:
- condition: state
entity_id: !input motion_sensor
state: "on"
then:
- parallel:
- alias: "Activate Scene: On"
service: scene.turn_on
target:
entity_id: !input scene_on
- alias: "Activate Scene: On"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_on
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- alias: "Double countdown timer when user interrupts the warning period."
service: input_number.set_value
data:
entity_id: !input delay_seconds_helper
value: "{{ states[delay_seconds_helper].state | int * delay_seconds_multiplier }}"
- alias: "Limit delay seconds to its defined maximum"
if:
- condition: template
@ -620,6 +680,7 @@ action:
data:
name: "Limiting delay seconds"
message: "Delay limited to max: {{ states[delay_seconds_helper].state }}"
- alias: "Log new delay seconds"
if:
- condition: template
@ -672,11 +733,15 @@ action:
name: "Won't run on-before-off"
message: "Won't run on-before-off because seconds was less than 1"
- parallel:
- alias: "Activate Scene: Off (Done)"
service: scene.turn_on
target:
entity_id: !input scene_off
- alias: "Activate Scene: Off (Done)"
repeat:
count: "{{ scene_repeat_count }}"
sequence:
- service: scene.turn_on
target:
entity_id: !input scene_off
- delay:
milliseconds: "{{ scene_repeat_delay_milliseconds }}"
- alias: "Reset countdown timer after turning off lights."
service: input_number.set_value