84 lines
4.6 KiB
Markdown
84 lines
4.6 KiB
Markdown
---
|
|
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` 以上整数。
|
|
- 内存认证的同步持久化回调保留在登录串行区内,以确保并发登录/刷新严格有序且回调失败时不安装候选认证。把回调移到所有内部锁外会允许旧回调在新认证提交后覆盖外部状态;票据或 worker 若同步等待,调用者持有回调所需锁时仍会形成 ABBA。因此公共契约明确要求回调和 Client 调用遵守锁顺序,而不是承诺任意应用锁安全。
|
|
- 文件客户端默认把设备 spec 缓存在认证文件目录;内存客户端默认不缓存,只有显式 `WithDeviceCacheDir` 才写入 spec 文件。
|
|
|
|
## 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 ./...`:通过
|
|
- `go test ./... -count=20`:通过
|
|
- `go test -race ./...`:通过
|
|
- 独立规格审查和逐阶段代码质量审查均通过。
|
|
- 测试覆盖固定加密向量、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 | 五阶段实施与验证计划 |
|