diff --git a/lovelace_mop_multifloor.yaml b/lovelace_mop_multifloor.yaml new file mode 100644 index 0000000..bcaadb3 --- /dev/null +++ b/lovelace_mop_multifloor.yaml @@ -0,0 +1,250 @@ +### VERTICAL STACK CARD ### +type: vertical-stack +cards: + - entity: sensor.mop + type: custom:roomba-vacuum-card + mode: mop + - type: entities + entities: + - entities: + - entity: input_boolean.mop_schedule_1 + name: Master Ensuite Clean (11am M/W/F) + - entity: input_boolean.mop_schedule_2 + name: Apartment Clean (12pm Sun) + head: + label: Cleaning Schedules + type: section + type: custom:fold-entity-row + - entities: + - type: custom:select-list-card + entity: input_select.mop_maps + head: + label: Map Selection + type: section + type: custom:fold-entity-row + - default: + type: custom:blank-card + entity: input_select.mop_maps + states: + Kitchen: + entities: + - input_boolean.mop_clean_kitchen + - entity: automation.mop_clean_rooms + lock: + enabled: >- + [[[ + + if ((states['group.mop_rooms'].state == "on") && + (states['sensor.mop'].state == "Ready")) + return false; + return true; + + ]]] + exemptions: [] + name: Clean Rooms + styles: + card: + - height: 50px + tap_action: + action: call-service + service: automation.trigger + service_data: + entity_id: automation.mop_clean_rooms + type: custom:button-card + head: + label: Kitchen Selective Room Cleaning + type: section + type: custom:fold-entity-row + Entry: + entities: + - input_boolean.mop_clean_entry + - entity: automation.mop_clean_rooms + lock: + enabled: >- + [[[ + + if ((states['group.mop_rooms'].state == "on") && + (states['sensor.mop'].state == "Ready")) + return false; + return true; + + ]]] + exemptions: [] + name: Clean Rooms + styles: + card: + - height: 50px + tap_action: + action: call-service + service: automation.trigger + service_data: + entity_id: automation.mop_clean_rooms + type: custom:button-card + head: + label: Entry Selective Room Cleaning + type: section + type: custom:fold-entity-row + Bathroom: + entities: + - input_boolean.mop_clean_bathroom + - entity: automation.mop_clean_rooms + lock: + enabled: >- + [[[ + + if ((states['group.mop_rooms'].state == "on") && + (states['sensor.mop'].state == "Ready")) + return false; + return true; + + ]]] + exemptions: [] + name: Clean Rooms + styles: + card: + - height: 50px + tap_action: + action: call-service + service: automation.trigger + service_data: + entity_id: automation.mop_clean_rooms + type: custom:button-card + head: + label: Bathroom Selective Room Cleaning + type: section + type: custom:fold-entity-row + Master Ensuite: + entities: + - input_boolean.mop_clean_master_ensuite + - entity: automation.mop_clean_rooms + lock: + enabled: >- + [[[ + + if ((states['group.mop_rooms'].state == "on") && + (states['sensor.mop'].state == "Ready")) + return false; + return true; + + ]]] + exemptions: [] + name: Clean Rooms + styles: + card: + - height: 50px + tap_action: + action: call-service + service: automation.trigger + service_data: + entity_id: automation.mop_clean_rooms + type: custom:button-card + head: + label: Ensuite Selective Room Cleaning + type: section + type: custom:fold-entity-row + type: custom:state-switch + - entities: + - style: |- + .text-divider { + padding: 0em; + margin: 0em; + } h2 { + font-size: 1em; + margin-block-start: 0em; + margin-block-end: 0em; + } + text: Clean + type: custom:text-divider-row + - color: '#40bf6a' + due: true + entity: sensor.mop_maint_clean_pad + locale: en-au + severity: + - color: '#bfb540' + value: 0 days + - color: '#bf4060' + value: '-1 days' + style: | + ha-card { + padding: 2px; + --ha-card-box-shadow: 'none'; + --paper-card-background-color: rgba(0, 0, 0, 0); + } + timeout: 10 days + title: Mop Pad + type: custom:check-button-card + - color: '#40bf6a' + due: true + entity: sensor.mop_maint_clean_contacts + locale: en-au + severity: + - color: '#bfb540' + value: 0 days + - color: '#bf4060' + value: '-3 days' + style: | + ha-card { + padding: 2px; + --ha-card-box-shadow: 'none'; + --paper-card-background-color: rgba(0, 0, 0, 0); + } + timeout: 18 days + title: Contacts + type: custom:check-button-card + - color: '#40bf6a' + due: true + entity: sensor.mop_maint_clean_wheels + locale: en-au + severity: + - color: '#bfb540' + value: 0 days + - color: '#bf4060' + value: '-3 days' + style: | + ha-card { + padding: 2px; + --ha-card-box-shadow: 'none'; + --paper-card-background-color: rgba(0, 0, 0, 0); + } + timeout: 18 days + title: Wheels + type: custom:check-button-card + - style: |- + .text-divider { + padding: 0em; + margin: 0em; + } h2 { + font-size: 1em; + margin-block-start: 0em; + margin-block-end: 0em; + } + text: Replace + type: custom:text-divider-row + - color: '#40bf6a' + due: true + entity: sensor.mop_maint_replace_pad + locale: en-au + severity: + - color: '#bfb540' + value: 0 months + - color: '#bf4060' + value: '-4 months' + style: | + ha-card { + padding: 2px; + --ha-card-box-shadow: 'none'; + --paper-card-background-color: rgba(0, 0, 0, 0); + } + timeout: 8 months + title: Mop Pad + type: custom:check-button-card + head: + label: Maintenance + type: section + type: custom:fold-entity-row + +### PICTURE GLANCE CARD ### +aspect_ratio: 0% +camera_image: camera.roomba +entities: [] +type: picture-glance \ No newline at end of file diff --git a/mop.yaml b/mop.yaml new file mode 100644 index 0000000..96b2431 --- /dev/null +++ b/mop.yaml @@ -0,0 +1,709 @@ +################################### +# iRobot Mop Package +################################### + +################################### +# Sensor +################################### + +sensor: + # Braava via Rest980_2 Docker Image + - platform: rest + name: rest980_2 + json_attributes: + - batPct + - cleanMissionStatus + - dock + - pose + - signal + - bbmssn + - bbrun + - name + - pmaps + - openOnly + - twoPass + - softwareVer + - detectedPad + - padWetness + - mopReady + - rankOverlap + resource: !secret mop_state + value_template: 'OK' + scan_interval: 10 + - platform: template + sensors: + mop: + friendly_name_template: >- + {{ state_attr('sensor.rest980_2', 'name') }} + value_template: >- + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['cycle'] == 'none' and state_attr('sensor.rest980_2', 'cleanMissionStatus')['notReady'] == 39 %} + Pending + {% elif state_attr('sensor.rest980_2', 'cleanMissionStatus')['notReady'] > 0 %} + Not Ready + {% else %} + {% set mapper = { + 'clean' : 'Clean', + 'quick' : 'Clean', + 'spot' : 'Spot', + 'dock' : 'Dock', + 'train' : 'Train', + 'none' : 'Ready' } %} + {% set state = state_attr('sensor.rest980_2', 'cleanMissionStatus')['cycle'] %} + {{ mapper[state] if state in mapper else state }} + {% endif %} + icon_template: mdi:robot-vacuum + attribute_templates: + notready_msg: >- + {% set mapper = { + 0 : 'n-a', + 2 : 'Uneven Ground', + 15 : 'Low Battery', + 31 : 'Fill Tank', + 39 : 'Pending', + 48 : 'Path Blocked' } %} + {% set state = state_attr('sensor.rest980_2', 'cleanMissionStatus')['notReady'] %} + {{ mapper[state] if state in mapper else state }} + error_msg: >- + {% set mapper = { + 0 : 'n-a', + 18 : 'Docking Issue'} %} + {% set state = state_attr('sensor.rest980_2', 'cleanMissionStatus')['error'] %} + {{ mapper[state] if state in mapper else state }} + battery: >- + {{ state_attr('sensor.rest980_2', 'batPct') }} % + software_ver: >- + {% if state_attr('sensor.rest980_2', 'softwareVer') is defined %} + {% set version = state_attr('sensor.rest980_2', 'softwareVer') %} + {{ version.split('+')[1] }} + {% else %} + n-a + {% endif %} + phase: >- + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['phase'] == 'charge' and state_attr('sensor.rest980_2', 'batPct') == 100 %} + Idle + {% elif state_attr('sensor.rest980_2', 'cleanMissionStatus')['cycle'] == 'none' and state_attr('sensor.rest980_2', 'cleanMissionStatus')['phase'] == 'stop' %} + Stopped + {% else %} + {% set mapper = { + 'charge' : 'Charge', + 'run' : 'Run', + 'stop' : 'Paused', + 'stuck' : 'Stuck', + 'hmUsrDock' : 'Sent Home', + 'hmMidMsn' : 'Mid Dock', + 'hmPostMsn' : 'Final Dock' } %} + {% set state = state_attr('sensor.rest980_2', 'cleanMissionStatus')['phase'] %} + {{ mapper[state] if state in mapper else state }} + {% endif %} + location: >- + {% if state_attr('sensor.rest980_2', 'pose')['theta'] is defined %} + ({{ state_attr('sensor.rest980_2', 'pose')['point']['x'] }}, {{ state_attr('sensor.rest980_2', 'pose')['point']['y'] }}, {{ state_attr('sensor.rest980_2', 'pose')['theta'] }}) + {% else %} + n-a + {% endif %} + clean_mode: >- + {{ state_attr('sensor.rest980_2', 'padWetness')['disposable'] }} + mop_behavior: >- + {% if state_attr('sensor.rest980_2', 'rankOverlap') is defined %} + {% set mapper = { + 25 : 'Extended', + 67 : 'Standard', + 85 : 'Deep' } %} + {% set state = state_attr('sensor.rest980_2', 'rankOverlap') %} + {{ mapper[state] if state in mapper else state }} + {% else %} + n-a + {% endif %} + pad: >- + {% if state_attr('sensor.rest980_2', 'detectedPad') is defined %} + {% set mapper = { + 'reusableDry' : 'Dry', + 'reusableWet' : 'Wet', + 'dispDry' : 'Single Dry', + 'dispWet' : 'Single Wet' + } %} + {% set state = state_attr('sensor.rest980_2', 'detectedPad') %} + {{ mapper[state] if state in mapper else state}} + {% else %} + n-a + {% endif %} + tank: >- + {% if state_attr('sensor.rest980_2', 'mopReady')['tankPresent'] is defined %} + {% if state_attr('sensor.rest980_2', 'mopReady')['tankPresent'] and state_attr('sensor.rest980_2', 'mopReady')['lidClosed'] %} + Ready + {% elif state_attr('sensor.rest980_2', 'mopReady')['tankPresent'] and (state_attr('sensor.rest980_2', 'mopReady')['lidClosed'] == false) %} + Lid Open + {% else %} + Fill Tank + {% endif %} + {% else %} + n-a + {% endif %} + rssi: >- + {% if state_attr('sensor.rest980_2', 'signal')['rssi'] is defined %} + {{ state_attr('sensor.rest980_2', 'signal')['rssi'] }} + {% else %} + n-a + {% endif %} + total_area: >- + {% if state_attr('sensor.rest980_2', 'bbrun')['sqft'] is defined %} + {{ (state_attr('sensor.rest980_2', 'bbrun')['sqft'] / 10.764 * 100)| round() }}m² + {% else %} + n-a + {% endif %} + # {{ (state_attr('sensor.rest980_2', 'bbrun')['sqft'] }}ft² + total_time: >- + {% if state_attr('sensor.rest980_2', 'bbrun')['hr'] is defined %} + {{ state_attr('sensor.rest980_2', 'bbrun')['hr'] }}h {{ state_attr('sensor.rest980_2', 'bbrun')['min'] }}m + {% else %} + n-a + {% endif %} + total_jobs: >- + {% if state_attr('sensor.rest980_2', 'bbmssn')['nMssn'] is defined %} + {{ state_attr('sensor.rest980_2', 'bbmssn')['nMssn'] }} + {% else %} + n-a + {% endif %} + job_initiator: >- + {% set mapper = { + 'schedule' : 'Scheduler', + 'rmtApp' : 'App', + 'manual' : 'Robot', + 'localApp' : 'HA' } %} + {% set state = state_attr('sensor.rest980_2', 'cleanMissionStatus')['initiator'] %} + {{ mapper[state] if state in mapper else state }} + job_time: >- + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['mssnStrtTm'] is defined %} + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['mssnStrtTm'] != 0 %} + {% set time = state_attr('sensor.rest980_2', 'cleanMissionStatus')['mssnStrtTm'] | timestamp_local %} + {% set elapsed = ((as_timestamp(now()) - as_timestamp(time)) / 60) | round(0) %} + {% if elapsed > 60 %} + {{ elapsed // 60 }}h {{ '{:0>2d}'.format(elapsed%60) }}m + {% else %} + {{elapsed}}m + {% endif %} + {% else %} + n-a + {% endif %} + {% else %} + n-a + {% endif %} + job_recharge: >- + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['rechrgTm'] is defined %} + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['rechrgTm'] != 0 %} + {% set time = state_attr('sensor.rest980_2', 'cleanMissionStatus')['rechrgTm'] | timestamp_local %} + {% set resume = ((as_timestamp(time) - as_timestamp(now())) / 60) | round(0) %} + {% if resume > 60 %} + {{ resume // 60 }}h {{ '{:0>2d}'.format(resume%60) }}m + {% else %} + {{resume}}m + {% endif %} + {% else %} + n-a + {% endif %} + {% else %} + n-a + {% endif %} + job_expires: >- + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['expireTm'] is defined %} + {% if state_attr('sensor.rest980_2', 'cleanMissionStatus')['expireTm'] != 0 %} + {% set time = state_attr('sensor.rest980_2', 'cleanMissionStatus')['expireTm'] | timestamp_local %} + {% set resume = ((as_timestamp(time) - as_timestamp(now())) / 60) | round(0) %} + {% if resume > 60 %} + {{ resume // 60 }}h {{ '{:0>2d}'.format(resume%60) }}m + {% else %} + {{resume}}m + {% endif %} + {% else %} + n-a + {% endif %} + {% else %} + n-a + {% endif %} + clean_edges: >- + {% if state_attr('sensor.rest980_2', 'openOnly') is defined %} + {% if state_attr('sensor.rest980_2', 'openOnly') == 'true' %} + False + {% else %} + True + {% endif %} + {% else %} + n-a + {% endif %} + maint_due: >- + {% if is_state('input_boolean.mop_maint_due', 'on') %} + True + {% else %} + False + {% endif %} + pmap0_id: >- + {{ state_attr('sensor.rest980_2', 'pmaps')[0] | regex_findall_index("{'([\w\-]+)': '\w+'}") }} + pmap1_id: >- + {{ state_attr('sensor.rest980_2', 'pmaps')[1] | regex_findall_index("{'([\w\-]+)': '\w+'}") }} + pmap2_id: >- + {{ state_attr('sensor.rest980_2', 'pmaps')[2] | regex_findall_index("{'([\w\-]+)': '\w+'}") }} + pmap3_id: >- + {{ state_attr('sensor.rest980_2', 'pmaps')[3] | regex_findall_index("{'([\w\-]+)': '\w+'}") }} + mop_location: + friendly_name_template: >- + {{ state_attr('sensor.rest980_2', 'name') }} Location + value_template: >- + {{ state_attr('sensor.mop', 'location') }} + icon_template: mdi:home-map-marker + +################################### +# Rest Command +################################### + +rest_command: + mop_action: + url: >- + {{ states('input_text.mop_action') }}{{ command }} + verify_ssl: !secret mop_verify_ssl + method: 'get' + timeout: 20 + mop_clean: + url: >- + {{ states('input_text.mop_action') }}cleanRoom + verify_ssl: !secret mop_verify_ssl + method: POST + content_type: 'application/json' + payload: '{{ payload }}' + +################################### +# Input Boolean +################################### + +input_boolean: + mop_clean_kitchen: + name: Kitchen + icon: mdi:silverware-fork-knife + mop_clean_entry: + name: Entry + icon: mdi:coat-rack + mop_clean_bathroom: + name: Bathroom + icon: mdi:shower + mop_clean_master_ensuite: + name: Master Ensuite + icon: mdi:shower + mop_maint_due: + name: Mop Maintenance + icon: mdi:wrench + mop_schedule_1: + name: Mop Schedule 1 + icon: mdi:timetable + mop_schedule_2: + name: Mop Schedule 2 + icon: mdi:timetable + +################################### +# Input Text +################################### + +input_text: + mop_action: + name: Mop Action URL + initial: !secret mop_action + mop_map: + name: Mop Map URL + initial: !secret mop_map + mop_log: + name: Mop Log Path + initial: !secret mop_log + mop_dir: + name: Mop Dir Path + initial: !secret mop_dir + mop_rooms: + name: Mop Rooms + max: 255 + mop_pmap_id: + name: Mop PMap + + mop_clean_kitchen: + name: Kitchen + initial: !secret mop_kitchen + mop_clean_entry: + name: Entry + initial: !secret mop_entry + mop_clean_bathroom: + name: Bathroom + initial: !secret mop_bathroom + mop_clean_master_ensuite: + name: Master Ensuite + initial: !secret mop_master_ensuite + +################################### +# Group +################################### + +group: + mop_rooms: + entities: + - input_boolean.mop_clean_kitchen + - input_boolean.mop_clean_entry + - input_boolean.mop_clean_bathroom + - input_boolean.mop_clean_master_ensuite + +################################### +# Automation +################################### + +automation: + # Initiate Selective Room Clean + - alias: Mop Clean Rooms + trigger: + - platform: event + event_type: initiate_mop_clean + condition: + condition: not + conditions: + - condition: state + entity_id: input_text.mop_rooms + state: '' + action: + - service: rest_command.mop_clean + data_template: + payload: > + {% set rooms = states('input_text.mop_rooms') %} + {% if rooms[-1:] == ',' %} + {% set rooms = rooms[:-1] %} + {% endif %} + {% set rooms = rooms.split(",") %} + { + "ordered": 1, + "pmap_id": "{{ states('input_text.mop_pmap_id') | string }}", + "regions": [{% for id in rooms %} + {% set room = 'input_text.mop_clean_' + id %} {{ states(room) | string }} {%- if not loop.last %},{%- endif %} + {%- endfor %} + ] + } + - service: input_text.set_value + data: + entity_id: input_text.mop_rooms + value: '' + - service: input_boolean.turn_off + data: + entity_id: group.mop_rooms + + # Update Mop REST Sensor for Location Details + - alias: Mop Update Location + initial_state: true + trigger: + - platform: time_pattern + seconds: /2 + - platform: event + event_type: call_service + event_data: + domain: rest_command + service: mop_clean + condition: + condition: or + conditions: + - condition: template + value_template: "{{ is_state_attr('sensor.mop', 'phase', 'Run') }}" + - condition: template + value_template: "{{ is_state_attr('sensor.mop', 'phase', 'Sent Home') }}" + - condition: template + value_template: "{{ is_state_attr('sensor.mop', 'phase', 'Mid Dock') }}" + - condition: template + value_template: "{{ is_state_attr('sensor.mop', 'phase', 'Final Dock') }}" + action: + - service: homeassistant.update_entity + entity_id: sensor.rest980_2 + + # Log Mop Location to File + - alias: Mop Log Position + initial_state: true + trigger: + platform: state + entity_id: sensor.mop_location + condition: + condition: or + conditions: + - condition: state + entity_id: sensor.mop + state: 'Clean' + - condition: state + entity_id: sensor.mop + state: 'Train' + action: + - service: notify.mopfile + data_template: + message: "{{ states('sensor.mop_location') }}" + + # Initialize Blank Mop Log File + - alias: Mop Clean Log + initial_state: true + trigger: + - platform: state + entity_id: sensor.mop + from: 'Ready' + to: 'Clean' + - platform: state + entity_id: sensor.mop + from: 'Ready' + to: 'Train' + action: + - service: shell_command.mop_clear_log + - service: shell_command.mop_clear_image + + # Update Mop Log File with Finished Status + - alias: Mop Notify on Finished Cleaning + initial_state: true + trigger: + - platform: state + entity_id: sensor.mop + from: 'Clean' + to: 'Ready' + - platform: state + entity_id: sensor.mop + from: 'Train' + to: 'Ready' + - platform: state + entity_id: sensor.mop + from: 'Pending' + to: 'Ready' + action: + - service: notify.mopfile + data_template: + message: "Finished" + + # Update Mop Log File with Stuck Status + - alias: Mop Notify on Stuck Status + initial_state: true + trigger: + platform: template + value_template: "{{ is_state_attr('sensor.mop', 'phase', 'Stuck') }}" + action: + - service: notify.mopfile + data_template: + message: "Stuck" + # DELETE BELOW SECTION IF YOU DONT WANT NOTIFICATIONS + - delay: 5 + - service: !secret mop_notify + data_template: + title: "{{ state_attr('sensor.rest980_2', 'name') }} requires your attention" + message: "{{ state_attr('sensor.rest980_2', 'name') }} is stuck." + # ============= + # NOTICE THIS SECTION IS VALID FOR IOS USERS ONLY! + data: + attachment: + content-type: jpeg + push: + category: camera + entity_id: camera.braava + # ============= + + # Generate Complete Mop Map + - alias: Mop Generate Image after Cleaning + initial_state: true + trigger: + - platform: state + entity_id: sensor.mop + from: 'Clean' + to: 'Ready' + for: + seconds: 10 + - platform: state + entity_id: sensor.mop + from: 'Train' + to: 'Ready' + for: + seconds: 10 + - platform: state + entity_id: sensor.mop + from: 'Pending' + to: 'Ready' + for: + seconds: 10 + action: + - service: shell_command.mop_generate_image + # DELETE BELOW SECTION IF YOU DONT WANT NOTIFICATIONS + - delay: 5 + - service: !secret mop_notify + data_template: + title: "{{ state_attr('sensor.rest980_2', 'name') }}" + message: "{{ state_attr('sensor.rest980_2', 'name') }} successfully completed a job!" + # ============= + # NOTICE THIS SECTION IS VALID FOR IOS USERS ONLY! + data: + attachment: + content-type: jpeg + push: + category: camera + entity_id: camera.braava + # ============= + + # Maintenance Check + - alias: Mop Maintenance Check + initial_state: true + trigger: + - platform: time_pattern + minutes: /15 + - platform: state + entity_id: [ + 'sensor.mop_maint_clean_contacts', + 'sensor.mop_maint_clean_pad', + 'sensor.mop_maint_clean_wheels', + 'sensor.mop_maint_replace_pad' + ] + action: + - service: >- + {% set ns = namespace(count = 0) %} + {% for item in states.sensor if 'sensor.mop_maint' in item.entity_id and (state_attr(item.entity_id, 'timeout_timestamp') < as_timestamp(now())) %} + {% set ns.count = loop.index %} + {% endfor %} + {% if ns.count > 0 %} + input_boolean.turn_on + {% else %} + input_boolean.turn_off + {% endif %} + entity_id: input_boolean.mop_maint_due + + # Add Rooms for Ordered Cleaning + - alias: Mop Add Rooms for Cleaning + initial_state: true + trigger: + platform: state + entity_id: [ + 'input_boolean.mop_clean_kitchen', + 'input_boolean.mop_clean_entry', + 'input_boolean.mop_clean_bathroom', + 'input_boolean.mop_clean_master_ensuite' + ] + to: 'on' + action: + service: input_text.set_value + data_template: + entity_id: input_text.mop_rooms + value: | + {% set room = trigger.entity_id %} + {% set room = room.replace("input_boolean.mop_clean_","") %} + {% if ((states('input_text.mop_rooms') == "unknown") or (states('input_text.mop_rooms') == "")) %} + {{ room }}, + {% else %} + {{ states('input_text.mop_rooms') }}{{ room }}, + {% endif %} + + # Remove Rooms for Ordered Cleaning + - alias: Mop Remove Rooms for Cleaning + initial_state: true + trigger: + platform: state + entity_id: [ + 'input_boolean.mop_clean_kitchen', + 'input_boolean.mop_clean_entry', + 'input_boolean.mop_clean_bathroom', + 'input_boolean.mop_clean_master_ensuite' + ] + to: 'off' + action: + service: input_text.set_value + data_template: + entity_id: input_text.mop_rooms + value: | + {% set room = trigger.entity_id %} + {% set room = room.replace("input_boolean.mop_clean_","") %} + {% set result = states('input_text.mop_rooms') %} + {% set result = result.replace(room + ",","") %} + {{ result }} + + # Mop Cleaning Schedule 1 + - alias: Mop Cleaning Schedule 1 + initial_state: true + trigger: + platform: time_pattern + hours: "11" + condition: + condition: and + conditions: + - condition: state + entity_id: input_boolean.mop_schedule_1 + state: 'on' + - condition: state + entity_id: input_boolean.vacation + state: 'off' + - condition: time + weekday: + - mon + - wed + - fri + action: + - service: input_select.select_option + data: + option: 'Master Ensuite' + entity_id: input_select.mop_maps + - service: input_text.set_value + entity_id: input_text.mop_rooms + data_template: + entity_id: input_text.mop_rooms + value: "master_ensuite" + - service: automation.trigger + entity_id: automation.mop_clean_rooms + + # Mop Cleaning Schedule 2 + - alias: Mop Cleaning Schedule 2 + initial_state: true + trigger: + platform: time_pattern + hours: "12" + condition: + condition: and + conditions: + - condition: state + entity_id: input_boolean.mop_schedule_2 + state: 'on' + - condition: state + entity_id: input_boolean.vacation + state: 'off' + - condition: time + weekday: + - sun + action: + - service: input_text.set_value + entity_id: input_text.mop_rooms + data_template: + entity_id: input_text.mop_rooms + value: "kitchen,entry,bathroom,master_ensuite" + - service: automation.trigger + entity_id: automation.mop_clean_rooms + + # Delete Old Mop Image Files + - alias: Mop Delete Images + initial_state: true + trigger: + platform: time_pattern + hours: "00" + action: + - service: shell_command.mop_delete_images + +################################### +# Notify +################################### + +notify: + - name: MopFile + platform: file + filename: !secret mop_log + +################################### +# Camera +################################### + +camera: + - platform: generic + still_image_url: >- + {{ states('input_text.mop_map') }} + content_type: image/png + name: Braava + +################################### +# Shell Command +################################### + +shell_command: + mop_clear_log: cp /dev/null {{ states('input_text.mop_log') }} + mop_clear_image: curl -X GET -s -O /dev/null '{{ states("input_text.mop_map") }}?clear=true' + mop_generate_image: curl -X GET -s -O /dev/null '{{ states("input_text.mop_map") }}?last=true' + mop_delete_images: find {{ states('input_text.mop_dir') }} -regex ".*[0-9]\.png" -type f -mtime +20 -exec rm -f {} \; diff --git a/mop_multifloor_example.yaml b/mop_multifloor_example.yaml new file mode 100644 index 0000000..72671ac --- /dev/null +++ b/mop_multifloor_example.yaml @@ -0,0 +1,109 @@ +################################### +# iRobot Mop Package - Multi Floor +################################### + +################################### +# Input Select +################################### + +input_select: + mop_maps: + name: Mop Maps + initial: Kitchen + options: + - Kitchen + - Entry + - Bathroom + - Master Ensuite + +################################### +# Input Text +################################### + +input_text: + mop_map_kitchen: + name: Mop Map URL Kitchen + initial: !secret mop_map_kitchen + mop_map_entry: + name: Mop Map URL Entry + initial: !secret mop_map_entry + mop_map_bathroom: + name: Mop Map URL Bathroom + initial: !secret mop_map_bathroom + mop_map_ensuite: + name: Mop Map URL Ensuite + initial: !secret mop_map_ensuite + +################################### +# Automation +################################### + +automation: + # Map Selection on Change and Startup + - alias: Mop Map Selection + initial_state: true + trigger: + - platform: state + entity_id: input_select.mop_maps + - platform: homeassistant + event: start + action: + # Turn off all Mop Rooms + - service: input_boolean.turn_off + data: + entity_id: group.mop_rooms + # Reset Ordered Mop Rooms List + - service: input_text.set_value + data: + entity_id: input_text.mop_rooms + value: '' + # Set Correct PMap based on Room + - service: input_text.set_value + data_template: + entity_id: input_text.mop_pmap_id + value: >- + {% if states('input_select.mop_maps') == 'Kitchen' %} + {{ state_attr('sensor.mop', 'pmap3_id') }} + {% elif states('input_select.mop_maps') == 'Entry' %} + {{ state_attr('sensor.mop', 'pmap2_id') }} + {% elif states('input_select.mop_maps') == 'Bathroom' %} + {{ state_attr('sensor.mop', 'pmap1_id') }} + {% elif states('input_select.mop_maps') == 'Master Ensuite' %} + {{ state_attr('sensor.mop', 'pmap0_id') }} + {% endif %} + # Set Correct Map URL for Shell Commands and Camera + - service: input_text.set_value + data_template: + entity_id: input_text.mop_map + value: >- + {% if states('input_select.mop_maps') == 'Kitchen' %} + {{ states('input_text.mop_map_kitchen') }} + {% elif states('input_select.mop_maps') == 'Entry' %} + {{ states('input_text.mop_map_entry') }} + {% elif states('input_select.mop_maps') == 'Bathroom' %} + {{ states('input_text.mop_map_bathroom') }} + {% elif states('input_select.mop_maps') == 'Master Ensuite' %} + {{ states('input_text.mop_map_ensuite') }} + {% endif %} + +################################### +# Camera +################################### + +camera: + - platform: generic + still_image_url: !secret mop_map_kitchen + content_type: image/png + name: Braava Kitchen + - platform: generic + still_image_url: !secret mop_map_entry + content_type: image/png + name: Braava Entry + - platform: generic + still_image_url: !secret mop_map_bathroom + content_type: image/png + name: Braava Bathroom + - platform: generic + still_image_url: !secret mop_map_ensuite + content_type: image/png + name: Braava Ensuite \ No newline at end of file diff --git a/secrets.yaml b/secrets.yaml index 93f95c1..45f6c50 100644 --- a/secrets.yaml +++ b/secrets.yaml @@ -20,4 +20,21 @@ vacuum_wardrobe: '{"region_id": "9", "type": "rid"}' vacuum_master_ensuite: '{"region_id": "10", "type": "rid"}' vacuum_master_bedroom: '{"region_id": "11", "type": "rid"}' vacuum_table: '{"region_id": "17", "type": "rid"}' -vacuum_clean_zone_fridge: '{"region_id": "0", "type": "zid"}' \ No newline at end of file +vacuum_clean_zone_fridge: '{"region_id": "0", "type": "zid"}' + +# Mop +mop_state: http://:/api/local/info/state +mop_action: http://:/api/local/action/ +mop_verify_ssl: false +mop_notify: notify.mobile_app_xxxxxxx # You can also use a notify group here +mop_map: http://:/mop_kitchen.php # My example is multi floor-plan, could just be mop.php +mop_log: /config/vacuum/mop.log +mop_dir: /config/vacuum +mop_kitchen: '{"region_id": "0", "type": "rid"}' +mop_entry: '{"region_id": "0", "type": "rid"}' +mop_bathroom: '{"region_id": "0", "type": "rid"}' +mop_master_ensuite: '{"region_id": "0", "type": "rid"}' +mop_map_kitchen: http://:/mop_kitchen.php +mop_map_entry: http://:/mop_entry.php +mop_map_bathroom: http://:/mop_bathroom.php +mop_map_ensuite: http://:/mop_ensuite.php \ No newline at end of file diff --git a/vacuum/braava.png b/vacuum/braava.png new file mode 100755 index 0000000..ac86ce1 Binary files /dev/null and b/vacuum/braava.png differ diff --git a/vacuum/braava_charging.png b/vacuum/braava_charging.png new file mode 100755 index 0000000..4878b25 Binary files /dev/null and b/vacuum/braava_charging.png differ diff --git a/vacuum/braava_stuck.png b/vacuum/braava_stuck.png new file mode 100755 index 0000000..f80e70c Binary files /dev/null and b/vacuum/braava_stuck.png differ