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

69 lines
3.9 KiB
Markdown

# 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](../reports/mijia-go-api.md)
## [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/serviceLogin``longPolling/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 图片生成(仅终端二维码)。