###################################################################### # @CCOSTAN - Follow Me on X # For more info visit https://www.vcloudinfo.com/click-here # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Dreame Vacuum Orchestration - Weekday cleaning schedules and alerts # Migrated from Neato D7; HACS dreame-vacuum - https://github.com/Tasshack/dreame-vacuum # ------------------------------------------------------------------- # Neato D7: https://amzn.to/2kqnnqu | Dreame: https://amzn.to/4f7NpFP # Authentication via Dreame developer portal; resume-friendly weekday runs ###################################################################### ###################################################################### ### Helpers - Track weekday cleaning cycles and last run timestamp ###################################################################### input_boolean: l10s_vacuum_weekday_cycle_active: name: L10s Weekday Cleaning Active icon: mdi:robot-vacuum l10s_vacuum_on_demand: name: Dream Clean (On-Demand) icon: mdi:rocket-launch input_datetime: l10s_vacuum_last_weekday_cycle: name: L10s Last Weekday Cleaning Cycle has_date: true has_time: true input_text: l10s_vacuum_room_queue: name: L10s Vacuum Room Queue icon: mdi:format-list-bulleted max: 255 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, # 17 Formal Dining, 13 Hallway, 8 Justin Bedroom, 6 Paige Bedroom, 4 Master Bathroom, 2 Office, 1 Pool Bath, 3 Kids Bathroom. Skip 11 Garage Hallway. initial: "14,12,10,7,15,9,17,13,8,6,4,2,1,3" icon: mdi:map max: 255 l10s_vacuum_rooms_cleaned_today: name: L10s Vacuum Rooms Cleaned Today icon: mdi:clipboard-check-outline max: 255 script: l10s_vacuum_start_next_room: alias: 'Away Vacuum: Start Next Room' mode: single sequence: - variables: is_weekend: "{{ now().weekday() in [5, 6] }}" cleaning_mode: "{{ 'mopping' if is_weekend else 'sweeping' }}" catalog_raw: "{{ states('input_text.l10s_vacuum_room_catalog') | string | replace(' ', '') }}" queue_raw: "{{ states('input_text.l10s_vacuum_room_queue') | string | replace(' ', '') }}" last_reset_raw: "{{ states('input_datetime.l10s_vacuum_last_weekday_cycle') }}" last_reset: "{{ as_datetime(last_reset_raw, default=None) }}" can_seed_today: "{{ last_reset is none or (last_reset | as_datetime).date() != now().date() }}" will_seed: "{{ queue_raw | length == 0 and can_seed_today and catalog_raw | length > 0 }}" seeded_queue: "{{ catalog_raw if will_seed else queue_raw }}" queue_list: "{{ (seeded_queue | string).split(',') if seeded_queue | length > 0 else [] }}" bath_ids: [1,3,4] queue_ints: "{{ queue_list | map('int') | list }}" bath_list: "{{ queue_ints | select('in', bath_ids) | list }}" nonbath_list: "{{ queue_ints | reject('in', bath_ids) | list }}" next_room: "{{ queue_list[0] if queue_list | length > 0 else '' }}" - choose: - conditions: - condition: template value_template: "{{ next_room == '' }}" sequence: - stop: 'No rooms left to clean today.' default: [] - choose: - conditions: - condition: template value_template: "{{ will_seed }}" sequence: - service: input_text.set_value target: entity_id: input_text.l10s_vacuum_room_queue data: value: "{{ catalog_raw }}" - 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: [] - service: select.select_option target: entity_id: select.l10s_vacuum_cleaning_mode data: option: "{{ cleaning_mode }}" - service: vacuum.set_fan_speed target: entity_id: vacuum.l10s_vacuum data: fan_speed: Standard - service: input_boolean.turn_on target: entity_id: input_boolean.l10s_vacuum_weekday_cycle_active - service: dreame_vacuum.vacuum_clean_segment target: entity_id: vacuum.l10s_vacuum data: # Service per https://github.com/Tasshack/dreame-vacuum/tree/master/docs/services.md#dreame_vacuumvacuum_clean_segment segments: "{{ nonbath_list if nonbath_list | length > 0 else queue_ints }}" automation: ############################################################################## ### Automations - Help Vacuum! ### https://www.vcloudinfo.com/2020/05/home-assistant-neato-vacuum-automation.html ############################################################################## - alias: 'Away Vacuum: Reset Queue (Mon/Sat)' id: 93a6e7dc-9c32-4d53-9f7c-651cd60f4b84 trigger: - platform: time at: '08:55:00' condition: - condition: time weekday: - mon - sat 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 target: entity_id: input_text.l10s_vacuum_rooms_cleaned_today data: value: "" - 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') }}" - service: input_boolean.turn_off target: entity_id: input_boolean.l10s_vacuum_on_demand - alias: 'Away Vacuum: Start or Resume When we leave or On-Demand' id: 7f7e0a3c-6452-4f6b-8464-c6c25770a148 trigger: - platform: state entity_id: group.family to: 'not_home' - platform: state entity_id: input_boolean.l10s_vacuum_on_demand to: 'on' condition: - condition: state entity_id: input_boolean.guest_mode state: 'off' - condition: template value_template: > {{ is_state('input_boolean.l10s_vacuum_on_demand', 'on') or is_state('group.family', 'not_home') }} - condition: template value_template: "{{ not is_state('vacuum.l10s_vacuum', 'cleaning') }}" action: - service: script.l10s_vacuum_start_next_room - alias: 'Away Vacuum: Dock When Family Returns' id: 1ef172f2-4b30-4e5b-953d-d4d1ca8701ad trigger: - platform: state entity_id: group.family to: 'home' - platform: state entity_id: input_boolean.l10s_vacuum_on_demand to: 'off' condition: - condition: state entity_id: input_boolean.l10s_vacuum_weekday_cycle_active state: 'on' - condition: template value_template: > {{ is_state('vacuum.l10s_vacuum', 'cleaning') or is_state('vacuum.l10s_vacuum', 'returning') or is_state('vacuum.l10s_vacuum', 'paused') }} action: - service: vacuum.pause target: entity_id: vacuum.l10s_vacuum - delay: 00:00:10 - service: vacuum.return_to_base target: entity_id: vacuum.l10s_vacuum - alias: 'Away Vacuum: Confirm Room Cleaned' id: c581c570-55b0-4acd-8b5d-53cfb486cc2a trigger: - platform: state entity_id: sensor.l10s_vacuum_current_room for: '00:03:00' 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'} queue_raw: "{{ states('input_text.l10s_vacuum_room_queue') | string | replace(' ', '') }}" queue_list: "{{ queue_raw.split(',') if queue_raw | length > 0 else [] }}" queue_ints: "{{ queue_list | map('int') | list }}" next_room_id: "{{ queue_list[0] | int(0) if queue_list | length > 0 else 0 }}" current_room_id: "{{ state_attr('sensor.l10s_vacuum_current_room', 'room_id') | int(0) }}" next_room_name: "{{ room_map.get(next_room_id, '') }}" current_room_name: "{{ states('sensor.l10s_vacuum_current_room') | lower | replace('-', ' ') }}" name_match: "{{ next_room_name | lower | replace('-', ' ') }}" matched_room_id: > {% set target = 0 %} {% if current_room_id in queue_ints %} {% set target = current_room_id %} {% else %} {% for r in queue_ints %} {% set nm = room_map.get(r, '') | lower | replace('-', ' ') %} {% if nm != '' and nm in current_room_name %} {% set target = r %} {% endif %} {% endfor %} {% endif %} {{ target }} remaining_rooms: > {% set rem = [] %} {% set removed = namespace(done=false) %} {% for r in queue_ints %} {% if not removed.done and r == matched_room_id %} {% set removed.done = true %} {% else %} {% set rem = rem + [r] %} {% endif %} {% endfor %} {{ rem | join(',') }} condition: - condition: template value_template: "{{ queue_ints | length > 0 }}" - condition: template value_template: "{{ matched_room_id != 0 }}" - condition: state entity_id: vacuum.l10s_vacuum state: 'cleaning' action: - condition: template value_template: > {% set head = (states('input_text.l10s_vacuum_room_queue') | string | replace(' ', '')).split(',')[0] if states('input_text.l10s_vacuum_room_queue') | length > 0 else '' %} {% set head_int = head | int(0) %} {% set head_name = room_map.get(head_int, '') %} {% set current_id = state_attr('sensor.l10s_vacuum_current_room', 'room_id') | int(0) %} {% set current_name = states('sensor.l10s_vacuum_current_room') | lower | replace('-', ' ') %} {% set name_match_check = head_name | lower | replace('-', ' ') %} {{ is_state('vacuum.l10s_vacuum', 'cleaning') and matched_room_id != 0 }} - service: input_text.set_value target: entity_id: input_text.l10s_vacuum_room_queue data: value: "{{ remaining_rooms }}" - variables: cleaned_raw: "{{ states('input_text.l10s_vacuum_rooms_cleaned_today') }}" room_name: "{{ room_map.get(matched_room_id, states('sensor.l10s_vacuum_current_room')) }}" updated_cleaned: > {% set parts = cleaned_raw.split(',') if cleaned_raw | length > 0 else [] %} {% if room_name not in parts %} {{ (parts + [room_name]) | join(', ') }} {% else %} {{ cleaned_raw }} {% endif %} remaining_count: "{{ remaining_rooms.split(',') | length if remaining_rooms | length > 0 else 0 }}" - service: input_text.set_value target: entity_id: input_text.l10s_vacuum_rooms_cleaned_today data: value: "{{ updated_cleaned }}" - service: script.notify_engine data: title: 'Vacuum room done' value1: "{{ room_name }} cleaned." value2: "Remaining: {{ remaining_count }}." ios_category: 'camera' camera_entity: 'camera.l10s_vacuum_map' content_type: 'jpeg' who: 'carlo' group: 'information' - choose: - conditions: - condition: template value_template: "{{ remaining_rooms | length == 0 }}" sequence: - 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: 'Away Vacuum: Cycle Complete' id: 8fa7779a-957b-49a3-84e7-36ca93c2e0d2 trigger: - platform: state entity_id: sensor.l10s_vacuum_task_status to: 'completed' - platform: state entity_id: vacuum.l10s_vacuum to: 'docked' for: 00:05:00 condition: - condition: template value_template: "{{ is_state('sensor.l10s_vacuum_task_status', 'completed') }}" - condition: template value_template: "{{ (states('input_text.l10s_vacuum_room_queue') | replace(' ', '')) | length == 0 }}" 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: 'Away Vacuum: Start Bathrooms When Pending' id: 6b8d7d0e-fc21-4a2f-bd9e-4c51f6b9c2c3 trigger: - platform: state entity_id: vacuum.l10s_vacuum to: 'docked' for: 00:02:00 condition: - condition: state entity_id: input_boolean.guest_mode state: 'off' - condition: template value_template: "{{ is_state('input_boolean.l10s_vacuum_weekday_cycle_active', 'on') or is_state('input_boolean.l10s_vacuum_on_demand', 'on') }}" - condition: template value_template: > {% set bath_ids = [1,3,4] %} {% set queue_raw = states('input_text.l10s_vacuum_room_queue') | string | replace(' ', '') %} {% set q = queue_raw.split(',') if queue_raw | length > 0 else [] %} {% set q_int = q | map('int') | list %} {% set non_bath = q_int | reject('in', bath_ids) | list %} {% set baths = q_int | select('in', bath_ids) | list %} {{ baths | length > 0 and non_bath | length == 0 }} - condition: template value_template: "{{ not is_state('vacuum.l10s_vacuum', 'cleaning') }}" action: - variables: bath_ids: [1,3,4] queue_raw: "{{ states('input_text.l10s_vacuum_room_queue') | string | replace(' ', '') }}" q: "{{ queue_raw.split(',') if queue_raw | length > 0 else [] }}" baths: "{{ q | map('int') | list | select('in', bath_ids) | list }}" - service: input_text.set_value target: entity_id: input_text.l10s_vacuum_room_queue data: value: "{{ baths | join(',') }}" - service: dreame_vacuum.vacuum_clean_segment target: entity_id: vacuum.l10s_vacuum data: segments: "{{ baths }}" - service: input_boolean.turn_on target: entity_id: input_boolean.l10s_vacuum_weekday_cycle_active - alias: 'Vacuum Sensor Cleaning Silencer' id: 6548de52-a4a4-4df2-9d66-9c2c15577a7f trigger: - platform: numeric_state entity_id: sensor.l10s_vacuum_sensor_dirty_left below: 10 condition: - condition: state entity_id: sensor.l10s_vacuum_task_status state: 'completed' action: - service: button.press target: entity_id: button.l10s_vacuum_reset_sensor - alias: 'Help Vacuum' id: 6548de52-a4a4-4df2-9d66-9c2c15577a7e trigger: - platform: state entity_id: sensor.l10s_vacuum_error - platform: event event_type: event_did_someone_help_vacuum_loop condition: - condition: template value_template: "{{ states('sensor.l10s_vacuum_error') not in ['no_error', 'unavailable'] }}" action: - service: script.notify_engine data: title: 'Help vacuum' value1: "{{ states('sensor.l10s_vacuum_error') }} - {{states('sensor.l10s_vacuum_current_room')}}" who: 'family' ios_category: 'camera' camera_entity: 'camera.l10s_vacuum_map' content_type: 'jpeg' group: 'information' - wait_template: "{{ is_state('group.bed', 'off') }}" - wait_template: "{{ is_state('group.family', 'home') }}" - delay: 00:03:00 - service: vacuum.locate entity_id: vacuum.l10s_vacuum - service: script.speech_engine data: value1: > {{ "Vacuum error: " ~ states('sensor.l10s_vacuum_error') ~ " [ask Residents to help]" }} Currently in {{states('sensor.l10s_vacuum_current_room')}}" - delay: 00:01:00 - service: vacuum.locate entity_id: vacuum.l10s_vacuum - delay: 00:20:00 - event: event_did_someone_help_vacuum_loop