Files
xiaomihome/xiaomi/README.md
T
4566704 8ffc9e4e44 refactor: 重构 token 管理,新增 NATS adapter 和多示例
按照 TOKEN_REFACTOR.md / FIX_CONFIG.md 方案重构核心层:
- miot: Init() 优先读 storage token,新增 RefreshAuthInfo() 和刷新回调
- xiaomi: Config 改用 AuthInfo 结构体(含 UUID/UID/DataDir/OnTokenRefreshed)
- xiaomi: NewClient(ctx, Config) 新签名,新增 RefreshToken(),后台自动刷新
- xiaomi/devices: 设备控制接口重构
- 新增示例 12_ac_subscribe,其余示例适配新 Config
2026-06-30 12:16:49 +08:00

360 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# xiaomi — Xiaomi 智能家居 Go SDK 高级封装层
`xiaomihome/xiaomi` 是 `xiaomihome/miot` 的强类型上层封装,将底层 `map[string]interface{}` API 转换为主流 Go 风格的 struct + interface,并提供设备控制抽象,隐藏 siid/piid 细节。
## 快速开始
```go
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`)
```go
client := xiaomi.NewClient(inner *miot.MIoTClient, httpClient *miot.MIoTHttpClient)
client.Inner() // 返回底层 MIoTClient
client.HTTPClient() // 返回底层 MIoTHttpClient
```
### 用户信息 (`user.go`)
```go
user, err := client.GetUserInfo()
// → *UserInfo{UID, NickName, AvatarURL}
```
### 家庭/房间 (`homes.go`)
```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`)
```go
devices, err := client.GetDevices(opts...) // → []*DeviceInfo
device, err := client.GetDevice(did) // → *DeviceInfo
err := client.RefreshDevices() // 强制刷新设备列表
```
**过滤器(组合使用):**
```go
// 只获取在线设备
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`)
```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`)
```go
result, err := client.Action(did, siid, aiid, params) // → *ActionResult
```
### 订阅通知 (`subscribe.go`)
```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`)
所有设备控制对象共享的基础方法:
```go
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`)
```go
sw, _ := devices.NewSwitch(client, info)
sw.TurnOn()
sw.TurnOff()
sw.Toggle()
on, _ := sw.IsOn()
// 订阅状态变化
sw.OnStateChanged(func(on bool) { ... })
```
#### 灯光 (`Light`)
```go
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`)
```go
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`)
```go
fan, _ := devices.NewFan(client, info)
fan.TurnOn() / TurnOff()
fan.SetFanLevel(2) // 1-4 档位
fan.SetOscillation(true) // 摇头
fan.SetNaturalWind(true) // 自然风
```
#### 窗帘 (`Cover`)
```go
cover, _ := devices.NewCover(client, info)
cover.Open() / Close() / Stop()
cover.SetPosition(50) // 0=全关, 100=全开
pos, _ := cover.GetPosition()
```
#### 加湿器 (`Humidifier`)
```go
h, _ := devices.NewHumidifier(client, info)
h.TurnOn() / TurnOff()
h.SetTargetHumidity(60)
h.SetFanLevel(2)
```
#### 扫地机 (`Vacuum`)
```go
v, _ := devices.NewVacuum(client, info)
v.Start() / Stop()
v.ReturnToDock()
v.SetFanLevel(3) // 吸力档位
status, _ := v.GetStatus() // → VacuumStatus{On, Mode, FanLevel, Battery}
```
#### 热水器 (`WaterHeater`)
```go
wh, _ := devices.NewWaterHeater(client, info)
wh.TurnOn() / TurnOff()
wh.SetTargetTemp(45.0)
wh.GetCurrentTemp()
```
#### 温控器 (`Thermostat`)
```go
t, _ := devices.NewThermostat(client, info)
t.TurnOn() / TurnOff()
t.SetTargetTemp(22.0)
t.SetHeating(true)
```
#### 工厂函数 (`factory.go`)
自动根据设备 URN 创建对应的控制接口:
```go
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:
```go
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` 判断:
```go
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 方式:
```go
// 读取空调开关 (常见 siid=2, piid=1)
prop, _ := client.GetProp("did", 2, 1)
// 写入目标温度
client.SetProp("did", 2, 3, 26.0)
```
但强烈推荐使用设备控制抽象,它会自动从 SPEC 解析正确的 siid/piid。
## 完整示例
参见 [`examples/main.go`](examples/main.go)。