CLI, MCP server, and web dashboard for the Roborock Saros 10R robot vacuum, built on top of python-roborock. Control cleaning, monitor status, manage schedules, and query history — all from the terminal, your browser, or an AI assistant.
Cloud API only. The Saros 10R uses a newer local protocol not yet supported by
python-roborock. All commands are relayed through Roborock's cloud MQTT broker (usiot.roborock.com:8883), even when the device is on the same LAN. See Connectivity for details.
Current scheduler follow-up work is tracked in docs/scheduler-reliability-plan.md.
- CLI (
vacuum) — status, start/stop/pause/dock, room selection, routines, consumables, history - Web dashboard (
vacuum-dashboard) — full-featured browser UI with scheduling, clean history, consumable gauges, and per-room cleaning intervals - MCP server (
vacuum-mcp) — exposes vacuum tools to AI assistants via the Model Context Protocol (works with Claude Desktop) - Scheduling — SQLite-backed per-room cleaning intervals with overdue detection and priority scoring
- Clean history — paginated history with duration, area, and completion status
- Consumables — percentage-remaining gauges for main brush, side brush, filter, and sensor
| Schedule (Gantt) | Mobile |
|---|---|
![]() |
![]() |
- Python 3.11+
- A Roborock account (the same login used in the Roborock app)
- A Roborock Saros 10R (other Roborock devices may work but are untested)
- Internet access (cloud API)
1. Install
git clone https://github.com/yourusername/saros-dashboard
cd saros-dashboard
pip install -e .2. Configure credentials
cp .env.example .env
# Edit .env and fill in your Roborock username and password.env contents:
ROBOROCK_USERNAME=your@email.com
ROBOROCK_PASSWORD=yourpassword
ROBOROCK_DEVICE_NAME= # optional — defaults to first device found
3. Authenticate
vacuum login # use this if password login doesn't work (triggers email code flow)After first login, the session token is cached at .roborock_session.json. Subsequent runs skip the login round-trip.
vacuum status # Current state, battery, dock status
vacuum clean # Start a full home clean
vacuum stop # Stop cleaning
vacuum pause # Pause in place
vacuum dock # Return to dock
vacuum locate # Play locator sound
vacuum map # Show rooms and segment IDs
vacuum rooms "Kitchen" "Living room" # Clean specific rooms
vacuum rooms "Kitchen" --repeat 2 # Clean a room twice
vacuum sequence # Raw clean-sequence / segment-status diagnostics
vacuum map-debug # Charger/room coordinates and dock-distance ranking
vacuum test-order "Kitchen" "Hall" --dry-run # Show exact ordered room payload
vacuum routine --list # List available routines
vacuum routine "morning-clean" # Run a named routine
vacuum history # Recent clean history
vacuum consumables # Brush / filter / sensor wearvacuum test-order is a diagnostic tool only. Physical testing on April 11, 2026 showed that the Saros 10R did not reliably follow the submitted room list order for APP_SEGMENT_CLEAN; see docs/scheduler-reliability-plan.md.
vacuum-dashboard # Start on default port 9103
make install # Install as a systemd user service (auto-starts on login)
make deploy # Rebuild frontend and restart service (standard iteration command)Then open http://<your-lan-ip>:9103 in your browser. The dashboard auto-refreshes every 30 seconds and works as an iOS "Add to Home Screen" PWA.
Add to your claude_desktop_config.json:
{
"mcpServers": {
"vacuum": {
"command": "vacuum-mcp",
"env": {
"ROBOROCK_USERNAME": "your@email.com",
"ROBOROCK_PASSWORD": "yourpassword"
}
}
}
}Available MCP tools: vacuum_status, start_cleaning, stop_cleaning, pause_cleaning, return_to_dock, locate_vacuum, get_map, room_clean, zone_clean, run_routine, get_cleaning_schedule, get_overdue_rooms, set_room_interval, plan_clean, set_room_notes
The dashboard exposes an HTTP API on port 9103 that Home Assistant can call directly using rest_command. No authentication is required (LAN-trust model).
A trigger is a named entry you create in the dashboard UI (Triggers panel). Each trigger has a budget (minutes) and a mode (vacuum or mop). When fired:
- A cleaning window opens for the configured number of minutes.
- The dispatch loop sends the vacuum to whichever rooms are most overdue during that window.
- When the window expires (or
stopis called), the vacuum docks.
Firing the same trigger while a window is already open extends the window rather than resetting it.
rest_command:
# Fire a named trigger (replace "evening" with your trigger name)
vacuum_trigger_evening:
url: "http://192.168.1.111:9103/api/trigger/evening/fire"
method: POST
# Stop the current clean and dock immediately
vacuum_trigger_stop:
url: "http://192.168.1.111:9103/api/trigger/stop"
method: POST
# Open a raw time window without a named trigger (budget_min required)
vacuum_window_open:
url: "http://192.168.1.111:9103/api/window/open"
method: POST
content_type: "application/json"
payload: '{"budget_min": {{ budget_min }}}'Replace 192.168.1.111 with your machine's LAN IP (visible at service startup or via make status).
POST /api/trigger/{name}/fire — success:
{
"ok": true,
"window": {
"active": true,
"remaining_minutes": 45.0
}
}POST /api/trigger/{name}/fire — trigger not found (HTTP 404):
{ "detail": "Trigger 'evening' not found" }POST /api/trigger/stop:
{ "ok": true, "window": { "active": false } }GET /api/window — poll window state:
{
"active": true,
"remaining_minutes": 32.5,
"current_clean": {
"event_id": 42,
"segment_ids": [3, 5],
"mode": "vacuum",
"started": true
}
}current_clean is null when no room dispatch is in progress (e.g. window is open but vacuum hasn't started yet, or is between rooms).
Fire the "evening" trigger when a person arrives home after 5 pm:
automation:
- alias: "Vacuum on arrival (evening)"
trigger:
- platform: state
entity_id: person.you
to: "home"
condition:
- condition: time
after: "17:00:00"
before: "22:00:00"
action:
- service: rest_command.vacuum_trigger_eveningStop the vacuum if someone is cooking (noise sensor, etc.):
automation:
- alias: "Stop vacuum when kitchen occupied"
trigger:
- platform: state
entity_id: binary_sensor.kitchen_motion
to: "on"
action:
- service: rest_command.vacuum_trigger_stopcurl http://192.168.1.111:9103/api/triggersReturns:
[
{ "name": "evening", "budget_min": 45, "mode": "vacuum", "notes": "after dinner" },
{ "name": "quick", "budget_min": 20, "mode": "vacuum", "notes": null }
]Triggers are created and managed in the Triggers panel of the web dashboard.
All commands travel:
Your client (CLI / browser / AI)
↓
FastAPI / Typer / MCP handler
↓
python-roborock (persistent MQTT connection)
↓
Roborock Cloud MQTT (usiot.roborock.com:8883, TCP/TLS)
↓
Saros 10R device
There is no local path — the device is on the same LAN but uses a newer local protocol version not yet supported by python-roborock (tracked in home-assistant/core#152136). This means:
- Commands require an internet connection
- Latency is cloud round-trip (~1–3s typical)
- Occasional timeouts occur during cloud maintenance windows
- The dashboard auto-reconnects after MQTT session drops
src/vacuum/
cli.py # `vacuum` CLI (Typer)
mcp_server.py # `vacuum-mcp` MCP server
dashboard.py # `vacuum-dashboard` FastAPI web app
client.py # VacuumClient — all device logic
scheduler.py # SQLite-backed cleaning scheduler
config.py # Credentials and session management
See CLAUDE.md for a detailed developer reference (API docs, gotchas, architecture notes).
- Fork the repo and create a feature branch
- Install in editable mode:
pip install -e . - Make your changes — see
CLAUDE.mdfor the full API reference - Open a pull request
Bug reports and feature requests are welcome via GitHub Issues.
MIT — see LICENSE.


