Hi @ia74 . I have tried following the steps and have been able to partly add the integration. What I mean is that, the integration is added but with a Failed setup where is says “Error Communicating with API”. I have got the same result for both Roomba S9+ and Roomba Combo 10 Max (I have 2 Roombas and 1 Braava M6). Would appreciate any guidance on what I can do from here as I am stuck. Thanks
Roomba i7+ (Rest980) with Selective Room Cleaning - Integrate your iRobot Roomba with Home Assistant
Hello everyone, I integrated my Roomba i7+ using rest980 and I can see all the entities. I had two entities called “unnamed room” that I disabled because they didn’t belong to any of the rooms I have.
I tried selecting One Pass on a couple of rooms and starting the vacuum, but after exactly 6 minutes—when in the app it shows “discovering”—the robot returns to the base and I get a localization error. Am I doing something wrong?
UPDATE:
Maybe I found the problem, but I don’t know how to solve it:
I have two maps because I have two houses.
In the attributes of vacuum.robotname, pmap0_id is equal to the map ID of the other house, and I have no way to force it to use the correct one.
Any ideas?
Hey there! Sorry, this is unfortunately a known issue. I’m working on a fix that won’t have too much collateral damage, as it has to do with unique ID.
Hey! You know, this isn’t a scenario I had in mind, but this is definitely an easier fix from my end [than the multiple roomba bug]. Sorryabout that! It happens because starting a job defaults on Roomba’s default pmap0 ID for every room.
Hi, I’m happy that it can be easily resolved.
Looking forward to the update ![]()
Hey all, sorry for the wait!
v1.18.0 is out now and fixes the issue with multiple maps selecting the wrong room for the wrong map ID. It’s a simple fix; for now, you can only execute cleaning rooms in one map per run (as of right now). I’ll look into multiple selections across maps, but I’m not sure how that’ll look.
As for the multiple Roomba situation, the reason why that’s taking so long has to do with the way roomba_rest980 handles unique IDs. The way it works now is: HA selects a unique ID, which should be per Roomba but is instead per server, by MD5 hashing your local server URL. This will be fixed with a config flow version upgrade (automatic migration, this time it will be based on BLID.)
Hello.
Sorry for dumb questions, but I just want to make sure I got it right.
You mentioned you’re updating dorita/rest980.
Is it this one
GitHub - ia74/roomba_rest980: Integrate Home Assistant with your iRobot Roomba using rest980. ?
I am using the old one, so I need to double check.
The Docker image is koalazak/rest980:latest, right?
Would all of this work with cloud features disabled?
I’m asking because in GitHub - ia74/roomba_rest980: Integrate Home Assistant with your iRobot Roomba using rest980. you say
“You must check “Enable cloud features?” for the cloud API to be used.”
And after irobot filing for bancrupcy and being overtaken by some Chinese company, I don’t feel ok with using cloud anymore.
Would I miss selective room clearing and other features? I do know room ids and I’m fine using those.
Thank you very much for all the work.
No dumb questions here!
- Yes, correct, the docker image is by koalazak.
- It does work without cloud features; they’re all supplemental. You wouldn’t miss selective room cleaning, as you can re-implement it with the start API/event (there will be a guide to do so in the future, but you can also read the start implementation).
- Without Cloud, pressing Start on HA’s native entity just starts a clean everywhere/default routine.
Thanks for giving my integration a try!
Thank you.
How substantial was the recent change to rest980 image? I noticed it grew by some 100-200 MB.
How can I read start implementation? Sorry, I’m rather new to this.
Would something like this work?
curl -X POST http://192.168.60.139:3011/api/local
-H “Content-Type: application/json”
-d ‘{“command”:“start”}’
I tried to follow this:
But it failed utterly. I suppose because he used cloud and he also implemented 2pass vacuuming on his own before you did.
Yes, except you should POST this to /api/local/action/cleanRoom
{
"ordered": 1,
"pmap_id": "ID",
"regions": [
{
"region_id": 0,
"type": "rid",
"params": {"noAutoPasses": False, "twoPass": False}
},
]
}
Where for zones it should be “zid” and i think “zone_id” if i haven’t forgot
Hey all!
HA is adding room-based vacuum support natively in 2026.3.3!
Naturally, my integration will be updated as soon as possible (once HA updates too & docs are provided) to support this feature!
Alongside this, I’ll look into adding support for MatterHub too!
And maybe add some more local and easier to read guides.
However! I am currently busier than usual as of right now, so these features will take a little longer, as I aim for quality.
Thank you all for the support! Feel free to open issues, they’re helpful as I only own an i7.
Do you plan to provide some examples of use?
I use HA only for a while and this is for me quite a challenge to put together.
With the help of AI I hallucinated this roomba.yaml and lovelace card.
# Roomba custom cleaning package
# /config/automations/roomba.yaml
sensor:
- platform: rest
name: roomba_state_raw
resource: "http://192.168.60.139:3011/api/local/info/state"
method: GET
scan_interval: 60
json_attributes:
- pmaps
template:
- sensor:
- name: roomba_user_pmapv_id
state: >
{% set p = state_attr('sensor.roomba_state_raw','pmaps') %}
{% if p %}
{{ p[0]['VabtFPvZSveRlmdU2K1n4Q'] }}
{% else %}
unavailable
{% endif %}
rest_command:
roomba_clean_custom:
url: "http://192.168.60.139:3011/api/local/action/cleanRoom"
method: POST
content_type: "application/json"
payload: "{{ payload }}"
input_select:
vacuum_room_kitchen:
name: Kitchen
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:chef-hat
vacuum_room_diningroom:
name: Dining Room
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:table-chair
vacuum_room_livingroom:
name: Living Room
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:sofa-outline
vacuum_room_hallway:
name: Hallway
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:shoe-print
vacuum_room_bedroom:
name: Bedroom
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:bed-outline
vacuum_room_childsroom:
name: Child Room
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:teddy-bear
vacuum_room_bathroom:
name: Bathroom
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:shower-head
vacuum_room_eating_area:
name: Eating Area
options: ["off", "1x", "2x"]
initial: "off"
icon: mdi:cake-variant-outline
script:
roomba_cycle_room:
mode: parallel
fields:
entity:
description: "input_select room entityi"
sequence:
- variables:
current: "{{ states(entity) }}"
- choose:
- conditions: "{{ current == 'off' }}"
sequence:
- service: input_select.select_option
target: { entity_id: "{{ entity }}" }
data: { option: "1x" }
- conditions: "{{ current == '1x' }}"
sequence:
- service: input_select.select_option
target: { entity_id: "{{ entity }}" }
data: { option: "2x" }
- conditions: "{{ current == '2x' }}"
sequence:
- service: input_select.select_option
target: { entity_id: "{{ entity }}" }
data: { option: "off" }
roomba_start_selected_rooms:
mode: single
sequence:
- variables:
room_map:
vacuum_room_kitchen: { id: 7, type: "rid" }
vacuum_room_diningroom: { id: 1, type: "rid" }
vacuum_room_livingroom: { id: 10, type: "rid" }
vacuum_room_hallway: { id: 8, type: "rid" }
vacuum_room_bedroom: { id: 4, type: "rid" }
vacuum_room_childsroom: { id: 3, type: "rid" }
vacuum_room_bathroom: { id: 9, type: "rid" }
vacuum_room_eating_area: { id: 0, type: "zid" }
regions_list: >
{% set ns = namespace(regions=[]) %}
{% for entity, data in room_map.items() %}
{% set value = states('input_select.' ~ entity) %}
{% if value in ['1x', '2x'] %}
{% set two_pass = (value == '2x') %}
{% if data.type == 'zid' %}
{% set region = {
'zone_id': data.id | int,
'type': data.type,
'params': {'noAutoPasses': true, 'twoPass': two_pass}
} %}
{% else %}
{% set region = {
'region_id': data.id | int,
'type': data.type,
'params': {'noAutoPasses': true, 'twoPass': two_pass}
} %}
{% endif %}
{% set ns.regions = ns.regions + [region] %}
{% endif %}
{% endfor %}
{{ ns.regions }}
ordered_flag: >
{{ 1 if regions_list | length > 1 else 0 }}
clean_payload: >
{{ {
'ordered': ordered_flag,
'pmap_id': 'VabtFPvZSveRlmdU2K1n4Q',
'user_pmapv_id': states('sensor.roomba_user_pmapv_id'),
'regions': regions_list
} | to_json }}
- condition: template
value_template: "{{ regions_list | length > 0 }}"
# --- DEBUG ---
- service: persistent_notification.create
data:
title: "Roomba debug payload"
message: "{{ clean_payload }}"
# ------------------------------------------------------------
- service: rest_command.roomba_clean_custom
data:
payload: "{{ clean_payload }}"
- service: input_select.select_option
target:
entity_id: >
{{ room_map.keys() | map('regex_replace', '^', 'input_select.') | list }}
data:
option: "off"
Lovelace card:
type: vertical-stack
cards:
- type: custom:roomba-vacuum-card
entity: vacuum.robik
battery_entity: sensor.robik_battery
clean_base: false
- type: grid
columns: 2
square: false
cards:
- type: tile
entity: input_select.vacuum_room_kitchen
name: Kitchen
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_kitchen
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_kitchen', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_kitchen', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_diningroom
name: Dining Room
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_diningroom
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_diningroom', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_diningroom', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_eating_area
name: Eating Area
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_eating_area
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_eating_area', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_eating_area', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_livingroom
name: Livingroom
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_livingroom
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_livingroom', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_livingroom', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_hallway
name: Hallway
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_hallway
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_hallway', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_hallway', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_bedroom
name: Bedroom
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_bedroom
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_bedroom', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_bedroom', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_childsroom
name: Child Room
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_childsroom
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_childsroom', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_childsroom', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: tile
entity: input_select.vacuum_room_bathroom
name: Bathroom
tap_action:
action: call-service
service: script.roomba_cycle_room
data:
entity: input_select.vacuum_room_bathroom
card_mod:
style: |
:host {
{% if is_state('input_select.vacuum_room_bathroom', '1x') %}
--tile-color: var(--warning-color);
{% elif is_state('input_select.vacuum_room_bathroom', '2x') %}
--tile-color: var(--error-color);
{% else %}
--tile-color: var(--state-inactive-color);
{% endif %}
}
- type: button
name: Start Vacuuming
icon: mdi:play-circle
tap_action:
action: call-service
service: script.roomba_start_selected_rooms
visibility:
- condition: or
conditions:
- condition: state
entity: input_select.vacuum_room_kitchen
state_not: "off"
- condition: state
entity: input_select.vacuum_room_diningroom
state_not: "off"
- condition: state
entity: input_select.vacuum_room_eating_area
state_not: "off"
- condition: state
entity: input_select.vacuum_room_livingroom
state_not: "off"
- condition: state
entity: input_select.vacuum_room_hallway
state_not: "off"
- condition: state
entity: input_select.vacuum_room_bedroom
state_not: "off"
- condition: state
entity: input_select.vacuum_room_childsroom
state_not: "off"
- condition: state
entity: input_select.vacuum_room_bathroom
state_not: "off"
It looks nice, but doesn’t work at all and I fail to recognize valid code from halucination.
I just want to be able to select rooms as I like and let roomba vacuum. That’s it.
