roborock(石头扫地机)
石头扫地机走局域网直连(AES-256-GCM 加密的私有协议),运行期完全不接触石头云。
接一台要两样东西:IP 和 local_key。
[[device]]name = "robot"adapter = "roborock"ip = "192.168.52.66"local_key = "${ROBOROCK_LOCAL_KEY}"consumable_every = 40local_key 必须带外获取
Section titled “local_key 必须带外获取”rha 不做石头云登录,crate 里连 HTTP 客户端都没有。这不是偷懒:那条路会因为「用户
协议版本」这类服务端状态整个卡死(实测先撞 2031 需要二次验证,再撞 3006 用户协议),
把它焊进守护进程等于引入一个不可控的外部故障源。
取法任选其一,都只做一次:
pipx run --spec python-roborock roborock login --email <你的邮箱>pipx run --spec python-roborock roborock list_devices # 取目标设备的 localKey或者从已在用的 Home Assistant Roborock 集成的配置项里读同一个值。登录被 3006 拒是
石头服务端要求重新接受用户协议——先在石头 App 里点一次确认再重试。
设备不重新配网,这个 key 就不会变。
key 错了的症状是「握手成功但所有命令失败」:握手载荷是空的、不加密,所以错的 key
也能连上。日志里是 payload decryption failed (wrong local_key?)——别误诊成网络问题。
IP 必须写死
Section titled “IP 必须写死”这台机器不发 UDP 发现广播(Mac 与 PVE 上各监听约 5 分钟,一条未收到),所以没有 miot 那种「按 device_id 自动重定位」的兜底。建议在路由器上给它做 DHCP 保留,否则租约 一漂就得改配置。
耗材按 consumable_every 分频
Section titled “耗材按 consumable_every 分频”主刷、边刷、滤网寿命是小时级变化的量,没必要跟状态一起每 15 秒问一遍。所以耗材单独
分频:每 consumable_every 轮状态轮询才拉一次,缺省 40 轮 ≈ 10 分钟。
写 0 会在 check 阶段直接报 consumable_every must be >= 1——不拦的话它会变成「无声地
永不拉耗材」,那些实体永远没有值。
实体集是固定的
Section titled “实体集是固定的”describe() 必须离线(rha check 不允许探测设备),所以实体表写死在代码里,不像 miot
那样从 spec.d 读:
- 传感器:
state(字符串状态名)、battery、error、clean_area、clean_time、main_brush_life、side_brush_life、filter_life - 可写:
action("clean"/"pause"/"stop"/"dock")、fan_power、water_box_mode、clean_room
action 的值是命令词字符串,不是布尔:
[[rule]]name = "clean_at_9am"trigger = { cron = "0 9 * * *" }action = { entity = "robot.action", set = "clean" }未知的命令词一律报错,绝不静默忽略——静默忽略会让规则引擎收到「成功」却什么都没发生。
这台机器上只有 battery 有跨生态的语义类:action / fan_power / water_box_mode /
clean_room 虽然可写,但值是命令词或档位整数而不是布尔,任何 class 都表达不了,因此
它们导不进 HomeKit。
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
consumable_every | integer | 40 | 每多少轮状态轮询才拉一次耗材。耗材是小时级变化的量。 |
ip* | string | — | 扫地机在局域网里的地址, 必须是字面 IP: 主机名/mDNS 名过不了 与 miot 不同, 这里没有"配了 device_id 就自动重新定位"那一层(见
|
local_key* | string | — | 敏感值。建议写成 ${ENV} 由环境变量插值, 不要把明文提交进配置文件。该值绝不进日志与 trace。 |
max_timeouts | integer | 3 | 连续多少轮轮询超时后放弃(默认 3), 交给 supervise 标 unavailable + 指数退避重启。 只数超时(连着但不回话)。传输层断开不计入 —— 设备本来就会隔几分钟主动断一次, 那条路径是内部透明重连, 属常态而非故障。任何一轮成功都会把计数清零。 因此从设备真的失联到实体变 unavailable, 大约要 |
poll_ms | integer | 15000 | 状态轮询间隔, 默认 15 秒。一"轮"就是一次 它同时是 往大调是安全的: 连接保活是独立的 10 秒 PING(设备约 4.5 分钟收不到包就会主动 断连), 不靠状态轮询撑着。 |
poll_timeout_ms | integer | 5000 | 每一次 RPC 等响应的上限(毫秒), 默认 5000 —— 不只是状态轮询: 拉耗材、保活
PING、下发命令, 以及分区清扫前那次 调小了先坏的往往是命令: 设备执行慢一点就会以 |