######################## ### Blueprint definition ######################## mode: queued max: 25 blueprint: name: "Mike's Motion Activated Scenes" description: "Activate scenes based on motion with notice and warning phases, plus an increasing delay when motion is continuously detected." domain: automation input: motion_sensor: name: "Motion Sensor" description: "Entity ID of the motion sensor" selector: entity: domain: binary_sensor # for some reason, using a device_class of motion_sensor ends up hiding the actual motion sensors # device_class: motion_sensor delay_seconds_helper: name: "Delay seconds (helper)" description: "The delay before the motion-off sequence will be initiated. Must be a helper so it can persist across activations." selector: entity: domain: input_number delay_seconds_default: name: "Default delay seconds" description: "The default delay before the motion-off sequence begins. Will be used to reset the running delay." default: 30 selector: number: min: 1 max: 86400 step: 1 mode: box delay_seconds_multiplier: name: "Delay seconds multiplier" description: "When the off sequence gets interrupted by new motion, multiply the delay seconds by this number. A decent value might be 2." default: 2.0 selector: number: min: 1.0 max: 1000.0 step: 0.1 mode: box delay_seconds_max: name: "Maximum delay seconds" description: "The maximum delay during no motion before the motion-off sequence begins." default: 3600 selector: number: min: 5 max: 3000000 step: 1 mode: box notice_seconds: name: "Notice seconds" description: "The number of seconds to wait in the Notice scene." default: 15 selector: number: min: 1 max: 86400 step: 1 mode: box warning_seconds: name: "Warning seconds" description: "The number of seconds to wait in the Warning scene." default: 15 selector: number: min: 1 max: 86400 step: 1 mode: box scene_on: name: "On Scene" description: "The scene to activate when motion is detected" selector: entity: domain: scene scene_notice: name: "Notice Scene" description: "The first scene to activate when motion is no longer detected (notice phase)." selector: entity: domain: scene scene_warning: name: "Warning Scene" description: "The second scene to activate when motion is no longer detected (warning phase)." selector: entity: domain: scene scene_off: name: "Off Scene" description: "The scene to activate when the user has failed to produce motion, and the light should be considered \"off\"." selector: 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\"" description: > When the "Off" scene is about to be shown, you may wish to quickly show the "On" scene for a number of seconds just before. This is sometimes helpful to mitigate an issue where the "On" scene doesn't apply all colors and settings correctly from an "Off" state. Set this to a value greater than 0 to enable. A value of 0 will disable this feature. Note that sometimes, a value that is too short (e.g., 1) may also cause issues. default: 1 selector: number: min: 1 max: 86400 step: 0.1 mode: box enabled_helper: name: "Enabled helper" description: > Helper variable that will control whether motion activation is enabled or 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: entity: domain: input_boolean disabled_scene: name: "Disabled scene" description: > Choose the scene that will be activated when the "enabled_helper" is set to off. In the "auto" mode, the "on" or "off" scene will be chosen based on whether motion is currently detected. selector: select: options: - "auto" - "on" - "off" - "notice" - "warning" debug_mode: name: "Debug mode." description: "Enable debug mode, which increases logging." default: false selector: boolean: {} ############# ### Variables ############# 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 scene_repeat_count: !input scene_repeat_count scene_repeat_delay_milliseconds: 500 trigger_variables: enabled_helper: !input enabled_helper ############ ### Triggers ############ trigger: - platform: state id: Motion changed entity_id: - !input motion_sensor to: - "on" - "off" - platform: template id: "Enabled helper is on" value_template: "{{ (enabled_helper != None) and is_state( enabled_helper, 'on' ) }}" - platform: template id: "Enabled helper is off" value_template: "{{ (enabled_helper != None) and is_state( enabled_helper, 'off' ) }}" ########### ### Actions ########### action: # Action #0 - alias: "Initial debug logging" if: - condition: template value_template: "{{ debug_mode == true }}" then: - alias: "Debug log whether the enabled_helper was set" if: - condition: template value_template: "{{ not enabled_helper == None }}" then: - service: logbook.log data: name: "Enabled helper" message: "Enabled helper is: {{ enabled_helper }}" else: - service: logbook.log data: name: "Enabled helper" message: "Enabled helper is not set." - alias: "Debug log the enabled_helper name" service: logbook.log data: name: "Enabled helper" message: "Enabled helper name is: {{ enabled_helper }}" - alias: "Debug log the current delay_seconds value" service: logbook.log data: name: "Delay seconds" message: "Delay seconds is: {{ states[delay_seconds_helper].state }}" - 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 #1 - if: - condition: trigger id: - Enabled helper is on - Enabled helper is off then: # Debug log - if: - condition: template value_template: "{{ debug_mode == true }}" then: - 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 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" if: - condition: template value_template: "{{ disabled_scene == 'auto' or (enabled_helper != None and is_state( enabled_helper, 'on' )) }}" then: - if: - condition: state entity_id: !input motion_sensor state: "on" then: - 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: - 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." # 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: - 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 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: - 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 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: - 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 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: - 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" # 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 value_template: "{{ enabled_helper != None }}" - condition: template value_template: "{{ not is_state( enabled_helper, 'on' ) }}" then: stop: "Refusing to continue because the enabled-helper is OFF." - if: - condition: state entity_id: !input motion_sensor state: "on" # Motion is detected then: - alias: "Debug Log that motion was detected." if: - condition: template value_template: "{{ debug_mode == true }}" then: service: logbook.log data: name: "Motion On" message: "Motion was detected." - 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 value_template: "{{ debug_mode == true }}" then: service: logbook.log data: 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' 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: - condition: template value_template: "{{ enabled_helper != None }}" - condition: template value_template: "{{ is_state( enabled_helper, 'off' ) }}" then: stop: "Interrupted initial delay because the enabled helper turned OFF." - alias: "Motion detected again during initial delay; Interrupt initial delay" if: - condition: state entity_id: !input motion_sensor state: "on" then: - stop: "Motion detected again during initial delay." - alias: "Log that the initial delay has finished." if: - condition: template value_template: "{{ debug_mode == true }}" then: service: logbook.log data: name: "Motion Off" message: "Finished initial no-motion delay" # Notice period, where the notice scene is shown - alias: "Log that the notice period has begun." if: - condition: template value_template: "{{ debug_mode == true }}" then: service: logbook.log data: name: "Begin notice period" message: "Begin notice period; Waiting while motion is still off." - 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 }}" - alias: "Quit now if the enabled-helper went OFF" if: - condition: template value_template: "{{ enabled_helper != None }}" - condition: template value_template: "{{ is_state( enabled_helper, 'off' ) }}" then: stop: "Interrupting NOTICE period because the enabled-helper went OFF." - alias: "If motion interrupted the NOTICE period, activate the ON scene" if: - condition: state entity_id: !input motion_sensor state: "on" then: - 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: 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 value_template: "{{ states[delay_seconds_helper].state | int > delay_seconds_max | int }}" then: - service: input_number.set_value data: entity_id: !input delay_seconds_helper value: "{{ delay_seconds_max | int }}" - if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "Limiting delay seconds" message: "Delay limited to max: {{ states[delay_seconds_helper].state }}" - alias: "Log new delay" if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "New delay" message: "New delay is {{ states[delay_seconds_helper].state }} seconds" - stop: "Motion detected during notice period." - if: condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "Notice period finished." message: "Notice period has finished (motion is still off)." # Warning period, just before the light turns off. The warning scene is shown. - alias: "Log that the warning period has begun." if: - condition: template value_template: "{{ debug_mode == true }}" then: service: logbook.log data: name: "Begin warning period" message: "Begin warning period; Waiting while motion is still off." - 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 value_template: "{{ enabled_helper != None }}" - condition: template value_template: "{{ is_state( enabled_helper, 'off' ) }}" then: stop: "Interrupting the WARNING sequence because the enabled-helper went OFF." - alias: "If motion interrupts the WARNING sequence, just activate the ON scene" if: - condition: state entity_id: !input motion_sensor state: "on" then: - 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 value_template: "{{ states[delay_seconds_helper].state | int > delay_seconds_max | int }}" then: - service: input_number.set_value data: entity_id: !input delay_seconds_helper value: "{{ delay_seconds_max | int }}" - if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "Limiting delay seconds" message: "Delay limited to max: {{ states[delay_seconds_helper].state }}" - alias: "Log new delay seconds" if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "New delay seconds" message: "New delay is {{ states[delay_seconds_helper].state }} seconds." - stop: "Motion detected during the warning period." - if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "Warning period finished." message: "Warning period has finished and motion is still off." # Finally, we decide to actually turn off the lights with the "Off" scene. # Maybe turn on the "On" scene just before the "Off" scene, if the user enabled this option - if: - condition: template value_template: "{{ on_just_before_off_seconds > 0 }}" then: - alias: "Log the number of on-before-off seconds" if: - condition: template value_template: "{{ debug_mode == true }}" then: service: logbook.log data: name: "On-Before-Off seconds" message: "Will turn on for {{ on_just_before_off_seconds }} seconds before off." - service: scene.turn_on target: entity_id: !input scene_on - alias: "Wait a second to set the \"On\" scene." delay: seconds: "{{ on_just_before_off_seconds }}" else: - if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "Won't run on-before-off" message: "Won't run on-before-off because seconds was less than 1" - 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 data: entity_id: !input delay_seconds_helper value: "{{ delay_seconds_default }}" - if: - condition: template value_template: "{{ debug_mode == true }}" then: - service: logbook.log data: name: "Turning off light, due to motion off." message: "Activated off-scene and reset delay."