@ -3,8 +3,8 @@
# For more info visit https://www.vcloudinfo.com/click-here
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# Dreame Vacuum Orchestration - Room queue, away/on-demand runs
# Dreame Vacuum Orchestration - Continuous phased sweep/mop with away/on-demand
# Weekday sweep, weekend mop, bathrooms last, notifications
# Phases: sweep main, sweep baths, mop main, mop baths; notifications + idle auto-start
# -------------------------------------------------------------------
# -------------------------------------------------------------------
######################################################################
######################################################################
@ -12,29 +12,31 @@
######################################################################
######################################################################
input_boolean:
input_boolean:
l10s_vacuum_weekday_cycle_active:
name : L10s Weekday Cleaning Active
icon : mdi:robot-vacuum
l10s_vacuum_on_demand:
l10s_vacuum_on_demand:
name : Dream Clean (On-Demand)
name : Dreame Clean (On-Demand)
icon : mdi:ro cket-launch
icon : mdi:robot-vacuum
input_datetime:
input_select:
l10s_vacuum_last_weekday_cycle:
l10s_vacuum_phase:
name : L10s Last Weekday Cleaning Cycle
name : L10s Vacuum Phase
has_date : true
options:
has_time : true
- sweep_main
- sweep_bath
- mop_main
- mop_bath
initial : sweep_main
icon : mdi:playlist-check
input_text:
input_text:
l10s_vacuum_room_queue:
l10s_vacuum_room_queue:
name : L10s Vacuum Room Queue
name : L10s Vacuum Room Queue
# Room order (id:name): 14 Kitchen, 12 Dining, 10 Living, 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.
icon : mdi:format-list-bulleted
icon : mdi:format-list-bulleted
max : 255
max : 255
l10s_vacuum_room_catalog:
l10s_vacuum_room_catalog:
name : L10s Vacuum Room Catalog
name : L10s Vacuum Room Catalog
# Room order (id:name): 14 Kitchen, 12 Dining, 10 Living, 7 Master Bedroom, 15 Foyer, 9 Stacey Office,
initial : "6,7,8,9,10,12,13,14,15,17,2,4,1,3"
# 17 Formal Dining, 13 Hallway, 8 Justin Bedroom, 6 Paige Bedroom, 4 Master Bathroom, 2 Office, 1 Pool Bath, 3 Kids Bathroom.
initial : "14,12,10,7,15,9,17,13,8,6,4,2,1,3"
icon : mdi:map
icon : mdi:map
max : 255
max : 255
l10s_vacuum_rooms_cleaned_today:
l10s_vacuum_rooms_cleaned_today:
@ -52,54 +54,39 @@ script:
mode : single
mode : single
sequence:
sequence:
- variables:
- variables:
# Weekday runs are sweeping (vacuum only), weekend runs are mopping
cleaning_mode : "{{ 'mopping' if now().weekday() in [5, 6] else 'sweeping' }}"
catalog_raw : "{{ states('input_text.l10s_vacuum_room_catalog') | default('', true) | string | replace(' ', '') }}"
catalog_raw : "{{ states('input_text.l10s_vacuum_room_catalog') | default('', true) | string | replace(' ', '') }}"
queue_raw : "{{ states('input_text.l10s_vacuum_room_queue') | default('', true) | string | replace(' ', '') }}"
last_reset_raw : "{{ states('input_datetime.l10s_vacuum_last_weekday_cycle') }}"
last_reset_date_str : >
{% set dt = as_datetime(last_reset_raw, default=None) %}
{{ dt.date().isoformat() if dt is not none else '' }}
catalog_ints : "{{ catalog_raw | regex_findall('[0-9]+') | map('int') | select('gt', 0) | list }}"
catalog_ints : "{{ catalog_raw | regex_findall('[0-9]+') | map('int') | select('gt', 0) | list }}"
queue_ints : "{{ queue_raw | regex_findall('[0-9]+') | map('int') | select('gt', 0) | list }}"
# Seed if queue is empty AND last reset was not today
can_seed_today : "{{ last_reset_date_str == '' or last_reset_date_str != now().date().isoformat() }}"
will_seed : >
{% set empty_queue = queue_ints | length == 0 %}
{% set on_demand = is_state('input_boolean.l10s_vacuum_on_demand', 'on') %}
{{ (empty_queue or (on_demand and queue_ints | length <= 1)) and catalog_ints | length > 0 and (can_seed_today or on_demand) }}
seeded_queue_list : "{{ catalog_ints if will_seed else queue_ints }}"
valid_queue_list : "{{ seeded_queue_list }}"
# Define bathroom IDs for mopping separation
bath_ids : [ 1 , 3 , 4 ]
bath_ids : [ 1 , 3 , 4 ]
nonbath_list : "{{ valid_queue_list | reject('in', bath_ids) | list }}"
main_ids : "{{ catalog_ints | reject('in', bath_ids) | list }}"
bath_list : "{{ valid_queue_list | select('in', bath_ids) | list }}"
phase_order : [ 'sweep_main' , 'sweep_bath' , 'mop_main' , 'mop_bath' ]
# Prioritize non-bathrooms first, then bathrooms
phase_state : "{{ states('input_select.l10s_vacuum_phase') }}"
segments_to_clean : >
phase : "{{ phase_state if phase_state in phase_order else 'sweep_main' }}"
{% if nonbath_list | length > 0 %}
cleaning_mode : "{{ 'mopping' if 'mop_' in phase else 'sweeping' }}"
{{ nonbath_list }}
queue_raw : "{{ states('input_text.l10s_vacuum_room_queue') | default('', true) | string | replace(' ', '') }}"
{% elif bath_list | length > 0 %}
queue_ints : "{{ queue_raw | regex_findall('[0-9]+') | map('int') | select('gt', 0) | list }}"
{{ bath_list }}
phase_segments : >
{% if phase == 'sweep_main' %}
{{ main_ids }}
{% elif phase == 'sweep_bath' %}
{{ bath_ids }}
{% elif phase == 'mop_main' %}
{{ main_ids }}
{% else %}
{% else %}
[ ]
{{ bath_ids }}
{% endif %}
{% endif %}
segments_to_clean : "{{ queue_ints if queue_ints | length > 0 else phase_segments }}"
# 1. Seed the queue if necessary
# 1. Seed the queue if necessary
- choose:
- choose:
- conditions:
- conditions:
- condition : template
- condition : template
value_template : "{{ will_seed }}"
value_template : "{{ queue_ints | length == 0 and phase_segments | length > 0 }}"
sequence:
sequence:
- 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
data:
data:
value : "{{ catalog_raw }}"
value : "{{ phase_segments | join(',') }}"
- service : input_datetime.set_datetime
target:
entity_id : input_datetime.l10s_vacuum_last_weekday_cycle
data:
datetime : "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
default : [ ]
default : [ ]
# 2. Check if there is anything to clean and stop if not
# 2. Check if there is anything to clean and stop if not
@ -122,9 +109,6 @@ script:
entity_id : vacuum.l10s_vacuum
entity_id : vacuum.l10s_vacuum
data:
data:
fan_speed : Standard
fan_speed : Standard
- service : input_boolean.turn_on
target:
entity_id : input_boolean.l10s_vacuum_weekday_cycle_active
- service : dreame_vacuum.vacuum_clean_segment
- service : dreame_vacuum.vacuum_clean_segment
target:
target:
entity_id : vacuum.l10s_vacuum
entity_id : vacuum.l10s_vacuum
@ -138,36 +122,30 @@ script:
automation:
automation:
- alias: ' Away Vacuum: Reset Queue (Mon/Sat) '
- alias: ' Vacuum: Reset Cleaned List at 5am '
id : 93a6e7dc-9c32-4d53-9f7c-651cd60f4b84
id : 18f7b6d3-c02c-4ec1-88b3-0c3b8b4c6f7b
trigger:
trigger:
- platform : time
- platform : time
at : '08:55:00'
at : '05:00:00'
condition:
- condition : time
weekday:
- mon
- sat
action:
action:
- service : input_text.set_value
target:
entity_id : input_text.l10s_vacuum_room_queue
data:
value : "{{ states('input_text.l10s_vacuum_room_catalog') }}"
- service : input_text.set_value
- service : input_text.set_value
target:
target:
entity_id : input_text.l10s_vacuum_rooms_cleaned_today
entity_id : input_text.l10s_vacuum_rooms_cleaned_today
data:
data:
value : ""
value : ""
- service : input_boolean.turn_off
target:
- alias: 'Vacuum : Auto-Start if Idle 3 Days'
entity_id : input_boolean.l10s_vacuum_weekday_cycle_active
id : c6b3f1e8-9a3f-4098-9b9e-1c7f2d6f1d11
- service : input_datetime.set_datetime
trigger:
target:
- platform : time
entity_id : input_datetime.l10s_vacuum_last_weekday_cycle
at : '16:00:00'
data:
condition:
datetime : "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
- condition : template
- service : input_boolean.turn_off
value_template : >
{% set last = state_attr('script.l10s_vacuum_start_next_room','last_triggered') %}
{{ last is none or (now() - last).days >= 3 }}
action:
- service : input_boolean.turn_on
target:
target:
entity_id : input_boolean.l10s_vacuum_on_demand
entity_id : input_boolean.l10s_vacuum_on_demand
@ -202,9 +180,6 @@ automation:
entity_id : input_boolean.l10s_vacuum_on_demand
entity_id : input_boolean.l10s_vacuum_on_demand
to : 'off'
to : 'off'
condition:
condition:
- condition : state
entity_id : input_boolean.l10s_vacuum_weekday_cycle_active
state : 'on'
- condition : template
- condition : template
value_template : >
value_template : >
{{ is_state('vacuum.l10s_vacuum', 'cleaning') or is_state('vacuum.l10s_vacuum', 'returning') or is_state('vacuum.l10s_vacuum', 'paused') }}
{{ is_state('vacuum.l10s_vacuum', 'cleaning') or is_state('vacuum.l10s_vacuum', 'returning') or is_state('vacuum.l10s_vacuum', 'paused') }}
@ -223,52 +198,47 @@ automation:
- platform : state
- platform : state
entity_id : sensor.l10s_vacuum_current_room
entity_id : sensor.l10s_vacuum_current_room
for : '00:03:00'
for : '00:03:00'
- platform : state
entity_id : vacuum.l10s_vacuum
to : 'cleaning'
for : '00:03:00'
variables:
variables:
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' }
catalog_raw : "{{ states('input_text.l10s_vacuum_room_catalog') | default('', true) | string | replace(' ', '') }}"
catalog_ints : "{{ catalog_raw | regex_findall('[0-9]+') | map('int') | select('gt', 0) | list }}"
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') | select('gt', 0) | list | default([], true) }}"
queue_ints : "{{ queue_raw | regex_findall('[0-9]+') | map('int') | list | default([], true) }}"
working_queue : "{{ queue_ints if queue_ints | length > 0 else catalog_ints }}"
current_room_id : "{{ trigger.to_state.attributes.room_id | default(state_attr('sensor.l10s_vacuum_current_room', 'room_id'), true) | int(0) }}"
current_room_id : "{{ trigger.to_state.attributes.room_id | default(state_attr('sensor.l10s_vacuum_current_room', 'room_id'), true) | int(0) }}"
matched_room_id : "{{ current_room_id if current_room_id > 0 and current_room_id in (working_queue | default([], true)) else 0 }}"
matched_room_id : "{{ current_room_id if current_room_id > 0 and current_room_id in (queue_ints | default([], true)) else 0 }}"
remaining_rooms : "{{ working_queue | reject('equalto', matched_room_id) | list | join(',') }}"
remaining_list : >
remaining_value : >
{% set rem = [] %}
{% set rem = remaining_rooms | string %}
{% set removed = namespace(done=false) %}
{% if rem | length == 0 and working_queue | length > 1 %}
{% for r in queue_ints %}
{{ working_queue | join(',') }}
{% if not removed.done and r == matched_room_id %}
{% else %}
{% set removed.done = true %}
{{ rem }}
{% else %}
{% endif %}
{% set rem = rem + [r] %}
remaining_value_str : >
{% endif %}
{% set rv = remaining_value %}
{% endfor %}
{% if rv is string %}
{{ rem }}
{{ rv }}
remaining_rooms : "{{ remaining_list | join(',') }}"
{% elif rv is iterable %}
remaining_count : "{{ remaining_list | length }}"
{{ rv | map('string') | join(',') }}
phase_order : [ 'sweep_main' , 'sweep_bath' , 'mop_main' , 'mop_bath' ]
{% else %}
phase_state : "{{ states('input_select.l10s_vacuum_phase') }}"
{{ rv | string }}
phase : "{{ phase_state if phase_state in phase_order else 'sweep_main' }}"
{% endif %}
phase_index : "{{ phase_order.index(phase) if phase in phase_order else 0 }}"
has_next_phase : "{{ phase_index < (phase_order | length) - 1 }}"
next_phase : "{{ phase_order[phase_index + 1] if has_next_phase else '' }}"
condition:
condition:
# Only run if there's actually a queue and a room was successfully matched to the start of the queue
- condition : template
value_template : "{{ working_queue | length > 0 }}"
- condition : template
- condition : template
value_template : "{{ matched_room_id != 0 }}"
value_template : "{{ queue_ints | length > 0 }}"
- condition : template
- condition : template
value_template : "{{ matched_room_id != 0 }}"
value_template : "{{ matched_room_id != 0 }}"
- condition : state
entity_id : vacuum.l10s_vacuum
state : 'cleaning'
action:
action:
- 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
data:
data:
value : "{{ remaining_ value_st r }}"
value : "{{ remaining_ rooms }}"
- 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 }}"
@ -280,7 +250,6 @@ automation:
{% else %}
{% else %}
{{ parts | join(', ') }}
{{ parts | join(', ') }}
{% endif %}
{% endif %}
remaining_count : "{{ remaining_value_str | regex_findall('[^,]+') | length if remaining_value_str | length > 0 else 0 }}"
- service : input_text.set_value
- service : input_text.set_value
target:
target:
entity_id : input_text.l10s_vacuum_rooms_cleaned_today
entity_id : input_text.l10s_vacuum_rooms_cleaned_today
@ -299,46 +268,39 @@ automation:
- choose:
- choose:
- conditions:
- conditions:
- condition : template
- condition : template
value_template : "{{ remaining_ rooms | length > 0 }}"
value_template : "{{ remaining_ count > 0 }}"
sequence:
sequence:
- service : script.l10s_vacuum_start_next_room
- service : script.l10s_vacuum_start_next_room
- conditions:
- conditions:
- condition : template
- condition : template
value_template : "{{ remaining_ rooms | length == 0 }}"
value_template : "{{ remaining_ count == 0 and has_next_phase }}"
sequence:
sequence:
- service : input_ boolean.turn_off
- service : input_ select.select_option
target:
target:
entity_id : input_boolean.l10s_vacuum_weekday_cycle_active
entity_id : input_select.l10s_vacuum_phase
- service : input_datetime.set_datetime
data:
option : "{{ next_phase }}"
- service : input_text.set_value
target:
target:
entity_id : input_datetime.l10s_vacuum_last_weekday_cycle
entity_id : input_ text.l10s_vacuum_room_queu e
data:
data:
datetime : "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
value : ""
- service : script.l10s_vacuum_start_next_room
- alias: 'Away Vacuum : Cycle Complete'
- conditions:
id : 8fa7779a-957b-49a3-84e7-36ca93c2e0d2
- condition : template
trigger:
value_template : "{{ remaining_count == 0 and not has_next_phase }}"
- platform : state
sequence:
entity_id : sensor.l10s_vacuum_task_status
- service : input_select.select_option
to : 'completed'
target:
- platform : state
entity_id : input_select.l10s_vacuum_phase
entity_id : vacuum.l10s_vacuum
data:
to : 'docked'
option : "sweep_main"
for : 00 : 05 : 00
- service : input_text.set_value
condition:
target:
- condition : template
entity_id : input_text.l10s_vacuum_room_queue
value_template : "{{ is_state('sensor.l10s_vacuum_task_status', 'completed') }}"
data:
- condition : template
value : ""
value_template : "{{ (states('input_text.l10s_vacuum_room_queue') | replace(' ', '')) | length == 0 }}"
- service : script.l10s_vacuum_start_next_room
action:
- service : input_boolean.turn_off
target:
entity_id : input_boolean.l10s_vacuum_weekday_cycle_active
- service : input_datetime.set_datetime
target:
entity_id : input_datetime.l10s_vacuum_last_weekday_cycle
data:
datetime : "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
- alias : 'Vacuum Sensor Cleaning Silencer'
- alias : 'Vacuum Sensor Cleaning Silencer'
id : 6548de52-a4a4-4df2-9d66-9c2c15577a7f
id : 6548de52-a4a4-4df2-9d66-9c2c15577a7f