chore: 提交高层 xiaomi 封装库及示例
- 新增 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 示例(风扇/窗帘/传感器控制)
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,68 @@
|
||||
// Package main demonstrates user/home/room information retrieval.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
user, err := client.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("获取用户信息失败:", err)
|
||||
}
|
||||
log.Printf("用户: %s (UID: %s)", user.NickName, user.UID)
|
||||
|
||||
homes, err := client.GetHomeList(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("获取家庭列表失败:", err)
|
||||
}
|
||||
log.Printf("共 %d 个家庭", len(homes))
|
||||
|
||||
for _, home := range homes {
|
||||
log.Printf(" 家庭: %s (ID: %s)", home.Name, home.ID)
|
||||
log.Printf(" 位置: UID=%s GroupID=%s", home.UID, home.GroupID)
|
||||
log.Printf(" 分享状态: %v", home.ShareState)
|
||||
|
||||
rooms, err := client.GetRooms(ctx, home.ID)
|
||||
if err != nil {
|
||||
log.Printf(" 获取房间列表失败: %v", err)
|
||||
continue
|
||||
}
|
||||
log.Printf(" 共 %d 个房间", len(rooms))
|
||||
|
||||
for _, room := range rooms {
|
||||
log.Printf(" 房间: %s (ID: %s)", room.Name, room.ID)
|
||||
didList, err := client.GetRoomDevices(ctx, home.ID, room.ID)
|
||||
if err != nil {
|
||||
log.Printf(" 获取设备列表失败: %v", err)
|
||||
continue
|
||||
}
|
||||
log.Printf(" 设备: %v", didList)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,108 @@
|
||||
// Package main demonstrates device list retrieval with various filters.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
log.Println("刷新设备列表...")
|
||||
if err := client.RefreshDevices(ctx); err != nil {
|
||||
log.Printf("刷新设备列表失败: %v", err)
|
||||
}
|
||||
|
||||
allDevices, err := client.GetDevices(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("获取设备列表失败:", err)
|
||||
}
|
||||
log.Printf("=== 全部设备 (%d 个) ===", len(allDevices))
|
||||
for _, d := range allDevices {
|
||||
kind := d.DeviceKind()
|
||||
log.Printf(" %-20s | %-25s | 种类=%-18s | 在线=%v", d.Name, d.Model, kind, d.Online)
|
||||
}
|
||||
|
||||
onlineDevices, err := client.GetDevices(ctx, xiaomi.FilterByOnline())
|
||||
if err != nil {
|
||||
log.Printf("过滤在线设备失败: %v", err)
|
||||
} else {
|
||||
log.Printf("=== 在线设备 (%d 个) ===", len(onlineDevices))
|
||||
for _, d := range onlineDevices {
|
||||
log.Printf(" %s (%s)", d.Name, d.Model)
|
||||
}
|
||||
}
|
||||
|
||||
switchDevices, err := client.GetDevices(ctx, xiaomi.FilterByKind(xiaomi.KindSwitch))
|
||||
if err != nil {
|
||||
log.Printf("过滤开关类设备失败: %v", err)
|
||||
} else {
|
||||
log.Printf("=== 开关类设备 (%d 个) ===", len(switchDevices))
|
||||
for _, d := range switchDevices {
|
||||
log.Printf(" %s (%s)", d.Name, d.Model)
|
||||
}
|
||||
}
|
||||
|
||||
lightDevices, err := client.GetDevices(ctx, xiaomi.FilterByKind(xiaomi.KindLight))
|
||||
if err != nil {
|
||||
log.Printf("过滤灯光类设备失败: %v", err)
|
||||
} else {
|
||||
log.Printf("=== 灯光类设备 (%d 个) ===", len(lightDevices))
|
||||
for _, d := range lightDevices {
|
||||
log.Printf(" %s (%s)", d.Name, d.Model)
|
||||
}
|
||||
}
|
||||
|
||||
onlineLights, err := client.GetDevices(ctx,
|
||||
xiaomi.FilterByOnline(),
|
||||
xiaomi.FilterByKind(xiaomi.KindLight),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("组合过滤失败: %v", err)
|
||||
} else {
|
||||
log.Printf("=== 在线灯光类设备 (%d 个) ===", len(onlineLights))
|
||||
for _, d := range onlineLights {
|
||||
log.Printf(" %s (%s)", d.Name, d.Model)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allDevices) > 0 {
|
||||
first := allDevices[0]
|
||||
detail, err := client.GetDevice(ctx, first.DID)
|
||||
if err != nil {
|
||||
log.Printf("获取设备详情失败: %v", err)
|
||||
} else {
|
||||
log.Printf("=== 设备详情: %s ===", detail.Name)
|
||||
log.Printf(" DID: %s", detail.DID)
|
||||
log.Printf(" 型号: %s", detail.Model)
|
||||
log.Printf(" URN: %s", detail.URN)
|
||||
log.Printf(" 固件: %s", detail.FWVersion)
|
||||
log.Printf(" IP: %s (RSSI: %d)", detail.IP, detail.RSSI)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("设备列表演示完毕")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,95 @@
|
||||
// Package main demonstrates switch device control.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
const targetDID = "2170429382"
|
||||
|
||||
info, err := client.GetDevice(ctx, targetDID)
|
||||
if err != nil {
|
||||
log.Fatalf("获取设备 %s 失败: %v", targetDID, err)
|
||||
}
|
||||
|
||||
sw, err := devices.NewSwitch(client, info)
|
||||
if err != nil {
|
||||
log.Fatalf("创建 Switch 失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("开关: %s (DID: %s, 型号: %s)", info.Name, info.DID, info.Model)
|
||||
|
||||
log.Printf("通道数: %d", sw.ChannelCount())
|
||||
|
||||
if sw.ChannelCount() == 1 {
|
||||
demoSingleChannel(ctx, sw)
|
||||
} else {
|
||||
demoMultiChannel(ctx, sw)
|
||||
}
|
||||
|
||||
log.Println("开关控制演示完毕")
|
||||
}
|
||||
|
||||
func demoSingleChannel(ctx context.Context, sw devices.Switch) {
|
||||
on, _ := sw.IsOn(ctx)
|
||||
log.Printf("当前状态: on=%v", on)
|
||||
|
||||
log.Println("执行 Toggle...")
|
||||
sw.Toggle(ctx)
|
||||
time.Sleep(1 * time.Second)
|
||||
newOn, _ := sw.IsOn(ctx)
|
||||
log.Printf("Toggle 后: on=%v", newOn)
|
||||
}
|
||||
|
||||
func demoMultiChannel(ctx context.Context, sw devices.Switch) {
|
||||
for ch := 1; ch <= sw.ChannelCount(); ch++ {
|
||||
on, err := sw.IsChannelOn(ctx, ch)
|
||||
if err != nil {
|
||||
log.Printf("通道 %d: 查询失败 %v", ch, err)
|
||||
continue
|
||||
}
|
||||
log.Printf("通道 %d: on=%v", ch, on)
|
||||
|
||||
log.Printf(" 通道 %d 执行 Toggle...", ch)
|
||||
sw.ToggleChannel(ctx, ch)
|
||||
time.Sleep(1 * time.Second)
|
||||
newOn, _ := sw.IsChannelOn(ctx, ch)
|
||||
log.Printf(" 通道 %d Toggle 后: on=%v", ch, newOn)
|
||||
|
||||
// 恢复原状态
|
||||
if on {
|
||||
log.Printf(" 通道 %d 恢复为 开", ch)
|
||||
sw.TurnOnChannel(ctx, ch)
|
||||
} else {
|
||||
log.Printf(" 通道 %d 恢复为 关", ch)
|
||||
sw.TurnOffChannel(ctx, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,108 @@
|
||||
// Package main demonstrates light device control - brightness, color temperature, RGB, and state subscription.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
const targetDID = "2169700407"
|
||||
|
||||
info, err := client.GetDevice(ctx, targetDID)
|
||||
if err != nil {
|
||||
log.Fatalf("获取设备 %s 失败: %v", targetDID, err)
|
||||
}
|
||||
|
||||
lt, err := devices.NewLight(client, info)
|
||||
if err != nil {
|
||||
log.Fatalf("创建 Light 失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("灯光: %s (DID: %s, 型号: %s)", info.Name, info.DID, info.Model)
|
||||
|
||||
log.Println("开启灯光...")
|
||||
if err := lt.TurnOn(ctx); err != nil {
|
||||
log.Printf("开启失败: %v", err)
|
||||
}
|
||||
|
||||
log.Println("设置亮度...")
|
||||
if err := lt.SetBrightness(ctx, 50); err != nil {
|
||||
log.Printf("设置亮度失败(可能不支持): %v", err)
|
||||
}
|
||||
brightness, err := lt.GetBrightness(ctx)
|
||||
if err != nil {
|
||||
log.Printf("获取亮度失败: %v", err)
|
||||
} else {
|
||||
log.Printf("当前亮度: %d", brightness)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
log.Println("设置色温 4000K...")
|
||||
if err := lt.SetColorTemp(ctx, 4000); err != nil {
|
||||
log.Printf("设置色温失败(可能不支持): %v", err)
|
||||
}
|
||||
colorTemp, err := lt.GetColorTemp(ctx)
|
||||
if err != nil {
|
||||
log.Printf("获取色温失败: %v", err)
|
||||
} else {
|
||||
log.Printf("当前色温: %dK", colorTemp)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
log.Println("设置颜色 R=255 G=100 B=50...")
|
||||
if err := lt.SetColor(ctx, 255, 100, 50); err != nil {
|
||||
log.Printf("设置颜色失败(可能不支持): %v", err)
|
||||
}
|
||||
r, g, b, err := lt.GetColor(ctx)
|
||||
if err != nil {
|
||||
log.Printf("获取颜色失败: %v", err)
|
||||
} else {
|
||||
log.Printf("当前颜色: R=%d G=%d B=%d", r, g, b)
|
||||
}
|
||||
|
||||
subID, err := lt.OnStateChanged(func(state devices.LightState) {
|
||||
log.Printf("灯光状态变化: power=%v brightness=%d colorTemp=%dK rgb=[%d,%d,%d]",
|
||||
state.Power, state.Brightness, state.ColorTemp,
|
||||
state.ColorRGB[0], state.ColorRGB[1], state.ColorRGB[2])
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("订阅状态失败: %v", err)
|
||||
} else {
|
||||
log.Printf("已订阅灯光状态: subID=%s", subID)
|
||||
}
|
||||
|
||||
log.Println("执行 Toggle...")
|
||||
if err := lt.Toggle(ctx); err != nil {
|
||||
log.Printf("Toggle 失败: %v", err)
|
||||
}
|
||||
|
||||
log.Println("灯光控制演示完毕")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:air-conditioner:0000A004:090615-k20:1","description":"Air Conditioner","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:090615-k20:1","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:090615-k20:1","description":"Device Manufacturer","format":"string","access":["read"]},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:090615-k20:1","description":"Device Model","format":"string","access":["read"]},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:090615-k20:1","description":"Device ID","format":"string","access":["read"]},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:090615-k20:1","description":"Current Firmware Version","format":"string","access":["read"]},{"iid":5,"type":"urn:miot-spec-v2:property:serial-no:000000FF:090615-k20:1","description":"Serial Number","format":"string","access":["read","notify"]}]},{"iid":2,"type":"urn:miot-spec-v2:service:air-conditioner:0000780F:090615-k20:1","description":"Air Conditioner","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:090615-k20:1","description":"Switch Status","format":"bool","access":["read","write","notify"]},{"iid":2,"type":"urn:miot-spec-v2:property:mode:00000008:090615-k20:1","description":"Mode","format":"uint8","access":["read","write","notify"],"value-list":[{"value":0,"description":"Cool"},{"value":1,"description":"Heat"},{"value":2,"description":"Fan"},{"value":3,"description":"Dry"}],"gatt-access":[]},{"iid":3,"type":"urn:miot-spec-v2:property:fault:00000009:090615-k20:1","description":"Device Fault","format":"uint8","access":["read","notify"],"value-list":[{"value":0,"description":"No Faults"},{"value":1,"description":"Error"}],"gatt-access":[]},{"iid":4,"type":"urn:miot-spec-v2:property:target-temperature:00000021:090615-k20:1","description":"Target Temperature","format":"float","access":["read","write","notify"],"unit":"celsius","value-range":[16,32,1]}],"actions":[{"iid":1,"type":"urn:miot-spec-v2:action:toggle:00002811:090615-k20:1","description":"Toggle","in":[],"out":[]},{"iid":2,"type":"urn:miot-spec-v2:action:favorite-toggle:000028B5:090615-k20:1","description":"Favorite Toggle","in":[],"out":[]},{"iid":3,"type":"urn:miot-spec-v2:action:favorite-toggle:000028B5:090615-k20:1","description":"Favorite Toggle","in":[],"out":[]},{"iid":4,"type":"urn:miot-spec-v2:action:favorite-toggle:000028B5:090615-k20:1","description":"Favorite Toggle","in":[],"out":[]}]},{"iid":3,"type":"urn:miot-spec-v2:service:fan-control:00007809:090615-k20:1","description":"Fan Control","properties":[{"iid":2,"type":"urn:miot-spec-v2:property:fan-level:00000016:090615-k20:1","description":"Fan Level","format":"uint8","access":["read","write","notify"],"value-list":[{"value":0,"description":"Auto"},{"value":1,"description":"Low"},{"value":2,"description":"Medium"},{"value":3,"description":"High"}],"gatt-access":[]},{"iid":3,"type":"urn:miot-spec-v2:property:horizontal-position:000001FD:090615-k20:1","description":"Horizontal Position","format":"uint8","access":["read","write","notify"],"value-range":[1,200,1],"gatt-access":[]},{"iid":4,"type":"urn:miot-spec-v2:property:swing-mode:000001A6:090615-k20:1","description":"Swing Mode","format":"uint8","access":["read","write","notify"],"value-list":[{"value":0,"description":"Reactive Swing"},{"value":1,"description":"Symmetrical Swing"}],"gatt-access":[]}],"actions":[{"iid":1,"type":"urn:miot-spec-v2:action:turn-left:00002886:090615-k20:1","description":"Turn Left","in":[],"out":[]},{"iid":2,"type":"urn:miot-spec-v2:action:turn-right:00002887:090615-k20:1","description":"Turn Right","in":[],"out":[]}]},{"iid":4,"type":"urn:miot-spec-v2:service:environment:0000780A:090615-k20:1","description":"Environment","properties":[{"iid":7,"type":"urn:miot-spec-v2:property:temperature:00000020:090615-k20:1","description":"Temperature","format":"float","access":["read","notify"],"unit":"celsius","value-range":[-30,50,1],"gatt-access":[]}]}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:air-conditioner:0000A004:lumi-mcn02:1","description":"Air Conditioner","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:lumi-mcn02:1","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:lumi-mcn02:1","description":"Device Manufacturer","format":"string","access":["read"]},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:lumi-mcn02:1","description":"Device Model","format":"string","access":["read"]},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:lumi-mcn02:1","description":"Device Serial Number","format":"string","access":["read"]},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:lumi-mcn02:1","description":"Current Firmware Version","format":"string","access":["read"]}]},{"iid":2,"type":"urn:miot-spec-v2:service:air-conditioner:0000780F:lumi-mcn02:1","description":"Air Conditioner","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:lumi-mcn02:1","description":"Switch Status","format":"bool","access":["read","write","notify"]},{"iid":2,"type":"urn:miot-spec-v2:property:mode:00000008:lumi-mcn02:1","description":"Mode","format":"uint8","access":["read","write","notify"],"value-list":[{"value":0,"description":"Auto"},{"value":1,"description":"Cool"},{"value":2,"description":"Dry"},{"value":3,"description":"Heat"},{"value":4,"description":"Fan"}]},{"iid":3,"type":"urn:miot-spec-v2:property:target-temperature:00000021:lumi-mcn02:1","description":"Target Temperature","format":"float","access":["read","write","notify"],"unit":"celsius","value-range":[16,30,1]}]},{"iid":3,"type":"urn:miot-spec-v2:service:fan-control:00007809:lumi-mcn02:1","description":"Fan Control","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:fan-level:00000016:lumi-mcn02:1","description":"Fan Level","format":"uint8","access":["read","write","notify"],"value-list":[{"value":0,"description":"Auto"},{"value":1,"description":"Low"},{"value":2,"description":"Medium"},{"value":3,"description":"High"}]},{"iid":2,"type":"urn:miot-spec-v2:property:vertical-swing:00000018:lumi-mcn02:1","description":"Vertical Swing","format":"bool","access":["read","write","notify"]}]},{"iid":4,"type":"urn:miot-spec-v2:service:air-condition-outlet-matching:000078B9:lumi-mcn02:1","description":"Air Condition Outlet Matching","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:match-state:00000141:lumi-mcn02:1","description":"Match State","format":"uint8","access":["read","write"],"value-list":[{"value":0,"description":"Out Of Matching"},{"value":1,"description":"Matching"}]},{"iid":2,"type":"urn:miot-spec-v2:property:ac-work-mode:00000142:lumi-mcn02:1","description":"Ac Work Mode","format":"uint8","access":["read","write"],"value-list":[{"value":0,"description":"Ac Mode With Outlet"},{"value":1,"description":"Ac Mode Without Outlet"}]},{"iid":3,"type":"urn:miot-spec-v2:property:ac-state:00000143:lumi-mcn02:1","description":"Ac State","format":"string","access":["read","write"]},{"iid":4,"type":"urn:miot-spec-v2:property:brand-id:00000144:lumi-mcn02:1","description":"Brand Id","format":"uint32","access":[],"value-range":[1,4294967295,1]},{"iid":5,"type":"urn:miot-spec-v2:property:remote-id:00000145:lumi-mcn02:1","description":"Remote Id","format":"uint32","access":[],"value-range":[1,4294967295,1]},{"iid":6,"type":"urn:miot-spec-v2:property:ac-type:00000146:lumi-mcn02:1","description":"Ac Type","format":"uint32","access":[],"value-list":[{"value":0,"description":"Stateless"},{"value":1,"description":"Stateful"}]},{"iid":7,"type":"urn:miot-spec-v2:property:ac-ctrl-library:00000148:lumi-mcn02:1","description":"Ac Ctrl Library","format":"string","access":[]},{"iid":8,"type":"urn:miot-spec-v2:property:ac-ctrl-library-crc32:00000147:lumi-mcn02:1","description":"Ac Ctrl Library Crc32","format":"uint32","access":[],"value-range":[1,4294967295,1]}],"actions":[{"iid":1,"type":"urn:miot-spec-v2:action:set-ac-model:00002865:lumi-mcn02:1","description":"Set Ac Model","in":[4,5,6],"out":[]},{"iid":2,"type":"urn:miot-spec-v2:action:set-ac-ctrl-info:00002866:lumi-mcn02:1","description":"Set Ac Ctrl Info","in":[5,8,7],"out":[]}]},{"iid":5,"type":"urn:miot-spec-v2:service:power-consumption:0000780E:lumi-mcn02:1","description":"Power Consumption","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:electric-power:00000066:lumi-mcn02:1","description":"Electric Power","format":"float","access":["read","notify"],"unit":"watt","value-range":[0,10000,0.01]}]}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:air-conditioner:0000A004:qdhkl-b27:1","description":"Air Conditioner","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:qdhkl-b27:1","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:qdhkl-b27:1","description":"Device Manufacturer","format":"string","access":["read"]},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:qdhkl-b27:1","description":"Device Model","format":"string","access":["read"]},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:qdhkl-b27:1","description":"Device ID","format":"string","access":["read"]},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:qdhkl-b27:1","description":"Current Firmware Version","format":"string","access":["read"]},{"iid":5,"type":"urn:miot-spec-v2:property:serial-no:000000FF:qdhkl-b27:1","description":"Serial Number","format":"string","access":["read","notify"]}]},{"iid":2,"type":"urn:miot-spec-v2:service:air-conditioner:0000780F:qdhkl-b27:1","description":"Air Conditioner","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:qdhkl-b27:1","description":"Switch Status","format":"bool","access":["read","write","notify"]},{"iid":2,"type":"urn:miot-spec-v2:property:mode:00000008:qdhkl-b27:1","description":"Mode","format":"uint8","access":["read","write","notify"],"value-list":[{"value":8,"description":"Heat"},{"value":1,"description":"Cool"},{"value":2,"description":"Dry"},{"value":4,"description":"Fan"}],"gatt-access":[]},{"iid":3,"type":"urn:miot-spec-v2:property:fault:00000009:qdhkl-b27:1","description":"Device Fault","format":"uint8","access":["read","notify"],"value-list":[{"value":0,"description":"No Faults"}]},{"iid":4,"type":"urn:miot-spec-v2:property:target-temperature:00000021:qdhkl-b27:1","description":"Target Temperature","format":"float","access":["read","write","notify"],"unit":"celsius","value-range":[16,32,1],"gatt-access":[]}],"actions":[{"iid":1,"type":"urn:miot-spec-v2:action:toggle:00002811:qdhkl-b27:1","description":"Toggle","in":[],"out":[]}]},{"iid":3,"type":"urn:miot-spec-v2:service:fan-control:00007809:qdhkl-b27:1","description":"Fan Control","properties":[{"iid":2,"type":"urn:miot-spec-v2:property:fan-level:00000016:qdhkl-b27:1","description":"Fan Level","format":"uint8","access":["read","write","notify"],"value-list":[{"value":3,"description":"High"},{"value":2,"description":"Medium"},{"value":1,"description":"Low"},{"value":0,"description":"Auto"}],"gatt-access":[]}]},{"iid":4,"type":"urn:miot-spec-v2:service:environment:0000780A:qdhkl-b27:1","description":"Environment","properties":[{"iid":7,"type":"urn:miot-spec-v2:property:temperature:00000020:qdhkl-b27:1","description":"Temperature","format":"float","access":["read","notify"],"unit":"celsius","value-range":[-30,100,1]}]},{"iid":5,"type":"urn:qdhkl-spec:service:custom:00007801:qdhkl-b27:1","description":"custom","actions":[{"iid":1,"type":"urn:qdhkl-spec:action:raise-one-degree:00002801:qdhkl-b27:1","description":"raise-one-degree","in":[],"out":[]},{"iid":2,"type":"urn:qdhkl-spec:action:down-one-degree:00002802:qdhkl-b27:1","description":"down-one-degree","in":[],"out":[]},{"iid":3,"type":"urn:qdhkl-spec:action:fan-speed-up:00002803:qdhkl-b27:1","description":"fan-speed-up","in":[],"out":[]},{"iid":4,"type":"urn:qdhkl-spec:action:fan-speed-down:00002804:qdhkl-b27:1","description":"fan-speed-down","in":[],"out":[]}]}]}
|
||||
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,111 @@
|
||||
// Package main demonstrates full air conditioner control.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
const targetDID = "2053225728"
|
||||
|
||||
info, err := client.GetDevice(ctx, targetDID)
|
||||
if err != nil {
|
||||
log.Fatalf("获取设备 %s 失败: %v", targetDID, err)
|
||||
}
|
||||
|
||||
ac, err := devices.NewAirConditioner(client, info)
|
||||
if err != nil {
|
||||
log.Fatalf("创建 AirConditioner 失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("空调: %s (DID: %s, 型号: %s)", info.Name, info.DID, info.Model)
|
||||
|
||||
on, _ := ac.IsOn(ctx)
|
||||
mode, _ := ac.GetMode(ctx)
|
||||
targetTemp, _ := ac.GetTargetTemp(ctx)
|
||||
currentTemp, _ := ac.GetCurrentTemp(ctx)
|
||||
fanSpeed, _ := ac.GetFanSpeed(ctx)
|
||||
swing, _ := ac.GetSwing(ctx)
|
||||
log.Printf("当前状态: on=%v mode=%s target=%.1f current=%.1f fan=%s swing=%s",
|
||||
on, mode, targetTemp, currentTemp, fanSpeed, swing)
|
||||
|
||||
log.Println("=== 执行控制: 开 -> 制冷 -> 26度 -> 自动风速 -> 垂直摆风 ===")
|
||||
|
||||
if !on {
|
||||
log.Println(" 开启空调...")
|
||||
ac.TurnOn(ctx)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
log.Println(" 设置制冷模式...")
|
||||
ac.SetMode(ctx, devices.ModeCool)
|
||||
log.Println(" 设置目标温度 26度...")
|
||||
ac.SetTargetTemp(ctx, 26.0)
|
||||
log.Println(" 设置自动风速...")
|
||||
ac.SetFanSpeed(ctx, devices.SpeedAuto)
|
||||
log.Println(" 设置垂直摆风...")
|
||||
ac.SetSwing(ctx, devices.SwingVertical)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
mode, _ = ac.GetMode(ctx)
|
||||
targetTemp, _ = ac.GetTargetTemp(ctx)
|
||||
currentTemp, _ = ac.GetCurrentTemp(ctx)
|
||||
fanSpeed, _ = ac.GetFanSpeed(ctx)
|
||||
swing, _ = ac.GetSwing(ctx)
|
||||
log.Printf("更新后状态: mode=%s target=%.1f current=%.1f fan=%s swing=%s",
|
||||
mode, targetTemp, currentTemp, fanSpeed, swing)
|
||||
|
||||
log.Println("设置目标湿度 50...")
|
||||
if err := ac.SetTargetHumidity(ctx, 50); err != nil {
|
||||
log.Printf(" 设置湿度失败(可能不支持): %v", err)
|
||||
} else {
|
||||
humidity, _ := ac.GetTargetHumidity(ctx)
|
||||
curHumidity, _ := ac.GetCurrentHumidity(ctx)
|
||||
log.Printf(" 湿度: target=%d current=%d", humidity, curHumidity)
|
||||
}
|
||||
|
||||
log.Println("关闭电辅热...")
|
||||
ac.SetAuxHeat(ctx, false)
|
||||
log.Println("开启节能模式...")
|
||||
ac.SetEco(ctx, true)
|
||||
|
||||
subID, err := ac.OnStateChanged(func(state devices.ACState) {
|
||||
log.Printf("空调状态变化: power=%v mode=%s target=%.1f current=%.1f fan=%s swing=%s",
|
||||
state.Power, state.Mode, state.TargetTemp, state.CurrentTemp,
|
||||
state.FanSpeed, state.Swing)
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("订阅状态失败: %v", err)
|
||||
} else {
|
||||
log.Printf("已订阅空调状态: subID=%s", subID)
|
||||
}
|
||||
|
||||
log.Println("空调控制演示完毕")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:fan:0000A005:jipin-f003:1:0000D062","description":"jipin fan f003","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:jipin-f003:1:0000D062","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:jipin-f003:1:0000D062","description":"Device Manufacturer","format":"string","access":["read"],"source":4},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:jipin-f003:1:0000D062","description":"Device Model","format":"string","access":["read"],"source":4},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:jipin-f003:1:0000D062","description":"Device ID","format":"string","access":["read"],"source":4},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:jipin-f003:1:0000D062","description":"Current Firmware Version","format":"string","access":["read"],"source":4},{"iid":5,"type":"urn:miot-spec-v2:property:serial-no:000000FF:jipin-f003:1:0000D062","description":"Serial Number","format":"string","access":["read"],"source":4}]},{"iid":2,"type":"urn:miot-spec-v2:service:fan:00007808:jipin-f003:1:0000D062","description":"fan","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:jipin-f003:1:0000D062","description":"switch status","format":"bool","access":["read","write","notify"],"source":1},{"iid":3,"type":"urn:miot-spec-v2:property:mode:00000008:jipin-f003:1:0000D062","description":"mode","format":"uint8","access":["read","write","notify"],"value-list":[{"value":0,"description":"None"},{"value":1,"description":"Sleep"}],"source":1,"gatt-access":[]},{"iid":4,"type":"urn:miot-spec-v2:property:fan-level:00000016:jipin-f003:1:0000D062","description":"Gear Fan Level","format":"uint8","access":["read","write","notify"],"value-range":[1,8,1],"source":1,"gatt-access":[]},{"iid":2,"type":"urn:miot-spec-v2:property:fault:00000009:jipin-f003:1:0000D062","description":"Device Fault","format":"uint8","access":["read","notify"],"value-list":[{"value":0,"description":"No Faults"}],"source":1,"gatt-access":[]},{"iid":6,"type":"urn:miot-spec-v2:property:horizontal-swing:00000017:jipin-f003:1:0000D062","description":"Horizontal Swing","format":"bool","access":["read","write","notify"],"source":1},{"iid":8,"type":"urn:miot-spec-v2:property:vertical-swing:00000018:jipin-f003:1:0000D062","description":"Vertical Swing","format":"bool","access":["read","write","notify"],"source":1},{"iid":14,"type":"urn:miot-spec-v2:property:anion:00000025:jipin-f003:1","description":"Anion","format":"bool","access":["read","write","notify"]}],"actions":[{"iid":3,"type":"urn:miot-spec-v2:action:toggle:00002811:jipin-f003:1:0000D062","description":"toggle","in":[],"out":[]}]},{"iid":5,"type":"urn:miot-spec-v2:service:indicator-light:00007803:jipin-f003:1:0000D062","description":"Indicator-light","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:jipin-f003:1:0000D062","description":"Switch Status","format":"bool","access":["read","write","notify"],"source":1}]},{"iid":7,"type":"urn:miot-spec-v2:service:alarm:00007804:jipin-f003:1:0000D062","description":"Alarm","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:alarm:00007804:jipin-f003:1:0000D062","description":"Alarm","format":"bool","access":["read","write","notify"],"source":1}]},{"iid":11,"type":"urn:miot-spec-v2:service:countdown:0000782D:jipin-f003:1","description":"Countdown","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:countdown-time:00000055:jipin-f003:1","description":"Countdown Time","format":"uint16","access":["read","write","notify"],"unit":"minutes","value-range":[0,720,1],"gatt-access":[]}]},{"iid":12,"type":"urn:miot-spec-v2:service:night-light:00007883:jipin-f003:1","description":"Night Light","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:jipin-f003:1","description":"Switch Status","format":"bool","access":["read","write","notify"]}]}]}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Package main demonstrates fan device control.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
log.Println("=== 风扇控制 ===")
|
||||
const targetDID = "820891929"
|
||||
info, err := client.GetDevice(ctx, targetDID)
|
||||
if err != nil {
|
||||
log.Fatalf("获取设备 %s 失败: %v", targetDID, err)
|
||||
}
|
||||
log.Printf("设备: %s (DID: %s, 型号: %s)", info.Name, info.DID, info.Model)
|
||||
|
||||
fan, err := devices.NewFan(client, info)
|
||||
if err != nil {
|
||||
log.Fatalf("创建 Fan 控制失败: %v", err)
|
||||
}
|
||||
|
||||
log.Println(" 开启风扇...")
|
||||
fan.TurnOn(ctx)
|
||||
|
||||
log.Println(" 设置 2 档风...")
|
||||
if err := fan.SetFanLevel(ctx, 2); err != nil {
|
||||
log.Printf(" 调速失败(可能不支持): %v", err)
|
||||
} else {
|
||||
level, _ := fan.GetFanLevel(ctx)
|
||||
log.Printf(" 当前档位: %d", level)
|
||||
}
|
||||
|
||||
log.Println(" 开启摇头...")
|
||||
if err := fan.SetOscillation(ctx, true); err != nil {
|
||||
log.Printf(" 摇头失败(可能不支持): %v", err)
|
||||
} else {
|
||||
osc, _ := fan.IsOscillating(ctx)
|
||||
log.Printf(" 摇头状态: %v", osc)
|
||||
}
|
||||
|
||||
log.Println(" 开启自然风...")
|
||||
if err := fan.SetNaturalWind(ctx, true); err != nil {
|
||||
log.Printf(" 自然风失败(可能不支持): %v", err)
|
||||
} else {
|
||||
nw, _ := fan.IsNaturalWind(ctx)
|
||||
log.Printf(" 自然风状态: %v", nw)
|
||||
}
|
||||
|
||||
on, _ := fan.IsOn(ctx)
|
||||
level, _ := fan.GetFanLevel(ctx)
|
||||
osc, _ := fan.IsOscillating(ctx)
|
||||
nw, _ := fan.IsNaturalWind(ctx)
|
||||
log.Printf(" 风扇状态汇总: on=%v level=%d oscillation=%v naturalWind=%v", on, level, osc, nw)
|
||||
|
||||
log.Println("风扇演示完毕")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,72 @@
|
||||
// Package main demonstrates property and device state subscriptions for all devices.
|
||||
// Press Ctrl+C to exit.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
"xiaomihome/xiaomi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
devList, err := client.GetDevices(ctx, xiaomi.FilterByOnline())
|
||||
if err != nil {
|
||||
log.Fatal("获取设备列表失败:", err)
|
||||
}
|
||||
if len(devList) == 0 {
|
||||
log.Fatal("未找到任何在线设备")
|
||||
}
|
||||
|
||||
log.Printf("找到 %d 个在线设备,开始订阅...", len(devList))
|
||||
|
||||
for _, dev := range devList {
|
||||
did := dev.DID
|
||||
name := dev.Name
|
||||
|
||||
// 订阅该设备所有属性
|
||||
subID, err := client.SubProp(did, 0, 0,
|
||||
func(did string, p *xiaomi.PropertyValue) {
|
||||
log.Printf("[%s] siid=%d piid=%d value=%v", name, p.SIID, p.PIID, p.Value)
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[%s] 订阅失败: %v", name, err)
|
||||
} else {
|
||||
log.Printf("[%s] 已订阅 subID=%s", name, subID)
|
||||
}
|
||||
|
||||
// 订阅设备在线状态
|
||||
if err := client.SubDeviceState(did, func(did string, state xiaomi.DeviceState) {
|
||||
log.Printf("[%s] 状态变化: %s", name, state)
|
||||
}); err != nil {
|
||||
log.Printf("[%s] 状态订阅失败: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("\n按 Ctrl+C 退出...")
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt)
|
||||
<-sigCh
|
||||
log.Println("已退出")
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Package main demonstrates advanced device filtering, classification, and factory pattern for batch device creation.
|
||||
//
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 1. 加载配置
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 3. 获取全部设备
|
||||
allDevices, err := client.GetDevices(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("获取设备列表失败:", err)
|
||||
}
|
||||
log.Printf("=== 全部设备 (%d 个) ===", len(allDevices))
|
||||
|
||||
// 4. 按 Kind 分类统计
|
||||
kindCount := make(map[xiaomi.Kind]int)
|
||||
for _, d := range allDevices {
|
||||
kind := d.DeviceKind()
|
||||
kindCount[kind]++
|
||||
}
|
||||
|
||||
log.Println("=== 设备分类统计 ===")
|
||||
for kind, count := range kindCount {
|
||||
log.Printf(" %-20s: %d 个", kind, count)
|
||||
}
|
||||
|
||||
// 5. 按种类单独过滤
|
||||
allKinds := []xiaomi.Kind{
|
||||
xiaomi.KindSwitch,
|
||||
xiaomi.KindLight,
|
||||
xiaomi.KindAirConditioner,
|
||||
xiaomi.KindFan,
|
||||
xiaomi.KindCover,
|
||||
xiaomi.KindHumidifier,
|
||||
xiaomi.KindVacuum,
|
||||
xiaomi.KindWaterHeater,
|
||||
xiaomi.KindThermostat,
|
||||
}
|
||||
|
||||
for _, kind := range allKinds {
|
||||
filtered, _ := client.GetDevices(ctx, xiaomi.FilterByKind(kind))
|
||||
if len(filtered) > 0 {
|
||||
log.Printf(" %-20s: %d 个", kind, len(filtered))
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 工厂模式: 批量创建设备控制接口
|
||||
log.Println("=== 工厂批量创建 ===")
|
||||
log.Printf("%-6s %-20s %-25s %-18s %-8s",
|
||||
"", "设备名", "型号", "种类", "状态")
|
||||
|
||||
supportedCount := 0
|
||||
for i, info := range allDevices {
|
||||
ctrl, err := devices.Create(client, info)
|
||||
if err != nil {
|
||||
log.Printf("[%d] %s 创建失败: %v", i, info.Name, err)
|
||||
continue
|
||||
}
|
||||
if ctrl == nil {
|
||||
continue // 不支持的类型
|
||||
}
|
||||
|
||||
kind := info.DeviceKind()
|
||||
status := "离线"
|
||||
if info.Online {
|
||||
status = "在线"
|
||||
}
|
||||
|
||||
switch d := ctrl.(type) {
|
||||
case devices.Switch:
|
||||
_ = d
|
||||
log.Printf("[%d] %-20s %-25s %-18s %-8s", i, info.Name, info.Model, kind, status)
|
||||
case devices.Light:
|
||||
brightness := "N/A"
|
||||
if b, err := d.GetBrightness(ctx); err == nil && b >= 0 {
|
||||
brightness = fmt.Sprintf("%d%%", b)
|
||||
}
|
||||
log.Printf("[%d] %-20s %-25s %-18s %-8s 亮度=%s",
|
||||
i, info.Name, info.Model, kind, status, brightness)
|
||||
case devices.AirConditioner:
|
||||
temp := "N/A"
|
||||
if t, err := d.GetCurrentTemp(ctx); err == nil {
|
||||
temp = fmt.Sprintf("%.1fC", t)
|
||||
}
|
||||
mode := "N/A"
|
||||
if m, err := d.GetMode(ctx); err == nil {
|
||||
mode = string(m)
|
||||
}
|
||||
log.Printf("[%d] %-20s %-25s %-18s %-8s 温度=%s 模式=%s",
|
||||
i, info.Name, info.Model, kind, status, temp, mode)
|
||||
case devices.Fan:
|
||||
level := "N/A"
|
||||
if l, err := d.GetFanLevel(ctx); err == nil {
|
||||
level = fmt.Sprintf("档位%d", l)
|
||||
}
|
||||
log.Printf("[%d] %-20s %-25s %-18s %-8s %s",
|
||||
i, info.Name, info.Model, kind, status, level)
|
||||
case devices.Cover:
|
||||
pos := "N/A"
|
||||
if p, err := d.GetPosition(ctx); err == nil {
|
||||
pos = fmt.Sprintf("%d%%", p)
|
||||
}
|
||||
log.Printf("[%d] %-20s %-25s %-18s %-8s 位置=%s",
|
||||
i, info.Name, info.Model, kind, status, pos)
|
||||
default:
|
||||
log.Printf("[%d] %-20s %-25s %-18s %-8s",
|
||||
i, info.Name, info.Model, kind, status)
|
||||
}
|
||||
supportedCount++
|
||||
}
|
||||
log.Printf("共创建 %d 个控制接口(%d 个不支持的类型已跳过)",
|
||||
supportedCount, len(allDevices)-supportedCount)
|
||||
|
||||
// 7. 在线设备摘要
|
||||
log.Println("=== 在线设备摘要 ===")
|
||||
onlineDevices, _ := client.GetDevices(ctx, xiaomi.FilterByOnline())
|
||||
var lines []string
|
||||
for _, d := range onlineDevices {
|
||||
kind := d.DeviceKind()
|
||||
lines = append(lines, fmt.Sprintf(" %s | %s | %s | %s",
|
||||
d.Name, d.Model, kind, d.RoomName))
|
||||
}
|
||||
log.Printf("在线设备 (%d 个):\n%s", len(onlineDevices), strings.Join(lines, "\n"))
|
||||
|
||||
log.Println("高级过滤+分类+工厂演示完毕")
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Package main demonstrates the SPEC parser's independent usage — resolving device
|
||||
//
|
||||
// properties, events, and actions without requiring a device to be online.
|
||||
//
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/miot"
|
||||
"xiaomihome/xiaomi"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
"xiaomihome/xiaomi/specs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 1. 加载配置
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 3. 获取全部设备
|
||||
allDevices, err := client.GetDevices(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("获取设备列表失败:", err)
|
||||
}
|
||||
if len(allDevices) == 0 {
|
||||
log.Fatal("未找到任何设备")
|
||||
}
|
||||
|
||||
// 4. 创建 SPEC 解析器(需要存储目录用于缓存 SPEC 文件)
|
||||
storage := miot.NewMIoTStorage("./data")
|
||||
parser := miot.NewMIoTSpecParser(miot.DEFAULT_INTEGRATION_LANGUAGE, storage, "./data")
|
||||
|
||||
// 5. 对每个设备解析 SPEC 并打印属性/事件/动作
|
||||
for _, dev := range allDevices {
|
||||
if dev.URN == "" || dev.Model == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("=== %s (型号: %s) ===", dev.Name, dev.Model)
|
||||
log.Printf(" URN: %s", dev.URN)
|
||||
|
||||
specInst, err := parser.Parse(dev.URN, dev.Model)
|
||||
if err != nil {
|
||||
log.Printf(" 解析 SPEC 失败: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
resolver := specs.NewPropertyResolver(specInst)
|
||||
log.Printf(" 服务数: %d", len(specInst.Services))
|
||||
|
||||
for _, svc := range specInst.Services {
|
||||
log.Printf(" --- 服务 %d: %s (%s) ---", svc.IID, svc.Type, svc.Description)
|
||||
|
||||
// 属性
|
||||
if len(svc.Properties) > 0 {
|
||||
log.Printf(" 属性 (%d 个):", len(svc.Properties))
|
||||
for _, prop := range svc.Properties {
|
||||
access := accessStr(prop.Access)
|
||||
log.Printf(" [%d/%d] %s type=%s format=%s access=%s",
|
||||
prop.SIID, prop.PIID, prop.Description, prop.Type, prop.Format, access)
|
||||
}
|
||||
}
|
||||
|
||||
// 事件
|
||||
if len(svc.Events) > 0 {
|
||||
log.Printf(" 事件 (%d 个):", len(svc.Events))
|
||||
for _, evt := range svc.Events {
|
||||
log.Printf(" [%d/%d] %s args=%v",
|
||||
evt.SIID, evt.EIID, evt.Type, evt.Arguments)
|
||||
}
|
||||
}
|
||||
|
||||
// 动作
|
||||
if len(svc.Actions) > 0 {
|
||||
log.Printf(" 动作 (%d 个):", len(svc.Actions))
|
||||
for _, act := range svc.Actions {
|
||||
log.Printf(" [%d/%d] %s in=%v out=%v",
|
||||
act.SIID, act.AIID, act.Type, act.In, act.Out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 演示 resolver 查找
|
||||
log.Printf(" --- Resolver 查找示例 ---")
|
||||
if siid, piid, found := resolver.FindByType("on"); found {
|
||||
log.Printf(" FindByType(\"on\"): siid=%d piid=%d ok", siid, piid)
|
||||
} else {
|
||||
log.Printf(" FindByType(\"on\"): not found")
|
||||
}
|
||||
if siid, piid, found := resolver.FindByType("mode"); found {
|
||||
log.Printf(" FindByType(\"mode\"): siid=%d piid=%d ok", siid, piid)
|
||||
}
|
||||
if siid, piid, found := resolver.FindByType("temperature"); found {
|
||||
log.Printf(" FindByType(\"temperature\"): siid=%d piid=%d ok", siid, piid)
|
||||
}
|
||||
if siid, piid, found := resolver.FindByType("brightness"); found {
|
||||
log.Printf(" FindByType(\"brightness\"): siid=%d piid=%d ok", siid, piid)
|
||||
}
|
||||
if siid, piid, found := resolver.FindByType("color-temperature"); found {
|
||||
log.Printf(" FindByType(\"color-temperature\"): siid=%d piid=%d ok", siid, piid)
|
||||
}
|
||||
if siid, piid, found := resolver.FindByType("target-temperature"); found {
|
||||
log.Printf(" FindByType(\"target-temperature\"): siid=%d piid=%d ok", siid, piid)
|
||||
}
|
||||
if siid, piid, found := resolver.FindByType("fan-level"); found {
|
||||
log.Printf(" FindByType(\"fan-level\"): siid=%d piid=%d ok", siid, piid)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("SPEC 解析器演示完毕")
|
||||
}
|
||||
|
||||
func contains(slice []string, s string) bool {
|
||||
for _, v := range slice {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func accessStr(access []string) string {
|
||||
readable := contains(access, "read")
|
||||
writable := contains(access, "write")
|
||||
if readable && writable {
|
||||
return "rw"
|
||||
}
|
||||
if readable {
|
||||
return "r"
|
||||
}
|
||||
if writable {
|
||||
return "w"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Package main demonstrates cover (curtain) device control.
|
||||
// 运行前提:已配置环境变量 XIAOMI_CLIENT_ID / XIAOMI_CLIENT_SECRET / XIAOMI_ACCESS_TOKEN。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
log.Println("=== 窗帘控制 ===")
|
||||
coverList, err := client.GetDevices(ctx, xiaomi.FilterByKind(xiaomi.KindCover))
|
||||
if err != nil {
|
||||
log.Fatalf("获取窗帘列表失败: %v", err)
|
||||
}
|
||||
if len(coverList) == 0 {
|
||||
log.Fatal("未找到窗帘类设备")
|
||||
}
|
||||
|
||||
info := coverList[0]
|
||||
log.Printf("设备: %s (DID: %s, 型号: %s)", info.Name, info.DID, info.Model)
|
||||
|
||||
cv, err := devices.NewCover(client, info)
|
||||
if err != nil {
|
||||
log.Fatalf("创建 Cover 控制失败: %v", err)
|
||||
}
|
||||
|
||||
pos, err := cv.GetPosition(ctx)
|
||||
if err != nil {
|
||||
log.Printf(" 获取位置失败: %v", err)
|
||||
} else {
|
||||
log.Printf(" 当前位置: %d", pos)
|
||||
}
|
||||
|
||||
log.Println(" 打开窗帘...")
|
||||
cv.Open(ctx)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
log.Println(" 设置到 50...")
|
||||
cv.SetPosition(ctx, 50)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
log.Println(" 停止...")
|
||||
cv.Stop(ctx)
|
||||
|
||||
log.Println(" 关闭窗帘...")
|
||||
cv.Close(ctx)
|
||||
|
||||
pos, _ = cv.GetPosition(ctx)
|
||||
log.Printf(" 最终位置: %d", pos)
|
||||
|
||||
log.Println("窗帘演示完毕")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBazCCAQ+gAwIBAgIEA/UKYDAMBggqhkjOPQQDAgUAMCIxEzARBgNVBAoTCk1p
|
||||
amlhIFJvb3QxCzAJBgNVBAYTAkNOMCAXDTE2MTEyMzAxMzk0NVoYDzIwNjYxMTEx
|
||||
MDEzOTQ1WjAiMRMwEQYDVQQKEwpNaWppYSBSb290MQswCQYDVQQGEwJDTjBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABL71iwLa4//4VBqgRI+6xE23xpovqPCxtv96
|
||||
2VHbZij61/Ag6jmi7oZ/3Xg/3C+whglcwoUEE6KALGJ9vccV9PmjLzAtMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFJa3onw5sblmM6n40QmyAGDI5sURMAwGCCqGSM49
|
||||
BAMCBQADSAAwRQIgchciK9h6tZmfrP8Ka6KziQ4Lv3hKfrHtAZXMHPda4IYCIQCG
|
||||
az93ggFcbrG9u2wixjx1HKW4DUA5NXZG0wWQTpJTbQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATWgAwIBAgIBATAKBggqhkjOPQQDAjAiMRMwEQYDVQQKEwpNaWppYSBS
|
||||
b290MQswCQYDVQQGEwJDTjAgFw0yMjA2MDkxNDE0MThaGA8yMDcyMDUyNzE0MTQx
|
||||
OFowLDELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFE1JT1QgQ0VOVFJBTCBHQVRFV0FZ
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdYrzbnp/0x/cZLZnuEDXTFf8mhj4
|
||||
CVpZPwgj9e9Ve5r3K7zvu8Jjj7JF1JjQYvEC6yhp1SzBgglnK4L8xQzdiqNQME4w
|
||||
HQYDVR0OBBYEFCf9+YBU7pXDs6K6CAQPRhlGJ+cuMB8GA1UdIwQYMBaAFJa3onw5
|
||||
sblmM6n40QmyAGDI5sURMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIh
|
||||
AKUv+c8v98vypkGMTzMwckGjjVqTef8xodsy6PhcSCq+AiA/n9mDs62hAo5zXyJy
|
||||
Bs1s7mqXPf1XgieoxIvs1MqyiA==
|
||||
-----END CERTIFICATE-----
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:motion-sensor:0000A014:linp-hs1bb2:1","description":"Motion Sensor","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:linp-hs1bb2:1","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:linp-hs1bb2:1","description":"Device Manufacturer","format":"string","access":["read"]},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:linp-hs1bb2:1","description":"Device Model","format":"string","access":["read"]},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:linp-hs1bb2:1","description":"Device ID","format":"string","access":["read"]},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:linp-hs1bb2:1","description":"Current Firmware Version","format":"string","access":["read"]},{"iid":5,"type":"urn:miot-spec-v2:property:serial-no:000000FF:linp-hs1bb2:1","description":"Serial Number","format":"string","access":["read","notify"]}]},{"iid":2,"type":"urn:miot-spec-v2:service:motion-sensor:00007825:linp-hs1bb2:1","description":"Motion Sensor","properties":[{"iid":1005,"type":"urn:miot-spec-v2:property:illumination:0000004E:linp-hs1bb2:1","description":"Illumination","format":"float","access":["notify","read"],"unit":"lux","value-range":[0,1000,1],"source":1,"gatt-access":[]},{"iid":1024,"type":"urn:miot-spec-v2:property:no-motion-duration:000000CB:linp-hs1bb2:1","description":"No Motion Duration","format":"uint16","access":["notify","read"],"unit":"seconds","value-range":[0,65535,1],"source":1},{"iid":2,"type":"urn:miot-spec-v2:property:custom-detect-interval:0000029C:linp-hs1bb2:1","description":"Custom Detect Interval","format":"uint16","access":[],"unit":"seconds","value-range":[10,50,10],"source":3,"gatt-access":["read","write","notify"]},{"iid":1053,"type":"urn:miot-spec-v2:property:custom-no-motion-time:0000029D:linp-hs1bb2:1","description":"Custom No Motion Time","format":"uint16","access":["notify","read"],"unit":"minutes","value-range":[1,60,1],"source":4,"gatt-access":["read","write","notify"]}],"events":[{"iid":1028,"type":"urn:miot-spec-v2:event:device-be-reset:0000503C:linp-hs1bb2:1","description":"Device Be Reset","arguments":[]},{"iid":1008,"type":"urn:miot-spec-v2:event:motion-detected:00005001:linp-hs1bb2:1","description":"Motion Detected","arguments":[1005]}]},{"iid":3,"type":"urn:miot-spec-v2:service:battery:00007805:linp-hs1bb2:1","description":"Battery","properties":[{"iid":1003,"type":"urn:miot-spec-v2:property:battery-level:00000014:linp-hs1bb2:1","description":"Battery Level","format":"uint8","access":["notify","read"],"unit":"percentage","value-range":[0,100,1],"source":1}]},{"iid":4,"type":"urn:miot-spec-v2:service:indicator-light:00007803:linp-hs1bb2:1","description":"Indicator Light","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:on:00000006:linp-hs1bb2:1","description":"Switch Status","format":"bool","access":[],"source":3,"gatt-access":["read","write","notify"]}]},{"iid":5,"type":"urn:linp-spec:service:custom-cfg:00007801:linp-hs1bb2:1","description":"custom-cfg","properties":[{"iid":1,"type":"urn:linp-spec:property:custom-no-one-time-a:00000001:linp-hs1bb2:1","description":"custom-no-one-time-a","format":"uint16","access":[],"unit":"seconds","value-range":[60,3600,1],"source":3,"gatt-access":["read","write"]},{"iid":2,"type":"urn:linp-spec:property:custom-no-one-time-b:00000002:linp-hs1bb2:1","description":"custom-no-one-time-b","format":"uint16","access":[],"unit":"seconds","value-range":[60,3600,1],"source":3,"gatt-access":["read","write"]},{"iid":3,"type":"urn:linp-spec:property:pir-single-report:00000003:linp-hs1bb2:1","description":"pir-single-report","format":"bool","access":[],"source":3,"gatt-access":["read","write"]}]}]}
|
||||
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:smoke-sensor:0000A023:lumi-mcn02:1","description":"Smoke Sensor","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:lumi-mcn02:1","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:lumi-mcn02:1","description":"Device Manufacturer","format":"string","access":["read"]},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:lumi-mcn02:1","description":"Device Model","format":"string","access":["read"]},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:lumi-mcn02:1","description":"Device Serial Number","format":"string","access":["read"]},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:lumi-mcn02:1","description":"Current Firmware Version","format":"string","access":["read"]}]},{"iid":2,"type":"urn:miot-spec-v2:service:smoke-sensor:00007838:lumi-mcn02:1","description":"Smoke Sensor","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:status:00000007:lumi-mcn02:1","description":"Status","format":"uint8","access":["read","notify"],"value-list":[{"value":1,"description":"Busy"},{"value":2,"description":"Fault"}]}],"events":[{"iid":1,"type":"urn:miot-spec-v2:event:high-concentration-of-smoke-detected:00005027:lumi-mcn02:1","description":"High Concentration Of Smoke Detected","arguments":[]}]},{"iid":3,"type":"urn:miot-spec-v2:service:battery:00007805:lumi-mcn02:1","description":"Battery","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:battery-level:00000014:lumi-mcn02:1","description":"Battery Level","format":"uint8","access":["read","notify"],"unit":"percentage","value-range":[0,100,1]}]}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"type":"urn:miot-spec-v2:device:temperature-humidity-sensor:0000A00A:xiaomi-mini:1:0000D063","description":"xiaomi sensor_ht mini","services":[{"iid":1,"type":"urn:miot-spec-v2:service:device-information:00007801:xiaomi-mini:1:0000D063","description":"Device Information","properties":[{"iid":1,"type":"urn:miot-spec-v2:property:manufacturer:00000001:xiaomi-mini:1:0000D063","description":"Device Manufacturer","format":"string","access":["read"],"source":4,"gatt-access":["read"]},{"iid":2,"type":"urn:miot-spec-v2:property:model:00000002:xiaomi-mini:1:0000D063","description":"Device Model","format":"string","access":["read"],"source":4,"gatt-access":["read"]},{"iid":3,"type":"urn:miot-spec-v2:property:serial-number:00000003:xiaomi-mini:1:0000D063","description":"Device ID","format":"string","access":["read"],"source":4,"gatt-access":["read"]},{"iid":4,"type":"urn:miot-spec-v2:property:firmware-revision:00000005:xiaomi-mini:1:0000D063","description":"Current Firmware Version","format":"string","access":["read"],"source":4,"gatt-access":["read"]},{"iid":5,"type":"urn:miot-spec-v2:property:serial-no:000000FF:xiaomi-mini:1:0000D063","description":"Serial Number","format":"string","access":["read"],"source":4,"gatt-access":["read"]}]},{"iid":2,"type":"urn:miot-spec-v2:service:temperature-humidity-sensor:00007814:xiaomi-mini:1:0000D063","description":"temperature humidity sensor","properties":[{"iid":1002,"type":"urn:miot-spec-v2:property:relative-humidity:0000000C:xiaomi-mini:1:0000D063","description":"Relative Humidity","format":"uint8","access":["read","notify"],"unit":"percentage","value-range":[0,100,1],"source":4,"gatt-access":["read","notify"]},{"iid":1001,"type":"urn:miot-spec-v2:property:temperature:00000020:xiaomi-mini:1:0000D063","description":"Temperature","format":"float","access":["read","notify"],"unit":"celsius","value-range":[-30,100,0.1],"source":4,"gatt-access":["read","notify"]}]},{"iid":3,"type":"urn:miot-spec-v2:service:battery:00007805:xiaomi-mini:1:0000D063","description":"Battery","properties":[{"iid":1003,"type":"urn:miot-spec-v2:property:battery-level:00000014:xiaomi-mini:1:0000D063","description":"Battery Level","format":"uint8","access":["read","notify"],"unit":"percentage","value-range":[0,100,1],"source":4,"gatt-access":["read","notify"]}],"events":[{"iid":1001,"type":"urn:miot-spec-v2:event:low-battery:00005003:xiaomi-mini:1","description":"Low Battery","arguments":[]}]}]}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Package main demonstrates sensor device reading (temp/humidity & occupancy).
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"xiaomihome/xiaomi"
|
||||
"xiaomihome/xiaomi/devices"
|
||||
miotconfig "xiaomihome/miot/examples/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := miotconfig.LoadConfig("../../../miot/examples/config.json")
|
||||
if err != nil {
|
||||
log.Fatal("请先运行 miot/examples/cloud_oauth 获取 token:", err)
|
||||
}
|
||||
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
ClientSecret: "",
|
||||
AccessToken: cfg.AuthInfo.AccessToken,
|
||||
UID: cfg.UID,
|
||||
UUID: cfg.UUID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 获取所有传感器
|
||||
sensorList, err := client.GetDevices(ctx, xiaomi.FilterByKind(xiaomi.KindSensor))
|
||||
if err != nil {
|
||||
log.Fatalf("获取传感器列表失败: %v", err)
|
||||
}
|
||||
if len(sensorList) == 0 {
|
||||
log.Fatal("未找到传感器类设备")
|
||||
}
|
||||
|
||||
log.Printf("共 %d 个传感器\n", len(sensorList))
|
||||
|
||||
for _, info := range sensorList {
|
||||
kind := info.DeviceKind()
|
||||
log.Printf("=== %s (DID: %s, 型号: %s, 种类: %s) ===",
|
||||
info.Name, info.DID, info.Model, kind)
|
||||
|
||||
// 尝试温湿度传感器
|
||||
if ht, err := devices.NewTempHumiditySensor(client, info); err == nil {
|
||||
if temp, err := ht.GetTemperature(ctx); err == nil {
|
||||
log.Printf(" 温度: %.1f°C", temp)
|
||||
}
|
||||
if hum, err := ht.GetHumidity(ctx); err == nil {
|
||||
log.Printf(" 湿度: %.0f%%", hum)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 尝试人在传感器
|
||||
if occ, err := devices.NewOccupancySensor(client, info); err == nil {
|
||||
if occupied, err := occ.IsOccupied(ctx); err == nil {
|
||||
log.Printf(" 有人: %v", occupied)
|
||||
}
|
||||
if lux, err := occ.GetIlluminance(ctx); err == nil {
|
||||
log.Printf(" 光照: %.0f lux", lux)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf(" (无支持的传感器协议)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user