Dreame D20 Pro Plus MCP Server
by sandraschi
README.md
# Dreame D20 Pro Plus MCP Server
<p align="center">
<a href="https://github.com/casey/just"><img src="https://img.shields.io/badge/just-ready_to_go-7c5cfc?style=flat-square&logo=just&logoColor=white" alt="Just"></a>
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
<a href="https://python.org"><img src="https://img.shields.io/badge/Python-3.13+-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python"></a>
<a href="https://biomejs.dev"><img src="https://img.shields.io/badge/Linted_with-Biome-60a5fa?style=flat-square&logo=biome&logoColor=white" alt="Biome"></a>
<a href="https://github.com/PrefectHQ/fastmcp"><img src="https://img.shields.io/badge/FastMCP-3.2-7c5cfc?style=flat-square" alt="FastMCP"></a>
</p>
> 📖 **[Installation Guide](INSTALL.md)** — quick start, manual setup, and troubleshooting
FastMCP 3.2.0 MCP server and webapp for the **Dreame D20 Pro Plus** robot vacuum. Uses the **DreameHome cloud API**; local **miio** is optional (hybrid mode: see below).
Protocol layer extracted from [Tasshack/dreame-vacuum](https://github.com/Tasshack/dreame-vacuum).
## Features
- **MCP tools**: `dreame_tool(operation=...)` status, map, start_clean, stop, pause, go_home, find_robot, battery
- **MCP tools**: `dreame_help(category)`, `dreame_agentic_workflow(goal)` with SEP-1577 sampling, `dreame_shutdown()`, `show_dreame_status_app()` (Prefab card)
- **Prompts**: `dreame_quick_start`, `dreame_diagnostics`
- **Skills**: `skill://dreame-operator/SKILL.md` (also `GET /api/skills`)
- **Resources**: `dreame://status`
- **REST API**: GET /api/v1/health, /api/v1/status, /api/v1/map (+ png/pgm/yaml), /api/v1/diagnostics; POST /api/v1/control/{cmd}, /api/v1/shutdown
- **Webapp**: Dashboard, LIDAR Map, Status, Controls, Settings, Help, MCP Tools, Logs
## Ports
- Backend: **10894** (REST + MCP SSE)
- Dashboard: **10895** (Vite dev server)
## Prerequisites
1. Clone **this** repo and enter it:
```powershell
git clone https://github.com/sandraschi/dreame-mcp.git
Set-Location dreame-mcp
```
2. Clone the Tasshack dreame-vacuum reference repo (protocol + map layer). Default ref path is `D:/Dev/repos/tasshack_dreame_vacuum_ref`:
```powershell
Set-Location D:\Dev\repos
git clone https://github.com/Tasshack/dreame-vacuum tasshack_dreame_vacuum_ref
Set-Location dreame-mcp
```
(Adjust paths if your dev folder is not `D:\Dev\repos`; set `DREAME_REF_PATH` accordingly.)
3. Install Python deps from the **dreame-mcp** repository root:
```powershell
uv sync
```
## Environment variables
| Variable | Required | Description |
|---|---|---|
| `DREAME_USER` | | DreameHome email or phone |
| `DREAME_PASSWORD` | | DreameHome password |
| `DREAME_COUNTRY` | | Cloud region, default `eu` |
| `DREAME_DID` | | Device ID, auto-discovered if single device |
| `DREAME_AUTH_KEY` | | Refresh token from previous login (speeds up startup) |
| `DREAME_REF_PATH` | | Path to tasshack ref clone (default: `D:/Dev/repos/tasshack_dreame_vacuum_ref`) |
| `DREAME_MCP_PORT` | | Backend port (default: `10894`) |
| `DREAME_IP` / `DREAME_TOKEN` | | Local miio (null token if token empty); hybrid with cloud for maps |
## Development and tests
- **Python** (repo root): `uv run ruff check src tests`, `uv run pytest` (CI sets `PYTHONPATH=src`; on Windows: `$env:PYTHONPATH = 'src'; uv run pytest tests`).
- **MCP tool `dreame(operation=...)`** returns **Markdown** for LLM context. For **structured dicts** (same shapes as the REST handlers), import `fetch_status_data`, `fetch_map_data`, and `execute_control_data` from `dreame_mcp.portmanteau` (see `tests/test_map.py`).
- **Live tests** (real robot + cloud): `DREAME_LIVE=1 uv run pytest tests --live` or `--live` flag.
- **Webapp** (`webapp/`): `npm ci` then `npm run biome:ci` and `npm run build`.
## Connection Modes
This server supports three operational modes depending on your `.env` configuration:
| Mode | Credentials | Commands | Lidar Map | Notes |
| :--- | :--- | :---: | :---: | :--- |
| **Local** | `DREAME_IP` | ⚡ Local | ❌ No | Uses the **Null Token trick** (`000...000`) |
| **Cloud** | `USER` + `PWD` | ☁️ Cloud | ✅ Yes | Subject to cloud latency and API rate limits |
| **Hybrid** | **Both** | ⚡ **Local** | ✅ **Yes** | **Recommended**: Fast control + Full visual map |
### The "Null Token" Trick (Bypass)
For users avoiding the DreameHome cloud for controls, you do not need to extract a secret 32-character token. By providing only the `DREAME_IP`, the backend automatically uses a **Null Token** (`32 zeros`). This works on many bridged or circumvention-ready firmwares (like those used with the Tasshack protocol).
## Quick Start
```powershell
git clone https://github.com/sandraschi/dreame-mcp
cd dreame-mcp
just
```
This opens an interactive dashboard showing all available commands. Run `just bootstrap` to install dependencies, then `just serve` to start the full stack, or `just test` for the mocked suite.
### Manual Setup
If you don't have `just` installed:
## Setup
1. **Configure credentials**: Copy `.env.example` to `.env` and fill in your details.
```powershell
# Typical Hybrid Setup (.env)
DREAME_IP=192.168.0.178
DREAME_USER=your@email.com
DREAME_PASSWORD=yourpassword
```
2. **Start the system**:
```powershell
# Start backend + webapp together
.\webapp\start.ps1
```
## MCP client config
```json
{
"mcpServers": {
"dreame": {
"url": "http://localhost:10894/sse",
"transport": "sse"
}
}
}
```
## Map (LIDAR / floor plan)
**Map data does not require miIO** on this server: it uses DreameHome cloud + the Tasshack ref at `DREAME_REF_PATH`. Local miIO is optional and often unavailable on DreameHome-only firmware.
- **REST:** `GET http://localhost:10894/api/v1/map` (same JSON as MCP `dreame_tool(operation='map')`).
- **Fields:** `image` (base64-encoded image when decode/render works), `raw_b64` (always on successful cloud fetch; use for custom decoders or **robotics-mcp / yahboom-mcp**), optional `map_data`, optional `render_error` if the PNG path failed.
- **Dashboard:** **Map** page at `http://localhost:10895` shows the image when `image` is present.
**Download path (cloud):** matches Home Assistant’s Tasshack integration — resolve **`OBJECT_NAME`** (property 6.3) when available, then **`get_interim_file_url` / `get_file`** (signed object storage). **`get_device_file`** is only a fallback; it often returns `80001` if the cloud cannot reach the device at that moment.
**Render path:** raw bytes are decoded with **`DreameVacuumMapDecoder.decode_map`** and drawn with **`DreameVacuumMapRenderer.render_map`** (not `DreameMapVacuumMapManager` methods, which only orchestrate HA state). The ref clone’s `custom_components.…` packages are given proper `__path__` at load time so `map.py` imports cleanly.
See **[docs/MAP_AND_ROBOTICS.md](docs/MAP_AND_ROBOTICS.md)** for fleet integration, the JSON contract, and operations.
### Map rendering (dependencies)
The rendered image requires the Tasshack stack: `py-mini-racer`, `numpy`, `Pillow`, `cryptography`, and related pins from `uv.lock`. If `dreame_tool(operation='map')` has `render_error` but `raw_b64` is set, the fetch worked and only decode/render failed; check logs and dependencies.
## Troubleshooting: `Unable to discover the device` / status 502
`GET /api/v1/health` includes **`local_miot`**: `true` only after a successful UDP miio handshake to `DREAME_IP` (port 54321). If you see **`Unable to discover the device` `192.168.x.x`** in logs or **`local_miot: false`**, the robot did not answer the standard miio discovery on the LAN. Typical causes: **DreameHome-only firmware** (no or limited LAN miio), **wrong IP**, **null token not accepted** (add a real **`DREAME_TOKEN`**), or **cloud login failed** (fix **`DREAME_USER` / `DREAME_PASSWORD` / `DREAME_COUNTRY`**, captcha, 2FA) so you get **`DREAME_DID`** and maps. Set **`DREAME_DID`** manually in `.env` when you know it from the app or cloud.
## Docs
- [The Dreame Robo Hoover Saga](docs/ROBO_HOOVER_SAGA.md) how control worked, broke, and was restored (with the map!)
- [Configuration](docs/CONFIGURATION.md) env vars, connection modes, ports
- [Development](docs/DEVELOPMENT.md) stack, recipes, five-gate verification
- [Tools & API](docs/TOOLS.md) MCP tools, prompts, REST endpoints
- [Troubleshooting](docs/TROUBLESHOOTING.md) common failures and fixes
- [Onboarding](docs/ONBOARDING.md) first-run setup (DreameHome account)
- [Map and fleet robotics](docs/MAP_AND_ROBOTICS.md) HTTP/MCP consumption, miIO vs cloud, yahboom / robotics integration
- [PRD](docs/PRD.md) product context, ports, **5 Map API contract**
- [Token and Home Assistant](docs/TOKEN_AND_HOME_ASSISTANT.md) historical miIO reference (v0.2+ uses cloud)
## 🛡️ Industrial Quality Stack
This project adheres to **SOTA** industrial standards for high-fidelity agentic orchestration:
- **Python (Core)**: [Ruff](https://astral.sh/ruff) for linting and formatting. Zero-tolerance for `print` statements in core handlers (`T20`).
- **Python (Types)**: [Pyright](https://github.com/microsoft/pyright) static type checking (five-gate CI).
- **Webapp (UI)**: [Biome](https://biomejs.dev/) for sub-millisecond linting. Strict `noConsoleLog` enforcement.
- **Protocol Compliance**: Hardened `stdout/stderr` isolation to ensure crash-resistant JSON-RPC communication.
- **Automation**: [Justfile](./justfile) recipes for all fleet operations (`just lint`, `just fix`, `just serve`).
- **Security**: Automated audits via `bandit` and `safety`.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessNo issues