initial commit

This commit is contained in:
root
2026-06-28 14:27:20 -04:00
commit ae0f1f559e
115 changed files with 30411 additions and 0 deletions
@@ -0,0 +1,213 @@
blueprint:
name: ESPHome Device Auto Bulk Update
description: 'Automatically updates ESPHome devices when a specified number of them
have pending updates. Optionally sends a notification before updating.
DEVICE FIRMWARE ENTITIES MUST BE ENABLED
FLOW:
1. Triggers when device update count exceeds the threshold, or daily sweep at
the start of the time window.
2. Checks the time window and device count conditions are met.
3. Sends a notification listing devices to be updated with Update Now and Cancel
buttons.
4. Waits for the delay period listening for a button response.
5. If Cancel is tapped - sends a cancellation notification and stops.
6. If Update Now is tapped or delay expires - proceeds with updates.
7. Updates all devices at once or sequentially depending on mode selected.
8. Sends a completion notification listing updated devices.
9. Waits out the cooldown period before the automation can trigger again.
'
domain: automation
source_url: https://github.com/bferd/homeassistant-blueprints/blob/main/esphome_auto_bulk_update.yaml
input:
update_threshold:
name: Device Update Threshold
description: How many devices need a pending update before updates are pushed.
default: 1
selector:
number:
min: 1.0
max: 20.0
step: 1.0
mode: slider
update_delay:
name: Delay Before Updating
description: How many minutes to wait before pushing updates.
default: 30
selector:
number:
min: 0.0
max: 60.0
step: 1.0
mode: slider
unit_of_measurement: minutes
cooldown:
name: Cooldown After Update
description: Minutes to wait after updates complete before the automation can
trigger again.
default: 30
selector:
number:
min: 0.0
max: 120.0
step: 1.0
mode: slider
unit_of_measurement: minutes
time_window_start:
name: Update Window Start
description: Earliest time of day updates can be triggered.
default: 08:00:00
selector:
time: {}
time_window_end:
name: Update Window End
description: Latest time of day updates can be triggered.
default: '21:00:00'
selector:
time: {}
notify_device:
name: Notification Device
description: Device to notify before updates are pushed.
selector:
device:
integration: mobile_app
multiple: false
update_mode:
name: Update Mode
description: Update all devices at once or one at a time sequentially.
default: all_at_once
selector:
select:
options:
- label: All at once
value: all_at_once
- label: One at a time (Sequential)
value: sequential
custom_value: false
multiple: false
sort: false
sequential_delay:
name: Delay Between Sequential Updates
description: Minutes to wait between each device when using sequential mode.
default: 0
selector:
number:
min: 0.0
max: 10.0
step: 1.0
mode: slider
unit_of_measurement: minutes
variables:
update_threshold: !input update_threshold
update_delay: !input update_delay
cooldown: !input cooldown
update_mode: !input update_mode
sequential_delay: !input sequential_delay
device_list: "{{ expand(integration_entities('esphome'))\n | selectattr(\"entity_id\",
\"contains\", \"update\")\n | selectattr(\"state\", \"eq\", \"on\")\n | map(attribute='entity_id')
| list }}\n"
device_names: "{{ expand(integration_entities('esphome'))\n | selectattr(\"entity_id\",
\"contains\", \"update\")\n | selectattr(\"state\", \"eq\", \"on\")\n | map(attribute='name')
| join(', ') }}\n"
device_count: "{{ expand(integration_entities('esphome'))\n | selectattr(\"entity_id\",
\"contains\", \"update\")\n | selectattr(\"state\", \"eq\", \"on\")\n | list
| count }}\n"
trigger:
- platform: template
value_template: "{{ expand(integration_entities('esphome'))\n | selectattr(\"entity_id\",
\"contains\", \"update\")\n | selectattr(\"state\", \"eq\", \"on\")\n | list
| count > update_threshold }}\n"
- platform: time
at: !input time_window_start
- platform: time_pattern
hours: /1
condition:
- condition: time
after: !input time_window_start
before: !input time_window_end
- condition: template
value_template: "{{ expand(integration_entities('esphome'))\n | selectattr(\"entity_id\",
\"contains\", \"update\")\n | selectattr(\"state\", \"eq\", \"on\")\n | list
| count > update_threshold }}\n"
action:
- domain: mobile_app
type: notify
device_id: !input notify_device
title: ESPHome Device Update(s)
message: '{{ device_count }} device(s) need updating: {{ device_names }}. Updates
will begin in {{ update_delay }} minute(s).
'
data:
sticky: 'true'
persistent: 'true'
actions:
- action: UPDATE_NOW_ESPHOME
title: Update Now
- action: CANCEL_ESPHOME_UPDATE
title: Cancel
- wait_for_trigger:
- platform: event
event_type: mobile_app_notification_action
event_data:
action: CANCEL_ESPHOME_UPDATE
- platform: event
event_type: mobile_app_notification_action
event_data:
action: UPDATE_NOW_ESPHOME
timeout:
minutes: '{{ update_delay }}'
continue_on_timeout: true
- if:
- condition: template
value_template: "{{ wait.trigger is not none and\n wait.trigger.event.data.action
== 'CANCEL_ESPHOME_UPDATE' }}\n"
then:
- domain: mobile_app
type: notify
device_id: !input notify_device
title: ESPHome Update Cancelled
message: ESPHome device updates have been cancelled.
else:
- if:
- condition: template
value_template: '{{ update_mode == ''sequential'' }}'
then:
- repeat:
for_each: '{{ device_list }}'
sequence:
- action: update.install
continue_on_error: true
data: {}
target:
entity_id: '{{ repeat.item }}'
- delay:
minutes: '{{ 0 if repeat.last else sequential_delay }}'
else:
- action: update.install
continue_on_error: true
data: {}
target:
entity_id: '{{ device_list }}'
- domain: mobile_app
type: notify
device_id: !input notify_device
title: ESPHome Updates Complete
message: '{{ device_count }} device(s) have been updated: {{ device_names }}.
'
- delay:
minutes: '{{ cooldown }}'
mode: single
@@ -0,0 +1,335 @@
blueprint:
name: Elegoo Printer Progress Notification (v4)
description: Sends notifications for printer progress, status changes, and error
states.
author: Daniel Cherubini
homeassistant:
min_version: 2024.6.0
domain: automation
input:
percent_complete_entity:
name: Percent Complete Entity
description: The percent complete sensor for the Elegoo printer (look for entities
ending in '_percent_complete').
selector:
entity:
domain:
- sensor
integration: elegoo_printer
multiple: false
reorder: false
print_status_entity:
name: Print Status Entity
description: The print status sensor (should end with '_print_status').
selector:
entity:
domain:
- sensor
integration: elegoo_printer
device_class:
- enum
multiple: false
reorder: false
current_status_entity:
name: Current Status Entity
description: The current status sensor (should end with '_current_status').
selector:
entity:
domain:
- sensor
integration: elegoo_printer
device_class:
- enum
multiple: false
reorder: false
error_status_reason_entity:
name: Error Status Reason Entity
description: The error status reason sensor (should end with '_current_print_error_status_reason').
selector:
entity:
domain:
- sensor
integration: elegoo_printer
device_class:
- enum
multiple: false
reorder: false
notify_device:
name: Notification Device
description: The device to send notifications to.
selector:
device:
integration: mobile_app
multiple: false
percentage_divisor:
name: Notification Frequency
description: Notify when the percentage complete is divisible by this number.
Use 1 to be notified on every percentage change.
selector:
select:
options:
- '1'
- '2'
- '5'
sort: false
custom_value: false
multiple: false
default: '5'
camera_entity:
name: Printer Camera (Optional)
description: Override the default camera entity for the printer. Leave blank
to use the default camera from the printer device.
default: ''
selector:
entity:
domain:
- camera
multiple: false
reorder: false
dashboard_url:
name: Dashboard URL (Optional)
description: The path to open when the notification is clicked (e.g., '/dashboard-example/example',
not the full URL).
default: ''
enable_status_notifications:
name: Enable Status Change Notifications
description: Send notifications when printer status changes (e.g., homing, heating,
etc.)
default: true
selector:
boolean: {}
source_url: https://github.com/danielcherubini/elegoo-homeassistant/blob/main/blueprints/automation/elegoo_printer/elegoo_printer_progress.yaml
mode: single
max_exceeded: silent
variables:
percent_complete_entity: !input percent_complete_entity
notify_device: !input notify_device
percentage_divisor: !input percentage_divisor
camera_entity_input: !input camera_entity
dashboard_url: !input dashboard_url
enable_status_notifications: !input enable_status_notifications
print_status_entity: !input print_status_entity
current_status_entity: !input current_status_entity
error_status_reason_entity: !input error_status_reason_entity
printer_device: '{{ device_id(percent_complete_entity) }}'
notification_group: '{{ device_attr(printer_device, ''name'') | slugify }}'
end_time_entity: '{{ device_entities(printer_device) | select(''search'', ''_end_time'')
| first }}'
file_name_entity: '{{ device_entities(printer_device) | select(''search'', ''_file_name'')
| first }}'
current_layer_entity: '{{ device_entities(printer_device) | select(''search'', ''_current_layer'')
| first }}'
total_layers_entity: '{{ device_entities(printer_device) | select(''search'', ''_total_layers'')
| first }}'
camera_entity: "{{ (camera_entity_input\n if camera_entity_input != ''\n else
(device_entities(printer_device) | select('match', '^camera\\.') | first))\n |
default('', true) }}"
trigger:
- platform: state
entity_id: !input percent_complete_entity
- platform: state
entity_id: !input print_status_entity
- platform: state
entity_id: !input current_status_entity
- platform: state
entity_id: !input error_status_reason_entity
condition: []
action:
- choose:
- conditions:
- condition: template
value_template: '{{ trigger.entity_id == percent_complete_entity }}'
- condition: template
value_template: '{{ states(percent_complete_entity) not in [''unknown'', ''unavailable'']
}}'
- condition: template
value_template: '{{ states(percent_complete_entity) | int(0) < 100 }}'
- condition: template
value_template: '{{ states(percent_complete_entity) | int(0) % (percentage_divisor
| int) == 0 }}'
- condition: template
value_template: '{{ current_status_entity != none and states(current_status_entity)
== ''printing'' }}'
- condition: template
value_template: '{{ notify_device != '''' }}'
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: 'Printing: {{ states(percent_complete_entity) | int(0) }}%
Layer: {{ (states(current_layer_entity)|default(''?'', true)) }}/{{ (states(total_layers_entity)|default(''?'',
true)) }}'
message: '{{ states(file_name_entity)|default(''Unknown file'', true) }}'
data:
chronometer: true
when: '{{ as_timestamp(states(end_time_entity))|int if end_time_entity !=
none and states(end_time_entity) not in [''unknown'', ''unavailable''] else
0 }}'
progress: '{{ states(percent_complete_entity)|int(0) }}'
progress_max: 100
image: '{{ (''/api/camera_proxy/'' ~ camera_entity) if (camera_entity|default('''',
true)) != '''' else '''' }}'
url: '{{ dashboard_url }}'
clickAction: '{{ dashboard_url }}'
group: '{{ notification_group }}'
channel: '{{ notification_group }}'
tag: '{{ notification_group }}'
alert_once: true
sticky: true
push:
interruption-level: passive
- conditions:
- condition: template
value_template: '{{ notify_device != '''' }}'
- condition: template
value_template: '{{ states(print_status_entity) == ''complete'' }}'
- condition: or
conditions:
- condition: and
conditions:
- condition: template
value_template: '{{ trigger.entity_id == print_status_entity }}'
- condition: template
value_template: '{{ trigger.from_state.state != ''complete'' }}'
- condition: and
conditions:
- condition: template
value_template: '{{ trigger.entity_id == percent_complete_entity }}'
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: "\U0001F389 Print Complete!"
message: Print has finished successfully
data:
image: '{{ (''/api/camera_proxy/'' ~ camera_entity) if (camera_entity|default('''',
true)) != '''' else '''' }}'
url: '{{ dashboard_url }}'
clickAction: '{{ dashboard_url }}'
group: '{{ notification_group }}'
channel: '{{ notification_group }}'
tag: '{{ notification_group }}'
sticky: true
alert_once: true
push:
interruption-level: time-sensitive
- conditions:
- condition: template
value_template: '{{ trigger.entity_id == print_status_entity }}'
- condition: template
value_template: '{{ notify_device != '''' }}'
- condition: template
value_template: '{{ enable_status_notifications }}'
- condition: template
value_template: '{{ states(print_status_entity) not in [''complete'', ''stopped'',
''stopping'', ''idle'', ''unknown'', ''unavailable''] }}'
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: Printer Status Update
message: "{% set status = states(print_status_entity) %} {% set file_name =
states(file_name_entity)|default('Unknown file', true) %} {% if status ==
'homing' %}\n \U0001F3E0 Printer is homing\n{% elif status == 'printing'
%}\n \U0001F5A8 Started printing: {{ file_name }}\n{% elif status == 'paused'
or status == 'pausing' %}\n ⏸️ Print paused: {{ file_name }}\n{% elif status
== 'loading' %}\n \U0001F4E5 Loading filament\n{% elif status == 'dropping'
%}\n \U0001F53D Platform dropping\n{% elif status == 'lifting' %}\n \U0001F53C
Platform lifting\n{% elif status == 'file_checking' %}\n \U0001F4C1 Checking
print file\n{% elif status == 'recovery' or status == 'printing_recovery'
%}\n \U0001F504 Print recovery in progress\n{% elif status == 'preheating'
%}\n \U0001F525 Preheating for print\n{% elif status == 'leveling' %}\n \U0001F4D0
Bed leveling in progress\n{% else %}\n \U0001F4CA Status: {{ status }}\n{%
endif %}"
data:
url: '{{ dashboard_url }}'
clickAction: '{{ dashboard_url }}'
group: '{{ notification_group }}'
channel: '{{ notification_group }}'
tag: '{{ notification_group }}_status'
alert_once: true
push:
interruption-level: passive
- conditions:
- condition: template
value_template: '{{ trigger.entity_id == current_status_entity }}'
- condition: template
value_template: '{{ notify_device != '''' }}'
- condition: template
value_template: '{{ enable_status_notifications }}'
- condition: template
value_template: '{{ states(current_status_entity) not in [''idle'', ''printing'',
''unknown'', ''unavailable''] }}'
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: Machine Status Update
message: "{% set status = states(current_status_entity) %} {% set file_name
= states(file_name_entity)|default('Unknown file', true) %} {% if status ==
'file_transferring' %}\n \U0001F4C1 File transfer in progress\n{% elif status
== 'exposure_testing' %}\n \U0001F52C Exposure test running\n{% elif status
== 'devices_testing' %}\n \U0001F527 Device self-check running\n{% elif status
== 'leveling' %}\n \U0001F4D0 Bed leveling in progress\n{% elif status ==
'loading_unloading' %}\n \U0001F4E5\U0001F4E4 Loading/unloading filament\n{%
else %}\n \U0001F4CA Machine status: {{ status }}\n{% endif %}"
data:
url: '{{ dashboard_url }}'
clickAction: '{{ dashboard_url }}'
group: '{{ notification_group }}'
channel: '{{ notification_group }}'
tag: '{{ notification_group }}_machine_status'
alert_once: true
push:
interruption-level: passive
- conditions:
- condition: template
value_template: '{{ trigger.entity_id == error_status_reason_entity }}'
- condition: template
value_template: '{{ notify_device != '''' }}'
- condition: template
value_template: '{{ states(error_status_reason_entity) not in [''ok'', ''none'',
''unknown'', ''unavailable''] }}'
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: "\U0001F6A8 Critical Printer Error!"
message: "{% set error = states(error_status_reason_entity) %} {% set file_name
= states(file_name_entity)|default('current print', true) %} {% if error ==
'filament_runout' %}\n \U0001F9F5 Filament runout detected during {{ file_name
}}! Please load new filament.\n{% elif error == 'filament_about_to_runout'
%}\n ⚠️ Filament runout imminent for {{ file_name }}! Please prepare new
filament.\n{% elif error == 'filament_jam' %}\n \U0001F6AB Filament jam detected
during {{ file_name }}! Please check the extruder.\n{% elif error == 'temp_error'
%}\n \U0001F321 Temperature error detected! Please check nozzle and bed
temperatures.\n{% elif error == 'level_failed' %}\n \U0001F4D0 Bed leveling
failed! Please check the bed leveling system.\n{% elif error == 'home_failed'
or error == 'home_failed_x' or error == 'home_failed_y' or error == 'home_failed_z'
%}\n \U0001F3E0 Homing failed! Please check the printer axes and endstops.\n{%
elif error == 'bed_adhesion_failed' %}\n \U0001F6CF Print detached from
bed during {{ file_name }}!\n{% elif error == 'move_abnormal' %}\n ⚙️ Motor
movement abnormality detected!\n{% elif error == 'file_error' %}\n \U0001F4C1
Print file error during {{ file_name }}!\n{% elif error == 'udisk_remove'
%}\n \U0001F4BE USB drive was removed during printing!\n{% elif error ==
'nozzle_temp_sensor_offline' %}\n \U0001F321 Nozzle temperature sensor is
offline!\n{% elif error == 'bed_temp_sensor_offline' %}\n \U0001F321 Bed
temperature sensor is offline!\n{% elif error == 'camera_error' %}\n \U0001F4F7
Camera connection error!\n{% elif error == 'network_error' %}\n \U0001F310
Network connection error!\n{% elif error == 'server_connect_failed' %}\n \U0001F5A5
Server connection failed!\n{% elif error == 'disconnect_app' %}\n \U0001F4F1
Controlling app disconnected during print!\n{% else %}\n ⚠️ Error: {{ error
}}\n{% endif %}"
data:
image: '{{ (''/api/camera_proxy/'' ~ camera_entity) if (camera_entity|default('''',
true)) != '''' else '''' }}'
url: '{{ dashboard_url }}'
clickAction: '{{ dashboard_url }}'
group: '{{ notification_group }}'
channel: '{{ notification_group }}'
tag: '{{ notification_group }}_critical_error'
sticky: true
alert_once: false
push:
interruption-level: time-sensitive
@@ -0,0 +1,66 @@
blueprint:
name: Duck area players volume while assist in progress
description: 'Temporarily lowers the volume of media players in the same area when
any selected Assist Satellite starts listening, then restores their volume after
the interaction ends.
'
domain: automation
input:
satellites:
name: Assist Satellites
description: List of Assist Satellite entities to monitor.
selector:
entity:
domain:
- assist_satellite
multiple: true
reorder: false
duck_volume:
name: Duck Volume Level
description: Volume level to set while assist is listening (e.g. 0.2 = 20%).
default: 0.2
selector:
number:
min: 0.0
max: 1.0
step: 0.01
unit_of_measurement: fraction (01)
mode: slider
source_url: https://raw.githubusercontent.com/formatBCE/Respeaker-Lite-ESPHome-integration/refs/heads/main/blueprints/automation/formatbce/duck_players_on_satellite_working.yaml
trigger:
- platform: state
entity_id: !input satellites
to: listening
variables:
v_duck_volume: !input duck_volume
v_players: "{{ states.media_player\n | selectattr('state', 'equalto', 'playing')\n
\ | selectattr('attributes.volume_level', 'defined')\n | selectattr('entity_id',
'in', area_entities(area_id(trigger.entity_id)))\n | rejectattr('entity_id',
'in', device_entities(device_id(trigger.entity_id)))\n | rejectattr('attributes.active_queue',
'in', device_entities(device_id(trigger.entity_id)))\n | map(attribute='entity_id')\n
\ | list\n}}"
v_volumes: "{% set vol = namespace(umes = []) %} {% for i in v_players %}\n {%
set vol.umes = vol.umes + [{'id': i, 'volume_diff': state_attr(i, 'volume_level')
- v_duck_volume }] %}\n{% endfor %} {{ vol.umes }}"
action:
- service: media_player.volume_set
data:
volume_level: '{{ v_duck_volume }}'
target:
entity_id: '{{ v_players }}'
- wait_for_trigger:
- platform: template
value_template: '{{ is_state(trigger.entity_id, ''idle'') }}'
timeout: 00:03:00
- repeat:
for_each: '{{ v_volumes }}'
sequence:
- service: media_player.volume_set
data:
volume_level: '{{ state_attr(repeat.item.id, ''volume_level'') + repeat.item.volume_diff
}}'
target:
entity_id: '{{ repeat.item.id }}'
mode: parallel
max: 10
@@ -0,0 +1,44 @@
blueprint:
name: Play TTS URI via Media Player
description: Listen for a TTS URI event from an ESPHome device and play it using
a selected media player.
domain: automation
input:
tts_device:
name: ESPHome TTS Device
description: Select the ESPHome device that sends the TTS URI event.
selector:
device:
entity:
- domain:
- assist_satellite
filter:
- manufacturer: formatbce
model: Respeaker Lite Satellite
- manufacturer: formatbce
model: Koala Satellite
multiple: false
target_media_player:
name: Target Media Player
description: Media player entity that should play the TTS URI.
selector:
entity:
domain:
- media_player
multiple: false
reorder: false
source_url: https://raw.githubusercontent.com/formatBCE/Respeaker-Lite-ESPHome-integration/refs/heads/main/blueprints/automation/formatbce/redirect_respeaker_tts.yaml
trigger:
- platform: event
event_type: esphome.tts_uri
event_data:
device_id: !input tts_device
action:
- service: media_player.play_media
target:
entity_id: !input target_media_player
data:
announce: true
media_content_type: music
media_content_id: '{{ trigger.event.data.uri }}'
mode: single
@@ -0,0 +1,43 @@
blueprint:
name: Set Respeaker Alarm Time from Time Helper
domain: automation
input:
datetime_helper:
name: Datetime Helper
description: The input_datetime helper that triggers the alarm time update.
Must have time.
selector:
entity:
filter:
- domain:
- input_datetime
multiple: false
reorder: false
esphome_device:
name: Respeaker Device
description: The ESPHome device with set_alarm_time service
selector:
device:
entity:
- domain:
- assist_satellite
filter:
- manufacturer: formatbce
model: Respeaker Lite Satellite
- manufacturer: formatbce
model: Koala Satellite
- manufacturer: formatbce
model: Respeaker XVF3800 Satellite
multiple: false
source_url: https://raw.githubusercontent.com/formatBCE/Respeaker-Lite-ESPHome-integration/refs/heads/main/blueprints/automation/formatbce/set_respeaker_alarm_time_from_input_datetime_helper.yaml
variables:
esphome_device: !input esphome_device
triggers:
- trigger: state
entity_id: !input datetime_helper
actions:
- action: esphome.{{ device_attr(esphome_device, 'name') | slugify }}_set_alarm_time
data:
alarm_time_hh_mm: '{{ trigger.to_state.attributes.hour }}:{{ trigger.to_state.attributes.minute
}}'
mode: single
@@ -0,0 +1,58 @@
blueprint:
name: Motion-activated Light
description: Turn on a light when motion is detected.
domain: automation
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml
author: Home Assistant
input:
motion_entity:
name: Motion Sensor
selector:
entity:
filter:
- device_class: occupancy
domain: binary_sensor
- device_class: motion
domain: binary_sensor
light_target:
name: Light
selector:
target:
entity:
domain: light
no_motion_wait:
name: Wait time
description: Time to leave the light on after last motion is detected.
default: 120
selector:
number:
min: 0
max: 3600
unit_of_measurement: seconds
# If motion is detected within the delay,
# we restart the script.
mode: restart
max_exceeded: silent
triggers:
trigger: state
entity_id: !input motion_entity
from: "off"
to: "on"
actions:
- alias: "Turn on the light"
action: light.turn_on
target: !input light_target
- alias: "Wait until there is no motion from device"
wait_for_trigger:
trigger: state
entity_id: !input motion_entity
from: "on"
to: "off"
- alias: "Wait the number of seconds that has been set"
delay: !input no_motion_wait
- alias: "Turn off the light"
action: light.turn_off
target: !input light_target
@@ -0,0 +1,50 @@
blueprint:
name: Zone Notification
description: Send a notification to a device when a person leaves a specific zone.
domain: automation
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml
author: Home Assistant
input:
person_entity:
name: Person
selector:
entity:
filter:
domain: person
zone_entity:
name: Zone
selector:
entity:
filter:
domain: zone
notify_device:
name: Device to notify
description: Device needs to run the official Home Assistant app to receive notifications.
selector:
device:
filter:
integration: mobile_app
triggers:
trigger: state
entity_id: !input person_entity
variables:
zone_entity: !input zone_entity
# This is the state of the person when it's in this zone.
zone_state: "{{ states[zone_entity].name }}"
person_entity: !input person_entity
person_name: "{{ states[person_entity].name }}"
conditions:
condition: template
# The first case handles leaving the Home zone which has a special state when zoning called 'home'.
# The second case handles leaving all other zones.
value_template: "{{ zone_entity == 'zone.home' and trigger.from_state.state == 'home' and trigger.to_state.state != 'home' or trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}"
actions:
- alias: "Notify that a person has left the zone"
domain: mobile_app
type: notify
device_id: !input notify_device
message: "{{ person_name }} has left {{ zone_state }}"
@@ -0,0 +1,514 @@
blueprint:
name: Bambu Lab - Spaghetti Detection
description: Bambu Lab - Spaghetti Detection
domain: automation
input:
home_assistant_host:
name: Home Assistant Host
description: Home Assistant host
default: http://192.168.1.123:8123
obico_host:
name: Obico ML API Host
description: Obico ML API host
default: http://192.168.1.123:3333
obico_auth_token:
name: Obico ML API Auth Token
description: Obico ML API authentication token
default: obico_api_secret
detection_frequency:
name: Detection Frequency
description: The detection algorithm will run in every defined seconds
default: /5
selector:
select:
options:
- label: Every second
value: /1
- label: Every 5 seconds
value: /5
- label: Every 10 seconds
value: /10
- label: Every 30 seconds
value: /30
- label: Every 60 seconds
value: /59
multiple: false
mode: dropdown
sort: false
custom_value: false
auto_turn_on_light:
name: Automatically Turn On Printer Lights
description: Turns on printer lights before spaghetti detection operation
default: true
selector:
boolean: {}
notification_settings:
name: Notification Settings
description: Type of notification to send after detecting a failure
default: standard
selector:
select:
mode: dropdown
options:
- label: Critical Notification
value: critical
- label: Standard Notification
value: standard
- label: None
value: none
sort: false
multiple: false
custom_value: false
failure_action:
name: On Failure Action
description: What to do after detecting a failure
default: pause
selector:
select:
mode: dropdown
options:
- label: Pause
value: pause
- label: Stop
value: stop
- label: Warn
value: warn
sort: false
multiple: false
custom_value: false
notification_service:
name: Mobile devices notification service
description: The notification service for mobile devices (eg. notify.mobile_app_<your_device_id_here>).
You can provide both a notify group or a single notify device here.
default: notify.notify
selector:
text: {}
printer_print_status_sensor:
name: Printer Print Status Sensor
description: Bambu Lab printer print status sensor
selector:
entity:
filter:
- integration: bambu_lab
domain:
- sensor
device_class:
- enum
multiple: false
printer_current_stage_sensor:
name: Printer Current Stage Sensor
description: Bambu Lab printer current stage sensor
selector:
entity:
filter:
- integration: bambu_lab
domain:
- sensor
device_class:
- enum
multiple: false
printer_camera:
name: Printer Camera Entity
description: Bambu Lab printer camera entity
selector:
entity:
filter:
- domain:
- camera
multiple: false
printer_pause_button:
name: Printer Pause Button Entity
description: Bambu Lab printer pause button entity
selector:
entity:
filter:
- integration: bambu_lab
domain:
- button
multiple: false
printer_resume_button:
name: Printer Resume Button Entity
description: Bambu Lab printer resume button entity
selector:
entity:
filter:
- integration: bambu_lab
domain:
- button
multiple: false
printer_stop_button:
name: Printer Stop Button Entity
description: Bambu Lab printer stop button entity
selector:
entity:
filter:
- integration: bambu_lab
domain:
- button
multiple: false
printer_chamber_light:
name: Printer Chamber Light
description: Bambu Lab printer chamber light
selector:
entity:
filter:
- integration: bambu_lab
domain:
- light
multiple: false
source_url: https://github.com/nberktumer/ha-bambu-lab-p1-spaghetti-detection/blob/main/blueprints/spaghetti_detection.yaml
variables:
HOME_ASSISTANT_HOST_VAR: !input home_assistant_host
PRINTER_CAMERA_VAR: !input printer_camera
FAILURE_ACTION_VAR: !input failure_action
NOTIFICATION_SETTINGS_VAR: !input notification_settings
DETECTION_FREQUENCY_VAR: !input detection_frequency
mode: single
max_exceeded: silent
trigger:
- platform: state
entity_id:
- !input printer_current_stage_sensor
to: printing
id: BAMBU_LAB_PRINTER_STAGE_CHANGE
- platform: event
event_type: mobile_app_notification_action
id: BAMBU_LAB_PAUSE_PRINTING
event_data:
action: BAMBU_LAB_PAUSE_PRINTING
- platform: event
event_type: mobile_app_notification_action
id: BAMBU_LAB_RESUME_PRINTING
event_data:
action: BAMBU_LAB_RESUME_PRINTING
- platform: event
event_type: mobile_app_notification_action
id: BAMBU_LAB_STOP_PRINTING
event_data:
action: BAMBU_LAB_STOP_PRINTING
- trigger: time_pattern
id: BAMBU_LAB_DETECTION_TRIGGER
seconds: !input detection_frequency
condition: []
action:
- choose:
- conditions:
- condition: trigger
id: BAMBU_LAB_PRINTER_STAGE_CHANGE
sequence:
- service: number.set_value
data:
value: 0
target:
entity_id:
- number.bambu_lab_p1_spaghetti_detection_current_frame_number
- number.bambu_lab_p1_spaghetti_detection_ewm_mean
- number.bambu_lab_p1_spaghetti_detection_rolling_mean_short
- number.bambu_lab_p1_spaghetti_detection_rolling_mean_long
- number.bambu_lab_p1_spaghetti_detection_normalized_p
- number.bambu_lab_p1_spaghetti_detection_adjusted_ewm_mean
- number.bambu_lab_p1_spaghetti_detection_p_sum
- if:
- condition: and
conditions:
- condition: state
entity_id: !input printer_chamber_light
state: 'off'
- condition: template
value_template: !input auto_turn_on_light
then:
- service: light.turn_on
target:
entity_id:
- !input printer_chamber_light
- conditions:
- condition: trigger
id:
- BAMBU_LAB_PAUSE_PRINTING
- BAMBU_LAB_RESUME_PRINTING
- BAMBU_LAB_STOP_PRINTING
sequence:
- choose:
- conditions:
- condition: trigger
id:
- BAMBU_LAB_PAUSE_PRINTING
sequence:
- service: button.press
data: {}
target:
entity_id: !input printer_pause_button
- conditions:
- condition: trigger
id: BAMBU_LAB_RESUME_PRINTING
sequence:
- service: button.press
data: {}
target:
entity_id: !input printer_resume_button
- conditions:
- condition: trigger
id: BAMBU_LAB_STOP_PRINTING
sequence:
- service: button.press
data: {}
target:
entity_id: !input printer_stop_button
- conditions:
- condition: trigger
id: BAMBU_LAB_DETECTION_TRIGGER
sequence:
- if:
- condition: not
conditions:
- condition: state
entity_id: !input printer_print_status_sensor
state: running
then:
- stop: ''
- if:
- condition: template
value_template: '{{ now().second % (DETECTION_FREQUENCY_VAR | replace(''/'', '''') | int(0)) > 0 }}'
then:
- stop: ''
- if:
- condition: and
conditions:
- condition: state
entity_id: !input printer_chamber_light
state: 'off'
- condition: template
value_template: !input auto_turn_on_light
then:
- service: light.turn_on
target:
entity_id:
- !input printer_chamber_light
- service: bambu_lab_p1_spaghetti_detection.predict
data:
obico_host: !input obico_host
obico_auth_token: !input obico_auth_token
image_url: '{{ HOME_ASSISTANT_HOST_VAR }}{{ state_attr(PRINTER_CAMERA_VAR,
''entity_picture'') }}'
response_variable: result
- service: number.set_value
data:
value: '{{ result.result.detections | map(attribute=1) | sum | float }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_p_sum
- service: number.set_value
data:
value: '{{ states(''number.bambu_lab_p1_spaghetti_detection_current_frame_number'')
| float + 1 }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_current_frame_number
- service: number.set_value
data:
value: '{{ states(''number.bambu_lab_p1_spaghetti_detection_lifetime_frame_number'')
| float + 1 }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_lifetime_frame_number
- service: number.set_value
data:
value: '{{ (states(''number.bambu_lab_p1_spaghetti_detection_p_sum'') | float)
* (2 / (12 + 1)) + (states(''number.bambu_lab_p1_spaghetti_detection_ewm_mean'')
| float) * (1 - (2 / (12 + 1))) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_ewm_mean
- service: number.set_value
data:
value: '{{ (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_short'')
| float) + ((states(''number.bambu_lab_p1_spaghetti_detection_p_sum'') |
float) - (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_short'')
| float)) / (310 if 310 <= (states(''number.bambu_lab_p1_spaghetti_detection_current_frame_number'')
| float) else (states(''number.bambu_lab_p1_spaghetti_detection_current_frame_number'')
| float) + 1) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_rolling_mean_short
- service: number.set_value
data:
value: '{{ (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_long'')
| float) + ((states(''number.bambu_lab_p1_spaghetti_detection_p_sum'') |
float) - (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_long'')
| float)) / (7200 if 7200 <= (states(''number.bambu_lab_p1_spaghetti_detection_lifetime_frame_number'')
| float) else (states(''number.bambu_lab_p1_spaghetti_detection_lifetime_frame_number'')
| float) + 1) }}'
entity_id: number.bambu_lab_p1_spaghetti_detection_rolling_mean_long
- if:
- condition: numeric_state
entity_id: number.bambu_lab_p1_spaghetti_detection_current_frame_number
below: 30
then:
- stop: ''
alias: if current_frame_num < 30
- service: number.set_value
data:
value: '{{ (states(''number.bambu_lab_p1_spaghetti_detection_ewm_mean'') |
float) - (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_long'')
| float) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_adjusted_ewm_mean
- service: number.set_value
data:
value: '{{ ((states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_short'')
| float) - (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_long'')
| float)) * 3.8 }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_rolling_mean_diff
- service: number.set_value
data:
value: '{{ min(0.78, max(0.33, (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_diff'')
| float))) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_thresh_warning
- service: number.set_value
data:
value: '{{ (states(''number.bambu_lab_p1_spaghetti_detection_thresh_warning'')
| float) * 1.75 }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_thresh_failure
- service: number.set_value
data:
value: '{{ (states(''number.bambu_lab_p1_spaghetti_detection_ewm_mean'') |
float) - (states(''number.bambu_lab_p1_spaghetti_detection_rolling_mean_long'')
| float) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_p
- choose:
- conditions:
- condition: numeric_state
entity_id: number.bambu_lab_p1_spaghetti_detection_p
above: number.bambu_lab_p1_spaghetti_detection_thresh_failure
sequence:
- service: number.set_value
data:
value: '{{ min(1.0, max(2.0 / 3.0, ((((states(''number.bambu_lab_p1_spaghetti_detection_p'')
| float) - (states(''number.bambu_lab_p1_spaghetti_detection_thresh_failure'')
| float)) * (1.0 - 2.0 / 3.0)) / ((states(''number.bambu_lab_p1_spaghetti_detection_thresh_failure'')
| float) * 1.5 - (states(''number.bambu_lab_p1_spaghetti_detection_thresh_failure'')
| float))) + 2.0 / 3.0)) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_normalized_p
- conditions:
- condition: numeric_state
entity_id: number.bambu_lab_p1_spaghetti_detection_p
above: number.bambu_lab_p1_spaghetti_detection_thresh_warning
sequence:
- service: number.set_value
data:
value: '{{ min(2.0 / 3.0, max(1.0 / 3.0, ((((states(''number.bambu_lab_p1_spaghetti_detection_p'')
| float) - (states(''number.bambu_lab_p1_spaghetti_detection_thresh_warning'')
| float)) * (2.0 / 3.0 - 1.0 / 3.0)) / ((states(''number.bambu_lab_p1_spaghetti_detection_thresh_failure'')
| float) - (states(''number.bambu_lab_p1_spaghetti_detection_thresh_warning'')
| float))) + 1.0 / 3.0)) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_normalized_p
default:
- service: number.set_value
data:
value: '{{ min(1.0 / 3.0, max(0, ((states(''number.bambu_lab_p1_spaghetti_detection_p'')
| float) * 1.0 / 3.0) / (states(''number.bambu_lab_p1_spaghetti_detection_thresh_warning'')
| float))) }}'
target:
entity_id: number.bambu_lab_p1_spaghetti_detection_normalized_p
- if:
- condition: numeric_state
entity_id: number.bambu_lab_p1_spaghetti_detection_adjusted_ewm_mean
below: 0.38
then:
- stop: ''
- if:
- condition: and
conditions:
- condition: numeric_state
entity_id: number.bambu_lab_p1_spaghetti_detection_adjusted_ewm_mean
below: 0.78
- condition: numeric_state
entity_id: number.bambu_lab_p1_spaghetti_detection_adjusted_ewm_mean
below: number.bambu_lab_p1_spaghetti_detection_rolling_mean_diff
then:
- stop: ''
- if:
- condition: template
value_template: '{{ now() - states(''datetime.bambu_lab_p1_spaghetti_detection_last_notify_time'')
| as_datetime | as_local < timedelta(minutes=1) }}'
then:
- stop: ''
alias: if now() - last_notify_time < 1min
- choose:
- conditions:
- condition: template
value_template: '{{ FAILURE_ACTION_VAR == ''pause'' }}'
sequence:
- service: button.press
data: {}
target:
entity_id: !input printer_pause_button
- conditions:
- condition: template
value_template: '{{ FAILURE_ACTION_VAR == ''stop'' }}'
sequence:
- service: button.press
data: {}
target:
entity_id: !input printer_stop_button
- choose:
- conditions:
- condition: template
value_template: '{{ NOTIFICATION_SETTINGS_VAR == ''critical'' }}'
sequence:
- service: !input notification_service
data:
title: Bambu Lab - Spaghetti Detected
message: 'Confidence: {{ (states(''number.bambu_lab_p1_spaghetti_detection_normalized_p'')
| float * 100) | int }}%'
data:
image: '{{ HOME_ASSISTANT_HOST_VAR }}{{ state_attr(PRINTER_CAMERA_VAR,
''entity_picture'') }}'
ttl: 0
priority: high
channel: alarm_stream
push:
sound:
name: default
critical: 1
volume: 0.75
actions:
- action: BAMBU_LAB_RESUME_PRINTING
title: Resume Printing
- action: BAMBU_LAB_STOP_PRINTING
title: Stop Printing
- conditions:
- condition: template
value_template: '{{ NOTIFICATION_SETTINGS_VAR == ''standard'' }}'
sequence:
- service: !input notification_service
data:
title: Bambu Lab - Spaghetti Detected
message: 'Confidence: {{ (states(''number.bambu_lab_p1_spaghetti_detection_normalized_p'')
| float * 100) | int }}%'
data:
image: '{{ HOME_ASSISTANT_HOST_VAR }}{{ state_attr(PRINTER_CAMERA_VAR,
''entity_picture'') }}'
actions:
- action: BAMBU_LAB_RESUME_PRINTING
title: Resume Printing
- action: BAMBU_LAB_STOP_PRINTING
title: Stop Printing
- service: number.set_value
data:
value: 0
target:
entity_id:
- number.bambu_lab_p1_spaghetti_detection_current_frame_number
- number.bambu_lab_p1_spaghetti_detection_ewm_mean
- number.bambu_lab_p1_spaghetti_detection_rolling_mean_short
- number.bambu_lab_p1_spaghetti_detection_rolling_mean_long
- number.bambu_lab_p1_spaghetti_detection_normalized_p
- number.bambu_lab_p1_spaghetti_detection_adjusted_ewm_mean
- number.bambu_lab_p1_spaghetti_detection_p_sum
- service: datetime.set_value
data:
datetime: '{{ now() }}'
target:
entity_id: datetime.bambu_lab_p1_spaghetti_detection_last_notify_time