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,88 @@
|
||||
package xiaomi
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFilterByHome(t *testing.T) {
|
||||
f := FilterByHome("home1")
|
||||
if !f(&DeviceInfo{HomeID: "home1"}) {
|
||||
t.Error("FilterByHome(home1) should match home1")
|
||||
}
|
||||
if f(&DeviceInfo{HomeID: "home2"}) {
|
||||
t.Error("FilterByHome(home1) should not match home2")
|
||||
}
|
||||
if f(&DeviceInfo{HomeID: ""}) {
|
||||
t.Error("FilterByHome(home1) should not match empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterByRoom(t *testing.T) {
|
||||
f := FilterByRoom("room1")
|
||||
if !f(&DeviceInfo{RoomID: "room1"}) {
|
||||
t.Error("FilterByRoom(room1) should match room1")
|
||||
}
|
||||
if f(&DeviceInfo{RoomID: "room2"}) {
|
||||
t.Error("FilterByRoom(room1) should not match room2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterByOnline(t *testing.T) {
|
||||
f := FilterByOnline()
|
||||
if !f(&DeviceInfo{Online: true}) {
|
||||
t.Error("FilterByOnline should match online=true")
|
||||
}
|
||||
if f(&DeviceInfo{Online: false}) {
|
||||
t.Error("FilterByOnline should not match online=false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterByModel(t *testing.T) {
|
||||
f := FilterByModel("xiaomi.light.v1")
|
||||
if !f(&DeviceInfo{Model: "xiaomi.light.v1"}) {
|
||||
t.Error("FilterByModel should match exact model")
|
||||
}
|
||||
if f(&DeviceInfo{Model: "xiaomi.light.v2"}) {
|
||||
t.Error("FilterByModel should not match different model")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceFilterComposition(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
device *DeviceInfo
|
||||
filters []DeviceFilter
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
"all pass",
|
||||
&DeviceInfo{HomeID: "home1", RoomID: "room1", Online: true},
|
||||
[]DeviceFilter{FilterByHome("home1"), FilterByRoom("room1"), FilterByOnline()},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"one fails",
|
||||
&DeviceInfo{HomeID: "home1", RoomID: "room2", Online: true},
|
||||
[]DeviceFilter{FilterByHome("home1"), FilterByRoom("room1"), FilterByOnline()},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no filters",
|
||||
&DeviceInfo{HomeID: "home1"},
|
||||
nil,
|
||||
true, // empty filters = no-op
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ok := true
|
||||
for _, f := range tt.filters {
|
||||
if !f(tt.device) {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok != tt.want {
|
||||
t.Errorf("got %v, want %v", ok, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user