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:
2026-06-29 08:51:21 +08:00
parent 35efed7d15
commit a3d94c4b9f
93 changed files with 7582 additions and 157 deletions
+108
View File
@@ -0,0 +1,108 @@
package xiaomi
import "xiaomihome/miot"
// ========== 属性订阅 ==========
// SubProp 订阅设备属性变化通知
// 内部调用 miot.MIoTClient.SubProp,在 adapter handler 中完成 map→PropertyValue 转换
// siid/piid 为 0 表示订阅该设备所有属性
// 返回 subscription ID(与 miot.SubProp 一致)
func (c *Client) SubProp(did string, siid, piid int, handler PropHandler) (string, error) {
wrappedHandler := func(params map[string]interface{}, ctx interface{}) {
prop := &PropertyValue{
DID: strVal(params, "did"),
SIID: intVal(params, "siid"),
PIID: intVal(params, "piid"),
Code: intVal(params, "code"),
Value: params["value"],
Type: strVal(params, "type"),
}
handler(did, prop)
}
subID := c.inner.SubProp(did, wrappedHandler, siid, piid)
c.mu.Lock()
c.propSubs[subID] = did
c.mu.Unlock()
return subID, nil
}
// UnsubProp 取消属性订阅
func (c *Client) UnsubProp(did string, subID string) error {
c.inner.UnsubProp(did, subID)
c.mu.Lock()
delete(c.propSubs, subID)
c.mu.Unlock()
return nil
}
// ========== 事件订阅 ==========
// SubEvent 订阅设备事件通知
// 内部调用 miot.MIoTClient.SubEvent,完成 map→EventOccurred 转换
func (c *Client) SubEvent(did string, siid, eiid int, handler EventHandler) (string, error) {
wrappedHandler := func(params map[string]interface{}, ctx interface{}) {
event := &EventOccurred{
DID: strVal(params, "did"),
SIID: intVal(params, "siid"),
EIID: intVal(params, "eiid"),
Timestamp: int64(intVal(params, "timestamp")),
}
if args, ok := params["arguments"].(map[string]interface{}); ok {
event.Arguments = args
}
handler(did, event)
}
subID := c.inner.SubEvent(did, wrappedHandler, siid, eiid)
c.mu.Lock()
c.eventSubs[subID] = did
c.mu.Unlock()
return subID, nil
}
// UnsubEvent 取消事件订阅
func (c *Client) UnsubEvent(did string, subID string) error {
c.inner.UnsubEvent(did, subID)
c.mu.Lock()
delete(c.eventSubs, subID)
c.mu.Unlock()
return nil
}
// ========== 设备状态订阅 ==========
// SubDeviceState 订阅设备在线状态变化
// 内部调用 miot.MIoTClient.SubDeviceState,完成 MIoTDeviceState→DeviceState 转换
func (c *Client) SubDeviceState(did string, handler DeviceStateHandler) error {
wrappedHandler := func(did string, state miot.MIoTDeviceState, ctx interface{}) {
var ds DeviceState
switch state {
case miot.DeviceStateOnline:
ds = StateOnline
case miot.DeviceStateOffline:
ds = StateOffline
default:
return // DeviceStateDisable 忽略
}
handler(did, ds)
}
return c.inner.SubDeviceState(did, wrappedHandler)
}
// UnsubDeviceState 取消设备状态订阅
func (c *Client) UnsubDeviceState(did string) error {
return c.inner.UnsubDeviceState(did)
}