**AC 驱动增强** - 支持 thermostat + air-conditioner + air-fresh 多服务三合一设备,属性跨服务合并解析 - 新增 specResolver.findValListInService/findInService/hasService/serviceShortName 方法 - 移除 KindThermostat,heater/electric-blanket/thermostat 重新归类为 KindAirConditioner **Cover 窗帘重构** - CoverProps 结构化:Position/Status/Fault 分离 - SetPosition 优先写 target-position,回退 current-position - OnPropsChanged/FetchProps 支持多属性同步刷新 **新增设备类型** - Plug 驱动:插座/插头,支持开合、在线状态、属性变更订阅 - Gateway 驱动:网关设备类型识别 - miot/miot_errors.go:新增 MIoT 云端错误码定义 **MIoT 客户端健壮性** - SetProp 空结果视为失败;设备被移除或离线时自动刷新设备列表 - GetDevices 的 has_more 改用 parseBool 严格解析 - MIPS SubscribeMany 批量订阅减少往返 - matchURN 改为按段精确匹配,避免 tofan 误匹配 fan **Bridge** - 移除 Thermostat case,Cover 改用结构化 CoverProps - 新增设备创建后自动订阅属性变更 **其他** - 日志整体 Info → Debug 降噪 - .gitignore 忽略 /.codebuddy/memory - 更新 devices_test 与 08_adv_filter_classify 示例
131 lines
4.0 KiB
Go
131 lines
4.0 KiB
Go
// Command diag_sdk 用完整 SDK 封装(xiaomi.NewClient + devices.Create)重现生产控制路径。
|
||
//
|
||
// 目的:验证 SDK 封装层解析出的属性绑定(on/target-temperature 绑到哪个 siid)。
|
||
// 与生产同链路:ac.TurnOn / ac.SetTargetTemp → SetProp(绑定siid) → 云端。
|
||
//
|
||
// 注意:会初始化 MQTT,但用自定义 uuid(client id 唯一,不和生产进程冲突)。
|
||
// 在开发机运行;dataDir 指向与生产相同的 spec 缓存(拷一份或指定生产目录)。
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"time"
|
||
|
||
"xiaomihome/xiaomi"
|
||
"xiaomihome/xiaomi/devices"
|
||
)
|
||
|
||
// ── 配置:按需修改 ──
|
||
const (
|
||
accessToken = "V3_1GfW88WpawoKACRqwdYBb86In_I5e8r8stZdRPXI1PR4zZgIEm4cbjNwn0wYa6-ZQtIliJJerf-iI0W9Lx8t6fm7Ln16w0RVAmYO3e9XubJ0dK7D3eM_zBlCcVmQCla6Sx0BKQv_YXI9E2zXXopKaRjIUWyx0lb0YcE7oEopLuvfA0sEFoTohVKt543KnHJH" // 米家 OAuth access_token
|
||
uid = "2897121707" // 小米帐号 UID
|
||
homeID = int64(619001787902) // 家庭 ID
|
||
targetDID = "2188673403" // 目标设备 did
|
||
dataDir = "D:/xiaomihome/go-xiaomihome/miot/examples/diag_sdk/data" // 本 demo 自己的 spec 缓存目录
|
||
// ──────────────────────
|
||
)
|
||
|
||
func main() {
|
||
ctx := context.Background()
|
||
|
||
cfg := xiaomi.Config{
|
||
AuthInfo: xiaomi.AuthInfo{AccessToken: accessToken},
|
||
UUID: "diag-sdk-" + time.Now().Format("150405"), // 唯一 uuid,不占生产 client id
|
||
UID: uid,
|
||
DataDir: dataDir,
|
||
HomeIDs: []int64{homeID},
|
||
}
|
||
client, err := xiaomi.NewClient(ctx, cfg)
|
||
if err != nil {
|
||
fmt.Fprintf(os.Stderr, "NewClient 失败: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
defer client.Close()
|
||
fmt.Println("SDK 客户端 OK")
|
||
|
||
info, err := client.GetDevice(ctx, targetDID)
|
||
if err != nil || info == nil {
|
||
fmt.Fprintf(os.Stderr, "获取设备 %s 失败: %v\n", targetDID, err)
|
||
os.Exit(1)
|
||
}
|
||
fmt.Printf("设备: %s (%s)\nURN: %s\n", info.Name, info.Model, info.URN)
|
||
|
||
dev, err := devices.NewDevice(client, info)
|
||
if err != nil {
|
||
fmt.Fprintf(os.Stderr, "创建设备控制失败: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
if dev == nil {
|
||
fmt.Fprintf(os.Stderr, "设备类型不支持\n")
|
||
os.Exit(1)
|
||
}
|
||
|
||
ac, ok := dev.(devices.AirConditioner)
|
||
if !ok {
|
||
fmt.Fprintf(os.Stderr, "设备不是 AirConditioner(got %T)\n", dev)
|
||
os.Exit(1)
|
||
}
|
||
fmt.Println("AirConditioner 封装 OK")
|
||
|
||
// 读取当前状态(封装层)
|
||
if props, err := ac.FetchProps(ctx); err != nil {
|
||
fmt.Printf("FetchProps 失败: %v\n", err)
|
||
} else {
|
||
fmt.Printf("封装层状态: on=%v mode=%s target=%.0f current=%.0f fan=%s hum=%d\n",
|
||
props.On, props.Mode, props.TargetTemperature, props.Temperature,
|
||
props.FanLevel, props.CurrentHumidity)
|
||
}
|
||
|
||
// 控制(观察日志里 SetProp 写的 siid)
|
||
fmt.Println("\n=== 控制序列(看 SetProp 日志的 siid) ===")
|
||
if err := ac.TurnOn(ctx); err != nil {
|
||
fmt.Printf("TurnOn 失败: %v\n", err)
|
||
} else {
|
||
fmt.Println("TurnOn OK")
|
||
}
|
||
time.Sleep(500 * time.Millisecond)
|
||
|
||
if err := ac.SetTargetTemp(ctx, 26); err != nil {
|
||
fmt.Printf("SetTargetTemp 失败: %v\n", err)
|
||
} else {
|
||
fmt.Println("SetTargetTemp(26) OK")
|
||
}
|
||
time.Sleep(500 * time.Millisecond)
|
||
|
||
if ac.HasFanSpeed() {
|
||
if err := ac.SetFanSpeed(ctx, "medium"); err != nil {
|
||
fmt.Printf("SetFanSpeed 失败: %v\n", err)
|
||
} else {
|
||
fmt.Println("SetFanSpeed(medium) OK")
|
||
}
|
||
} else {
|
||
fmt.Println("HasFanSpeed = false")
|
||
}
|
||
time.Sleep(500 * time.Millisecond)
|
||
|
||
if err := ac.SetMode(ctx, "cool"); err != nil {
|
||
fmt.Printf("SetMode 失败: %v\n", err)
|
||
} else {
|
||
fmt.Println("SetMode(cool) OK")
|
||
}
|
||
time.Sleep(500 * time.Millisecond)
|
||
|
||
if err := ac.TurnOff(ctx); err != nil {
|
||
fmt.Printf("TurnOff 失败: %v\n", err)
|
||
} else {
|
||
fmt.Println("TurnOff OK")
|
||
}
|
||
time.Sleep(500 * time.Millisecond)
|
||
|
||
// 控制后回读
|
||
if props, err := ac.FetchProps(ctx); err != nil {
|
||
fmt.Printf("FetchProps 失败: %v\n", err)
|
||
} else {
|
||
fmt.Printf("控制后状态: on=%v mode=%s target=%.0f current=%.0f fan=%s hum=%d\n",
|
||
props.On, props.Mode, props.TargetTemperature, props.Temperature,
|
||
props.FanLevel, props.CurrentHumidity)
|
||
}
|
||
}
|