feat: AC 三合一设备支持 + 新增 Plug/Gateway 驱动 + Cover 结构化重构
**AC 驱动增强** - 支持 thermostat + air-conditioner + air-fresh 多服务三合一设备,属性跨服务合并解析 - 新增 specResolver.findValListInService/findInService/hasService/serviceShortName 方法 - 移除 KindThermostat,heater/electric-blanket/thermostat 重新归类为 KindAirConditioner **Cover 窗帘重构** - CoverProps 结构化:Position/Status/Fault 分离 - SetPosition 优先写 target-position,回退 current-position - OnPropsChanged/FetchProps 支持多属性同步刷新 **新增设备类型** - Plug 驱动:插座/插头,支持开合、在线状态、属性变更订阅 - Gateway 驱动:网关设备类型识别 - miot/miot_errors.go:新增 MIoT 云端错误码定义 **MIoT 客户端健壮性** - SetProp 空结果视为失败;设备被移除或离线时自动刷新设备列表 - GetDevices 的 has_more 改用 parseBool 严格解析 - MIPS SubscribeMany 批量订阅减少往返 - matchURN 改为按段精确匹配,避免 tofan 误匹配 fan **Bridge** - 移除 Thermostat case,Cover 改用结构化 CoverProps - 新增设备创建后自动订阅属性变更 **其他** - 日志整体 Info → Debug 降噪 - .gitignore 忽略 /.codebuddy/memory - 更新 devices_test 与 08_adv_filter_classify 示例
This commit is contained in:
+43
-19
@@ -25,7 +25,6 @@ const (
|
||||
mipsQoS = 2
|
||||
mipsReconnectIntervalMin = 10.0 // seconds
|
||||
mipsReconnectIntervalMax = 600.0 // seconds
|
||||
mipsSubPatch = 300
|
||||
mipsSubInterval = 1 // second
|
||||
mipsMQTTInterval = 1 // second
|
||||
uint32max = 0xFFFFFFFF
|
||||
@@ -269,6 +268,7 @@ type MQTTClient interface {
|
||||
Disconnect()
|
||||
IsConnected() bool
|
||||
Subscribe(topic string, qos byte, handler func(topic string, payload []byte)) error
|
||||
SubscribeMany(topics map[string]byte) error
|
||||
Unsubscribe(topic string) error
|
||||
Publish(topic string, qos byte, payload []byte) error
|
||||
SetCredentials(username, password string)
|
||||
@@ -528,34 +528,42 @@ func (c *mipsClient) flushPendingSubs() {
|
||||
return
|
||||
}
|
||||
|
||||
// 收集所有待订阅 topic,一次 SUBSCRIBE 包批量订阅(大幅减少网络往返)
|
||||
c.pendingSubMu.Lock()
|
||||
subbed := 0
|
||||
batch := make(map[string]byte, len(c.pendingSubs))
|
||||
for topic, count := range c.pendingSubs {
|
||||
if subbed >= mipsSubPatch {
|
||||
break
|
||||
}
|
||||
if count > 3 {
|
||||
delete(c.pendingSubs, topic)
|
||||
c.lgr.Debugf("[mips] retry sub exceeded: %s", topic)
|
||||
continue
|
||||
}
|
||||
subbed++
|
||||
|
||||
if err := c.mqttConn.Subscribe(topic, byte(mipsQoS), func(t string, p []byte) {
|
||||
c.onMQTTMessage(t, p)
|
||||
}); err == nil {
|
||||
delete(c.pendingSubs, topic)
|
||||
c.lgr.Debugf("[mips] sub success: %s", topic)
|
||||
} else {
|
||||
c.pendingSubs[topic] = count + 1
|
||||
c.lgr.Debugf("[mips] retry sub %d: %s %v", count, topic, err)
|
||||
}
|
||||
batch[topic] = byte(mipsQoS)
|
||||
}
|
||||
|
||||
hasMore := len(c.pendingSubs) > 0
|
||||
c.pendingSubMu.Unlock()
|
||||
|
||||
if hasMore {
|
||||
if len(batch) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.mqttConn.SubscribeMany(batch); err == nil {
|
||||
c.pendingSubMu.Lock()
|
||||
for t := range batch {
|
||||
delete(c.pendingSubs, t)
|
||||
c.lgr.Debugf("[mips] sub success: %s", t)
|
||||
}
|
||||
hasMore := len(c.pendingSubs) > 0
|
||||
c.pendingSubMu.Unlock()
|
||||
if hasMore {
|
||||
time.Sleep(time.Duration(mipsSubInterval) * time.Second)
|
||||
c.flushPendingSubs()
|
||||
}
|
||||
} else {
|
||||
c.lgr.Debugf("[mips] batch sub error: %v", err)
|
||||
c.pendingSubMu.Lock()
|
||||
for t := range batch {
|
||||
c.pendingSubs[t] = c.pendingSubs[t] + 1
|
||||
}
|
||||
c.pendingSubMu.Unlock()
|
||||
time.Sleep(time.Duration(mipsSubInterval) * time.Second)
|
||||
c.flushPendingSubs()
|
||||
}
|
||||
@@ -1042,6 +1050,22 @@ func (p *pahoMQTTClient) Subscribe(topic string, qos byte, handler func(topic st
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubscribeMany 用单个 SUBSCRIBE 包批量订阅多个 topic,显著减少网络往返。
|
||||
// 消息分发仍走 DefaultPublishHandler,与 Subscribe 行为一致。
|
||||
func (p *pahoMQTTClient) SubscribeMany(topics map[string]byte) error {
|
||||
if p.client == nil || !p.client.IsConnected() {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
if len(topics) == 0 {
|
||||
return nil
|
||||
}
|
||||
token := p.client.SubscribeMultiple(topics, nil)
|
||||
if token.Wait() && token.Error() != nil {
|
||||
return token.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pahoMQTTClient) Unsubscribe(topic string) error {
|
||||
if p.client == nil || !p.client.IsConnected() {
|
||||
return fmt.Errorf("not connected")
|
||||
|
||||
Reference in New Issue
Block a user