{"id":48677021,"url":"https://github.com/dacrypt/xiao","last_synced_at":"2026-04-30T11:01:07.529Z","repository":{"id":350510275,"uuid":"1195327344","full_name":"dacrypt/xiao","owner":"dacrypt","description":"Control a Xiaomi Robot Vacuum X20+ via the xiao CLI. Agent-ready: AGENTS.md, llms.txt, SKILL.md, --json output, canonical exit codes.","archived":false,"fork":false,"pushed_at":"2026-04-21T21:38:57.000Z","size":349,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-21T22:03:18.441Z","etag":null,"topics":["agent-tool","agents-md","claude-skill","cli","home-automation","iot","llm-tool","llms-txt","mcp","python","roborock","robot-vacuum","smart-home","vacuum","xiaomi"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dacrypt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-29T14:36:12.000Z","updated_at":"2026-04-21T21:38:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"52e3c4a2-1b22-4ab5-923c-8e8a645e59bf","html_url":"https://github.com/dacrypt/xiao","commit_stats":null,"previous_names":["dacrypt/xiao"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/dacrypt/xiao","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dacrypt%2Fxiao","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dacrypt%2Fxiao/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dacrypt%2Fxiao/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dacrypt%2Fxiao/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dacrypt","download_url":"https://codeload.github.com/dacrypt/xiao/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dacrypt%2Fxiao/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32462304,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T22:27:22.272Z","status":"online","status_checked_at":"2026-04-30T02:00:05.929Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-tool","agents-md","claude-skill","cli","home-automation","iot","llm-tool","llms-txt","mcp","python","roborock","robot-vacuum","smart-home","vacuum","xiaomi"],"created_at":"2026-04-10T18:10:47.309Z","updated_at":"2026-04-30T11:01:07.522Z","avatar_url":"https://github.com/dacrypt.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xiao — Xiaomi Vacuum CLI \u0026 Mission Control\n\n[![CI](https://github.com/dacrypt/xiao/actions/workflows/ci.yml/badge.svg)](https://github.com/dacrypt/xiao/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/xiao-cli)](https://pypi.org/project/xiao-cli/)\n[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Agent-ready](https://img.shields.io/badge/agent--ready-AGENTS.md%20%7C%20llms.txt-blueviolet)](AGENTS.md)\n\nCLI + web dashboard to control a **Xiaomi Robot Vacuum X20+** (model: `xiaomi.vacuum.c102gl`) via the **Xiaomi Cloud API**.\n\n\u003e **Cloud-only.** This vacuum model has no local-network control path. Every command goes through Xiaomi Cloud with RC4-signed MIoT requests.\n\n\u003e **Agent-first.** `xiao` is designed to be driven by LLM agents (Claude, OpenClaw, Cursor, Aider, Codex…) as a subprocess. If you are an agent reading this repo, start with [AGENTS.md](AGENTS.md).\n\n![xiao demo](docs/demo/demo.gif)\n\n---\n\n## Agent Quick Reference\n\n| Fact | Value |\n|---|---|\n| Tool name | `xiao` |\n| Install | `pip install xiao-cli` |\n| Verify | `xiao --help` → lists subcommands; exits 0 |\n| State command | `xiao status` (Rich panel) or `xiao status --json` (machine-readable) |\n| Machine-readable | `xiao status --json` / `xiao consumables --json`; or `xiao web` → `GET /api/status` |\n| Exit codes | `0` success · `1` generic · `2` not configured · `77` auth failed · `78-80` reserved ([AGENTS.md](AGENTS.md#exit-codes)) |\n| Agent guide | [AGENTS.md](AGENTS.md) — canonical commands, intent mapping, error recovery |\n| Machine index | [llms.txt](llms.txt) |\n\n---\n\n## Features\n\n- Full vacuum control — start / stop / pause / dock / find / room / zone / spot.\n- Base-station controls — mop wash, mop dry, dust collect, tray eject.\n- Settings — fan speed, water level, volume, Do-Not-Disturb window.\n- Consumable tracking with remaining life %.\n- Schedule viewer with parsed room / day / setting data.\n- **Mission Control** — glassmorphism web dashboard with real-time status, advanced X20+ settings controls, and a saved dark/light theme toggle.\n- Auto token refresh via a persistent Chromium session (no repeated email 2FA).\n- Full MIoT property/action support for `c102gl`.\n\n---\n\n## Installation\n\n```bash\npip install xiao-cli\n# or from source\ngit clone https://github.com/dacrypt/xiao.git\ncd xiao \u0026\u0026 uv sync\n```\n\nThen install Playwright's Chromium (needed for the cloud-login fallback):\n\n```bash\nplaywright install chromium\n```\n\n### Docker\n\nA lightweight image is published to GHCR on every release. It's meant for\nrunning vacuum commands against an existing config — run `xiao setup\ncloud` once on your workstation, then mount the resulting config dir:\n\n```bash\ndocker run --rm -v \"$HOME/.config/xiao:/root/.config/xiao\" \\\n  ghcr.io/dacrypt/xiao status\n```\n\n(On macOS the host path is `~/Library/Application Support/xiao`.)\n\n### MCP server (optional)\n\nInstall with the `mcp` extra and `xiao` exposes vacuum control as a\n[Model Context Protocol](https://modelcontextprotocol.io) server, so\nhosts like Claude Desktop / Cursor / mcp.so can drive the vacuum as\nstructured tools (no shelling out):\n\n```bash\npip install \"xiao-cli[mcp]\"\nxiao mcp           # speaks MCP over stdio\n```\n\nHost config snippet (Claude Desktop `claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"xiao\": { \"command\": \"xiao\", \"args\": [\"mcp\"] }\n  }\n}\n```\n\nExposed tools: `status`, `start_cleaning`, `stop_cleaning`,\n`pause_cleaning`, `return_to_dock`, `find_vacuum`, `consumables`,\n`clean_room`, `list_rooms`. The server reuses the same `config.toml` and\nbrowser profile as the CLI.\n\n**Compatibility:** Python 3.12+. Tested against vacuum model `xiaomi.vacuum.c102gl` on Xiaomi Cloud API as of 2024. Token refresh tested with Chromium ≥ 120 (currently running 147).\n\n### Shell completions (optional)\n\n```bash\nxiao --install-completion   # bash / zsh / fish / pwsh — auto-detected\n```\n\nRestart your shell afterwards. `xiao \u003cTAB\u003e` will now complete subcommands.\n\n### Handy defaults\n\n- `xiao` with no subcommand prints the current vacuum status.\n- `XIAO_DEBUG=1 xiao ...` enables verbose logging for issue reports.\n- `XIAO_NO_CTA=1` silences the GitHub-star banner.\n\n---\n\n## Prerequisites\n\nBefore any `xiao` command will work:\n\n1. `xiao` installed (see above) + `playwright install chromium` run once.\n2. `xiao setup cloud` completed — writes `config.toml` at:\n   - macOS: `~/Library/Application Support/xiao/config.toml`\n   - Linux: `~/.config/xiao/config.toml`\n\nThat's it. `xiao setup cloud` will also offer to run `xiao setup\nbrowser-login` at the end: a one-time Chromium window where you log into\n`account.xiaomi.com`. The session cookies are saved to a private profile\nunder your config dir, so every future token refresh runs headless — no\ncaptcha, no email 2FA.\n\nYou can rerun `xiao setup browser-login` anytime if a token expires or\nthe profile is cleared. Power users who already maintain a Chromium\nsession exposed over CDP can skip the profile entirely by setting\n`XIAO_CDP_PORT=18800` (or whatever port).\n\n---\n\n## Setup\n\n```bash\nxiao setup cloud         # Interactive: email → password → region → device discovery → save\nxiao setup show          # Print current config (tokens redacted)\n```\n\n---\n\n## Quick Start\n\n```bash\nxiao status              # Current state, battery, fan speed, mode\nxiao status --full       # Comprehensive status (+ DND, water, consumables)\nxiao start               # Full-house clean\nxiao stop                # Stop cleaning\nxiao dock                # Return to charging dock\nxiao find                # Beep to locate vacuum\nxiao report              # Full report: status + consumables + schedules + history\n```\n\nExample `xiao status`:\n\n```\n╭─────────────────────────────── Vacuum Status ────────────────────────────────╮\n│   State:    Drying                                                           │\n│   Battery:  ███████████░░░░░░░░░ 56%                                         │\n│   Fan:      turbo                                                            │\n│   Charging: Charging                                                         │\n│   dry_left_time_min:  470                                                    │\n╰──────────────────────────────────────────────────────────────────────────────╯\n```\n\nExample `xiao consumables`:\n\n```\n                Consumables\n┏━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┓\n┃ Component  ┃ Life ┃ Hours Left ┃ Status ┃\n┡━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━┩\n│ Main Brush │  90% │       272h │        │\n│ Side Brush │  86% │       172h │        │\n│ Filter     │  81% │       122h │        │\n│ Mop Pad    │  72% │        57h │        │\n└────────────┴──────┴────────────┴────────┘\n```\n\n---\n\n## Command Reference\n\nEvery command. One row each. Full catalog also lives in [AGENTS.md](AGENTS.md).\n\n| Command | What it does | Notable flags |\n|---|---|---|\n| `xiao status [--full] [--json]` | State + battery + fan + mode | `--full` adds DND/water/consumables; `--json` for machine-readable output |\n| `xiao start` | Full-house clean (always mop-washes first) | — |\n| `xiao stop` / `xiao pause` | Stop / pause current clean | — |\n| `xiao dock` | Return to charging dock | — |\n| `xiao find` | Beep to locate | — |\n| `xiao clean -r \u003cid\\|alias\u003e` | Clean one or more rooms | `-r` repeatable, `--speed`, `-w`, `--repeat` |\n| `xiao clean -z \"x1,y1,x2,y2\"` | Clean a rectangle (coords in mm) | `-z` repeatable, `--speed`, `-w`, `--repeat` |\n| `xiao clean -s` | Spot clean at current location | `--speed`, `-w` |\n| `xiao wash` / `xiao dry [--stop]` | Base-station mop wash / dry | — |\n| `xiao dust` / `xiao eject` | Dust collect / eject tray | — |\n| `xiao settings speed \u003clevel\u003e` | Fan: `silent` \\| `standard` \\| `medium` \\| `turbo` | no arg → print current |\n| `xiao settings water \u003clevel\u003e` | Mop water: `low` \\| `medium` \\| `high` | no arg → print current |\n| `xiao settings volume \u003c0-100\u003e` | Voice volume | no arg → print current |\n| `xiao settings dnd on/off` | Do-not-disturb | `--start HH:MM --end HH:MM` |\n| `xiao settings resume-after-charge on/off` | Auto-resume cleaning after charging | no arg → print current |\n| `xiao settings carpet-boost on/off` | Boost suction on carpets | no arg → print current |\n| `xiao settings child-lock on/off` | Lock physical buttons on the robot | no arg → print current |\n| `xiao settings smart-wash on/off` | Toggle smart mop washing at the base station | no arg → print current |\n| `xiao settings carpet-avoidance avoid\\|auto` | Choose whether carpets are actively avoided or handled automatically | no arg → print current |\n| `xiao settings clean-rags-tip \u003c0-120\u003e` | Set base-station mop wash reminder interval in minutes | no arg → print current |\n| `xiao map rooms` | List room IDs → names | — |\n| `xiao map show` | Show raw map metadata from the cloud | — |\n| `xiao rooms alias \u003cid\u003e \"\u003cname\u003e\"` | Add friendly-name alias | — |\n| `xiao rooms rename \u003cid\\|alias\u003e \"\u003cnew\u003e\"` | Rename an alias | — |\n| `xiao schedule` | Show parsed schedules in a compact table | `--json` for machine-readable output |\n| `xiao schedule list` | Alias for `xiao schedule` | `--json` |\n| `xiao consumables [--json]` | Brush / filter / mop health | `--json` for machine-readable output |\n| `xiao consumables reset \u003cpart\u003e` | Reset counter (`main_brush` \\| `side_brush` \\| `filter` \\| `mop` \\| `all`) | — |\n| `xiao report` | Full combined report | — |\n| `xiao cloud-login` | Force full re-login (captcha + 2FA) | — |\n| `xiao web` | Launch Mission Control dashboard | `--port 8120` |\n| `xiao raw \u003csiid\u003e \u003caiid\u003e [params…]` | Raw MIoT call (escape hatch) | — |\n\n### Flag reference\n\n- **`--speed` / `--fan`:** `silent` · `standard` · `medium` · `turbo`\n- **`-w` / `--water`:** `low` · `medium` · `high`\n- **`--repeat N`:** integer ≥ 1, default `1`\n\n---\n\n## Room Cleaning\n\n```bash\nxiao clean -r 4                   # Clean Study\nxiao clean -r 3 -r 7              # Clean Living Room + Dining Room\nxiao clean --speed turbo -r 8     # Turbo clean Kitchen\nxiao clean -r 3 -w high           # Living Room with high water (heavy mop)\n```\n\n### Room Management\n\nRoom IDs are specific to your vacuum's map — they're generated by the Xiaomi Home app the first time the robot maps the house.\n\n```bash\nxiao map rooms                    # ALWAYS run this first to discover IDs\nxiao rooms alias 3 \"Living Room\"  # Create an alias\nxiao rooms rename 3 \"Lounge\"      # Rename\n```\n\nOnce aliased, names also work:\n\n```bash\nxiao clean -r \"Living Room\" -r \"Kitchen\"\n```\n\n\u003e **Rule for agents:** never pass a room ID you haven't verified with `xiao map rooms`. Maps can be re-generated by the Xiaomi Home app.\n\n---\n\n## Zone Cleaning\n\nZones clean an **arbitrary rectangle** — useful for spot work (*\"just the dining table area\"*) or for rooms the vacuum hasn't segmented correctly.\n\nA zone is four coordinates in millimeters on the vacuum's internal map:\n\n```\nx1,y1,x2,y2\n└─┬─┘ └─┬─┘\n top-    bottom-\n left    right\n```\n\nOrigin `(0,0)` sits at the center of the map. Rooms typically fall in the `20000–40000` range on each axis. The easiest way to get real numbers is to draw a zone once inside the Xiaomi Home app and copy them.\n\n```bash\n# Single zone\nxiao clean -z \"25000,25000,35000,35000\"\n\n# Multiple zones in one run\nxiao clean -z \"23000,24000,27000,28000\" \\\n           -z \"31000,25000,35000,30000\"\n\n# Zone + turbo fan + high water (deep-clean a specific patch)\nxiao clean -z \"25000,25000,35000,35000\" --speed turbo -w high\n\n# Two passes over the same zone\nxiao clean -z \"25000,25000,35000,35000\" --repeat 2\n```\n\n---\n\n## Base Station\n\n```bash\nxiao wash                # Start mop washing\nxiao dry                 # Start mop drying\nxiao dry --stop          # Stop drying\nxiao dust                # Dust collection\nxiao eject               # Eject base tray\n```\n\n---\n\n## Settings\n\n```bash\nxiao settings speed turbo        # silent | standard | medium | turbo\nxiao settings water high         # low | medium | high (mop water level)\nxiao settings volume 50          # 0-100\nxiao settings dnd on --start 22:00 --end 07:00\nxiao settings dnd off\nxiao settings resume-after-charge on\nxiao settings carpet-boost off\nxiao settings child-lock on\nxiao settings smart-wash on\nxiao settings carpet-avoidance auto\nxiao settings clean-rags-tip 45\n\n# Call any of the above without an argument to print the current value:\nxiao settings speed              # → Current fan speed: turbo\nxiao settings water              # → Water level: High (raw: 3)\nxiao settings child-lock         # → Child lock: Off (raw: 0)\nxiao settings smart-wash         # → Smart wash: On (raw: 1)\nxiao settings carpet-avoidance   # → Carpet avoidance: Avoid (raw: 1)\nxiao settings clean-rags-tip     # → Clean rags tip: 45 min (raw: 45)\n```\n\n### Machine-readable output\n\nUse `--json` on read commands for deterministic parsing:\n\n```bash\nxiao status --json\n# {\n#   \"state\": \"Drying\",\n#   \"battery\": 84,\n#   \"fan_speed\": \"turbo\",\n#   \"charging\": \"Charging\",\n#   \"dry_left_time_min\": 388\n# }\n\nxiao consumables --json\n```\n\n---\n\n## Agent Intent Mapping\n\nShort table of \"user says X → run Y\". The canonical, longer version lives in [AGENTS.md](AGENTS.md#intent-mapping).\n\n| User request | Command |\n|---|---|\n| \"clean the house\" | `xiao start` |\n| \"stop\" / \"cancel\" | `xiao stop` |\n| \"go home\" / \"dock\" | `xiao dock` |\n| \"battery?\" / \"status?\" | `xiao status` |\n| \"clean the `\u003croom\u003e`\" | `xiao map rooms` → find id → `xiao clean -r \u003cid\u003e` |\n| \"turbo mode in `\u003croom\u003e`\" | `xiao clean -r \u003cid\u003e --speed turbo` |\n| \"deep mop `\u003croom\u003e`\" | `xiao clean -r \u003cid\u003e --speed turbo -w high` |\n| \"wash the mop\" | `xiao wash` |\n| \"empty the dust bin\" | `xiao dust` |\n| \"do not disturb 22 to 7\" | `xiao settings dnd on --start 22:00 --end 07:00` |\n| \"open the dashboard\" | `xiao web --port 8120` |\n\n---\n\n## Error Recovery\n\nAgents should apply these in order before asking the user.\n\n| Signal | Retry step | If still failing |\n|---|---|---|\n| Stderr contains `token` / `401` / `auth` | Re-run the same command once (CDP refresh fires on next call) | `xiao cloud-login`, then retry |\n| Stderr: `Cannot connect to browser CDP on port 18800` | Launch `chromium --remote-debugging-port=18800 --user-data-dir=~/.xiao-chromium`, user logs in at `account.xiaomi.com` | Fall back to `xiao cloud-login` |\n| `State: 21` / \"WashingMopPause\" / \"Water Tank Alert\" | User refills clean water / empties dirty water → `xiao start` | Press physical play button on the robot |\n| `xiao clean -r \u003cid\u003e` returns code 0 but `xiao status` after 10s still shows `Idle`/`Docked` | Fall back to `xiao start` | Report to user |\n| Fan speed set rejected while docked | Start the clean first, then set speed; or pass `--speed` inline on the clean command | — |\n| `xiao map rooms` → \"No rooms found\" | Vacuum hasn't mapped the house yet — user must run `xiao start` once first | — |\n| Fan level printed as raw % (e.g. `68`) | Map via thresholds: `≤30=Silent, ≤55=Standard, ≤75=Medium, \u003e75=Turbo` | — |\n\n---\n\n## Mission Control (Web Dashboard)\n\n```bash\nxiao web --port 8120\n# Open http://localhost:8120\n```\n\nGlassmorphism + neon sci-fi dashboard:\n\n- Animated vacuum SVG (pulses cleaning, dims docked).\n- Battery ring with gradient + estimated runtime.\n- Room selector with fan / water presets.\n- Base-station controls with status badges.\n- Consumable health bars (color-coded, days-until-replacement).\n- Cleaning history stats, schedule table, and a settings panel that now covers advanced X20+ `vacuum-extend` controls.\n- Keyboard: `S`=start, `X`=stop, `D`=dock, `F`=find, `R`=refresh.\n- Dark/light theme toggle with saved browser preference.\n- Mobile-first responsive, auto-refresh every 10s.\n\n### REST API\n\nAll endpoints at `http://localhost:8120/api/` — use these for **programmatic / JSON** consumption:\n\n| Endpoint | Method | Description |\n|---|---|---|\n| `/status` | GET | State, battery, fan, mode |\n| `/status/live` | GET | Minimal payload for fast polling (5s) |\n| `/consumables` | GET | Brush / filter health |\n| `/rooms` | GET | Room list |\n| `/schedules` | GET | Parsed schedules |\n| `/settings` | GET | DND, volume, fan, water, resume-after-charge, carpet-boost, child-lock, smart-wash, carpet-avoidance, clean-rags-tip snapshot |\n| `/settings/resume-after-charge` | POST | `{enabled}` toggle auto-resume after charging |\n| `/settings/carpet-boost` | POST | `{enabled}` toggle carpet suction boost |\n| `/settings/child-lock` | POST | `{enabled}` toggle hardware button lock |\n| `/settings/smart-wash` | POST | `{enabled}` toggle smart mop washing |\n| `/settings/carpet-avoidance` | POST | `{mode}` where mode is `avoid` or `auto` |\n| `/settings/clean-rags-tip` | POST | `{minutes}` where minutes is `0-120` |\n| `/start` | POST | Start clean |\n| `/stop` | POST | Stop |\n| `/dock` | POST | Return to dock |\n| `/clean/rooms` | POST | `{room_ids, fan, water}` |\n| `/wash` | POST | Mop wash |\n| `/dry` | POST | Start/stop dry |\n| `/dust` | POST | Dust collect |\n| `/settings/speed` | POST | `{level: 0-3}` |\n| `/settings/volume` | POST | `{level: 0-100}` |\n\n---\n\n## Use `xiao` as a Skill in an Agent\n\nBecause `xiao` is just a CLI, teaching an agent to drive your vacuum is really just teaching it which commands to run. Paste this prompt into **OpenClaw** (or any LLM agent that can run a subprocess) to bootstrap:\n\n\u003e **\"Set up the Xiaomi vacuum skill\"**\n\u003e\n\u003e ```\n\u003e I want you to act as my Xiaomi vacuum controller using the `xiao` CLI\n\u003e (https://github.com/dacrypt/xiao). Read AGENTS.md from that repo and\n\u003e follow its instructions. Then set it up end-to-end:\n\u003e\n\u003e 1. Install:    pip install xiao-cli \u0026\u0026 playwright install chromium\n\u003e 2. Verify:     xiao --help\n\u003e 3. Walk me through `xiao setup cloud` interactively (ask me for email,\n\u003e    password, region — one of us/cn/eu/ru/sg/tw/i2 — then discover device).\n\u003e 4. Open https://account.xiaomi.com/pass/login in your own browser tab on\n\u003e    remote-debugging port 18800. I'll solve the captcha + email 2FA once.\n\u003e    Leave that tab open — it's the long-lived session xiao reuses.\n\u003e 5. Confirm with `xiao status` and `xiao consumables`.\n\u003e\n\u003e From now on, translate vacuum-related requests (\"clean the kitchen\",\n\u003e \"send it home\", \"what's the battery?\", \"turbo in the living room\") into\n\u003e the right `xiao` command using AGENTS.md's intent-mapping table. Never\n\u003e guess room IDs — always run `xiao map rooms` first.\n\u003e\n\u003e On errors, follow the Error Recovery protocol in AGENTS.md before\n\u003e asking me.\n\u003e ```\n\nWorks the same way in Claude Code, Cursor, Aider, Codex. A ready-made Claude Code skill ships with the repo at [`.claude/skills/xiao/SKILL.md`](.claude/skills/xiao/SKILL.md) — it's picked up automatically when you open this directory in Claude Code.\n\n---\n\n## Cloud Token Refresh\n\nXiaomi Cloud `serviceToken`s expire every ~6-8h. A full re-login requires captcha + email 2FA, which can't be automated headlessly. To avoid that, `xiao` reuses a long-lived Xiaomi session inside a Chromium browser you keep running in the background with CDP on port `18800`.\n\n**The browser** is just regular Chromium launched once with:\n\n```bash\nchromium --remote-debugging-port=18800 --user-data-dir=~/.xiao-chromium\n```\n\n…then logged into `https://account.xiaomi.com` manually. The login cookies persist in the user-data-dir, so future refreshes reuse them. No specific fork or external tool required.\n\n**Refresh flow** (when the saved `serviceToken` is missing/expired, [`core/token_refresh.py`](src/xiao/core/token_refresh.py)):\n\n1. Connects via CDP at `http://127.0.0.1:18800` (Playwright `connect_over_cdp`).\n2. Navigates to `account.xiaomi.com/pass/serviceLogin?sid=xiaomiio\u0026_json=true` to obtain a fresh `_sign` — httpOnly cookies travel automatically.\n3. Submits a hidden `\u003cform\u003e` POST to `serviceLoginAuth2` (form submission because `fetch`/`xhr` don't send httpOnly cookies). Response yields `ssecurity` + `location`.\n4. Follows `location`, which sets the `serviceToken` cookie on the redirect.\n5. Returns `{userId, serviceToken, ssecurity}` to the caller → persisted to `config.toml`.\n\nIf the browser isn't reachable, `xiao` falls back to a full Playwright login (headless Chromium). Manual trigger: `xiao cloud-login`.\n\n---\n\n## Troubleshooting\n\n*(Agents: the machine-oriented decision tree is in [Error Recovery](#error-recovery) above — this section is for humans.)*\n\n**`Cannot connect to browser CDP on port 18800`**\nThe Chromium instance that holds your Xiaomi session isn't running (or isn't listening on 18800). Launch it:\n```bash\nchromium --remote-debugging-port=18800 --user-data-dir=~/.xiao-chromium\n```\nThen log into `https://account.xiaomi.com` once in that window. Leave it running. As a fallback, `xiao cloud-login` performs a full re-login with captcha + email 2FA.\n\n**`Cloud mode enabled but not configured` (exit 2)**\nRun `xiao setup cloud` — the wizard asks for email / password / region and discovers your device.\n\n**Commands hang or time out**\nUsually a Xiaomi Cloud issue. Check connectivity (`curl -I https://api.io.mi.com`). The RC4-signed endpoint can also return 500s during Xiaomi maintenance windows.\n\n**`xiao clean -r 3` returns OK but the vacuum doesn't move**\nKnown issue with room-specific cleans on some X20+ firmwares. Fallback: `xiao start` (full-house clean). Tracked in [BACKLOG.md](BACKLOG.md).\n\n**\"No rooms found\" from `xiao map rooms`**\nThe robot hasn't mapped your house yet. Run `xiao start` once end-to-end and let it learn the layout, then retry.\n\n**Fan speed setting is rejected while docked**\nSome MIoT properties are only writable while the robot is off the dock. Start a clean first, or pass `--speed` inline on the `clean` command.\n\n**Water tank alert (state 21)**\nRefill the clean-water tank / empty the dirty-water tank at the base. Then `xiao start` to resume. If the robot still sits in state 21, press the physical play button on the top of the robot.\n\n---\n\n## Cleaning Cycle\n\nWhen you call `xiao start`, the X20+ runs the full sequence:\n\n1. **Washing mop** — washes pads at base station (~2-3 min).\n2. **Cleaning** — sweeps/mops the house.\n3. **Returning** — back to dock.\n4. **Drying** — auto-dries mop pads.\n\n\u003e `start` always triggers a mop wash first, even in sweep-only mode.\n\n---\n\n## Why this project exists\n\n*(Read this if you like origin stories. Agents can skip to [AGENTS.md](AGENTS.md).)*\n\n`xiao` started as a specific itch: drive my Xiaomi vacuum from an LLM agent — first **OpenClaw**, then **Claude**.\n\nThe internet wasn't encouraging. ChatGPT and most forum threads said some variant of *\"this model is cloud-only, the API is signed, you can't control it from a script.\"* That turned out to be wrong — just tedious. Sitting down with Claude Code and poking at my own robot (sniffing requests, reversing the RC4 signing, mapping the MIoT spec for `xiaomi.vacuum.c102gl`), the protocol gave up pretty quickly.\n\nOnce the CLI worked, the rest followed fast. Anthropic's general guidance for letting agents touch the real world is unsexy but effective: give them a CLI. The same binary you run by hand is the one the agent execs. So `xiao` is deliberately CLI-first — the dashboard, the REST API, the skill integrations are all thin layers over the same core.\n\nAt that point it stops mattering *which* LLM is driving, because the vacuum doesn't care. This repo is really a **skill** — a small, reusable capability you can bolt onto any agent. Routines, voice assistants, cross-device automations, home dashboards — all downstream.\n\n---\n\n## Development\n\n```bash\nuv sync\nuv run pytest tests/ -v\nuv run ruff check src/ tests/\nuv run ruff format src/ tests/\nuv run mypy src/xiao/\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for source-tree layout, MIoT spec notes, and PR guidelines.\n\n---\n\n## Privacy\n\n`xiao` collects **nothing**. No telemetry, no analytics, no backend. Your\nXiaomi credentials and session tokens live exclusively on your machine.\nSee [PRIVACY.md](PRIVACY.md) for the full statement.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdacrypt%2Fxiao","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdacrypt%2Fxiao","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdacrypt%2Fxiao/lists"}