feat: 完善 AC"set 后回读"机制 — 目标聚合 + 防抖重试验证
**回读目标聚合(recordReadback)** - 所有 set 方法(TurnOn/TurnOff/SetMode/SetTargetTemp/SetFanSpeed/ SetSwing/SetTargetHumidity/SetAuxHeat/SetEco)成功后记录目标值 rbTarget - 同属性目标覆盖、不同属性追加,连续 set 自动聚合 **防抖 + 重试验证(readbackLoop)** - 静默窗口 readbackIdle=3s:等待最后一次 set 无新操作才开始验证 - 未命中目标时按 readbackRetryInterval=3s 重试,上限 readbackMaxRetries=5 - 整体超时 readbackTimeout=30s,防云端卡住导致回读永久挂起 - 命中后逐字段回调 OnPropsChanged 注册的 handler(与 MQTT 推送同语义) - rbValueEqual 数值比较用 epsilon=0.01,bool/string 直接相等 **白名单配置透传** - xiaomi.Config 新增 ReadbackModels 白名单(留空 = 全部不回读,默认) - Client 新增 NeedReadback(model) 判定 + Logger() 访问器 - bridge.Pool.NewPool(readbackModels...) 透传;newBridge 传入 Client 配置 **其他** - bridge 设备创建失败(Warn)与不支持跳过(Debug)分别记录日志
This commit is contained in:
+23
-12
@@ -45,13 +45,18 @@ type OccupancyProps struct {
|
||||
|
||||
// Pool 全局路由:id → Bridge。
|
||||
type Pool struct {
|
||||
mu sync.RWMutex
|
||||
items map[int64]*Bridge
|
||||
mu sync.RWMutex
|
||||
items map[int64]*Bridge
|
||||
readbackModels []string // 需要 set 后回读的型号列表(透传到 xiaomi.Config)
|
||||
}
|
||||
|
||||
// NewPool 创建 Pool。
|
||||
func NewPool() *Pool {
|
||||
return &Pool{items: make(map[int64]*Bridge)}
|
||||
// NewPool 创建 Pool。readbackModels 为需要"set 后回读"的设备型号列表(可选,默认空)。
|
||||
func NewPool(readbackModels ...[]string) *Pool {
|
||||
var models []string
|
||||
if len(readbackModels) > 0 {
|
||||
models = readbackModels[0]
|
||||
}
|
||||
return &Pool{items: make(map[int64]*Bridge), readbackModels: models}
|
||||
}
|
||||
|
||||
// Get 按 id 查找。
|
||||
@@ -69,7 +74,7 @@ func (p *Pool) Add(ctx context.Context, id int64, uuid string, auth xiaomi.AuthI
|
||||
if _, exists := p.items[id]; exists {
|
||||
return nil, fmt.Errorf("bridge: %d already exists", id)
|
||||
}
|
||||
b, err := newBridge(ctx, id, uuid, auth, dataDir, homeIDs)
|
||||
b, err := newBridge(ctx, id, uuid, auth, dataDir, homeIDs, p.readbackModels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -115,14 +120,15 @@ type Bridge struct {
|
||||
onOnlineChanged func(id int64, did string, online bool)
|
||||
}
|
||||
|
||||
func newBridge(ctx context.Context, id int64, uuid string, auth xiaomi.AuthInfo, dataDir string, homeIDs []int64) (*Bridge, error) {
|
||||
func newBridge(ctx context.Context, id int64, uuid string, auth xiaomi.AuthInfo, dataDir string, homeIDs []int64, readbackModels []string) (*Bridge, error) {
|
||||
idStr := fmt.Sprintf("%d", id)
|
||||
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
|
||||
AuthInfo: auth,
|
||||
DataDir: dataDir,
|
||||
UUID: uuid,
|
||||
UID: idStr,
|
||||
HomeIDs: homeIDs,
|
||||
AuthInfo: auth,
|
||||
DataDir: dataDir,
|
||||
UUID: uuid,
|
||||
UID: idStr,
|
||||
HomeIDs: homeIDs,
|
||||
ReadbackModels: readbackModels,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bridge: %w", err)
|
||||
@@ -144,6 +150,11 @@ func newBridge(ctx context.Context, id int64, uuid string, auth xiaomi.AuthInfo,
|
||||
for _, info := range devList {
|
||||
d, createErr := devices.Create(client, info)
|
||||
if createErr != nil || d == nil {
|
||||
if createErr != nil {
|
||||
b.lgr.Warnf("[bridge] %d: create device %s (%s) failed: %v", id, info.DID, info.Model, createErr)
|
||||
} else {
|
||||
b.lgr.Debugf("[bridge] %d: device %s (%s) unsupported, skipped", id, info.DID, info.Model)
|
||||
}
|
||||
continue
|
||||
}
|
||||
b.dv[info.DID] = d
|
||||
|
||||
Reference in New Issue
Block a user