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 示例
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"auth_info": {
|
||||
"access_token": "V3_R0Je3kB-S6Mf1O7kKDPm3EDvgHiwsJMze4mVkPG0rRGHYv0KQEUVUyNRJ-ittoSSOmbNxso1syxYrKjOIAGck6EatzRIJsyULI5maduCo-GUyfHu4A6ZYa8zWD6EXp6pmnLim8KT8i1mDbczyOsFTQhNxWY1tq4CM54xlVhFqQwGS65SVV1DhrXvW9XU3nPa",
|
||||
"refresh_token": "R3_WZYXokg6Nnmj2Ceib_bcPtI2fr548hNrjCnK5aG_Lm8-8X5PJeK5SHpicsOBy4Z5cWP4672pviFfWORSsSCpH3tF7UXY8Xuib8uyQP4ppxRiPssybXb67VLFfEHa7raQYtVH_2NHkp53flOZKiRtcFHOX4HAZoC7_AKuIdfWcIa4P3k03XmQF1WG0Ccp4BMH12P7eOJBHyU3LScJbFqVLQ",
|
||||
"mac_key": "0K_213_6BrQPCpmmfolHVZpTR1Y",
|
||||
"expires_in": 259200,
|
||||
"expires_ts": 1783011905
|
||||
},
|
||||
"uid": "2897121707",
|
||||
"cloud_server": "cn",
|
||||
"uuid": "84259d29c352cad945d1f5e3dab3d89b",
|
||||
"redirect_url": "http://homeassistant.local:8123",
|
||||
"language": "en",
|
||||
"ctrl_mode": "auto",
|
||||
"storage_path": "",
|
||||
"nick_name": "Xiaomi"
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Command diag_control 直接控制米家设备(纯 HTTP,不初始化 MQTT)。
|
||||
//
|
||||
// 控制序列:开空调 → 设温度 → 设模式 → 回读验证。
|
||||
// 与生产 SDK 同一云端链路(miotspec/prop/set),不占 MQTT client id。
|
||||
//
|
||||
// 用法:token/did 改下面常量,在生产环境 go run .(Linux 下 dataDir 填生产路径)。
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"xiaomihome/miot"
|
||||
)
|
||||
|
||||
// ── 配置:按需修改 ──
|
||||
const (
|
||||
accessToken = "V3_1GfW88WpawoKACRqwdYBb86In_I5e8r8stZdRPXI1PR4zZgIEm4cbjNwn0wYa6-ZQtIliJJerf-iI0W9Lx8t6fm7Ln16w0RVAmYO3e9XubJ0dK7D3eM_zBlCcVmQCla6Sx0BKQv_YXI9E2zXXopKaRjIUWyx0lb0YcE7oEopLuvfA0sEFoTohVKt543KnHJH" // 米家 OAuth access_token
|
||||
targetDID = "2188673403" // 目标设备 did
|
||||
dataDir = "/home/ubuntu/server/zoSmartUser/data" // spec 缓存目录(生产 Linux)
|
||||
// ──────────────────────
|
||||
)
|
||||
|
||||
func main() {
|
||||
if accessToken == "" {
|
||||
fmt.Fprintln(os.Stderr, "请先填写 accessToken")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
httpClient, err := miot.NewMIoTHttpClient("cn", miot.OAUTH2_CLIENT_ID, accessToken)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "创建 HTTP 客户端失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("HTTP 客户端 OK(未连接 MQTT),设备 %s\n", targetDID)
|
||||
|
||||
// 读取当前状态(控制前)
|
||||
readAll("控制前状态", httpClient)
|
||||
|
||||
// ── 控制序列 ──
|
||||
set("开空调 siid=4/piid=1=true", 4, 1, true, httpClient)
|
||||
set("设温度 siid=4/piid=2=26", 4, 2, float64(26), httpClient)
|
||||
set("设模式 siid=2/piid=2=1(Cool)", 2, 2, float64(1), httpClient)
|
||||
set("关空调 siid=4/piid=1=false", 4, 1, false, httpClient)
|
||||
|
||||
// 回读验证(控制后)
|
||||
readAll("控制后状态", httpClient)
|
||||
}
|
||||
|
||||
// set 写属性并回读验证。
|
||||
func set(label string, siid, piid int, value interface{}, c *miot.MIoTHttpClient) {
|
||||
fmt.Printf("\n=== %s ===\n", label)
|
||||
rs, err := c.SetProp([]miot.PropParam{{DID: targetDID, Siid: siid, Piid: piid, Value: value}})
|
||||
if err != nil {
|
||||
fmt.Printf(" 写失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
for _, r := range rs {
|
||||
fmt.Printf(" 写返回 code=%d\n", r.Code)
|
||||
}
|
||||
// 回读
|
||||
gr, err := c.GetProps([]miot.PropParam{{DID: targetDID, Siid: siid, Piid: piid}})
|
||||
if err != nil {
|
||||
fmt.Printf(" 回读失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
if len(gr) > 0 {
|
||||
fmt.Printf(" 回读 siid=%d/piid=%d = %v\n", siid, piid, gr[0].Value)
|
||||
}
|
||||
}
|
||||
|
||||
// readAll 读取关键属性。
|
||||
func readAll(label string, c *miot.MIoTHttpClient) {
|
||||
fmt.Printf("\n--- %s ---\n", label)
|
||||
read := func(siid, piid int, name string) {
|
||||
rs, err := c.GetProps([]miot.PropParam{{DID: targetDID, Siid: siid, Piid: piid}})
|
||||
if err != nil {
|
||||
fmt.Printf(" %-28s 错误: %v\n", name, err)
|
||||
return
|
||||
}
|
||||
if len(rs) > 0 {
|
||||
fmt.Printf(" %-28s = %v\n", name, rs[0].Value)
|
||||
}
|
||||
}
|
||||
read(4, 1, "空调开关 siid=4/piid=1")
|
||||
read(4, 2, "空调温度 siid=4/piid=2")
|
||||
read(2, 2, "模式 siid=2/piid=2")
|
||||
read(2, 4, "地暖温度 siid=2/piid=4")
|
||||
read(5, 2, "环境温度 siid=5/piid=2")
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
// Command diag_device 诊断指定米家设备的云端属性(纯 HTTP,不初始化 MQTT)。
|
||||
//
|
||||
// 用途:对比 SDK 读到属性值 vs 米家 App 显示,验证 siid/piid 是否正确、
|
||||
// 控制是否生效、设备连接类型等。可在生产环境旁路运行(不与生产 MQTT 冲突)。
|
||||
//
|
||||
// 用法:token/did 直接改下面常量,然后 go run . 即可。
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"xiaomihome/miot"
|
||||
)
|
||||
|
||||
// ── 配置:按需修改 ──
|
||||
const (
|
||||
accessToken = "V3_1GfW88WpawoKACRqwdYBb86In_I5e8r8stZdRPXI1PR4zZgIEm4cbjNwn0wYa6-ZQtIliJJerf-iI0W9Lx8t6fm7Ln16w0RVAmYO3e9XubJ0dK7D3eM_zBlCcVmQCla6Sx0BKQv_YXI9E2zXXopKaRjIUWyx0lb0YcE7oEopLuvfA0sEFoTohVKt543KnHJH" // 米家 OAuth access_token
|
||||
targetDID = "2188673403" // 目标设备 did
|
||||
homeID = "619001787902" // 家庭 ID(留空=全部家庭;先跑一遍看家庭列表再填)
|
||||
dataDir = "/home/ubuntu/server/zoSmartUser/data" // spec 缓存目录(生产 Linux 路径)
|
||||
setTest = "4,1,false|4,2,26" // 写属性测试:"siid,piid,value",多组用 | 分隔;留空跳过
|
||||
// ──────────────────────
|
||||
)
|
||||
|
||||
func main() {
|
||||
if accessToken == "" {
|
||||
fmt.Fprintln(os.Stderr, "请先在 main.go 顶部 const 里填写 accessToken")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 纯 HTTP 客户端(不连 MQTT,不占 mqtt client id)
|
||||
httpClient, err := miot.NewMIoTHttpClient("cn", miot.OAUTH2_CLIENT_ID, accessToken)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "创建 HTTP 客户端失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("HTTP 客户端 OK(未连接 MQTT)")
|
||||
|
||||
// 1. 按 did 精确拉设备信息(不依赖家庭列表)
|
||||
devMap, err := httpClient.GetDevicesWithDIDs([]string{targetDID})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "获取设备信息失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
info, ok := devMap[targetDID]
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "设备 %s 未返回(共 %d 台)\n", targetDID, len(devMap))
|
||||
os.Exit(1)
|
||||
}
|
||||
infoMap := info
|
||||
fmt.Printf("\n=== 设备信息 %s ===\n", targetDID)
|
||||
printDeviceInfo(infoMap)
|
||||
|
||||
urn, _ := infoMap["urn"].(string)
|
||||
model, _ := infoMap["model"].(string)
|
||||
fmt.Printf("URN: %s\nMODEL: %s\n", urn, model)
|
||||
|
||||
// 2. 解析 SPEC
|
||||
parser := miot.NewMIoTSpecParser("en", miot.NewMIoTStorage(dataDir), dataDir+"/specs")
|
||||
spec, err := parser.Parse(urn, model)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "SPEC 解析失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("\n=== SPEC 服务/属性(siid 布局) ===\n")
|
||||
for _, svc := range spec.Services {
|
||||
if svc == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("svc[%d] %s\n", svc.IID, short(svc.Type))
|
||||
names := make([]string, 0, len(svc.Properties))
|
||||
for _, p := range svc.Properties {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
names = append(names, fmt.Sprintf("%s(%d)", short(p.Type), p.PIID))
|
||||
}
|
||||
sort.Strings(names)
|
||||
fmt.Printf(" %s\n", strings.Join(names, " "))
|
||||
}
|
||||
|
||||
// 3. 批量读取全部可读属性
|
||||
fmt.Printf("\n=== 读取全部可读属性 ===\n")
|
||||
var params []miot.PropParam
|
||||
keys := make([]string, 0)
|
||||
for _, svc := range spec.Services {
|
||||
if svc == nil {
|
||||
continue
|
||||
}
|
||||
for _, p := range svc.Properties {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
params = append(params, miot.PropParam{DID: targetDID, Siid: svc.IID, Piid: p.PIID})
|
||||
keys = append(keys, fmt.Sprintf("%d.%d %s", svc.IID, p.PIID, short(p.Type)))
|
||||
}
|
||||
}
|
||||
results, err := httpClient.GetProps(params)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "读取属性失败: %v\n", err)
|
||||
} else {
|
||||
for i, r := range results {
|
||||
name := ""
|
||||
if i < len(keys) {
|
||||
name = keys[i]
|
||||
}
|
||||
fmt.Printf(" %-24s code=%d value=%v\n", name, r.Code, r.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 三合一设备对比读取(空调/地暖双温度)
|
||||
fmt.Printf("\n=== 温度属性对比(三合一设备) ===\n")
|
||||
read := func(siid, piid int, label string) {
|
||||
rs, err := httpClient.GetProps([]miot.PropParam{{DID: targetDID, Siid: siid, Piid: piid}})
|
||||
if err != nil {
|
||||
fmt.Printf(" %s: 错误 %v\n", label, err)
|
||||
return
|
||||
}
|
||||
if len(rs) > 0 {
|
||||
fmt.Printf(" %-28s code=%d value=%v\n", label, rs[0].Code, rs[0].Value)
|
||||
}
|
||||
}
|
||||
read(2, 1, "siid=2/piid=1 on(空调)")
|
||||
read(2, 2, "siid=2/piid=2 mode")
|
||||
read(2, 4, "siid=2/piid=4 target-temperature")
|
||||
read(4, 2, "siid=4/piid=2 target-temperature")
|
||||
read(5, 2, "siid=5/piid=2 temperature(环境)")
|
||||
|
||||
// 5. 写属性测试(可选,多组用 | 分隔,如 "2,4,26|4,2,26")
|
||||
if setTest != "" {
|
||||
for _, spec := range strings.Split(setTest, "|") {
|
||||
doSetAndVerify(httpClient, strings.TrimSpace(spec))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// doSetAndVerify 执行一次写属性并回读验证。
|
||||
func doSetAndVerify(httpClient *miot.MIoTHttpClient, spec string) {
|
||||
parts := strings.Split(spec, ",")
|
||||
if len(parts) < 3 {
|
||||
fmt.Fprintf(os.Stderr, "setTest 格式错误: %s (应为 siid,piid,value)\n", spec)
|
||||
return
|
||||
}
|
||||
siid, _ := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||
piid, _ := strconv.Atoi(strings.TrimSpace(parts[1]))
|
||||
var val interface{}
|
||||
if b, err := strconv.ParseBool(parts[2]); err == nil {
|
||||
val = b
|
||||
} else if f, err := strconv.ParseFloat(strings.TrimSpace(parts[2]), 64); err == nil {
|
||||
val = f
|
||||
} else {
|
||||
val = strings.TrimSpace(parts[2])
|
||||
}
|
||||
fmt.Printf("\n=== 写属性 siid=%d piid=%d value=%v ===\n", siid, piid, val)
|
||||
rs, err := httpClient.SetProp([]miot.PropParam{{DID: targetDID, Siid: siid, Piid: piid, Value: val}})
|
||||
if err != nil {
|
||||
fmt.Printf("写属性失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
for _, r := range rs {
|
||||
fmt.Printf(" code=%d (0/1=成功,非0=失败)\n", r.Code)
|
||||
}
|
||||
// 写后立即回读验证(区分"写错服务" vs "云端假成功")
|
||||
fmt.Println("--- 写后回读 ---")
|
||||
verify := func(vsiid, vpiid int, label string) {
|
||||
vrs, err := httpClient.GetProps([]miot.PropParam{{DID: targetDID, Siid: vsiid, Piid: vpiid}})
|
||||
if err != nil {
|
||||
fmt.Printf(" %s: 错误 %v\n", label, err)
|
||||
return
|
||||
}
|
||||
if len(vrs) > 0 {
|
||||
fmt.Printf(" %-30s code=%d value=%v\n", label, vrs[0].Code, vrs[0].Value)
|
||||
}
|
||||
}
|
||||
verify(siid, piid, "回读 siid=目标")
|
||||
verify(2, 1, "siid=2/piid=1 on(地暖)")
|
||||
verify(2, 2, "siid=2/piid=2 mode(地暖)")
|
||||
verify(2, 4, "siid=2/piid=4 target-temperature(地暖)")
|
||||
verify(4, 1, "siid=4/piid=1 on(空调)")
|
||||
verify(4, 2, "siid=4/piid=2 target-temperature(空调)")
|
||||
}
|
||||
|
||||
func printDeviceInfo(m map[string]interface{}) {
|
||||
fields := []struct{ k, label string }{
|
||||
{"name", "名称"}, {"model", "型号"}, {"connect_type", "connect_type"},
|
||||
{"pid", "pid"}, {"online", "online"}, {"isOnline", "isOnline"},
|
||||
{"rssi", "rssi"}, {"local_ip", "local_ip"}, {"parent_id", "parent_id"},
|
||||
{"urn", "urn"},
|
||||
}
|
||||
for _, f := range fields {
|
||||
if v, ok := m[f.k]; ok {
|
||||
fmt.Printf(" %-12s = %v\n", f.label, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func short(urn string) string {
|
||||
parts := strings.Split(urn, ":")
|
||||
if len(parts) >= 4 {
|
||||
return parts[3]
|
||||
}
|
||||
return urn
|
||||
}
|
||||
|
||||
// toStrSlice 将 interface{}([]interface{})转为 []string。
|
||||
func toStrSlice(v interface{}) []string {
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(arr))
|
||||
for _, item := range arr {
|
||||
if s, ok := item.(string); ok {
|
||||
out = append(out, s)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user