Live maps, room cleaning, local control, enhanced sensors, and custom presets tailored specifically for the Xiaomi Robot Vacuum S20 (xiaomi.vacuum.d106gl).
[!NOTE] This repository is a specialized fork of letitbe-dull/xiaomi-vac focused on optimizing performance, expanded sensor data, and preset capabilities for the Xiaomi S20 (
d106gl).
While the upstream integration provides broad multi-model support, this fork is specifically enhanced for the Xiaomi Vacuum S20 (xiaomi.vacuum.d106gl). It expands consumable monitoring, exposes extended vacuum statistics, and introduces an improved dashboard card featuring interactive preset action buttons for automation scripts.
- Presets support: Launch standard or custom cleaning routines directly from the card using Home Assistant scripts.
- Expanded consumable tracking: Dedicated sensors for main brush, side brush, filter, and mop cloth replacement cycles.
- Live statistics: Sensors for total cleaned area and active cleaning duration.
- Optimized live map rendering: Cloud-assisted vector map parsing with live position, path trace, and segment highlighting tailored for the S20 platform.
- Xiaomi S20 - Full support
- Xiaomi S10 - Full support (Tested by @cpmarexel2f)
This repository isn't in the default HACS store, so it needs to be added as a custom repository first.
- Open HACS in your Home Assistant sidebar.
- Click the ⋮ (three-dot menu) in the top-right corner and choose Custom repositories.
- Paste in the repository URL:
https://github.com/m1xminus/xiaomi-vac-S20 - Set the category to Integration, then click Add.
- Find Xiaomi Vacuum in the HACS list, open it, and click Download.
- Restart Home Assistant (Settings → System → Restart).
- Go to Settings → Devices & Services → Add Integration, search for Xiaomi Vacuum, and follow the setup wizard.
The Lovelace card (xiaomi-vac-card) is registered automatically on startup — you don't need to add it as a separate dashboard resource.
- Download this repository (Code → Download ZIP, or
git clone). - Copy the
custom_components/xiaomi_vacfolder into your Home Assistant config directory, so you end up with:Copy only theconfig/custom_components/xiaomi_vac/xiaomi_vacfolder itself — not the whole repo, and not the repo root's other files (tests/,README.md, etc. don't belong incustom_components/). - Restart Home Assistant.
- Go to Settings → Devices & Services → Add Integration, search for Xiaomi Vacuum, and follow the setup wizard.
The integration includes an upgraded version of xiaomi-vac-card with full support for quick action preset buttons, allowing you to trigger complex scripts with a single tap.
Gravacao.de.Ecra.2026-08-05.183946.mp4
Get a live, updating Android notification while the vacuum cleans — current status, selected rooms, area covered, and battery — plus a finished summary with total duration and area cleaned once it's back on the dock.
- Home Assistant Companion App on Android (this uses
chronometerandprogressnotification fields that are Android-only — this exact style of live notification isn't available on iOS) - Integration version 1.2.8 or newer — the finished summary reads the
last_completed_clean_duration/last_completed_clean_areasensors added in that release - The
rooms_selectedsensor (added automatically once a map coordinator is configured)
Replace vacuum.your_vacuum_entity_id and the sensor entity IDs below with
your own, and list your own notify.mobile_app_* targets under phones.
expected_minutes only drives the progress-bar percentage — set it to
roughly how long an average clean takes for you.
alias: Vacuum - Live cleaning notification (Android)
description: ''
triggers:
- trigger: state
entity_id: vacuum.your_vacuum_entity_id
to: cleaning
actions:
- variables:
start_ts: '{{ as_timestamp(now()) | int }}'
- repeat:
for_each: '{{ phones }}'
sequence:
- action: '{{ repeat.item }}'
data:
title: 🧹 Cleaning started
message: Updating room details…
data:
tag: vacuum_clean_live
sticky: true
notification_icon: mdi:robot-vacuum
chronometer: true
when: '{{ start_ts }}'
progress: 0
progress_max: 100
- repeat:
while:
- condition: template
value_template: >-
{{ states('vacuum.your_vacuum_entity_id') in ['cleaning', 'returning',
'paused'] }}
- condition: template
value_template: '{{ repeat.index < 240 }}'
sequence:
- repeat:
for_each: '{{ phones }}'
sequence:
- action: '{{ repeat.item }}'
data:
title: >-
{% set s = states('vacuum.your_vacuum_entity_id') %} {{ '🧹
Cleaning' if s == 'cleaning'
else '⏸️ Paused' if s == 'paused'
else '🔙 Returning to dock' }}
message: >-
{% set r = state_attr('sensor.your_vacuum_rooms_selected',
'rooms') %}
Rooms: {{ r | join(', ') if r and r | length > 0 else 'Whole
house' }}
Area: {{ states('sensor.your_vacuum_clean_area') }} m² ·
Battery: {{ states('sensor.your_vacuum_battery') }}%
data:
tag: vacuum_clean_live
alert_once: true
sticky: true
notification_icon: mdi:robot-vacuum
chronometer: true
when: '{{ start_ts }}'
progress_max: 100
progress: |-
{{ [ ((states('sensor.your_vacuum_clean_time') | int(0))
/ expected_minutes * 100) | round(0) | int, 100 ] | min }}
- delay:
seconds: 30
- choose:
- conditions:
- condition: state
entity_id: vacuum.your_vacuum_entity_id
state: docked
sequence:
- repeat:
for_each: '{{ phones }}'
sequence:
- action: '{{ repeat.item }}'
data:
title: ✅ Cleaning finished
message: >-
{% set r =
state_attr('sensor.your_vacuum_rooms_selected', 'rooms')
%}
{% set dur =
states('sensor.your_vacuum_last_completed_clean_duration') %}
{% set area =
states('sensor.your_vacuum_last_completed_clean_area') %}
Rooms: {{ r | join(', ') if r and r | length > 0 else
'Whole house' }}
Duration: {{ dur ~ ' min' if dur not in
['unknown','unavailable','none'] else '—' }}
Area cleaned: {{ area ~ ' m²' if area not in
['unknown','unavailable','none'] else '—' }}
data:
tag: vacuum_clean_live
sticky: false
notification_icon: mdi:check-circle
progress: -1
default:
- repeat:
for_each: '{{ phones }}'
sequence:
- action: '{{ repeat.item }}'
data:
title: ⚠️ Cleaning stopped
message: >-
Stopped before docking — state is {{
states('vacuum.your_vacuum_entity_id') }}
data:
tag: vacuum_clean_live
sticky: false
notification_icon: mdi:alert
progress: -1
mode: restart
variables:
phones:
- notify.mobile_app_your_phone_1
- notify.mobile_app_your_phone_2
expected_minutes: 60Add this configuration to your Lovelace dashboard (replace entity names with your device's actual entity IDs):
type: custom:xiaomi-vac-card
vacuum: vacuum.your_vacuum_entity_id
map: camera.your_vacuum_map_entity_id
activeMap: select.your_vacuum_active_map_entity_id
fan: select.your_vacuum_fan_speed_entity_id
water: select.your_vacuum_water_level_entity_id
mode: select.your_vacuum_cleaning_mode_entity_id
show_vacuum_page: false
show_map: true
show_controls: true
show_fan: true
show_water: true
show_mode: true
show_room_labels: true
allow_room_cleaning: true
show_active_map: true
presets:
- name: Clean Entire House
script: script.clean_entire_house
- name: Vacuum & Mop Allowed Rooms
script: script.vacuum_and_mop_living_kitchenThis guide shows how to build Home Assistant scripts that clean specific rooms with specific settings — for example "vacuum and mop the kitchen and hallway on strong suction", triggered from a dashboard button, a voice assistant, or an automation.
No external tools needed. Everything here uses services provided by this integration.
Every room on your vacuum's map has a numeric ID. These are assigned by the vacuum, not by you, and they're what the scripts refer to.
The easiest way to find them:
- Go to Developer Tools → States
- Find your map camera entity (something like
camera.your_vacuum_map) - Look at its
roomsattribute
You'll see a list like:
rooms:
- id: 10
name: Living Room
- id: 11
name: Kitchen
- id: 12
name: HallwayWrite down the IDs for the rooms you care about. They stay stable unless you re-map your home or split/merge rooms in the Xiaomi app.
Note: These are map room IDs, which are not the same as the room names you may have assigned in the Mi Home app's own room list. Always use the IDs from the camera attribute above.
Every custom clean script follows the same two steps:
apply_room_preferences— tell the vacuum which rooms to clean and how to clean each oneclean_segment— tell it to start
These are separate because the vacuum stores the "how" as per-room settings first, then acts on them. A short delay between the two lets the settings commit before cleaning starts.
alias: Clean Kitchen
mode: single
sequence:
- action: xiaomi_vac.apply_room_preferences
target:
entity_id: vacuum.your_vacuum
data:
active_rooms:
- room_id: 11
clean_mode: 1
wind_power: 2
water_level: 2
- delay:
milliseconds: 1000
- action: xiaomi_vac.clean_segment
target:
entity_id: vacuum.your_vacuum
data:
segments: [11]This cleans room 11 (the kitchen) in sweep + mop mode, strong suction, water level 2.
Your vacuum entity — find it under Settings → Devices & Services → Xiaomi Vacuum. It looks like vacuum.something. It's the same in both steps.
The list of rooms to clean, each with its own settings. This is the important one to understand:
- Rooms you list here are marked active for the next clean.
- Rooms you don't list are marked inactive — they get skipped.
- Their saved settings are not erased, just deactivated. Your other scripts still work exactly as configured.
This means you never need to "clear" a previous selection. Listing the rooms you want is enough.
The numeric room ID from the camera attribute. Use plain numbers, not quoted strings — room_id: 11, not room_id: '11'.
One second between applying settings and starting. This isn't cosmetic — the vacuum needs a moment to commit the settings before the clean command lands. Without it, the clean can start using the previous settings.
The list of room IDs to actually clean. This should match the rooms you listed in active_rooms.
segments: [10, 11, 12]Important: Never pass an empty list. On this hardware, an empty room list is interpreted as "clean everything" rather than "clean nothing" — the integration will refuse an empty list rather than let that happen silently, but it's worth knowing why.
All settings are optional. Any setting you leave out keeps whatever that room already had saved — it does not reset to a default.
| Value | Meaning |
|---|---|
0 |
Sweep (vacuum only) |
1 |
Sweep + Mop (at the same time) |
2 |
Mop only |
3 |
Sweep, then Mop (two passes) |
| Value | Meaning |
|---|---|
0 |
Silent |
1 |
Basic |
2 |
Strong |
3 |
Full Speed |
| Value | Meaning |
|---|---|
0 |
Off / none |
1 |
Low |
2 |
Medium |
3 |
High |
Only relevant in modes that use water (1, 2, 3).
| Value | Meaning |
|---|---|
0 |
Single pass |
1 |
Clean each room twice |
| Value | Meaning |
|---|---|
0 |
Not set |
1 |
Auto carpet boost |
2 |
Off |
alias: Custom Clean - Vacuum & Mop (Living, Kitchen, Hall, WC)
description: ''
mode: single
sequence:
- action: xiaomi_vac.apply_room_preferences
target:
entity_id: vacuum.your_vacuum
data:
active_rooms:
- room_id: 10
clean_mode: 1
wind_power: 2
water_level: 2
- room_id: 14
clean_mode: 1
wind_power: 2
water_level: 2
- room_id: 15
clean_mode: 1
wind_power: 2
water_level: 2
- room_id: 16
clean_mode: 1
wind_power: 2
water_level: 2
- delay:
milliseconds: 1000
- action: xiaomi_vac.clean_segment
target:
entity_id: vacuum.your_vacuum
data:
segments: [10, 14, 15, 16]Each room is independent — you can mix and match freely:
active_rooms:
# Kitchen: mop heavily
- room_id: 11
clean_mode: 1
wind_power: 2
water_level: 3
# Bedroom: quiet, no water (carpet)
- room_id: 12
clean_mode: 0
wind_power: 0
water_level: 0Important
Please include debug logs with every issue. This integration talks to your vacuum over both local and cloud connections, and "it doesn't work" without logs is almost impossible to diagnose. Issues opened without logs will likely just get a request to add them, which slows everything down — save everyone the round-trip and include them up front.
Easiest way (no config file editing):
- Go to Settings → Devices & Services.
- Find Xiaomi Vacuum and click into it.
- Click the ⋮ (three-dot menu) → Enable debug logging.
- Reproduce the problem (trigger the button, service call, or automation that's misbehaving).
- Go back to the same ⋮ menu → Disable debug logging — this automatically downloads a
.logfile with everything captured while it was on.
Alternative (YAML method): add this to your configuration.yaml, then restart Home Assistant:
logger:
default: warning
logs:
custom_components.xiaomi_vac: debugReproduce the issue, then grab the log from Settings → System → Logs, or directly from config/home-assistant.log.
Debug logs from this integration can include information tied to your Xiaomi account and device. Search your log and redact (replace with [REDACTED]) any of the following before posting:
mac=...— your vacuum's MAC addresswifi_sn=...— your vacuum's WiFi serial numberuser_id=...— your Xiaomi account's numeric user IDdevice_id=.../did=...— your vacuum's device IDssecurity=...,service_token=...,pass_token=...— cloud session credentials- Your local network details — router/device IP addresses (e.g.
192.168.x.x), if they appear - Your Xiaomi account email/username, if it appears anywhere in the log
A quick way to check: search the log for your email address and any of the field names above before pasting it in. If in doubt, leave it out — a maintainer can always ask for a specific missing detail rather than you having to un-post a leaked token.
Please copy this template into your issue and fill it in:
**Vacuum model:** (e.g. xiaomi.vacuum.d106gl / Xiaomi S20)
**Home Assistant version:**
**Integration version:** (see custom_components/xiaomi_vac/manifest.json, or the HACS entry)
**What happened:**
**What you expected to happen:**
**Steps to reproduce:**
1.
2.
3.
**Debug log (redacted — see above):**paste your redacted log here
Issues with a clear description, reproduction steps, and a redacted debug log get resolved far faster than "it's broken" — thanks for taking the time to include them.





