Skip to content

Instantly share code, notes, and snippets.

@Ltek
Last active December 11, 2025 20:13
Show Gist options
  • Select an option

  • Save Ltek/f6e517ac49d3e9ecd58714c2b9afe7d3 to your computer and use it in GitHub Desktop.

Select an option

Save Ltek/f6e517ac49d3e9ecd58714c2b9afe7d3 to your computer and use it in GitHub Desktop.
Home Assistant Blueprint: Auto-Lock Door Monitor by LTek
blueprint:
name: "🔐 Auto-Lock Door Monitor by LTek"
description: |
Auto-Lock Door Monitor monitors door openings and closings. Allos for custom schedules and timings to locks the doors and send notifications for any phase of the automation.
🚀 Version 2025.12.10.10c
Author: LTek
***Features***
🚪 Door Sensor state monitoring to ensure door is closed before attempting to lock
🎯 Customizable Delays and Timeouts for Actions with Retry logic (for lock jams or misalignments)
🛑 Bypass entity - completely disable the automation based on state of an entitity of your choosing
🧭️ 3 Disable Schedules - Day and Time to Disable the activity
⏰ Schedule End Triggers - Check and lock door when a schedule ends
📢 Custom Notifications for all phases of the automation
📱 Mobile Notifications with iOS and Android specific options
📌 Persistent Web UI Notifications
source_url: https://gist.github.com/Ltek/f6e517ac49d3e9ecd58714c2b9afe7d3
domain: automation
input:
# Core Settings Section
door_lock:
name: "Door Lock"
description: "Lock entity to control"
selector:
entity:
domain: lock
door_sensor:
name: "Door Sensor"
description: "Binary Sensor that shows door open/closed state"
selector:
entity:
domain: binary_sensor
counter_variable:
name: "Retry Counter"
description: "Counter Helper to track attempts. note: you manually create this in HA"
default: "counter.door_lock_retries"
selector:
entity:
domain: counter
bypass_entity:
name: "Bypass Entity (optional)"
description: "Automation will not run when this Entity is ON"
default: ""
selector:
entity:
multiple: false
max_retries:
name: "Maximum Retries"
description: "Number of lock attempts after door is detected to be closed. Helpful with lock jams or alignment issues."
default: 3
selector:
number:
min: 1
max: 10
step: 1
door_close_timeout:
name: "Wait for Door to Close (minutes)"
description: "After initial door open (automation starts) we wait for the door to close. If door does not close within this time, the automation stops running (timesout). Uses Door Sensor you selected."
default: 1
selector:
number:
min: 1
max: 15
step: 1
verification_timeout:
name: "Door Closed Verification Time"
description: |
After the door closes, wait this amount of time then recheck if door is still closed.
Helpful in case you quickly reopen and close the door again after the initial close or frequently need to open and close a door for a while before locking it.
Range is 10 to 600 - see below to select if the value is in seconds or minutes.
default: 60
selector:
number:
min: 10
max: 600
step: 1
verification_timeout_unit:
name: "Unit type to use for Door Closed Verification Time"
description: "Time unit for the delay after door closes"
default: "seconds"
selector:
select:
options:
- label: "Seconds"
value: "seconds"
- label: "Minutes"
value: "minutes"
# Disable Schedule 1 Section (collapsible) - INPUTS NESTED INSIDE
disable_schedule_1:
name: "🧭 Disable Schedule 1"
description: Days and Time automation is disabled
collapsed: true
input:
disable_schedule_1_enabled:
name: "Use Schedule 1?"
description: "Turn on/off this Disable Schedule"
default: false
selector:
boolean: {}
enable_schedule_1_end_trigger:
name: "Use End Trigger?"
description: "Run automation at schedule end to lock door if unlocked"
default: true
selector:
boolean: {}
disable_schedule_1_start_time:
name: "Start Time"
description: "Automation is Disabled starting at this time"
default: "22:00:00"
selector:
time: {}
disable_schedule_1_end_time:
name: "End Time"
description: "Automation is Re-Enabled at this time"
default: "06:00:00"
selector:
time: {}
disable_schedule_1_weekdays:
name: "Days"
description: "Days this schedule applies"
default:
- mon
- tue
- wed
- thu
- fri
- sat
- sun
selector:
select:
multiple: true
mode: list
options:
- label: "Monday"
value: "mon"
- label: "Tuesday"
value: "tue"
- label: "Wednesday"
value: "wed"
- label: "Thursday"
value: "thu"
- label: "Friday"
value: "fri"
- label: "Saturday"
value: "sat"
- label: "Sunday"
value: "sun"
# Disable Schedule 2 Section (collapsible) - INPUTS NESTED INSIDE
disable_schedule_2:
name: "🧭 Disable Schedule 2"
description: Days and Time automation is disabled
collapsed: true
input:
disable_schedule_2_enabled:
name: "Use Schedule 2?"
description: "Turn on/off this Disable Schedule"
default: false
selector:
boolean: {}
enable_schedule_2_end_trigger:
name: "Use End Trigger?"
description: "Run automation at schedule end to lock door if unlocked"
default: true
selector:
boolean: {}
disable_schedule_2_start_time:
name: "Start Time"
description: "Automation is Disabled starting at this time"
default: "22:00:00"
selector:
time: {}
disable_schedule_2_end_time:
name: "End Time"
description: "Automation is Re-Enabled at this time"
default: "06:00:00"
selector:
time: {}
disable_schedule_2_weekdays:
name: "Days"
description: "Days this schedule applies"
default:
- mon
- tue
- wed
- thu
- fri
selector:
select:
multiple: true
mode: list
options:
- label: "Monday"
value: "mon"
- label: "Tuesday"
value: "tue"
- label: "Wednesday"
value: "wed"
- label: "Thursday"
value: "thu"
- label: "Friday"
value: "fri"
- label: "Saturday"
value: "sat"
- label: "Sunday"
value: "sun"
# Disable Schedule 3 Section (collapsible) - INPUTS NESTED INSIDE
disable_schedule_3:
name: "🧭 Disable Schedule 3"
description: Days and Time automation is disabled
collapsed: true
input:
disable_schedule_3_enabled:
name: "Use Schedule 3?"
description: "Turn on/off this Disable Schedule"
default: false
selector:
boolean: {}
enable_schedule_3_end_trigger:
name: "Use End Trigger?"
description: "Run automation at schedule end to lock door if unlocked"
default: true
selector:
boolean: {}
disable_schedule_3_start_time:
name: "Start Time"
description: "Automation is Disabled starting at this time"
default: "22:00:00"
selector:
time: {}
disable_schedule_3_end_time:
name: "End Time"
description: "Automation is Re-Enabled at this time"
default: "06:00:00"
selector:
time: {}
disable_schedule_3_weekdays:
name: "Days"
description: "Days this schedule applies"
default:
- sat
- sun
selector:
select:
multiple: true
mode: list
options:
- label: "Monday"
value: "mon"
- label: "Tuesday"
value: "tue"
- label: "Wednesday"
value: "wed"
- label: "Thursday"
value: "thu"
- label: "Friday"
value: "fri"
- label: "Saturday"
value: "sat"
- label: "Sunday"
value: "sun"
# Notification Settings Section (collapsible) - INPUTS NESTED INSIDE
notification_settings:
name: "📢 Notification Settings"
description: Configure alert preferences for Auto-Lock
collapsed: true
input:
enable_persistent_notifications:
name: "Enable Persistent UI notifications?"
description: "these display under Notifications in Home Assistant"
default: true
selector:
boolean: {}
enable_mobile_notifications:
name: "Enable Mobile Notifications (Home Assistant companion app)"
description: "Send notifications to mobile devices"
default: true
selector:
boolean: {}
notify_device:
name: "Devices to Notify"
description: "Select mobile devices to receive notifications"
default: []
selector:
device:
integration: mobile_app
multiple: true
notification_title:
name: "Notification Title"
description: "Title for all notification types"
default: "Auto-Lock Alert"
selector:
text: {}
notify_monitoring_start:
name: "Notify on Monitoring Start"
description: "Automations initial trigger and monitoring begins"
default: true
selector:
boolean: {}
monitoring_start_message:
name: "Monitoring Start Message"
description: "Custom message for monitoring start"
default: "Auto-Lock monitoring door"
selector:
text: {}
notify_timeout:
name: "Notify on Automation Timeout"
description: "Automation stops because door does not close"
default: true
selector:
boolean: {}
timeout_message:
name: "Timeout Message"
description: "Custom message for timeout"
default: "Auto-Lock Timeout waiting for door to close"
selector:
text: {}
notify_max_retries:
name: "Notify on Failure to Lock"
description: "Send notification when max attempts reached and door did not lock"
default: true
selector:
boolean: {}
max_retries_message:
name: "Max Retries Message"
description: "Custom message for max retries failure"
default: "Auto-Lock Failed to lock door after multiple attempts"
selector:
text: {}
notify_lock_success:
name: "Notify on Lock Success"
description: "Send notification when door successfully locks"
default: true
selector:
boolean: {}
lock_success_message:
name: "Lock Success Message"
description: "Custom message for successful lock"
default: "Auto-Lock locked door successfully"
selector:
text: {}
notify_door_open_on_schedule_end:
name: "Notify if Door Open on Schedule End"
description: "Send notification when schedule ends and door is open"
default: true
selector:
boolean: {}
door_open_on_schedule_end_message:
name: "Door Open on Schedule End Message"
description: "Custom message when door is open at schedule end"
default: "Door is still open at schedule end"
selector:
text: {}
notify_interruption_level:
name: "iOS Interruption Level"
description: "only for iOS devices"
default: "time-sensitive"
selector:
select:
options:
- label: "Default"
value: "active"
- label: "Critical Notifications"
value: "critical"
- label: "Time Sensitive Notifications"
value: "time-sensitive"
- label: "Quiet Notifications Without Waking Screen"
value: "passive"
notify_sound:
name: "iOS Notification Sound"
description: "only for iOS devices"
default: ""
selector:
text: {}
notify_data:
name: "Android Options"
description: "Android-specific notification options"
default: []
selector:
select:
multiple: true
mode: list
options:
- label: "High Priority"
value: "high_priority"
- label: "Sticky Notification"
value: "sticky"
- label: "Notification Channel"
value: "channel"
notify_channel:
name: "Android Notification Channel"
description: "only for Android devices"
default: ""
selector:
text: {}
variables:
door_lock: !input door_lock
door_sensor: !input door_sensor
counter_variable: !input counter_variable
max_retries: !input max_retries
door_close_timeout: !input door_close_timeout
verification_timeout: !input verification_timeout
verification_timeout_unit: !input verification_timeout_unit
bypass_entity: !input bypass_entity
verification_timeout_seconds: >
{% if verification_timeout_unit == 'minutes' %}
{{ verification_timeout | int * 60 }}
{% else %}
{{ verification_timeout | int }}
{% endif %}
disable_schedule_1_enabled: !input disable_schedule_1_enabled
disable_schedule_1_start_time: !input disable_schedule_1_start_time
disable_schedule_1_end_time: !input disable_schedule_1_end_time
disable_schedule_1_weekdays: !input disable_schedule_1_weekdays
enable_schedule_1_end_trigger: !input enable_schedule_1_end_trigger
disable_schedule_2_enabled: !input disable_schedule_2_enabled
disable_schedule_2_start_time: !input disable_schedule_2_start_time
disable_schedule_2_end_time: !input disable_schedule_2_end_time
disable_schedule_2_weekdays: !input disable_schedule_2_weekdays
enable_schedule_2_end_trigger: !input enable_schedule_2_end_trigger
disable_schedule_3_enabled: !input disable_schedule_3_enabled
disable_schedule_3_start_time: !input disable_schedule_3_start_time
disable_schedule_3_end_time: !input disable_schedule_3_end_time
disable_schedule_3_weekdays: !input disable_schedule_3_weekdays
enable_schedule_3_end_trigger: !input enable_schedule_3_end_trigger
enable_mobile_notifications: !input enable_mobile_notifications
notify_device: !input notify_device
notification_title: !input notification_title
notify_monitoring_start: !input notify_monitoring_start
monitoring_start_message: !input monitoring_start_message
notify_timeout: !input notify_timeout
timeout_message: !input timeout_message
notify_max_retries: !input notify_max_retries
max_retries_message: !input max_retries_message
notify_lock_success: !input notify_lock_success
lock_success_message: !input lock_success_message
notify_door_open_on_schedule_end: !input notify_door_open_on_schedule_end
door_open_on_schedule_end_message: !input door_open_on_schedule_end_message
notify_interruption_level: !input notify_interruption_level
notify_sound: !input notify_sound
notify_data: !input notify_data
notify_channel: !input notify_channel
enable_persistent_notifications: !input enable_persistent_notifications
current_retry_count: >
{% set count = states(counter_variable) | int(0) %}
{{ 0 if count < 0 else count }}
notification_data: >
{% set message = namespace(data={}) %}
{% set push = namespace(data={}) %}
{% if notify_interruption_level in ['active', 'critical', 'time-sensitive', 'passive'] %}
{% set push.data = dict(push.data, **{ 'interruption-level': notify_interruption_level }) %}
{% endif %}
{% if notify_sound != '' %}
{% set push.data = dict(push.data, **{ 'sound': notify_sound }) %}
{% endif %}
{% if push.data %}
{% set message.data = dict(message.data, **{ 'push': push.data }) %}
{% endif %}
{% if 'high_priority' in notify_data %}
{% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}
{% endif %}
{% if 'channel' in notify_data %}
{% set message.data = dict(message.data, **{ 'channel': notify_channel }) %}
{% endif %}
{% if 'sticky' in notify_data %}
{% set message.data = dict(message.data, **{ 'sticky': "true" }) %}
{% endif %}
{{ message.data }}
trigger:
- platform: state
entity_id: !input door_lock
to: "unlocked"
id: door_unlocked_trigger
- platform: state
entity_id: !input door_sensor
to: "on"
id: door_opened_trigger
- platform: time
at: !input disable_schedule_1_end_time
id: schedule_1_end_trigger
- platform: time
at: !input disable_schedule_2_end_time
id: schedule_2_end_trigger
- platform: time
at: !input disable_schedule_3_end_time
id: schedule_3_end_trigger
condition:
- condition: template
value_template: >-
{% if bypass_entity %}
{{ not is_state(bypass_entity, 'on') }}
{% else %}
true
{% endif %}
action:
- variables:
triggered_by_schedule_end: >
{% set trigger_id = trigger.id if trigger is defined else '' %}
{{ trigger_id in ['schedule_1_end_trigger', 'schedule_2_end_trigger', 'schedule_3_end_trigger'] }}
current_schedule_number: >
{% set trigger_id = trigger.id if trigger is defined else '' %}
{% if trigger_id == 'schedule_1_end_trigger' %}
1
{% elif trigger_id == 'schedule_2_end_trigger' %}
2
{% elif trigger_id == 'schedule_3_end_trigger' %}
3
{% else %}
0
{% endif %}
# SEPARATE LOGIC FOR SCHEDULE END TRIGGERS
- if:
- condition: template
value_template: "{{ triggered_by_schedule_end }}"
then:
# Check if this specific schedule and its end trigger are enabled
- variables:
schedule_is_enabled: >
{% if current_schedule_number == 1 %}
{{ disable_schedule_1_enabled and enable_schedule_1_end_trigger }}
{% elif current_schedule_number == 2 %}
{{ disable_schedule_2_enabled and enable_schedule_2_end_trigger }}
{% elif current_schedule_number == 3 %}
{{ disable_schedule_3_enabled and enable_schedule_3_end_trigger }}
{% else %}
false
{% endif %}
schedule_applies_today: >
{% set current_day = now().strftime('%a').lower() %}
{% if current_schedule_number == 1 %}
{{ current_day in disable_schedule_1_weekdays }}
{% elif current_schedule_number == 2 %}
{{ current_day in disable_schedule_2_weekdays }}
{% elif current_schedule_number == 3 %}
{{ current_day in disable_schedule_3_weekdays }}
{% else %}
false
{% endif %}
- if:
- condition: template
value_template: "{{ schedule_is_enabled and schedule_applies_today }}"
then:
# Wait 2 seconds after schedule end time
- delay:
seconds: 2
# Check if door is closed and unlocked, then lock it immediately
- if:
- condition: state
entity_id: !input door_sensor
state: "off"
- condition: state
entity_id: !input door_lock
state: "unlocked"
then:
- service: lock.lock
target:
entity_id: !input door_lock
# Send notifications if enabled
- if:
- condition: template
value_template: "{{ notify_lock_success }}"
then:
- if:
- condition: template
value_template: "{{ enable_mobile_notifications and notify_device | length > 0 }}"
then:
- repeat:
for_each: "{{ notify_device }}"
sequence:
- service: "notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}"
data:
title: "Schedule {{ current_schedule_number }} End Check"
message: "Door was closed but unlocked at schedule end. Auto-Lock secured the door."
data: "{{ notification_data }}"
- if:
- condition: template
value_template: "{{ enable_persistent_notifications }}"
then:
- service: persistent_notification.create
data:
title: "Schedule {{ current_schedule_number }} End Check"
message: "Door was closed but unlocked. Auto-Lock secured the door."
notification_id: "schedule_{{ current_schedule_number }}_end_lock"
# If door is open, send notification
else:
- if:
- condition: state
entity_id: !input door_sensor
state: "on"
- condition: template
value_template: "{{ notify_door_open_on_schedule_end }}"
then:
- if:
- condition: template
value_template: "{{ enable_mobile_notifications and notify_device | length > 0 }}"
then:
- repeat:
for_each: "{{ notify_device }}"
sequence:
- service: "notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}"
data:
title: "Schedule {{ current_schedule_number }} End Alert"
message: "{{ door_open_on_schedule_end_message }}"
data: "{{ notification_data }}"
- if:
- condition: template
value_template: "{{ enable_persistent_notifications }}"
then:
- service: persistent_notification.create
data:
title: "Schedule {{ current_schedule_number }} End Alert"
message: "{{ door_open_on_schedule_end_message }}"
notification_id: "schedule_{{ current_schedule_number }}_end_open"
# Stop here - don't run normal monitoring logic for schedule end triggers
- stop: "Schedule end trigger processed"
# NORMAL DOOR EVENT LOGIC (only runs if NOT a schedule end trigger)
# Check if we're CURRENTLY in a disabled schedule period (time-based check)
- variables:
is_currently_in_disabled_schedule: >
{% set is_disabled = false %}
{% set current_time = now().time() %}
{% set current_day = now().strftime('%a').lower() %}
{% if disable_schedule_1_enabled and current_day in disable_schedule_1_weekdays %}
{% set start_time = strptime(disable_schedule_1_start_time, '%H:%M:%S').time() %}
{% set end_time = strptime(disable_schedule_1_end_time, '%H:%M:%S').time() %}
{% if start_time < end_time %}
{% if current_time >= start_time and current_time < end_time %}
{% set is_disabled = true %}
{% endif %}
{% else %}
{% if current_time >= start_time or current_time < end_time %}
{% set is_disabled = true %}
{% endif %}
{% endif %}
{% endif %}
{% if not is_disabled and disable_schedule_2_enabled and current_day in disable_schedule_2_weekdays %}
{% set start_time = strptime(disable_schedule_2_start_time, '%H:%M:%S').time() %}
{% set end_time = strptime(disable_schedule_2_end_time, '%H:%M:%S').time() %}
{% if start_time < end_time %}
{% if current_time >= start_time and current_time < end_time %}
{% set is_disabled = true %}
{% endif %}
{% else %}
{% if current_time >= start_time or current_time < end_time %}
{% set is_disabled = true %}
{% endif %}
{% endif %}
{% endif %}
{% if not is_disabled and disable_schedule_3_enabled and current_day in disable_schedule_3_weekdays %}
{% set start_time = strptime(disable_schedule_3_start_time, '%H:%M:%S').time() %}
{% set end_time = strptime(disable_schedule_3_end_time, '%H:%M:%S').time() %}
{% if start_time < end_time %}
{% if current_time >= start_time and current_time < end_time %}
{% set is_disabled = true %}
{% endif %}
{% else %}
{% if current_time >= start_time or current_time < end_time %}
{% set is_disabled = true %}
{% endif %}
{% endif %}
{% endif %}
{{ is_disabled }}
- if:
- condition: template
value_template: "{{ is_currently_in_disabled_schedule }}"
then:
- stop: "Stop if currently in disabled schedule time period"
# Monitoring Start Notification
- if:
- condition: template
value_template: "{{ current_retry_count == 0 and notify_monitoring_start }}"
then:
- if:
- condition: template
value_template: "{{ enable_mobile_notifications and notify_device | length > 0 }}"
then:
- repeat:
for_each: "{{ notify_device }}"
sequence:
- service: "notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}"
data:
title: "{{ notification_title }}"
message: "{{ monitoring_start_message }}"
data: "{{ notification_data }}"
- if:
- condition: template
value_template: "{{ enable_persistent_notifications }}"
then:
- service: persistent_notification.create
data:
title: "Auto-Lock monitoring door"
message: ""
notification_id: "lock_monitoring_{{ door_lock }}"
# Max Retries Notification
- if:
- condition: template
value_template: "{{ current_retry_count >= max_retries and notify_max_retries }}"
then:
- if:
- condition: template
value_template: "{{ enable_mobile_notifications and notify_device | length > 0 }}"
then:
- repeat:
for_each: "{{ notify_device }}"
sequence:
- service: "notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}"
data:
title: "{{ notification_title }}"
message: "{{ max_retries_message }}"
data: "{{ notification_data }}"
- if:
- condition: template
value_template: "{{ enable_persistent_notifications }}"
then:
- service: persistent_notification.create
data:
title: "Auto-Lock Failed to lock door"
message: "after {{ max_retries }} attempts"
notification_id: "lock_failed_{{ door_lock }}"
- service: counter.reset
target:
entity_id: "{{ counter_variable }}"
# Increment counter
- service: counter.increment
target:
entity_id: "{{ counter_variable }}"
# Wait for door to close
- wait_template: "{{ is_state(door_sensor, 'off') }}"
timeout:
minutes: "{{ door_close_timeout }}"
continue_on_timeout: true
# Timeout Notification
- if:
- condition: state
entity_id: !input door_sensor
state: "on"
- condition: template
value_template: "{{ notify_timeout }}"
then:
- if:
- condition: template
value_template: "{{ enable_mobile_notifications and notify_device | length > 0 }}"
then:
- repeat:
for_each: "{{ notify_device }}"
sequence:
- service: "notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}"
data:
title: "{{ notification_title }}"
message: "{{ timeout_message }}"
data: "{{ notification_data }}"
- if:
- condition: template
value_template: "{{ enable_persistent_notifications }}"
then:
- service: persistent_notification.create
data:
title: "Auto-Lock Timeout, door didn't close in time"
message: ""
notification_id: "door_timeout_{{ door_sensor }}"
- service: automation.trigger
target:
entity_id: "{{ this.entity_id }}"
# Verification period
- wait_for_trigger:
- platform: state
entity_id: !input door_sensor
to: "off"
timeout:
seconds: "{{ verification_timeout_seconds }}"
continue_on_timeout: true
# Successful Lock
- if:
- condition: state
entity_id: !input door_sensor
state: "off"
then:
- service: lock.lock
target:
entity_id: !input door_lock
- service: counter.reset
target:
entity_id: "{{ counter_variable }}"
- if:
- condition: template
value_template: "{{ notify_lock_success }}"
then:
- if:
- condition: template
value_template: "{{ enable_mobile_notifications and notify_device | length > 0 }}"
then:
- repeat:
for_each: "{{ notify_device }}"
sequence:
- service: "notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}"
data:
title: "{{ notification_title }}"
message: "{{ lock_success_message }}"
data: "{{ notification_data }}"
- if:
- condition: template
value_template: "{{ enable_persistent_notifications }}"
then:
- service: persistent_notification.create
data:
title: "Auto-Lock locked door"
message: ""
notification_id: "lock_success_{{ door_lock }}"
mode: restart
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment