- 新增 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 示例(风扇/窗帘/传感器控制)
xiaomi — Xiaomi 智能家居 Go SDK 高级封装层
xiaomihome/xiaomi 是 xiaomihome/miot 的强类型上层封装,将底层 map[string]interface{} API 转换为主流 Go 风格的 struct + interface,并提供设备控制抽象,隐藏 siid/piid 细节。
快速开始
import (
"xiaomihome/miot"
"xiaomihome/xiaomi"
"xiaomihome/xiaomi/devices"
)
// 1. 创建底层 miot 客户端
miotClient := miot.NewMIoTClient("phone", entryData, "", "cn", miot.CtrlModeAuto)
miotClient.Init()
// 2. 创建 HTTP 客户端
httpClient, _ := miot.NewMIoTHttpClient(cloudServer, clientID, accessToken)
// 3. 包装为 xiaomi.Client
client := xiaomi.NewClient(miotClient, httpClient)
API 参考
Client 主入口 (client.go)
client := xiaomi.NewClient(inner *miot.MIoTClient, httpClient *miot.MIoTHttpClient)
client.Inner() // 返回底层 MIoTClient
client.HTTPClient() // 返回底层 MIoTHttpClient
用户信息 (user.go)
user, err := client.GetUserInfo()
// → *UserInfo{UID, NickName, AvatarURL}
家庭/房间 (homes.go)
homes, err := client.GetHomeList() // → []*HomeInfo
home, err := client.GetHome(homeID) // → *HomeInfo (含房间列表)
rooms, err := client.GetRooms(homeID) // → []*RoomInfo
dids, err := client.GetRoomDevices(homeID, roomID) // → []string
HomeInfo 结构:
| 字段 | 类型 | 说明 |
|---|---|---|
| ID | string |
家庭 ID |
| Name | string |
家庭名称 |
| UID | string |
用户 ID |
| GroupID | string |
MIPS 分组 ID |
| Rooms | []RoomInfo |
房间列表 |
| DidList | []string |
所有设备 DID |
| ShareState | bool |
是否为共享家庭 |
设备列表 (devices.go)
devices, err := client.GetDevices(opts...) // → []*DeviceInfo
device, err := client.GetDevice(did) // → *DeviceInfo
err := client.RefreshDevices() // 强制刷新设备列表
过滤器(组合使用):
// 只获取在线设备
client.GetDevices(xiaomi.FilterByOnline())
// 指定家庭 + 在线
client.GetDevices(
xiaomi.FilterByHome("home001"),
xiaomi.FilterByOnline(),
)
// 指定房间
client.GetDevices(xiaomi.FilterByRoom("room001"))
// 指定型号
client.GetDevices(xiaomi.FilterByModel("xiaomi.aircondition.c1"))
属性读写 (properties.go)
prop, err := client.GetProp(did, siid, piid) // → *PropertyValue
err := client.SetProp(did, siid, piid, value)
props, err := client.GetProps([]PropKey{...}) // → []*PropertyValue
results, err := client.SetProps([]PropKeyValue{...}) // → []*PropResult
Action 调用 (actions.go)
result, err := client.Action(did, siid, aiid, params) // → *ActionResult
订阅通知 (subscribe.go)
// 属性变化订阅
subID, err := client.SubProp(did, siid, piid, func(did string, prop *PropertyValue) {
fmt.Printf("属性变化: %v\n", prop.Value)
})
// 取消属性订阅
client.UnsubProp(did, subID)
// 事件订阅
subID, err := client.SubEvent(did, siid, eiid, func(did string, event *EventOccurred) {
fmt.Printf("事件: %v\n", event.Arguments)
})
client.UnsubEvent(did, subID)
// 设备在线状态订阅
client.SubDeviceState(did, func(did string, state xiaomi.DeviceState) {
if state == xiaomi.StateOnline {
fmt.Println("设备上线")
}
})
client.UnsubDeviceState(did)
设备控制抽象 (devices/)
基础能力 (BaseDevice)
所有设备控制对象共享的基础方法:
d.DID() // 设备 ID
d.Name() // 设备名称
d.Model() // 设备型号
d.Online() // 是否在线
d.Info() // 完整设备信息 *DeviceInfo
d.GetProp(siid, piid) // 读取属性
d.SetProp(siid, piid, value) // 写入属性
d.Action(siid, aiid, params) // 调用 Action
d.SubProp(siid, piid, handler) // 订阅属性变化
d.SubDeviceState(handler) // 订阅在线状态
开关 (Switch)
sw, _ := devices.NewSwitch(client, info)
sw.TurnOn()
sw.TurnOff()
sw.Toggle()
on, _ := sw.IsOn()
// 订阅状态变化
sw.OnStateChanged(func(on bool) { ... })
灯光 (Light)
light, _ := devices.NewLight(client, info)
light.TurnOn() / TurnOff() / Toggle()
light.SetBrightness(80) // 1-100
b, _ := light.GetBrightness()
light.SetColorTemp(4000) // 2700-6500K
k, _ := light.GetColorTemp()
light.SetColor(255, 0, 0) // RGB 0-255
r, g, b, _ := light.GetColor()
light.OnStateChanged(func(state LightState) { ... })
空调 (AirConditioner)
ac, _ := devices.NewAirConditioner(client, info)
// 基础
ac.TurnOn() / TurnOff()
on, _ := ac.IsOn()
// 模式
ac.SetMode(devices.ModeCool) // Cool/Heat/Fan/Dry/Auto
mode, _ := ac.GetMode()
// 温度
ac.SetTargetTemp(26.0)
target, _ := ac.GetTargetTemp()
current, _ := ac.GetCurrentTemp()
// 风速
ac.SetFanSpeed(devices.SpeedAuto) // Low/Medium/High/Auto
speed, _ := ac.GetFanSpeed()
// 摆风
ac.SetSwing(devices.SwingBoth) // Off/Vertical/Horizontal/Both
swing, _ := ac.GetSwing()
// 湿度(部分支持)
ac.SetTargetHumidity(50)
ac.GetTargetHumidity()
ac.GetCurrentHumidity()
// 电辅热 / 节能(部分支持)
ac.SetAuxHeat(true)
ac.SetEco(true)
ac.OnStateChanged(func(state ACState) { ... })
风扇 (Fan)
fan, _ := devices.NewFan(client, info)
fan.TurnOn() / TurnOff()
fan.SetFanLevel(2) // 1-4 档位
fan.SetOscillation(true) // 摇头
fan.SetNaturalWind(true) // 自然风
窗帘 (Cover)
cover, _ := devices.NewCover(client, info)
cover.Open() / Close() / Stop()
cover.SetPosition(50) // 0=全关, 100=全开
pos, _ := cover.GetPosition()
加湿器 (Humidifier)
h, _ := devices.NewHumidifier(client, info)
h.TurnOn() / TurnOff()
h.SetTargetHumidity(60)
h.SetFanLevel(2)
扫地机 (Vacuum)
v, _ := devices.NewVacuum(client, info)
v.Start() / Stop()
v.ReturnToDock()
v.SetFanLevel(3) // 吸力档位
status, _ := v.GetStatus() // → VacuumStatus{Power, Mode, FanLevel, Battery}
热水器 (WaterHeater)
wh, _ := devices.NewWaterHeater(client, info)
wh.TurnOn() / TurnOff()
wh.SetTargetTemp(45.0)
wh.GetCurrentTemp()
温控器 (Thermostat)
t, _ := devices.NewThermostat(client, info)
t.TurnOn() / TurnOff()
t.SetTargetTemp(22.0)
t.SetHeating(true)
工厂函数 (factory.go)
自动根据设备 URN 创建对应的控制接口:
for _, info := range deviceList {
ctrl, err := devices.Create(client, info)
if err != nil || ctrl == nil {
continue
}
switch d := ctrl.(type) {
case devices.Switch:
d.TurnOn()
case devices.Light:
d.SetBrightness(80)
case devices.AirConditioner:
d.SetTargetTemp(26.0)
case devices.Fan:
d.SetFanLevel(2)
case devices.Cover:
d.Open()
case devices.Humidifier:
d.SetTargetHumidity(60)
case devices.Vacuum:
d.Start()
case devices.WaterHeater:
d.SetTargetTemp(45.0)
case devices.Thermostat:
d.SetTargetTemp(22.0)
}
}
SPEC 解析器 (specs/)
用于直接从设备 SPEC 中按功能语义查找 siid/piid:
resolver := specs.NewPropertyResolver(spec)
siid, piid, ok := resolver.FindByType("on") // 按属性类型
piid, ok := resolver.FindByFormat("bool", siid) // 按数据格式
siid, piid, ok := resolver.FindByDescription("开关") // 按描述
aiid, ok := resolver.FindAction(siid, "toggle") // 按 Action 类型
props := resolver.GetAllProperties(siid) // 获取某服务所有属性
prop := resolver.GetProperty(siid, piid) // 获取单个属性
错误处理
所有错误都实现了 Go 标准 error 接口,同时可通过 errors.Is 判断:
if errors.Is(err, xiaomi.ErrDeviceOffline) { ... }
if errors.Is(err, xiaomi.ErrNotSupported) { ... }
// ClientError 携带操作上下文
var ce *xiaomi.ClientError
if errors.As(err, &ce) {
fmt.Println("操作:", ce.Op, "设备:", ce.DID)
}
关于 siid/piid
如果你需要直接操作底层属性,xiaomi 封装层仍支持传统的 siid/piid 方式:
// 读取空调开关 (常见 siid=2, piid=1)
prop, _ := client.GetProp("did", 2, 1)
// 写入目标温度
client.SetProp("did", 2, 3, 26.0)
但强烈推荐使用设备控制抽象,它会自动从 SPEC 解析正确的 siid/piid。
完整示例
参见 examples/main.go。