Files
mijia-go-api/docs/compose/specs/2026-07-16-mijia-go-api-design.md
2026-07-18 22:40:40 +08:00

3.9 KiB

mijia-api Go 重构设计(2026-07-16)

Note

This document may not reflect the current implementation. See the final report for up-to-date state: Final Report

[S1] 问题

将 Python 版 mijia-api(v4.1.2)重构为纯 Go 库,只保留 API 核心功能,排除 skills、MCP server、CLI、decrypt 调试脚本、文档站。

[S2] 方案概览

  • 项目形态:纯 Go 库,module 路径 git.misaka.ren/m1saka/mijia-go-api,包名 mijia,代码放仓库根目录。
  • 功能范围:完整底层 API + 高级设备封装(mijiaDevice 等价物)。
  • Go 版本:1.22+,尽量只用标准库;二维码终端输出用 github.com/mdp/qrterminal/v3

[S3] 包结构




/ (package mijia)
  crypto.go    ← miutils.py: GenNonce / SignedNonce / RC4-drop1024 加解密 / 签名 / GenerateEncParams(有序 KV)
  auth.go      ← 登录: QRLogin、passToken 静默刷新、auth.json 读写、UA/deviceId 生成
  client.go    ← Client 结构、session headers/cookies、统一加密请求 request()
  api.go       ← GetHomesList / GetDevicesList(分页) / GetSharedDevicesList / GetScenesList / RunScene /
                 GetConsumableItems / GetDevicesProp / SetDevicesProp / RunAction / GetStatistics / CheckNewMsg
  device.go    ← Device 高级封装: miot-spec 抓取解析+缓存、Get/Set(类型与 range 校验)/RunAction
  errors.go    ← ERROR_CODE 表 + 错误类型(LoginError/APIError/DeviceGetError/DeviceSetError 等)
  types.go     ← 请求/响应结构体

[S4] 认证与加密关键点

  1. 二维码扫码登录:account.xiaomi.com/pass/serviceLoginlongPolling/loginUrl → 终端二维码 → 长轮询(120s)→ 回调取 serviceToken;响应需去 &&&START&&& 前缀。
  2. passToken 有效时静默刷新 token;auth.json 保存 ua/deviceId/pass_o/ssecurity/serviceToken/passToken/userId/cUserId/expireTime(30 天)等。
  3. 加密请求(base https://api.mijia.tech/app,全部 POST form):
    • nonce = base64(8 字节随机 + 分钟时间戳字节);signedNonce = base64(SHA256(ssecurity||nonce))。
    • RC4 必须先丢弃 1024 字节 keystream(标准库 crypto/rc4 手动 drop)。
    • 双重签名:先对明文 params 签 rc4_hash__,RC4 加密后再签 signature;签名串顺序固定 POST&uri&data=..&rc4_hash__=..&signedNonce,Go 用有序 KV 切片而非 map。
    • data JSON 必须紧凑无空格。
    • 响应可能是 gzip 压缩后的 RC4 密文:先直接 JSON 解析,失败则解密,解密后 UTF-8 无效再 gzip 解压。
  4. Available():检查 auth 字段完备 + 调 check_new_msg 验证,结果缓存 60 秒。

[S5] API 方法映射

与 Python 版一一对应(URI、请求体 JSON 完全一致),注意:

  • GetDevicesList 分页(start_did/max_did,直到 has_more=false),home_id 为空时遍历所有家庭。
  • RunAction 逐条请求,prop get/set 批量。
  • 多数接口需 home 的 owner uid(从 GetHomesList 查)。
  • SetDevicesProp code==1 表示网关已接收但结果未知;非 0/1 查 ERROR_CODE 附中文消息。

[S6] 高级设备封装

NewDevice(api, did 或 name):

  • 从设备列表解析 model → GET https://home.miot-spec.com/spec/{model} 正则提取内嵌 JSON → 解析 services/properties/actions(siid/piid/aiid、类型、rw、range、value-list)→ 本地 JSON 缓存(auth.json 同目录)。
  • Get(name) / Set(name, value)(bool/int/float 类型转换、range 与枚举校验)/ RunAction(name, args...);属性名 -_ 互为别名。不做 Python 的动态属性语法糖。

[S7] 错误处理与测试

  • 错误:哨兵/自定义错误类型 + ERROR_CODE map(码→中文)。
  • 测试:crypto.go 的纯函数(nonce 格式、signedNonce、RC4-drop1024、签名串)用与 Python 实现对照生成的固定向量做单测;spec HTML 解析用本地样本。网络调用不做集成测试。

[S8] 排除项

mcp_server.py、skills/、docs/、decrypt/、__main__.py CLI、qrcode 图片生成(仅终端二维码)。