- 新增 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 示例(风扇/窗帘/传感器控制)
89 lines
2.0 KiB
Go
89 lines
2.0 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|