主要变更: **类型系统重构** - HomeInfo.ID/RoomInfo.ID/HomeID 从 string 改为 int64,json tag 使用 ,string - GetHome/GetRooms/GetRoomDevices 接口同步修改 - 新增 int64FromStr 工具函数,更新 parseHomeInfo 解析逻辑 **新增 Speaker 设备** - 新增 KindSpeaker 类型 - classify 映射覆盖 speaker/bluetooth-speaker/alarm-clock/media-player/playback/music **设备驱动重构** - AC 驱动彻底重写:从硬编码常量改为基于 SPEC ValueList 的动态 valMapper - 所有设备(Fan/Humidifier/Light/OccupancySensor/Switch/TempHumiditySensor/Thermostat/Vacuum/WaterHeater/Cover)新增 GetState() 和 FetchState(ctx) 接口 - base.go 新增 specResolver.format() 方法 **MIoT 模块增强** - MIoTClient 新增 homeIDs 字段,支持按家庭过滤设备 - RefreshDevices 支持 homeIDs 参数 - SpecStdLib 引入 logger.Logger,log.Printf 全部迁移为结构化日志 **其他** - Config 新增 RedirectURL 和 HomeIDs 字段 - AuthInfo.ExpiresIn 类型从 int 改为 int32 - UUID 生成简化为 hex.EncodeToString - 新增 github.com/sirupsen/logrus 依赖 - .gitignore 新增 examples/*/data/ 忽略规则 - 删除已提交的 config.json 敏感配置
169 lines
3.9 KiB
Go
169 lines
3.9 KiB
Go
package xiaomi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
)
|
|
|
|
func TestDeviceInfoParsing(t *testing.T) {
|
|
data := map[string]interface{}{
|
|
"name": "客厅空调",
|
|
"model": "xiaomi.aircondition.c1",
|
|
"manufacturer": "Xiaomi",
|
|
"fw_version": "2.1.0",
|
|
"icon": "https://example.com/icon.png",
|
|
"urn": "urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c1:1",
|
|
"token": "abc123token",
|
|
"local_ip": "192.168.1.100",
|
|
"rssi": float64(-55),
|
|
"ssid": "MyWiFi",
|
|
"bssid": "AA:BB:CC:DD:EE:FF",
|
|
"online": true,
|
|
"home_id": "home1",
|
|
"home_name": "我的家",
|
|
"room_id": "room1",
|
|
"room_name": "客厅",
|
|
"owner_id": "user1",
|
|
"parent_id": "",
|
|
"parent_model": "",
|
|
"group_id": "group1",
|
|
"connect_type": float64(0),
|
|
}
|
|
|
|
info := parseDeviceInfo("lumi.12345", data)
|
|
|
|
if info.DID != "lumi.12345" {
|
|
t.Errorf("DID = %q, want lumi.12345", info.DID)
|
|
}
|
|
if info.Name != "客厅空调" {
|
|
t.Errorf("Name = %q", info.Name)
|
|
}
|
|
if info.Model != "xiaomi.aircondition.c1" {
|
|
t.Errorf("Model = %q", info.Model)
|
|
}
|
|
if info.IP != "192.168.1.100" {
|
|
t.Errorf("IP = %q", info.IP)
|
|
}
|
|
if info.RSSI != -55 {
|
|
t.Errorf("RSSI = %d", info.RSSI)
|
|
}
|
|
if !info.Online {
|
|
t.Error("Online should be true")
|
|
}
|
|
if info.HomeID != "home1" {
|
|
t.Errorf("HomeID = %q", info.HomeID)
|
|
}
|
|
if info.RoomName != "客厅" {
|
|
t.Errorf("RoomName = %q", info.RoomName)
|
|
}
|
|
}
|
|
|
|
func TestDeviceInfoEmpty(t *testing.T) {
|
|
info := parseDeviceInfo("test.did", map[string]interface{}{})
|
|
if info.DID != "test.did" {
|
|
t.Errorf("DID = %q, want test.did", info.DID)
|
|
}
|
|
if info.Name != "" {
|
|
t.Errorf("Name should be empty, got %q", info.Name)
|
|
}
|
|
if info.Online {
|
|
t.Error("Online should be false")
|
|
}
|
|
}
|
|
|
|
func TestHomeInfoParsing(t *testing.T) {
|
|
data := map[string]interface{}{
|
|
"home_id": "123",
|
|
"home_name": "我的家",
|
|
"uid": "user001",
|
|
"group_id": "group001",
|
|
"dids": []interface{}{"device1", "device2"},
|
|
"room_info": map[string]interface{}{
|
|
"1": map[string]interface{}{
|
|
"room_id": "1",
|
|
"room_name": "客厅",
|
|
"dids": []interface{}{"device1"},
|
|
},
|
|
"2": map[string]interface{}{
|
|
"room_id": "2",
|
|
"room_name": "卧室",
|
|
"dids": []interface{}{"device2"},
|
|
},
|
|
},
|
|
}
|
|
|
|
home := parseHomeInfo(data, true)
|
|
|
|
if home.ID != 123 {
|
|
t.Errorf("ID = %d", home.ID)
|
|
}
|
|
if home.Name != "我的家" {
|
|
t.Errorf("Name = %q", home.Name)
|
|
}
|
|
if len(home.DidList) != 2 {
|
|
t.Errorf("DidList len = %d, want 2", len(home.DidList))
|
|
}
|
|
if len(home.Rooms) != 2 {
|
|
t.Errorf("Rooms len = %d, want 2", len(home.Rooms))
|
|
}
|
|
names := map[string]bool{"客厅": false, "卧室": false}
|
|
for _, r := range home.Rooms {
|
|
names[r.Name] = true
|
|
if r.HomeID != 123 {
|
|
t.Errorf("Room %s HomeID = %d, want 123", r.Name, r.HomeID)
|
|
}
|
|
}
|
|
if !names["客厅"] || !names["卧室"] {
|
|
t.Errorf("Expected rooms 客厅+卧室, got %v", names)
|
|
}
|
|
}
|
|
|
|
func TestUserInfoParsing(t *testing.T) {
|
|
data := map[string]interface{}{
|
|
"uid": "123456",
|
|
"miliao_nick": "测试用户",
|
|
"avatar_url": "https://example.com/avatar.jpg",
|
|
}
|
|
|
|
user := &UserInfo{
|
|
UID: strVal(data, "uid"),
|
|
NickName: strVal(data, "miliao_nick"),
|
|
AvatarURL: strVal(data, "avatar_url"),
|
|
}
|
|
|
|
if user.UID != "123456" {
|
|
t.Errorf("UID = %q", user.UID)
|
|
}
|
|
if user.NickName != "测试用户" {
|
|
t.Errorf("NickName = %q", user.NickName)
|
|
}
|
|
}
|
|
|
|
func TestJSONSerialization(t *testing.T) {
|
|
info := &DeviceInfo{
|
|
DID: "test.did",
|
|
Name: "测试设备",
|
|
Model: "test.model",
|
|
Online: true,
|
|
RSSI: -60,
|
|
IP: "192.168.1.1",
|
|
}
|
|
|
|
data, err := json.Marshal(info)
|
|
if err != nil {
|
|
t.Fatalf("json.Marshal: %v", err)
|
|
}
|
|
|
|
var restored DeviceInfo
|
|
if err := json.Unmarshal(data, &restored); err != nil {
|
|
t.Fatalf("json.Unmarshal: %v", err)
|
|
}
|
|
|
|
if restored.DID != info.DID {
|
|
t.Errorf("DID mismatch: %q vs %q", restored.DID, info.DID)
|
|
}
|
|
if restored.Online != info.Online {
|
|
t.Error("Online mismatch")
|
|
}
|
|
}
|