Compare commits

...

4 Commits

@ -63,6 +63,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this
### Dreame vacuum automations ### Dreame vacuum automations
- Logic lives in [vacuum.yaml](vacuum.yaml): continuous four-phase loop (sweep main, sweep baths, mop main, mop baths) driven by `input_select.l10s_vacuum_phase` and `input_text.l10s_vacuum_room_queue`, with per-room notifications and automatic reseeding between phases. - Logic lives in [vacuum.yaml](vacuum.yaml): continuous four-phase loop (sweep main, sweep baths, mop main, mop baths) driven by `input_select.l10s_vacuum_phase` and `input_text.l10s_vacuum_room_queue`, with per-room notifications and automatic reseeding between phases.
- Uses the Dreame HACS integration with segment IDs to enforce bathrooms last in each sweep/mop pass, dock on arrival, and auto-run if idle for 3+ days. - Uses the Dreame HACS integration with segment IDs to enforce bathrooms last in each sweep/mop pass, dock on arrival, and auto-run if idle for 3+ days.
- Room queue advances conservatively (dwell + leave + no quick revisit) so an interrupted/docked run doesnt mark rooms complete early.
![Dreame Automations](../www/custom_ui/floorplan/images/branding/Dreame%20Automations.png) ![Dreame Automations](../www/custom_ui/floorplan/images/branding/Dreame%20Automations.png)
### Blog & video deep dives ### Blog & video deep dives

@ -7,6 +7,10 @@
# Related Issue: 1550 # Related Issue: 1550
# Phases: sweep main, sweep baths, mop main, mop baths; notifications + idle auto-start # Phases: sweep main, sweep baths, mop main, mop baths; notifications + idle auto-start
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# Notes:
# - `sensor.l10s_vacuum_current_room` can change during transit; require a dwell (`for:`) and then wait for the vacuum to leave before dequeue/phase advance.
# - Only dequeue a room after it leaves and does not return shortly; prevents boundary "bouncing" and avoids marking a room done if we interrupt and dock.
# - Avoid reissuing `dreame_vacuum.vacuum_clean_segment` while already cleaning; only send a new segment job when starting/resuming or switching phases.
###################################################################### ######################################################################
## 1. Helpers ## 1. Helpers
@ -206,8 +210,10 @@ automation:
room_map: {14:'kitchen',12:'dining-room',10:'living room',7:'master-bedroom',15:'foyer',9:'stacey-office',17:'formal-dining',13:'hallway',8:'justin-bedroom',6:'paige-bedroom',4:'master-bathroom',2:'office',1:'pool-bath',3:'kids-bathroom'} room_map: {14:'kitchen',12:'dining-room',10:'living room',7:'master-bedroom',15:'foyer',9:'stacey-office',17:'formal-dining',13:'hallway',8:'justin-bedroom',6:'paige-bedroom',4:'master-bathroom',2:'office',1:'pool-bath',3:'kids-bathroom'}
queue_raw: "{{ states('input_text.l10s_vacuum_room_queue') | default('', true) | string | replace(' ', '') }}" queue_raw: "{{ states('input_text.l10s_vacuum_room_queue') | default('', true) | string | replace(' ', '') }}"
queue_ints: "{{ queue_raw | regex_findall('[0-9]+') | map('int') | list | default([], true) }}" queue_ints: "{{ queue_raw | regex_findall('[0-9]+') | map('int') | list | default([], true) }}"
current_room_id: "{{ trigger.to_state.attributes.room_id | default(state_attr('sensor.l10s_vacuum_current_room', 'room_id'), true) | int(0) }}" cleaned_room_state: "{{ trigger.to_state.state if trigger.to_state is not none else '' }}"
matched_room_id: "{{ current_room_id if current_room_id > 0 and current_room_id in (queue_ints | default([], true)) else 0 }}" cleaned_room_id: "{{ (trigger.to_state.attributes.room_id if trigger.to_state is not none else 0) | int(0) }}"
matched_room_id: "{{ cleaned_room_id if cleaned_room_id > 0 and cleaned_room_id in (queue_ints | default([], true)) else 0 }}"
room_name: "{{ room_map.get(matched_room_id, cleaned_room_state) }}"
remaining_list: > remaining_list: >
{% set rem = [] %} {% set rem = [] %}
{% set removed = namespace(done=false) %} {% set removed = namespace(done=false) %}
@ -229,6 +235,9 @@ automation:
next_phase: "{{ phase_order[phase_index + 1] if has_next_phase else '' }}" next_phase: "{{ phase_order[phase_index + 1] if has_next_phase else '' }}"
condition: condition:
- condition: template
value_template: >
{{ trigger.from_state is not none and trigger.to_state is not none and trigger.from_state.state != trigger.to_state.state }}
- condition: template - condition: template
value_template: "{{ queue_ints | length > 0 }}" value_template: "{{ queue_ints | length > 0 }}"
- condition: template - condition: template
@ -241,6 +250,61 @@ automation:
state: 'on' state: 'on'
action: action:
- wait_template: "{{ states('sensor.l10s_vacuum_current_room') != cleaned_room_state }}"
timeout: '02:00:00'
continue_on_timeout: false
# Do not mark a room "done" if we simply crossed a boundary, or if we are about to be interrupted and dock.
- delay: '00:00:20'
- choose:
- conditions:
- condition: state
entity_id: vacuum.l10s_vacuum
state: 'cleaning'
- condition: state
entity_id: input_boolean.l10s_vacuum_on_demand
state: 'on'
- condition: state
entity_id: group.family
state: 'not_home'
sequence:
- wait_template: "{{ states('sensor.l10s_vacuum_current_room') == cleaned_room_state }}"
timeout: '00:12:00'
continue_on_timeout: true
- choose:
- conditions:
- condition: template
value_template: "{{ wait.completed }}"
sequence:
- service: script.send_to_logbook
data:
topic: "VACUUM"
message: "{{ room_name }} revisit detected; keeping it queued."
- stop: "Vacuum returned to the same room; not dequeuing."
default: []
- choose:
- conditions:
- condition: state
entity_id: vacuum.l10s_vacuum
state: 'cleaning'
- condition: state
entity_id: group.family
state: 'not_home'
sequence: []
default:
- service: script.send_to_logbook
data:
topic: "VACUUM"
message: "{{ room_name }} not dequeued (vacuum state: {{ states('vacuum.l10s_vacuum') }}, family: {{ states('group.family') }})."
- stop: "Vacuum no longer cleaning / family returned; not dequeuing."
default:
- service: script.send_to_logbook
data:
topic: "VACUUM"
message: "{{ room_name }} not dequeued (vacuum state: {{ states('vacuum.l10s_vacuum') }}, family: {{ states('group.family') }})."
- stop: "Not in away-cleaning conditions; not dequeuing."
- service: input_text.set_value - service: input_text.set_value
target: target:
entity_id: input_text.l10s_vacuum_room_queue entity_id: input_text.l10s_vacuum_room_queue
@ -249,7 +313,6 @@ automation:
- variables: - variables:
cleaned_raw: "{{ states('input_text.l10s_vacuum_rooms_cleaned_today') | default('', true) | string }}" cleaned_raw: "{{ states('input_text.l10s_vacuum_rooms_cleaned_today') | default('', true) | string }}"
cleaned_parts: "{{ cleaned_raw | regex_findall('[^,]+') | map('trim') | reject('equalto','') | list }}" cleaned_parts: "{{ cleaned_raw | regex_findall('[^,]+') | map('trim') | reject('equalto','') | list }}"
room_name: "{{ room_map.get(matched_room_id, states('sensor.l10s_vacuum_current_room')) }}"
updated_cleaned: > updated_cleaned: >
{% set parts = cleaned_parts %} {% set parts = cleaned_parts %}
{% if room_name not in parts %} {% if room_name not in parts %}
@ -265,11 +328,11 @@ automation:
- service: script.send_to_logbook - service: script.send_to_logbook
data: data:
topic: "VACUUM" topic: "VACUUM"
message: "{{ room_name }} is clean. Phase: {{ phase }}. Remaining: {{ remaining_count }}." message: "{{ room_name }} completed. Phase: {{ phase }}. Remaining: {{ remaining_count }}."
- choose: - choose:
- conditions: - conditions:
- condition: template - condition: template
value_template: "{{ remaining_count > 0 }}" value_template: "{{ remaining_count > 0 and not is_state('vacuum.l10s_vacuum', 'cleaning') }}"
sequence: sequence:
- service: script.l10s_vacuum_start_next_room - service: script.l10s_vacuum_start_next_room
- conditions: - conditions:
@ -286,6 +349,9 @@ automation:
entity_id: input_text.l10s_vacuum_room_queue entity_id: input_text.l10s_vacuum_room_queue
data: data:
value: "" value: ""
- wait_template: "{{ not is_state('vacuum.l10s_vacuum', 'cleaning') }}"
timeout: '01:00:00'
continue_on_timeout: false
- service: script.l10s_vacuum_start_next_room - service: script.l10s_vacuum_start_next_room
- conditions: - conditions:
- condition: template - condition: template
@ -304,7 +370,10 @@ automation:
- service: input_boolean.turn_off - service: input_boolean.turn_off
target: target:
entity_id: input_boolean.l10s_vacuum_on_demand entity_id: input_boolean.l10s_vacuum_on_demand
- service: script.l10s_vacuum_start_next_room - service: vacuum.return_to_base
target:
entity_id: vacuum.l10s_vacuum
default: []
- alias: 'Vacuum Sensor Cleaning Silencer' - alias: 'Vacuum Sensor Cleaning Silencer'
id: 6548de52-a4a4-4df2-9d66-9c2c15577a7f id: 6548de52-a4a4-4df2-9d66-9c2c15577a7f

@ -1,13 +1,13 @@
<svg width="68.5" height="20" viewBox="0 0 685 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="2025.12.2"> <svg width="68.5" height="20" viewBox="0 0 685 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="2025.12.2">
<title>2025.12.2</title> <title>2025.12.2</title>
<linearGradient id="cnbWS" x2="0" y2="100%"> <linearGradient id="cRlhv" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/> <stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/> <stop offset="1" stop-opacity=".1"/>
</linearGradient> </linearGradient>
<mask id="Wfucn"><rect width="685" height="200" rx="30" fill="#FFF"/></mask> <mask id="BLlyG"><rect width="685" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#Wfucn)"> <g mask="url(#BLlyG)">
<rect width="685" height="200" fill="#08C" x="0"/> <rect width="685" height="200" fill="#08C" x="0"/>
<rect width="685" height="200" fill="url(#cnbWS)"/> <rect width="685" height="200" fill="url(#cRlhv)"/>
</g> </g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110"> <g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="65" y="148" textLength="570" fill="#000" opacity="0.25">2025.12.2</text> <text x="65" y="148" textLength="570" fill="#000" opacity="0.25">2025.12.2</text>

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 815 B

Loading…
Cancel
Save

Powered by TurnKey Linux.