- 新增 xiaomi/examples/ 下 8 个示例程序(用户/家庭查询、设备列表、开关/灯光/空调控制、属性订阅、高级过滤分类、SPEC 解析) - miot_client_sub.go: 重构 SubProp/SubEvent,使用 buildPropTopic/buildEventTopic 支持通配符订阅(siid/piid=0 → +),并修复锁顺序问题(将 RequestRefreshProp 移到 Lock 外) - spec_parser.go: 新增 downloadSpecFile 方法,本地 SPEC 文件缺失时自动从 miot-spec.org 下载 - ARCH_PLAN.md: 架构设计从 Proposed 更新为 Accepted(v1.0→v1.1),补充设备分类/工厂/SPEC 映射等模块设计 - xiaomi/: 新增 miot 上层强类型封装模块,包含 Client 主入口、用户/家庭/设备 API、属性读写、动作调用、订阅通知,以及 devices/ 设备控制抽象(Switch/Light/AirConditioner/Fan/Cover/Humidifier/Vacuum/WaterHeater/Thermostat)和 specs/ SPEC 查询辅助 - 更多 xiaomi 示例(风扇/窗帘/传感器控制)
320 lines
9.3 KiB
Markdown
320 lines
9.3 KiB
Markdown
# xiaomi 封装层示例程序规划
|
||
|
||
> 状态:Completed | 版本:v1.0 | 日期:2026-06-28 | 完成日期:2026-06-29
|
||
|
||
---
|
||
|
||
## 一、现状分析
|
||
|
||
### 已有示例
|
||
|
||
| 文件 | 状态 | 问题 |
|
||
|------|------|------|
|
||
| `xiaomi/examples/main.go` | 存在(224 行) | 几乎全是 `fmt.Println` 伪代码,非可运行的完整示例 |
|
||
|
||
### 目标
|
||
|
||
对标 `ARCH_PLAN.md` v1.1 的最终形态,覆盖完整 API 面,每个示例独立可理解、贴近实战。
|
||
|
||
---
|
||
|
||
## 二、示例架构
|
||
|
||
```
|
||
xiaomi/examples/
|
||
├── 01_user_home/ # 用户 + 家庭 + 房间信息
|
||
│ └── main.go
|
||
├── 02_device_list/ # 设备列表 + 过滤
|
||
│ └── main.go
|
||
├── 03_switch_control/ # 开关控制
|
||
│ └── main.go
|
||
├── 04_light_control/ # 灯光控制
|
||
│ └── main.go
|
||
├── 05_ac_control/ # 空调控制
|
||
│ └── main.go
|
||
├── 06_fan_cover/ # 风扇 + 窗帘控制
|
||
│ └── main.go
|
||
├── 07_prop_subscribe/ # 属性/事件/状态订阅
|
||
│ └── main.go
|
||
├── 08_adv_filter_classify/ # 高级过滤 + 分类 + 工厂
|
||
│ └── main.go
|
||
├── 09_spec_parser/ # SPEC 解析器独立使用(已有完整代码)
|
||
│ └── main.go
|
||
└── config/ # 共享配置模块(敏感信息统一管理)
|
||
└── config.go
|
||
```
|
||
|
||
---
|
||
|
||
## 三、各示例详述
|
||
|
||
### 01_user_home — 用户/家庭/房间信息
|
||
|
||
**覆盖 API**:
|
||
- `client.GetUserInfo(ctx)` → `*UserInfo`
|
||
- `client.GetHomeList(ctx)` → `[]*HomeInfo`(含经纬度、地址)
|
||
- `client.GetHome(ctx, homeID)` → `*HomeInfo`
|
||
- `client.GetRooms(ctx, homeID)` → `[]*RoomInfo`
|
||
- `client.GetRoomDevices(ctx, homeID, roomID)` → `[]string`
|
||
|
||
**演示重点**:
|
||
- context.WithTimeout 控制超时
|
||
- HomeInfo 的地里位置字段(CityID, Longitude, Latitude, Address)
|
||
- for-range 遍历家庭→房间→设备层级结构
|
||
|
||
---
|
||
|
||
### 02_device_list — 设备列表与过滤
|
||
|
||
**覆盖 API**:
|
||
- `client.GetDevices(ctx, opts...)` → `[]*DeviceInfo`
|
||
- `client.GetDevice(ctx, did)` → `*DeviceInfo`
|
||
- `client.RefreshDevices(ctx)` → error
|
||
- `FilterByHome` / `FilterByRoom` / `FilterByOnline` / `FilterByModel` / `FilterByKind`
|
||
- `DeviceInfo.DeviceKind()` 分类
|
||
|
||
**演示重点**:
|
||
- 多种过滤组合(在线+客厅、按型号、按种类)
|
||
- DeviceKind() 输出
|
||
- RefreshDevices 刷新时机
|
||
|
||
---
|
||
|
||
### 03_switch_control — 开关控制
|
||
|
||
**覆盖 API**:
|
||
- `devices.Create(client, info)` 工厂 → `Switch`
|
||
- `Switch.{TurnOn, TurnOff, Toggle, IsOn}(ctx)`
|
||
- `Switch.OnStateChanged(handler)` 状态订阅
|
||
|
||
**演示重点**:
|
||
- 工厂创建单设备
|
||
- TurnOn/TurnOff/Toggle 完整链路
|
||
- IsOn 状态查询
|
||
- OnStateChanged 回调演示
|
||
|
||
---
|
||
|
||
### 04_light_control — 灯光控制
|
||
|
||
**覆盖 API**:
|
||
- `Light.{TurnOn, TurnOff, Toggle}(ctx)`
|
||
- `Light.{SetBrightness, GetBrightness}(ctx, pct)`
|
||
- `Light.{SetColorTemp, GetColorTemp}(ctx, kelvin)`
|
||
- `Light.{SetColor, GetColor}(ctx, r, g, b)`
|
||
- `Light.OnStateChanged(handler)`
|
||
|
||
**演示重点**:
|
||
- 亮度 0-100 调节
|
||
- 色温 2700K-6500K 调节
|
||
- RGB 颜色控制
|
||
- 不支持功能的降级处理(`GetBrightness` 返回 -1 → 跳过)
|
||
|
||
---
|
||
|
||
### 05_ac_control — 空调控制
|
||
|
||
**覆盖 API**:
|
||
- `AirConditioner.{TurnOn, TurnOff, IsOn}(ctx)`
|
||
- `SetMode(ctx, ACMode)` / `GetMode(ctx)` — cool/heat/dry/fan/auto
|
||
- `SetTargetTemp(ctx, celsius)` / `GetTargetTemp(ctx)` / `GetCurrentTemp(ctx)`
|
||
- `SetFanSpeed(ctx, FanSpeed)` / `GetFanSpeed(ctx)` — low/medium/high/auto
|
||
- `SetSwing(ctx, SwingMode)` / `GetSwing(ctx)` — off/vertical/horizontal/both
|
||
- `SetTargetHumidity(ctx, pct)` / `GetTargetHumidity(ctx)` / `GetCurrentHumidity(ctx)`
|
||
- `SetAuxHeat(ctx, on)` / `GetAuxHeat(ctx)`
|
||
- `SetEco(ctx, on)` / `GetEco(ctx)`
|
||
- `OnStateChanged(handler)` → `ACState`
|
||
|
||
**演示重点**:
|
||
- 完整空调控制链路(开→设制冷→设26°C→自动风速→垂直摆风)
|
||
- OnStateChanged 实时状态回调
|
||
- 不支持的功能优雅降级(如无湿度传感器,跳过湿度设置)
|
||
|
||
---
|
||
|
||
### 06_fan_cover — 风扇 + 窗帘控制
|
||
|
||
**覆盖 API**:
|
||
- `Fan.{TurnOn, TurnOff, SetSpeed, SetOscillation, SetNaturalWind}(ctx)`
|
||
- `Cover.{Open, Close, Stop, SetPosition, GetPosition}(ctx)`
|
||
|
||
**演示重点**:
|
||
- 风扇 4 档调速 + 摇头 + 自然风切换
|
||
- 窗帘百分比定位(0%=关, 100%=全开, 50%=半开)
|
||
- Cover 的 Open/Close/Stop 语义对比 SetPosition
|
||
|
||
---
|
||
|
||
### 07_prop_subscribe — 属性/事件/状态订阅
|
||
|
||
**覆盖 API**:
|
||
- `client.SubProp(did, siid, piid, PropHandler)` → subID
|
||
- `client.UnsubProp(did, subID)`
|
||
- `client.SubEvent(did, siid, eiid, EventHandler)` → subID
|
||
- `client.UnsubEvent(did, subID)`
|
||
- `client.SubDeviceState(did, DeviceStateHandler)`
|
||
- `client.UnsubDeviceState(did)`
|
||
|
||
**演示重点**:
|
||
- 订阅前后属性值对比
|
||
- 等待N秒展示属性/事件推送
|
||
- 取消订阅后不再收到回调
|
||
- 设备上下线状态监听
|
||
|
||
---
|
||
|
||
### 08_adv_filter_classify — 高级过滤 + 分类 + 工厂模式
|
||
|
||
**覆盖 API**:
|
||
- `devices.Classify(info)` → `Kind` 枚举
|
||
- `FilterByKind(kind)` 过滤器
|
||
- `devices.Create(client, info)` 工厂 → 各设备接口
|
||
- `devices.Kind` 所有枚举值
|
||
|
||
**演示重点**:
|
||
- 获取全部设备 → 按 Kind 分类 → 统计各类型数量
|
||
- 工厂批量创建:遍历 DeviceInfo → Create → type switch 分流
|
||
- 不支持的设备返回 nil → continue 跳过
|
||
- 输出表格:"设备名 | 型号 | 种类 | 状态"
|
||
|
||
---
|
||
|
||
### 09_spec_parser — SPEC 解析器独立使用
|
||
|
||
**覆盖 API**:
|
||
- specs 包全部公开 API(如果 specs 是公开的)
|
||
- SPEC 属性/事件/动作的枚举和查询
|
||
|
||
**演示重点**:
|
||
- 无需设备在线即可解析 SPEC
|
||
- 打印设备有哪些属性(text/switch/select/number/sensor)
|
||
- 打印设备有哪些事件和动作
|
||
|
||
---
|
||
|
||
## 四、共享配置模块 `config/config.go`
|
||
|
||
```go
|
||
package config
|
||
|
||
type Config struct {
|
||
ClientID string
|
||
ClientSecret string
|
||
AccessToken string // OAuth token(从环境变量或文件读取)
|
||
}
|
||
|
||
// LoadFromEnv 从环境变量加载配置
|
||
func LoadFromEnv() (*Config, error)
|
||
|
||
// LoadFromFile 从 JSON 文件加载配置
|
||
func LoadFromFile(path string) (*Config, error)
|
||
```
|
||
|
||
**设计原则**:
|
||
- 所有示例复用一个 config 加载逻辑,避免重复
|
||
- 仅 3 个字段,对应 xiaomi.Config
|
||
- 敏感信息从环境变量或文件读取,不硬编码
|
||
|
||
---
|
||
|
||
## 五、编码规范
|
||
|
||
### 5.1 统一约定
|
||
|
||
```
|
||
1. 每个示例的 main() 必须有 context.WithTimeout(30s)
|
||
2. 所有 IO 操作带 ctx
|
||
3. 错误必须处理(不忽略 err)
|
||
4. defer cancel() 放在 func main() 顶部
|
||
5. 日志用 log 包,不用 fmt.Print(方便区分日志级别)
|
||
6. 每个示例一句话说明功能(注释在 main 函数上方)
|
||
```
|
||
|
||
### 5.2 模板骨架
|
||
|
||
```go
|
||
// Package main demonstrates <功能简述>.
|
||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"log"
|
||
"time"
|
||
|
||
"xiaomihome/xiaomi"
|
||
"xiaomihome/xiaomi/devices"
|
||
"xiaomihome/xiaomi/examples/config"
|
||
)
|
||
|
||
func main() {
|
||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||
defer cancel()
|
||
|
||
// 1. 加载配置
|
||
cfg, err := config.LoadFromEnv()
|
||
if err != nil {
|
||
log.Fatal("配置加载失败:", err)
|
||
}
|
||
|
||
// 2. 创建客户端(零 miot 引用)
|
||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||
ClientID: cfg.ClientID,
|
||
ClientSecret: cfg.ClientSecret,
|
||
AccessToken: cfg.AccessToken,
|
||
})
|
||
if err != nil {
|
||
log.Fatal("创建客户端失败:", err)
|
||
}
|
||
defer client.Close()
|
||
|
||
// 3. 业务逻辑(根据示例不同)
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、与 ARCH_PLAN.md 的对应关系
|
||
|
||
| ARCH_PLAN.md 目标 | 对应示例 |
|
||
|-------------------|---------|
|
||
| 用户/家庭/房间信息(GetUserInfo / GetHomeList) | 01_user_home |
|
||
| 设备列表 + Filter | 02_device_list |
|
||
| Switch 控制 + OnStateChanged | 03_switch_control |
|
||
| Light 控制 | 04_light_control |
|
||
| AirConditioner 完整控制 | 05_ac_control |
|
||
| Fan / Cover 控制 | 06_fan_cover |
|
||
| SubProp / SubEvent / SubDeviceState 订阅 | 07_prop_subscribe |
|
||
| Classify / FilterByKind / Create 工厂 | 08_adv_filter_classify |
|
||
| SPEC 解析器 | 09_spec_parser |
|
||
| context.Context + 错误处理 | 全部示例 |
|
||
|
||
---
|
||
|
||
## 七、实施计划
|
||
|
||
| 序号 | 示例 | 文件 | 优先级 | 复杂度 |
|
||
|------|------|------|--------|--------|
|
||
| 0 | config 共享模块 | `config/config.go` | P0 | 小 |
|
||
| 1 | 用户/家庭/房间 | `01_user_home/main.go` | P0 | 小 |
|
||
| 2 | 设备列表+过滤 | `02_device_list/main.go` | P0 | 中 |
|
||
| 3 | 开关控制 | `03_switch_control/main.go` | P0 | 小 |
|
||
| 4 | 灯光控制 | `04_light_control/main.go` | P0 | 中 |
|
||
| 5 | 空调控制 | `05_ac_control/main.go` | P0 | 大 |
|
||
| 6 | 风扇+窗帘 | `06_fan_cover/main.go` | P1 | 中 |
|
||
| 7 | 订阅通知 | `07_prop_subscribe/main.go` | P1 | 大 |
|
||
| 8 | 高级过滤+工厂 | `08_adv_filter_classify/main.go` | P1 | 中 |
|
||
| 9 | SPEC 解析 | `09_spec_parser/main.go` | P2 | 小 |
|
||
| — | 统一 README | `examples/README.md` | P2 | 小 |
|
||
|
||
---
|
||
|
||
## 八、与现有 main.go 的关系
|
||
|
||
现有 `xiaomi/examples/main.go` 将被**替换**为上述 9 个独立示例。如果作为归档保留,可移到 `examples/_archive/main_old.go`。
|
||
|
||
---
|
||
|
||
*本文档已全部实施完成。*
|