Files
xiaomihome/miot/examples/diag_sdk/main.go
T
4566704 86c21ce91c feat: AC 三合一设备支持 + 新增 Plug/Gateway 驱动 + Cover 结构化重构
**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 示例
2026-08-23 19:42:55 +08:00

131 lines
4.0 KiB
Go
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.
// 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)
}
}