Skip to content

Instantly share code, notes, and snippets.

@FrankTub
Last active September 10, 2025 11:11
Show Gist options
  • Save FrankTub/ba9af2b147a8b6795d9b93299a82aab1 to your computer and use it in GitHub Desktop.
Save FrankTub/ba9af2b147a8b6795d9b93299a82aab1 to your computer and use it in GitHub Desktop.
Wasp principle for HA
blueprint:
name: Occupancy
description: |
This blueprint implements the 'Wasp in a Box' principle, a method for detecting room occupancy based on motion and door sensors. It was inspired by an AppDaemon app with a similar approach.
You can find more information about this blueprint on the Home Assistant Community: https://community.home-assistant.io/t/occupancy-blueprint/477772.
Original credits go to [Alex Babel](https://community.home-assistant.io/u/alexbabel/summary).
Given that you already use a door and motion sensor this blueprint allows some additional configuration for scenario's where you want to deviate from the wasp in a box principle.
- (Optional) Clear occupancy on closing the door after a delay period
- (Optional) When door was opened and multiple motions have been detected a different turn off delay can be used (Open door mode)
Some sample use cases aside from the wasp in a box:
- For a toilet door you typically only close the door when entering, the next time you close the door you have left the room and the occupancy can be cleared right away -> Turn on "Clear occupancy on closing door after delay period"
- For a laundry room where you might not close the door you might want to have a different turn off delay when the door is open and multiple motions have been detected. For this take a look at ‘Open door mode’.
domain: automation
source_url: https://gist.github.com/FrankTub/ba9af2b147a8b6795d9b93299a82aab1
input:
required_inputs:
name: "Required inputs"
icon: mdi:list-box-outline
collapsed: false
input:
door_sensor:
name: Single Door Sensor or Door Sensor Group
selector:
entity:
filter:
- domain: binary_sensor
- domain: input_boolean
motion_sensor:
name: Single Motion Sensor or Motion Sensor Group
selector:
entity:
filter:
- domain: binary_sensor
- domain: input_boolean
occupancy_helper:
name: Occupancy Helper (Type input_boolean)
description: This helper tracks occupancy status and can be used as a trigger for automations, such as turning lights on or off.
selector:
entity:
domain: input_boolean
multiple: false
last_motion_helper:
name: Last Motion Helper (Type date and time)
description: This helper logs the last motion detection time to ensure occupancy clears correctly when the door is closed.
selector:
entity:
domain: input_datetime
multiple: false
configuration_settings:
name: "Configuration settings"
icon: mdi:tools
collapsed: true
input:
motion_sensor_turn_off_delay:
name: Motion Sensor turn off delay
description: "Time after the motion sensor no longer detects motion to set the occupancy to clear. (Default = 5s)"
default: 5
selector:
number:
mode: box
min: 0
max: 3600
unit_of_measurement: seconds
step: 1.0
door_sensor_turn_off_delay:
name: Door Sensor turn off delay
description: "In order to automatically set the occupancy to clear when the door was opened and left open. This only clears the occupancy when there was no motion detected between opening the door and the period of this turn off delay period. (Default = 15s)"
default: 15
selector:
number:
mode: box
min: 0
max: 3600
unit_of_measurement: seconds
step: 1.0
motion_sensor_delay:
name: Motion Sensor Delay
description: |
The time the motion sensor takes before clearing its detected state. (Default=-1 [Disabled])
It is recommended to add a small buffer to prevent false positives. For example, if your motion sensor has a 10-second delay, set this to 12s.
default: -1
selector:
number:
mode: box
min: -1
max: 3600
unit_of_measurement: seconds
step: 1.0
start_on_opening_door:
name: Start occupancy on opening the door
description: |
Turn on occupancy when the door is opened.
default: true
selector:
boolean:
reset_on_closing_door_after_delay_period:
name: Clear occupancy on closing door after delay period
description: |
Turn off occupancy when the door is closed after the greatest delay period (meaning Motion Sensor Delay + Motion Sensor turn off delay OR Door Sensor turn off delay) with a minimum of 20 seconds after the occupancy was turned on. This can be useful for doors that are not used frequently and prevent guests from doubting if they have to manually turn off a light, e.g. a toilet door. Other scenario where this could be usefull is a room where you don't necessarily close the door while using the room and when you leave the room you typically close the door, e.g. a laundry room.
default: false
selector:
boolean:
open_door_mode:
name: "Open door mode"
icon: mdi:walk
collapsed: true
input:
open_door_motion_counter:
name: Motion counter with door open (Type input_number)
description: (Optional) Create an input_number with min=0, max=100, step=1 and select it here. This counts the number of motion detections while the door is open. Can be usefull for scenario's where someone likes to use the toilet with the door open
default: null
selector:
entity:
filter:
- domain: input_number
multiple: false
open_door_turn_off_delay:
name: Open door mode turn-off delay
description: (Optional) Timeout to clear occupancy once in open door mode. This is only used if you have provided above option (Motion counter with door open) and the door is open and at least 2 times motion has been detected.
default: 300
selector:
number:
mode: box
min: 1
max: 3600
unit_of_measurement: seconds
step: 1.0
trigger_variables:
motion_sensor: !input motion_sensor
motion_sensor_delay: !input motion_sensor_delay
variables:
door_sensor: !input door_sensor
last_motion_helper: !input last_motion_helper
start_on_opening_door: !input start_on_opening_door
reset_on_closing_door_after_delay_period: !input reset_on_closing_door_after_delay_period
occupancy_helper: !input occupancy_helper
motion_sensor_turn_off_delay: !input motion_sensor_turn_off_delay
door_sensor_turn_off_delay: !input door_sensor_turn_off_delay
open_door_motion_counter: !input open_door_motion_counter
open_door_motion_count_threshold: 2
open_door_turn_off_delay: !input open_door_turn_off_delay
open_door_motion_counter_enabled: "{{ open_door_motion_counter is not none }}"
trigger:
- platform: state
entity_id: !input motion_sensor
from: "off"
to: "on"
id: motion
- platform: template
value_template: "{{ states(motion_sensor) in ['true', 'on'] }}"
enabled: "{{ motion_sensor_delay > 0 }}"
for:
seconds: !input motion_sensor_delay
id: motion_still_on
- platform: state
entity_id: !input door_sensor
id: door_opened
from: "off"
to: "on"
- platform: state
entity_id: !input door_sensor
id: door_closed
from: "on"
to: "off"
- platform: state
entity_id: !input motion_sensor
from: "on"
to: "off"
for: !input motion_sensor_turn_off_delay
id: no_motion
- platform: state
entity_id: !input door_sensor
to: "on"
for: !input door_sensor_turn_off_delay
id: door_left_open
- platform: state
entity_id: !input motion_sensor
from: "on"
to: "off"
for: !input open_door_turn_off_delay
id: open_door_mode_no_motion
condition: []
action:
- choose:
############################################################
# ANCHOR: Turning occupancy on
############################################################
- conditions:
- "{{ trigger.id in ['motion', 'motion_still_on'] }}"
sequence:
- if:
- "{{ trigger.id == 'motion' }}"
then:
- service: input_datetime.set_datetime
data:
timestamp: "{{ as_timestamp(now()) }}"
entity_id: "{{ last_motion_helper }}"
- if:
- "{{ trigger.id == 'motion' }}"
- "{{ states(occupancy_helper) in ['false', 'off'] }}"
then:
- service: input_boolean.turn_on
data:
entity_id: "{{ occupancy_helper }}"
- if:
- "{{ open_door_motion_counter_enabled }}"
- "{{ states(door_sensor) in ['true', 'on'] }}"
then:
- service: input_number.increment
target:
entity_id: "{{ open_door_motion_counter }}"
- conditions:
- "{{ trigger.id == 'door_opened' }}"
sequence:
- if:
- "{{ states(occupancy_helper) in ['false', 'off'] }}"
- "{{ start_on_opening_door }}"
then:
- service: input_boolean.turn_on
data:
entity_id: "{{ occupancy_helper }}"
############################################################
# ANCHOR: Turning occupancy off
############################################################
- conditions:
- "{{ states(occupancy_helper) in ['true', 'on'] }}"
- or:
- and:
- "{{ trigger.id == 'no_motion' }}"
- or:
- and:
- "{{ states(door_sensor) in ['true', 'on', 'unavailable', 'unknown'] }}"
- or:
- "{{ open_door_motion_counter_enabled and states(open_door_motion_counter) | int < open_door_motion_count_threshold }}"
- "{{ not open_door_motion_counter_enabled }}"
- and:
- "{{ states(door_sensor) in ['false', 'off'] }}"
- "{{ states[door_sensor].last_changed > as_local(as_datetime(states(last_motion_helper))) }}"
- "{{ motion_sensor_delay == -1 or (motion_sensor_delay > -1 and states[door_sensor].last_changed >= states[motion_sensor].last_changed - timedelta(seconds=motion_sensor_delay)) }}"
- and:
- "{{ trigger.id == 'open_door_mode_no_motion' }}"
- "{{ open_door_motion_counter_enabled }}"
- "{{ states(door_sensor) in ['true', 'on', 'unavailable', 'unknown'] }}"
- "{{ now().timestamp() - states[occupancy_helper].last_changed.timestamp() >= open_door_turn_off_delay }}"
- and:
- or:
- "{{ trigger.id == 'door_closed' }}"
- and:
- "{{ trigger.id == 'door_left_open' }}"
- or:
- "{{ open_door_motion_counter_enabled and states(open_door_motion_counter) | int < open_door_motion_count_threshold }}"
- "{{ not open_door_motion_counter_enabled }}"
- "{{ states(motion_sensor) in ['false', 'off'] }}"
- "{{ states[door_sensor].last_changed > as_local(as_datetime(states(last_motion_helper))) }}"
- and:
- "{{ trigger.id == 'door_closed' }}"
- "{{ reset_on_closing_door_after_delay_period }}"
- "{{ now().timestamp() - states[occupancy_helper].last_changed.timestamp() > max(door_sensor_turn_off_delay, max(0, motion_sensor_delay) + motion_sensor_turn_off_delay, 20) }}"
sequence:
- service: input_boolean.turn_off
data:
entity_id: "{{ occupancy_helper }}"
default: []
############################################################
# ANCHOR: Resetting number of motions if necessary
############################################################
- if:
- "{{ open_door_motion_counter_enabled }}"
- or:
- and:
- "{{ states(occupancy_helper) in ['false','off'] }}"
- or:
- "{{ states(door_sensor) in ['false','off'] }}"
- "{{ states(open_door_motion_counter) | int >= open_door_motion_count_threshold }}"
- "{{ trigger.id in ['door_closed', 'door_opened'] }}"
then:
- service: input_number.set_value
target:
entity_id: "{{ open_door_motion_counter }}"
data:
value: 0
mode: queued
max: 10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment