andrewlyeats/roborock-q10-cli

GitHub: andrewlyeats/roborock-q10-cli

一个非官方的 Roborock Q10 扫地机器人 CLI 工具及逆向工程协议文档,解决了从终端和脚本直接驱动纯云端设备的需求。

Stars: 1 | Forks: 0

# Roborock Q10 (B01) — 协议笔记 + 可用的 CLI ![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg) ![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg) ![status: unofficial](https://img.shields.io/badge/status-unofficial-orange.svg) 这是对 Roborock Q10 S5+ 如何与 Roborock 云端通信进行逆向工程的笔记,并附带了一个 CLI (`vac.py`)作为其可行的证明。Q10 S5+ 是一款 **“B01”设备:仅限云端**,没有局域网 控制路径;每一条命令都是通过 Roborock 的 MQTT broker(或其 REST API)中转的。 **只想使用你的扫地机器人?** [Home Assistant Roborock 集成][ha] 可能已经 通过 GUI 覆盖了它。这个代码库是为那些想要**理解或扩展 B01 协议**, 或者从终端 / cron / 自己的脚本中驱动扫地机器人的人准备的。 **状态:** 一个个人项目,按原样分享。非官方,无保修,不承诺提供支持 — 请参阅 下方的免责声明。 ### 本文档所涵盖的内容(值得阅读的部分) - **B01 地图格式** — 房间网格是 LZ4 压缩的;路径/位置以 protocol-301 帧的形式到达。端到端解码为带有标签的平面图 — 从帧头读取网格尺寸, 并且每次地图的路径↔网格配准会自动适配(原点并未在数据流中传输)。 → [DESIGN_NOTES.md](DESIGN_NOTES.md),[`decode_map.py`](decode_map.py) - **云端写入路径** — 房间清扫和计划任务写入通过 REST `/jobs` 调用进行,该调用需要 **Hawk *body* 签名**;如果弄错了,看起来完全就像“写入不起作用 / token 范围不对”, 但事实并非如此。→ [DESIGN_NOTES.md](DESIGN_NOTES.md) - **单连接 daemon** — 保持一个 MQTT 连接来处理所有命令,以避免触发 账户级别的 `135` 速率限制,否则该限制会同时锁定 CLI *和* App。→ [DESIGN_NOTES.md](DESIGN_NOTES.md) - **B01 数据点字典** — 每个数据点的含义及其 payload 如何解码(机器人 发出约 44 个;在 `status` 中仅建模了约 19 个)。→ [DP_DICTIONARY.md](DP_DICTIONARY.md) 已验证与仍待解决的内容范围详见 [CAPABILITIES.md](CAPABILITIES.md)(支持 / 不支持 / 未知)和 [ROADMAP.md](ROADMAP.md)。 ## ⚠️ 免责声明 这是一个**非官方的**、社区逆向工程的工具 — **不附属于、不受认可,也不受** Roborock 支持。它通过 Roborock 云端与你自己的账户通信,并依赖于厂商随时可能更改的未公开 内部机制。**按原样提供,无任何保修,使用风险自负。** 命令是可逆的,并且该项目倾向于安全(例如 `clean-rooms --dry-run` 会发布一个 *已禁用* 的任务),但你要对自己的设备和账户负责 — 不要在无法承受干扰的硬件或 账户上运行它。 ## 已测试的硬件 | 项目 | 已测试 | |---|---| | 型号 | Roborock Q10 S5+ (`roborock.vacuum.ss07`,B01 协议) | | 固件 | **最后针对 03.11.24 验证** (2026-06) | | Python | 3.11 (需要 3.11+) · `python-roborock` 5.14.x | 其他 Roborock 型号**未经测试** — 它们可能共享 B01 协议(在这种情况下,大部分内容 应该可行)或者有所不同。欢迎提供其他型号的报告([CONTRIBUTING.md](CONTRIBUTING.md))。 固件更新可能会与此处记录的内容产生偏差。 ## 设置 需要 Python **≥3.11** 以及 `requirements.txt` 中的依赖(python-roborock,lz4,Pillow): `pip install -r requirements.txt`(或使用 `requirements.lock.txt` 获取确切已知良好的锁定版本)。 首次认证(仅一次): ``` ./vac.py login --email you@example.com # emails you a 6-digit code ./vac.py discover # fetches + caches your device list ``` `login` 将 token 保存到 `~/.roborock_vac.json`(已被 gitignore;schema 见 [`credentials.example.json`](credentials.example.json));`discover` 将设备/家庭数据缓存到 `~/.roborock_vac_cache.pkl`,以便后续调用无需重新请求云端。 ## Daemon 一个小型后台 daemon 会保持**一个**持久的云端连接,并通过 本地 socket 处理每一条命令,因此命令不会每次都重新连接(也不会触发 `135`)。当有 daemon 运行时,这是 默认路径;如果没有,命令会提示如何启动它。它已**通过实时验证** — 一个保持的 连接在不重新连接的情况下处理了读取、监听和长达一小时的清扫。(具体机制 — 逐步升级的 退避、`--careful` 停止文件、自动 `135` 恢复状态 — 详见 [DESIGN_NOTES.md](DESIGN_NOTES.md)。) ``` ./vac.py daemon start --careful # recommended: holds one connection, stops on the first 135/auth complaint ./vac.py daemon status # device, health, last update, taps ./vac.py daemon stop ./vac.py daemon restart # e.g. after `pip install -U python-roborock` ./vac.py status --force # run ONE command standalone (own session; avoid repeating) ``` **遥测监听**(daemon 可以看到整个数据流,因此捕获在其中进行 — 可选,默认关闭): ``` ./vac.py daemon record --events ev.jsonl # every decoded data-point ./vac.py daemon record --novel new.jsonl # first-seen DP names (catch new behaviors) ./vac.py daemon record --bytes raw.jsonl # raw frames (incl. binary/map) ./vac.py daemon record --off ``` ## 使用方法 日常命令: ``` ./vac.py status # battery, state, fan, water, mode, clean time/area (+--json) ./vac.py start | pause | resume | stop | dock | dock-empty | find ./vac.py rooms # list rooms on the current map (id + name) ./vac.py clean-rooms kitchen study # clean only those rooms (full cycle; +--fan/--water/--route/--count) ./vac.py fan turbo # quiet | balanced | turbo | max | max_plus ./vac.py water high # off | low | medium | high ./vac.py mode vac_and_mop # vac_and_mop | vacuum | mop ./vac.py consumables # brush/filter/sensor life counters (+--json) ./vac.py dnd on --start 22:00 --end 08:00 # also: dnd off # Cloud schedules(通过 REST API 存储在服务器端): ./vac.py schedule list # id · time · rooms · fan/water ./vac.py schedule add --time 09:00 --days mon,wed,fri --rooms kitchen study ./vac.py schedule enable|disable|delete ``` 捕获与解码(逆向工程接口): ``` ./vac.py watch # live table of the modeled status fields (--out clean.csv for CSV) ./vac.py watch --raw --out clean.jsonl # EVERY decoded data-point, one JSON object per line ./vac.py map # render the labeled floor plan -> map_rooms.png (+ map_path.svg if cleaning) ./vac.py watch --bytes --out cap.jsonl && ./decode_map.py cap.jsonl # low-level: byte capture -> offline decode ./vac.py history --from-capture clean.jsonl # decode the per-clean back-catalog from a capture ./vac.py raw STATUS # send any raw B01 data-point (run `raw BADNAME` to list them) ``` 有多个机器人?添加 `--device `(通过 `./vac.py discover` 获取 DUID)。大多数 B01 命令是 即发即忘的(无响应主体);对于没有专用命令的功能,`raw` 是逃生舱口。 ## 文件 | 路径 | 用途 | |---|---| | `vac.py` | CLI | | `decode_map.py` | 从 `watch --bytes` 捕获中解码实时地图/路径(包括网格地理配准) | | `check_roborock_api.py` | 金丝雀测试:验证此工具依赖的 `python-roborock` 内部组件仍然存在(升级后运行) | | `clean.csv` | 示例 `watch --out` 输出(一次拖地运行;无害的遥测数据,无 PII) | | [CAPABILITIES.md](CAPABILITIES.md) | 每一项交互,范围明确:支持 / 不支持 / 未知 | | [DP_DICTIONARY.md](DP_DICTIONARY.md) | 每个数据点的含义 + 解码格式 | | [DESIGN_NOTES.md](DESIGN_NOTES.md) | 为什么以这种方式工作 + 逆向工程发现 | | [ROADMAP.md](ROADMAP.md) | 哪些可行,哪些已计划,已知的限制 | | `credentials.example.json` | `login` 写入 `~/.roborock_vac.json` 的登录文件 schema | | `~/.roborock_vac.json` · `~/.roborock_vac_cache.pkl` | 登录 token · 缓存的家庭数据(已 gitignore) | ## 已知限制 - **仅限云端。** 此型号(B01 协议)无本地控制。 - **地图。** `vac.py map` 渲染房间网格(颜色编码,带有房间名称标签)。即使在回充时,网格也会继续 流式传输;清扫路径 + 实时位置仅在清扫*期间*流式传输。地理配准: 网格尺寸来自帧头,并且每次捕获的路径↔网格原点(未在流中传输) 都会自动适配 → `map_overlay.png`。障碍物仅限云端(不在此数据中) — 该 库本身并不提供这些内容。 - **房间清扫**(`clean-rooms`)会为指定的房间发起一次性的 REST `/jobs` 清扫; `--dry-run` 会发布一个无效的*已禁用*任务。一个完整的周期(出仓 → 清扫 → 回充 → 充电)已 通过实时验证。任务会在大约 2 分钟后触发(已计划,并非立即执行)。详见 [DESIGN_NOTES.md](DESIGN_NOTES.md)。 - **结构化地图变更为只读** — 虚拟墙、禁扫/禁拖区、房间 拆分/合并/重命名可以解码,但无法从这里进行设置。 - **某些设置以云端为准**(音量 / 童锁 / 增强 / 勿扰) — 写入可能会回滚; 请在 App 中更改这些设置。运行时设置(风机/水量/模式)会保留。 - **耗材**显示已使用小时数 + 剩余百分比%(已与 App 确认:主刷 300 小时 / 边刷 200 小时 / 滤网 150 小时)。 ## 构建于 / 相关项目 这是一个建立在他人工作基础之上的非官方 CLI + daemon — 完整致谢见 [CREDITS.md](CREDITS.md): - [python-roborock][pr] — 此项目依赖的库。 - [Home Assistant Roborock 集成][ha] — daemon 所遵循的单连接协调器模式。 - [local_roborock_server](https://github.com/Python-roborock/local_roborock_server) / [Valetudo](https://github.com/Hypfer/Valetudo) — 如果你想要一个完整的*本地*云端替代方案。 - [XiaomiRobotVacuumProtocol](https://github.com/marcelrv/XiaomiRobotVacuumProtocol), [dustcloud](https://github.com/dgiese/dustcloud) — 协议逆向工程的脉络。 ## 贡献与许可 欢迎做出贡献 — 特别是来自其他 Roborock 型号的报告 ([CONTRIBUTING.md](CONTRIBUTING.md))。在对真实的机器人进行测试之前,请阅读上方的 **温和对待云端** 说明以及 [DESIGN_NOTES.md](DESIGN_NOTES.md)。基于 [MIT 许可证](LICENSE) 授权。 ## 在 AI 辅助下构建 在人类指导下使用 AI 编码助手(Anthropic 的 Claude Opus 4.8 和 Claude Sonnet 4.6)开发 — 包括代码、逆向工程和文档。人类审查了这项工作,并在真实设备上运行了所有的实时 测试。详见 [CREDITS.md](CREDITS.md)。
标签:Python, 无后门, 智能家居, 机器人, 物联网, 逆向工具