- 新增 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 示例(风扇/窗帘/传感器控制)
133 lines
3.2 KiB
Go
133 lines
3.2 KiB
Go
package xiaomi
|
||
|
||
import "context"
|
||
|
||
// GetHomeList 获取所有家庭列表
|
||
// 合并自有家庭 (home_list) 和共享家庭 (share_home_list)
|
||
func (c *Client) GetHomeList(ctx context.Context) ([]*HomeInfo, error) {
|
||
data, err := c.http.GetHomeInfos()
|
||
if err != nil {
|
||
return nil, wrapErr("GetHomeList", "", err)
|
||
}
|
||
|
||
var homes []*HomeInfo
|
||
|
||
// 解析自有家庭
|
||
if homeMap, ok := data["home_list"].(map[string]interface{}); ok {
|
||
for _, v := range homeMap {
|
||
if hm, ok := v.(map[string]interface{}); ok {
|
||
homes = append(homes, parseHomeInfo(hm, false))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 解析共享家庭
|
||
if shareMap, ok := data["share_home_list"].(map[string]interface{}); ok {
|
||
for _, v := range shareMap {
|
||
if hm, ok := v.(map[string]interface{}); ok {
|
||
homes = append(homes, parseHomeInfo(hm, true))
|
||
}
|
||
}
|
||
}
|
||
|
||
return homes, nil
|
||
}
|
||
|
||
// GetHome 获取指定家庭详情(含房间)
|
||
func (c *Client) GetHome(ctx context.Context, homeID string) (*HomeInfo, error) {
|
||
homes, err := c.GetHomeList(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for _, h := range homes {
|
||
if h.ID == homeID {
|
||
return h, nil
|
||
}
|
||
}
|
||
|
||
return nil, ErrHomeNotFound
|
||
}
|
||
|
||
// GetRooms 获取指定家庭的所有房间
|
||
func (c *Client) GetRooms(ctx context.Context, homeID string) ([]*RoomInfo, error) {
|
||
home, err := c.GetHome(ctx, homeID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
rooms := make([]*RoomInfo, len(home.Rooms))
|
||
for i := range home.Rooms {
|
||
rooms[i] = &home.Rooms[i]
|
||
}
|
||
return rooms, nil
|
||
}
|
||
|
||
// GetRoomDevices 获取指定房间的设备 DID 列表。
|
||
// 优先使用 homeroom API 返回的 dids,为空时从设备列表按 room_id 过滤。
|
||
func (c *Client) GetRoomDevices(ctx context.Context, homeID, roomID string) ([]string, error) {
|
||
rooms, err := c.GetRooms(ctx, homeID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for _, r := range rooms {
|
||
if r.ID == roomID {
|
||
if len(r.DidList) > 0 {
|
||
return r.DidList, nil
|
||
}
|
||
break
|
||
}
|
||
}
|
||
|
||
// 退化:从设备列表按 room_id 过滤
|
||
devices, err := c.GetDevices(ctx, FilterByRoom(roomID))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
dids := make([]string, 0, len(devices))
|
||
for _, d := range devices {
|
||
dids = append(dids, d.DID)
|
||
}
|
||
return dids, nil
|
||
}
|
||
|
||
// parseHomeInfo 从 miot 返回的 map 解析 HomeInfo(含房间列表)
|
||
func parseHomeInfo(m map[string]interface{}, shared bool) *HomeInfo {
|
||
h := &HomeInfo{
|
||
ID: strVal(m, "home_id"),
|
||
Name: strVal(m, "home_name"),
|
||
UID: strVal(m, "uid"),
|
||
GroupID: strVal(m, "group_id"),
|
||
CityID: strVal(m, "city_id"),
|
||
Address: strVal(m, "address"),
|
||
ShareState: shared,
|
||
}
|
||
|
||
// 地理位置
|
||
if v, ok := m["longitude"]; ok {
|
||
h.Longitude = toFloat64(v)
|
||
}
|
||
if v, ok := m["latitude"]; ok {
|
||
h.Latitude = toFloat64(v)
|
||
}
|
||
|
||
// 解析设备列表
|
||
h.DidList = strSliceVal(m, "dids")
|
||
|
||
// 解析房间(room_info 是 map[string]interface{},key=room_id)
|
||
if roomMap, ok := m["room_info"].(map[string]interface{}); ok {
|
||
for _, v := range roomMap {
|
||
if rm, ok := v.(map[string]interface{}); ok {
|
||
h.Rooms = append(h.Rooms, RoomInfo{
|
||
ID: strVal(rm, "room_id"),
|
||
Name: strVal(rm, "room_name"),
|
||
HomeID: h.ID,
|
||
DidList: strSliceVal(rm, "dids"),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
return h
|
||
}
|