--- feature: mijia-go-api status: delivered specs: - docs/compose/specs/2026-07-16-mijia-go-api-design.md plans: - docs/compose/plans/2026-07-16-mijia-go-api.md branch: none commits: none --- # mijia-api Go 重构 — Final Report ## What Was Built 项目提供纯 Go `mijia` 库,等价实现 Python mijia-api v4.1.2 的核心能力:小米账号二维码登录、passToken 静默刷新、RC4 加密 API 请求、家庭和设备查询、场景、耗材、属性读写、动作执行、统计数据,以及基于 MIoT spec 的高级设备封装。项目不包含 CLI、MCP、skills 或抓包解密工具。 客户端支持认证文件安全持久化、请求前在线 token 有效性缓存、每实例 CookieJar 隔离、并发认证快照和有界 gzip 响应读取。动态 API 字段通过 `Extra` 保留,属性大整数通过 `json.Number` 保持精度。 ## Architecture - `crypto.go` 实现 signed nonce、RC4-drop-1024、有序双重签名和响应解密。 - `auth.go` 与 `client.go` 实现认证文件、二维码流程、token 刷新、Cookie 和加密 HTTP 传输;`response.go` 统一限制原始与解压响应大小。 - `api.go` 与 `types.go` 提供家庭、设备、共享设备、场景、耗材、property、action 和 statistics API。 - `device.go` 获取、解析并缓存 MIoT spec,提供 `NewDevice`、`Get`、`Set` 和 `RunAction`。 ### Design Decisions - 保持 Python v4.1.2 的实际网络契约,因为目标是等价重构;包括共享设备的 `owner=true` 筛选、action 的 `value` 字段和耗材首分组行为。 - 单次请求使用同一认证快照,因为签名、Cookie 和响应解密不能混用不同代 token。 - 设备 spec 缓存同时兼容 Go 顶层标识和 Python `method` 格式,因为两个实现默认共享认证目录和缓存文件名。 - 整数规格校验使用精确十进制/有理数比较,因为 `float64` 无法安全表达 `2^53` 以上整数。 ## Usage 安装: ```bash go get git.misaka.ren/m1saka/mijia-go-api ``` 创建客户端并登录: ```go client, err := mijia.NewClient("") if err != nil { /* handle */ } auth, err := client.Login(ctx) ``` 底层控制使用 `GetDevices`、`GetProperties`、`SetProperties` 和 `RunActions`。高级控制使用 `NewDevice` 按 DID 或唯一名称选择设备,再调用 `device.Get`、`device.Set` 和 `device.RunAction`。完整示例和 option 名称见根目录 `README.md`。 认证文件默认位于 `~/.config/mijia-api/auth.json`,包含敏感 token,库以 `0600` 权限原子写入,不应提交到版本控制。 ## Verification - `gofmt -w *.go` - `go test -count=1 ./...`:通过 - `go vet ./...`:通过 - 独立规格审查和逐阶段代码质量审查均通过。 - 测试覆盖固定加密向量、gzip/大小限制、QR 与静默刷新、本地加密 HTTP 端点、分页异常、精确大整数、MIoT HTML 变体、Python 缓存迁移和 context 取消。 真实小米服务仍存在账号区域、Cookie 策略、限流和设备型号页面变化等集成风险;本地测试不使用真实账号或外网。 ## Journey Log > Brief notes on what informed the final design. Not required reading. - [lesson] 手动设置 `Accept-Encoding: gzip` 会关闭 Go Transport 自动解压,因此登录和 API 响应必须显式、有界解压。 - [pivot] 认证状态从公开可变字段改为深拷贝快照,避免并发请求混用 token 和外部 map 竞态。 - [lesson] IANA 时区存在负 DST(例如 Dublin),必须使用 `time.Time.IsDST()`,不能从 UTC offset 大小推断。 - [pivot] MIoT 数值校验改为精确有理数,避免大整数通过 `float64` 静默失真。 - [lesson] Python 与 Go 共用 spec 缓存路径时,格式迁移和语义校验属于实际兼容需求。 ## Source Materials | File | Role | Notes | |------|------|-------| | `docs/compose/specs/2026-07-16-mijia-go-api-design.md` | Initial design | 功能范围与协议约束 | | `docs/compose/plans/2026-07-16-mijia-go-api.md` | Implementation plan | 五阶段实施与验证计划 |