iRobot Roomba i7+ Configuration using Rest980

First of all, I wanted to thank you for all the hard work you have done towards integrating iRobot. It’s the only IoT in my house that didn’t work from the start and thanks to your work I was able to bring it to usability.

I also have a question related to additional parameters. In the original app, there is an option for Roomba to set the number of runs in a room. Is it also possible to do this from within the HA?

The same question applies Braava and parameters that control mopping behavior and clean mode (how much liquid to use).

I just did that today with the iRobot integration (1x / 2x)

Hi Everyone, sorry for the lack of response recently.

Use the vacuum_clean rest command to achieve this, this gets sent as a HTTP POST instead of a HTTP Get command

action: rest_command.vacuum_clean
data:
  payload: '{"ordered":1,"pmap_id":"***","regions":[{"region_id":"8","type":"rid"}]}'

you dont need the userpmap id anymore.

see above

if your using the addon, then the ip address will be of HA and the port you have specified in the addon-config.

glad its working for you!

you dont need both, it depends which one you are actively using, if its docker then just delete the addon and vice versa. the entries used in secrets.yaml will determine which one you are using.

this isnt possible through this integration currently. there is some talk of being able to pull it from the cloud.

its good to see that most of this functionaility is now available natively !

ideally just need a simple way of retrieving the zone identifiers and a more detailed explanation of the above values you mentioned and (in theory) this addon can be deprecated.

yes, these can be passed as additional variables from the native (as noted above) as well as from the rest980 addon

1 Like

After installing HA 2025.3.1 the Spook integration has a problem with these sensors :

PS
I downloaded the package a couple of years ago, so maybe things are changed meanwhile… ?

###################################
# iRobot Vacuum Package
###################################

###################################
# Sensor
###################################

sensor:
  # Roomba via Rest980 Docker Image
  - platform: rest
    name: rest980
    json_attributes:
      - batPct
      - bin
      - cleanMissionStatus
      - dock
      - pose
      - signal
      - bbmssn
      - bbrun
      - name
      - pmaps
      - vacHigh
      - openOnly
      - twoPass
      - noAutoPasses
      - softwareVer
    resource: !secret vacuum_state
    value_template: 'OK'
    scan_interval: 10
  - platform: template
    sensors:
      vacuum:
        friendly_name_template: >-
          {{ state_attr('sensor.rest980', 'name') }}
        value_template: >-
          {% if state_attr('sensor.rest980', 'cleanMissionStatus')['cycle'] == 'none' and state_attr('sensor.rest980', 'cleanMissionStatus')['notReady'] == 39 %}
            Pending
          {% elif state_attr('sensor.rest980', 'cleanMissionStatus')['notReady'] > 0 %}
            Not Ready
          {% else %}
          {% set mapper =  {
            'clean' : 'Clean',
            'quick' : 'Clean',
            'spot' : 'Spot',
            'evac' : 'Empty',
            'dock' : 'Dock',
            'train' : 'Train',
            'none' : 'Ready' } %}
          {% set state =  state_attr('sensor.rest980', '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',
              39 : 'Pending',
              48 : 'Path Blocked' } %}
            {% set state =  state_attr('sensor.rest980', 'cleanMissionStatus')['notReady'] %}
            {{ mapper[state] if state in mapper else state }}
          error_msg: >-
            {% set mapper =  {
              0 : 'n-a',
              15 : 'Reboot Required',
              18 : 'Docking Issue'} %}
            {% set state =  state_attr('sensor.rest980', 'cleanMissionStatus')['error'] %}
            {{ mapper[state] if state in mapper else state }}
          battery: >-
            {{ state_attr('sensor.rest980', 'batPct') }} %
          software_ver: >-
            {% if state_attr('sensor.rest980', 'softwareVer') is defined %}
              {% set version = state_attr('sensor.rest980', 'softwareVer') %}
              {% if '+' in version %}
                {{ version.split('+')[1] }}
              {% else %}
                {{ version }}
              {% endif %}
            {% else %}
              n-a
            {% endif %}
          phase: >-
            {% if state_attr('sensor.rest980', 'cleanMissionStatus')['phase'] == 'charge' and state_attr('sensor.rest980', 'batPct') == 100 %}
              Idle
            {% elif state_attr('sensor.rest980', 'cleanMissionStatus')['cycle'] == 'none' and state_attr('sensor.rest980', 'cleanMissionStatus')['phase'] == 'stop' %}
              Stopped
            {% else %}
            {% set mapper =  {
              'charge' : 'Charge',
              'run' : 'Run',
              'evac' : 'Empty',
              'stop' : 'Paused',
              'stuck' : 'Stuck',
              'hmUsrDock' : 'Sent Home',
              'hmMidMsn' : 'Mid Dock',
              'hmPostMsn' : 'Final Dock' } %}
            {% set state =  state_attr('sensor.rest980', 'cleanMissionStatus')['phase'] %}
            {{ mapper[state] if state in mapper else state }}
            {% endif %}
          bin: >-
            {% set mapper =  {
              true : 'Full',
              false : 'Not Full' } %}
            {% set state =  state_attr('sensor.rest980', 'bin')['full'] %}
            {{ mapper[state] if state in mapper else state }}
          bin_present: >-
            {% set mapper =  {
              true : 'Yes',
              false : 'No' } %}
            {% set state =  state_attr('sensor.rest980', 'bin')['present'] %}
            {{ mapper[state] if state in mapper else state }}
          clean_base: >-
            {% if state_attr('sensor.rest980', 'dock')['state'] is defined %}
              {% set mapper =  {
                300 : 'Ready',
                301 : 'Ready',
                302 : 'Empty',
                303 : 'Empty',
                350 : 'Bag Missing',
                351 : 'Clogged',
                352 : 'Sealing Problem',
                353 : 'Bag Full',
                360 : 'Comms Problem' } %}
              {% set state =  state_attr('sensor.rest980', 'dock')['state'] %}
              {{ mapper[state] if state in mapper else state }}
            {% else %}
              n-a
            {% endif %}
          location: >-
            {% if state_attr('sensor.rest980', 'pose')['theta'] is defined %}
              ({{ state_attr('sensor.rest980', 'pose')['point']['x'] }}, {{ state_attr('sensor.rest980', 'pose')['point']['y'] }}, {{ state_attr('sensor.rest980', 'pose')['theta'] }})
            {% else %}
              n-a
            {% endif %}
          rssi: >-
            {% if state_attr('sensor.rest980', 'signal')['rssi'] is defined %}
              {{ state_attr('sensor.rest980', 'signal')['rssi'] }}
            {% else %}
              n-a
            {% endif %}
          total_area: >-
            {% if state_attr('sensor.rest980', 'bbrun')['sqft'] is defined %}
              {{ (state_attr('sensor.rest980', 'bbrun')['sqft'] / 10.764 * 100)| round() }}m²
            {% else %}
              n-a
            {% endif %}
          #   {{ (state_attr('sensor.rest980', 'bbrun')['sqft'] }}ft²
          total_time: >-
            {% if state_attr('sensor.rest980', 'bbrun')['hr'] is defined %}
              {{ state_attr('sensor.rest980', 'bbrun')['hr'] }}h {{ state_attr('sensor.rest980', 'bbrun')['min'] }}m
            {% else %}
              n-a
            {% endif %}
          total_jobs: >-
            {% if state_attr('sensor.rest980', 'bbmssn')['nMssn'] is defined %}
              {{ state_attr('sensor.rest980', 'bbmssn')['nMssn'] }}
            {% else %}
              n-a
            {% endif %}
          dirt_events: >-
            {% if state_attr('sensor.rest980', 'bbrun')['nScrubs'] is defined %}
              {{ state_attr('sensor.rest980', 'bbrun')['nScrubs'] }}
            {% else %}
              n-a
            {% endif %}
          # evac_events I7+/S9+ Models (Clean Base)
          evac_events: >-
            {% if state_attr('sensor.rest980', 'bbrun')['nEvacs'] is defined %}
              {{ state_attr('sensor.rest980', 'bbrun')['nEvacs'] }}
            {% else %}
              n-a
            {% endif %}
          job_initiator: >-
            {% set mapper =  {
              'schedule' : 'Scheduler',
              'rmtApp' : 'App',
              'manual' : 'Robot',
              'localApp' : 'HA' } %}
            {% set state =  state_attr('sensor.rest980', 'cleanMissionStatus')['initiator'] %}
            {{ mapper[state] if state in mapper else state }}
          job_time: >-
            {% if state_attr('sensor.rest980', 'cleanMissionStatus')['mssnStrtTm'] is defined %}
              {% if state_attr('sensor.rest980', 'cleanMissionStatus')['mssnStrtTm'] != 0 %}
                {% set time = state_attr('sensor.rest980', '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', 'cleanMissionStatus')['rechrgTm'] is defined %}
              {% if state_attr('sensor.rest980', 'cleanMissionStatus')['rechrgTm'] != 0 %}
                {% set time = state_attr('sensor.rest980', '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', 'cleanMissionStatus')['expireTm'] is defined %}
              {% if state_attr('sensor.rest980', 'cleanMissionStatus')['expireTm'] != 0 %}
                {% set time = state_attr('sensor.rest980', '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_mode: >-
            {% if state_attr('sensor.rest980', 'noAutoPasses') is defined and state_attr('sensor.rest980', 'twoPass') is defined %}
              {% if state_attr('sensor.rest980', 'noAutoPasses') == true and state_attr('sensor.rest980', 'twoPass') == false %}
                One
              {% elif state_attr('sensor.rest980', 'noAutoPasses') == true and state_attr('sensor.rest980', 'twoPass') == true %}
                Two
              {% else %}
                Auto
              {% endif %}
            {% else %}
              n-a
            {% endif %}
          carpet_boost: >-
            {% if state_attr('sensor.rest980', 'vacHigh') is defined %}
              {% if state_attr('sensor.rest980', 'vacHigh') == false and state_attr('sensor.rest980', 'carpetBoost') == false %}
                Eco
              {% elif state_attr('sensor.rest980', 'vacHigh') == true and state_attr('sensor.rest980', 'carpetBoost') == false %}
                Performance
              {% else %}
                Auto
              {% endif %}
            {% else %}
              n-a
            {% endif %}
          clean_edges: >-
            {% if state_attr('sensor.rest980', 'openOnly') is defined %}
              {% if state_attr('sensor.rest980', 'openOnly') == true %}
                False
              {% else %}
                True
              {% endif %}
            {% else %}
              n-a
            {% endif %}
          maint_due: >-
            {% if is_state('input_boolean.vacuum_maint_due', 'on') %}
              True
            {% else %}
              False
            {% endif %}
          # pmap0_id I7/S9 Models
          pmap0_id: >-
            {% if state_attr('sensor.rest980', 'pmaps')[0] is defined %}
              {{ state_attr('sensor.rest980', 'pmaps')[0] | regex_findall_index("{'([\w\-]+)': '\w+'}") }}
            {% else %}
              n-a
            {% endif %}
      vacuum_location:
        friendly_name_template: >-
          {{ state_attr('sensor.rest980', 'name') }} Location
        value_template: >-
          {{ state_attr('sensor.vacuum', 'location') }}
        icon_template: mdi:home-map-marker

###################################
# Rest Command
###################################

rest_command:
  vacuum_action:
    url: >-
      {{ states('input_text.vacuum_action') }}{{ command }}
    verify_ssl: !secret vacuum_verify_ssl
    method: 'get'
    timeout: 20
  vacuum_clean:
    url: >-
      {{ states('input_text.vacuum_action') }}cleanRoom
    verify_ssl: !secret vacuum_verify_ssl
    method: POST
    content_type: 'application/json'
    payload: '{{ payload }}'

###################################
# Input Boolean
###################################

input_boolean:
  vacuum_clean_kitchen:
    name: Kitchen
    icon: mdi:silverware-fork-knife
  vacuum_clean_zone_fridge:
    name: Fridge Zone
    icon: mdi:fridge
  vacuum_clean_entry:
    name: Entry
    icon: mdi:coat-rack
  vacuum_clean_hall:
    name: Hall
    icon: mdi:ceiling-light
  vacuum_clean_living_room:
    name: Living Room
    icon: mdi:sofa
  vacuum_clean_bathroom:
    name: Bathroom
    icon: mdi:shower
  vacuum_clean_bedroom:
    name: Bedroom
    icon: mdi:bed-empty
  vacuum_clean_wardrobe:
    name: Wardrobe
    icon: mdi:wardrobe-outline
  vacuum_clean_master_ensuite:
    name: Master Ensuite
    icon: mdi:shower
  vacuum_clean_master_bedroom:
    name: Master Bedroom
    icon: mdi:bed-empty
  vacuum_clean_master_passage:
    name: Master Passage
    icon: mdi:ceiling-light
  vacuum_clean_table:
    name: Table
    icon: mdi:room-service
  vacuum_maint_due:
    name: Vacuum Maintenance
    icon: mdi:wrench
  vacuum_schedule_1:
    name: Vacuum Schedule 1
    icon: mdi:timetable
  vacuum_schedule_2:
    name: Vacuum Schedule 2
    icon: mdi:timetable

###################################
# Input Text
###################################

input_text:
  vacuum_action:
    name: Vacuum Action URL
    initial: !secret vacuum_action
  vacuum_map:
    name: Vacuum Map URL
    initial: !secret vacuum_map
  vacuum_log:
    name: Vacuum Log Path
    initial: !secret vacuum_log
  vacuum_dir:
    name: Vacuum Dir Path
    initial: !secret vacuum_dir
  vacuum_rooms:
    name: Vacuum Rooms
    max: 255

  vacuum_clean_kitchen:
    name: Kitchen
    initial: !secret vacuum_kitchen
  vacuum_clean_zone_fridge:
    name: Fridge Zone
    initial: !secret vacuum_clean_zone_fridge
  vacuum_clean_entry:
    name: Entry
    initial: !secret vacuum_entry
  vacuum_clean_hall:
    name: Hall
    initial: !secret vacuum_hall
  vacuum_clean_living_room:
    name: Living Room
    initial: !secret vacuum_living_room
  vacuum_clean_bathroom:
    name: Bathroom
    initial: !secret vacuum_bathroom
  vacuum_clean_bedroom:
    name: Bedroom
    initial: !secret vacuum_bedroom
  vacuum_clean_wardrobe:
    name: Wardrobe
    initial: !secret vacuum_wardrobe
  vacuum_clean_master_ensuite:
    name: Master Ensuite
    initial: !secret vacuum_master_ensuite
  vacuum_clean_master_bedroom:
    name: Master Bedroom
    initial: !secret vacuum_master_bedroom
#  vacuum_clean_master_passage:
#      name: Master Passage
#      initial: !secret vacuum_master_passage
  vacuum_clean_table:
    name: Table
    initial: !secret vacuum_table

###################################
# Group
###################################

group:
  vacuum_rooms:
    entities:
      - input_boolean.vacuum_clean_kitchen
      - input_boolean.vacuum_clean_zone_fridge
      - input_boolean.vacuum_clean_entry
      - input_boolean.vacuum_clean_hall
      - input_boolean.vacuum_clean_living_room
      - input_boolean.vacuum_clean_bathroom
      - input_boolean.vacuum_clean_bedroom
      - input_boolean.vacuum_clean_wardrobe
      - input_boolean.vacuum_clean_master_ensuite
      - input_boolean.vacuum_clean_master_bedroom
      - input_boolean.vacuum_clean_master_passage
      - input_boolean.vacuum_clean_table

###################################
# Automation
###################################

automation:
  # Initiate Selective Room Clean
  - alias: Vacuum Clean Rooms
    trigger:
    - platform: event
      event_type: initiate_vacuum_clean
    condition:
      condition: not
      conditions:
        - condition: state
          entity_id: input_text.vacuum_rooms
          state: ''
    action:
      - service: rest_command.vacuum_clean
        data_template:
          payload: >
            {% set rooms = states('input_text.vacuum_rooms') %}
            {% if rooms[-1:] == ',' %}
            {% set rooms = rooms[:-1] %}
            {% endif %}
            {% set rooms = rooms.split(",") %}
            {
              "ordered": 1,
              "pmap_id": "{{ state_attr('sensor.vacuum', 'pmap0_id') | string }}",
              "regions": [{% for id in rooms %}
                {% set room = 'input_text.vacuum_clean_' + id %} {{ states(room) | string }} {%- if not loop.last %},{%- endif %}
                {%- endfor %}
              ]
            }
      - service: input_text.set_value
        data:
          entity_id: input_text.vacuum_rooms
          value: ''
      - service: input_boolean.turn_off
        data:
          entity_id: group.vacuum_rooms

  # Update Vacuum REST Sensor for Location Details
  - alias: Vacuum Update Location
    initial_state: true
    trigger:
      - platform: time_pattern
        seconds: /2
      - platform: event
        event_type: call_service
        event_data:
          domain: rest_command
          service: vacuum_clean
    condition:
      condition: or
      conditions:
        - condition: template
          value_template: "{{ is_state_attr('sensor.vacuum', 'phase', 'Run') }}"
        - condition: template
          value_template: "{{ is_state_attr('sensor.vacuum', 'phase', 'Sent Home') }}"
        - condition: template
          value_template: "{{ is_state_attr('sensor.vacuum', 'phase', 'Mid Dock') }}"
        - condition: template
          value_template: "{{ is_state_attr('sensor.vacuum', 'phase', 'Final Dock') }}"
    action:
      - service: homeassistant.update_entity
        entity_id: sensor.rest980

  # Log Vacuum Location to File
  - alias: Vacuum Log Position
    initial_state: true
    trigger:
        platform: state
        entity_id: sensor.vacuum_location
    condition:
      condition: or
      conditions:
        - condition: state
          entity_id: sensor.vacuum
          state: 'Clean'
        - condition: state
          entity_id: sensor.vacuum
          state: 'Train'
    action:
#      - service: notify.vacuumfile
#        data_template:
#          message: "{{ states('sensor.vacuum_location') }}"
      - service: notify.send_message
        target: 
          entity_id: notify.vacuumfile
        data:
          message: "{{ states('sensor.vacuum_location') }}"

  # Initialize Blank Vacuum Log File
  - alias: Vacuum Clean Log
    initial_state: true
    trigger:
      - platform: state
        entity_id: sensor.vacuum
        from: 'Ready'
        to: 'Clean'
      - platform: state
        entity_id: sensor.vacuum
        from: 'Ready'
        to: 'Train'
    action:
      - service: shell_command.vacuum_clear_log
      - service: shell_command.vacuum_clear_image

  # Update Vacuum Log File with Finished Status
  #- alias: Vacuum Notify on Finished Cleaning
  #  initial_state: true
  #  trigger:
  #    - platform: state
  #      entity_id: sensor.vacuum
  #      from: 'Clean'
  #      to: 'Ready'
  #    - platform: state
  #      entity_id: sensor.vacuum
  #      from: 'Train'
  #      to: 'Ready'
  #    - platform: state
  #      entity_id: sensor.vacuum
  #      from: 'Pending'
  #      to: 'Ready'
  #  action:
  #    - service: notify.vacuumfile
  #      data_template:
  #        message: "Finished"
  #    - service: notify.send_message
  #      target: 
  #        entity_id: notify.vacuumfile
  #      data:
  #        message: "Finished"

  # Update Vacuum Log File with Stuck Status
  - alias: Vacuum Notify on Stuck Status
    initial_state: true
    trigger:
      platform: template
      value_template: "{{ is_state_attr('sensor.vacuum', 'phase', 'Stuck') }}"
    action:
      #- service: notify.vacuumfile
      #  data_template:
      #    message: "Stuck"
      - service: notify.send_message
        target: 
          entity_id: notify.vacuumfile
        data:
          message: "Stuck"
      # DELETE BELOW SECTION IF YOU DONT WANT NOTIFICATIONS
      - delay: 5
      - service: !secret vacuum_notify
        data_template:
          title: "{{ state_attr('sensor.rest980', 'name') }} requires your attention"
          message: "{{ state_attr('sensor.rest980', 'name') }} is stuck."
          # =============
          # NOTICE THIS SECTION IS VALID FOR IOS USERS ONLY!
          data:
            attachment:
              content-type: jpeg
            push:
              category: camera
            entity_id: camera.roomba
          # =============

  # Generate Complete Vacuum Map
  - alias: Vacuum Generate Image after Cleaning
    initial_state: true
    trigger:
      - platform: state
        entity_id: sensor.vacuum
        from: 'Clean'
        to: 'Ready'
        for:
          seconds: 10
      - platform: state
        entity_id: sensor.vacuum
        from: 'Train'
        to: 'Ready'
        for:
          seconds: 10
      - platform: state
        entity_id: sensor.vacuum
        from: 'Pending'
        to: 'Ready'
        for:
          seconds: 10
    action:
      - service: shell_command.vacuum_generate_image
      # DELETE BELOW SECTION IF YOU DONT WANT NOTIFICATIONS
      - delay: 5
      - service: !secret vacuum_notify
        data_template:
          title: "{{ state_attr('sensor.rest980', 'name') }}"
          message: "{{ state_attr('sensor.rest980', '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.roomba
          # =============

  # Maintenance Check
  - alias: Vacuum Maintenance Check
    initial_state: true
    trigger:
      - platform: time_pattern
        minutes: /15
      - platform: state
        entity_id: [
          'sensor.vacuum_maint_clean_brushes',
          'sensor.vacuum_maint_clean_contacts',
          'sensor.vacuum_maint_clean_filter',
          'sensor.vacuum_maint_clean_wheel',
          'sensor.vacuum_maint_clean_bin',
          'sensor.vacuum_maint_replace_brushes',
          'sensor.vacuum_maint_replace_filter',
          'sensor.vacuum_maint_replace_wheel'
        ]
    action:
      - service: >-
          {% set ns = namespace(count = 0) %}
          {% for item in states.sensor if 'sensor.vacuum_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.vacuum_maint_due
  # Add Rooms for Ordered Cleaning
  - alias: Vacuum Add Rooms for Cleaning
    initial_state: true
    trigger:
      platform: state
      entity_id: [
        'input_boolean.vacuum_clean_kitchen',
        'input_boolean.vacuum_clean_zone_fridge',
        'input_boolean.vacuum_clean_entry',
        'input_boolean.vacuum_clean_hall',
        'input_boolean.vacuum_clean_living_room',
        'input_boolean.vacuum_clean_bathroom',
        'input_boolean.vacuum_clean_bedroom',
        'input_boolean.vacuum_clean_wardrobe',
        'input_boolean.vacuum_clean_master_ensuite',
        'input_boolean.vacuum_clean_master_bedroom',
        'input_boolean.vacuum_clean_master_passage',
        'input_boolean.vacuum_clean_table'
      ]
      to: 'on'
    action:
      service: input_text.set_value
      data_template:
        entity_id: input_text.vacuum_rooms
        value: |
          {% set room = trigger.entity_id %}
          {% set room = room.replace("input_boolean.vacuum_clean_","") %}
          {% if ((states('input_text.vacuum_rooms') == "unknown") or (states('input_text.vacuum_rooms') == "")) %}
            {{ room }},
          {% else %}
            {{ states('input_text.vacuum_rooms') }}{{ room }},
          {% endif %}
  # Remove Rooms for Ordered Cleaning
  - alias: Vacuum Remove Rooms for Cleaning
    initial_state: true
    trigger:
      platform: state
      entity_id: [
        'input_boolean.vacuum_clean_kitchen',
        'input_boolean.vacuum_clean_zone_fridge',
        'input_boolean.vacuum_clean_entry',
        'input_boolean.vacuum_clean_hall',
        'input_boolean.vacuum_clean_living_room',
        'input_boolean.vacuum_clean_bathroom',
        'input_boolean.vacuum_clean_bedroom',
        'input_boolean.vacuum_clean_wardrobe',
        'input_boolean.vacuum_clean_master_ensuite',
        'input_boolean.vacuum_clean_master_bedroom',
        'input_boolean.vacuum_clean_master_passage',
        'input_boolean.vacuum_clean_table'
      ]
      to: 'off'
    action:
      service: input_text.set_value
      data_template:
        entity_id: input_text.vacuum_rooms
        value: |
          {% set room = trigger.entity_id %}
          {% set room = room.replace("input_boolean.vacuum_clean_","") %}
          {% set result = states('input_text.vacuum_rooms') %}
          {% set result = result.replace(room + ",","") %}
          {{ result }}
  # Vacuum Cleaning Schedule 1
  - alias: Vacuum Cleaning Schedule 1
    initial_state: true
    trigger:
      platform: time_pattern
      hours: "6"
    condition:
      condition: and
      conditions:
        - condition: state
          entity_id: input_boolean.vacuum_schedule_1
          state: 'on'
        - condition: state
          entity_id: input_boolean.toggle_op_vakantie
          state: 'off'
    action:
      - service: input_text.set_value
        entity_id: input_text.vacuum_rooms
        data_template:
          entity_id: input_text.vacuum_rooms
          value: "kitchen"
      - service: automation.trigger
        entity_id: automation.vacuum_clean_rooms

  # Vacuum Cleaning Schedule 2
  - alias: Vacuum Cleaning Schedule 2
    initial_state: true
    trigger:
      platform: time_pattern
      hours: "10"
    condition:
      condition: and
      conditions:
        - condition: state
          entity_id: input_boolean.vacuum_schedule_2
          state: 'on'
        - condition: state
          entity_id: input_boolean.toggle_op_vakantie
          state: 'off'
        - condition: time
          weekday:
            - mon
            - wed
            - fri
    action:
      - service: input_text.set_value
        entity_id: input_text.vacuum_rooms
        data_template:
          entity_id: input_text.vacuum_rooms
          value: "kitchen,entry,hall,living_room,bathroom,bedroom,wardrobe,master_ensuite,master_passage"
      - service: automation.trigger
        entity_id: automation.vacuum_clean_rooms

  # Delete Old Vacuum Image Files
  - alias: Vacuum Delete Images
    initial_state: true
    trigger:
      platform: time_pattern
      hours: "00"
    action:
      - service: shell_command.vacuum_delete_images

###################################
# Notify
###################################

notify:
#    - name: VacuumFile
#      platform: file
#      filename: !secret vacuum_log

###################################
# Camera
###################################

#camera:
#  - platform: generic
#    still_image_url: >-
#      {{ states('input_text.vacuum_map') }}
#    content_type: image/png
#    name: Roomba

###################################
# Shell Command
###################################

shell_command:
  vacuum_clear_log: cp /dev/null {{ states('input_text.vacuum_log') }}
  vacuum_clear_image: curl -X GET -s -O /dev/null '{{ states("input_text.vacuum_map") }}?clear=true'
  vacuum_generate_image: curl -X GET -s -O /dev/null '{{ states("input_text.vacuum_map") }}?last=true'
  vacuum_delete_images: find {{ states('input_text.vacuum_dir') }} -regex ".*[0-9]\.png" -type f -mtime +20 -exec rm -f {} \;

Those entities are created from the lovelace file, as check button cards as a simple method of tracking maintenance tasks for the robot.

file ref

If you are not using those, you can remove this automation.

OK, that means it can be done. And how is it done? I’m not too familiar with how such things are achieved, so if I could ask for a quick tutorial or some link where something similar is done, I’d be very grateful.

If you start a job from the iRobot App, then when reviewing the API the last command shows the following.

"lastCommand":{"command":"start","time":1741656240,"initiator":"rmtApp","ordered":1,"pmap_id":"xxxx","regions":[{"params":{"noAutoPasses":true,"twoPass":true},"region_id":"15","type":"rid"}],"user_pmapv_id":"xxxx"}

you will notice, that within the region identifier, it includes a new object for “params”

currently I have the regions statically defined in the secrets file, so allowing dynamically changing of this may take some rework.

if you had a specific room you wanted to update, say rid 15, then you could update secrets like this

vacuum_kitchen: '{"region_id": "15", "type": "rid", "params": { "noAutoPasses": true, "twoPass": true }}'

if you wanted to choose, then you could define separate entries for with and without two pass, and select the one you wish to clean.

1 Like

This is what I have done, but for the last few months, I haven’t been able to run any jobs from HA. When I run it from the app, I get:

"lastCommand":{"command":"start","initiator":"rmtApp","ordered":1,"pmap_id":"r1cvzlZcSB6LsvMQBTULaA","regions":[{"params":{"noAutoPasses":true,"twoPass":true},"region_id":"6","type":"rid"}],"time":1747392264,"user_pmapv_id":"250418T115624"}

But if I run it from HA, I get:

"lastCommand":{"command":"start","initiator":"localApp","ordered":1,"pmap_id":"iOu3hKOcROmC4HAOQ-8UhQ","regions":[{"params":{"noAutoPasses":true,"twoPass":true},"region_id":"6","type":"rid"}],"time":1747392858,"user_pmapv_id":null}

The vacuum then doesn’t clean the room, it just says discovering and cleaning and then gives up after a bit of driving round. It appears to be the ‘user_pmapv_id’ being null that’s different but I don’t know how this is populated.

Hi.
How to please properly remove the addon from Home Assistant? I have got a new Roborock vacuum and sold Roomba so I wanted to remove the addon from Home Assistant, but I keep getting warnings that a storage is missing for the 39c40414_rest980 addon.

Hello, I have this: The automation ‘Vacuum Notify on Finished Cleaning’ (automation.vacuum_notify_on_finished_cleaning) uses an unknown action: notify.vacuumfile."

I’m working on a custom integration based on this post to hopefully fix the issues I see people having here and to make it easier to use in automation.
I would like if anyone can test it, it’s available in HACS from the repo.

3 Likes

This looks really good! I switched to using only this now.
Think this needs its own topic :slight_smile:

Working really well for me, I gave up on the original version a while ago as there were too many errors randomly appearing

Glad you picked this up and giving it a rewrite

I made a new topic for it, thank you for the positive feedback!

1 Like

I removed that attribute from being sent from HA as it wasnt a required attribute? If you check the automation, is it listed in there still?

If you have removed it from the addon listing, I wouldnt expect anything else would be needed?

You can remove reference to this as the log was used for realtime mapping which has been removed from the firmware (likely be a good time to move to the integration mentioned further down this post)

:beers:

sorry to hear you were having issues - I hope the integration works for you !

As posted in your new post - I appreciate you taking the time to make this. And I will probably switch over to it in due time also :smile:

1 Like

If you mean in the vacuum.yaml in the GitHub repository, yes. There are 7 instances of it in the code.

Hey! Getting an error for “Rest980 did not output valid Roomba data.” When i go to the page its calling it is blank. Am i doing something wrong here?

Hey there! This is from my integration; wrong thread, but I can still attempt to* assist you.

This happens when the rest980 instance outputs nothing, or no robot object. If your credentials are correct, this may be an issue where when rest980 connects to the MQTT server using TLS, it fails when it exchanges ciphers w/ Roomba. Unfortunately, if this is the case I have mostly no control over this.

Hi everyone,

I have been slowly working through setting up my roombas in HA. I got my first one setup. But, I have two more to do. I know there is a Rest980_2 Docker Image to add another Roomba. But, what do I do about the 3rd unit? Do I just use one of the J7 Images? A little confused on the way to set this up.

Thanks for the great work on this!