node-red-contrib-symi-mesh 1.9.10 → 1.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -681,70 +681,20 @@ RS485通信桥接,支持Modbus协议透传与自定义指令映射。
681
681
 
682
682
  ## 更新日志
683
683
 
684
- ### v1.9.10 (2026-03-30)
685
-
686
- #### 发布包安装可靠性(必须)
687
- - 修复 `package.json / package-lock.json` 中误将自身 `node-red-contrib-symi-mesh-1.9.9.tgz` 写成 `file:` 本地依赖的问题;删除该错误引用后,`npm install node-red-contrib-symi-mesh@1.9.10` 会走正常 registry 安装流程,避免客户侧 `tarball data ... corrupted` / `ENOENT ... .tgz` 安装失败。
688
-
689
- ### v1.9.9 (2026-03-28,2026-03-30 行为补充)
690
-
691
- #### KNX 桥同步可靠性(推荐所有 KNX 项目升级)
692
- - **Mesh KNX 后的对称主控**:`Mesh→KNX` 反馈确认成功后,在数秒内登记 **Mesh 侧意图**;独立 **状态地址**上与该意图**相反**的滞后电报**不再**触发「校准到 Mesh」,避免「刚关又被状态反馈拉成开」等误动作。
693
- - **KNX 面板优先**:发起 **KNX→Mesh** 开关时自动清除上述 Mesh 主控记录,避免挡住真实面板操作。
694
- - **去重**:状态反馈若与**尚在确认中**的 **KNX→Mesh** 目标一致,**不再重复**下发同向同步。
695
- - **场景遮蔽(SceneVeil)**:收到 **KNX 场景、按键场景**或网关 **场景执行**事件后,默认约 **6 秒**内**仅因状态地址与缓存不一致**时**不会**向 Mesh 批量拉闸校准,减轻场景执行后的总线反馈风暴;时长可通过环境变量 **`SYMI_KNX_SCENE_VEIL_MS`**(毫秒,例如 `8000`)调整。
696
- - **Last Write Wins(LWW)**:**默认关闭**;仅在需要「KNX 强纠偏」的现场设 **`SYMI_KNX_LWW_ENABLED=1`** 恢复此前行为。
697
- - **DelayedSync 与 Mesh 查询回包(2026-03-30)**:开启「自动同步状态」时,步骤 1 会对 Mesh `0x32` 并登记 `pendingMeshQueries`。在**延迟约 1 秒后进入 KNX GroupValue_Read 阶段时**会**清除**本轮待查标记;**约 650ms** 再做一次带时间戳的早清(`MESH_QUERY_PENDING_EARLY_CLEAR_MS`);「查询回包」抑制窗口 **900ms**(`MESH_QUERY_RESPONSE_SUPPRESS_MS`)。**仅当**网关把查询回报误标为 **`isUserControl===true`** 时才改为 `false`,避免把米家/App 已标为用户的帧一律压成非用户而导致 **`正确忽略Mesh反馈`**、**Mesh→KNX 不写字**。
698
- - **DelayedSync 校准下发**:校准仅使用已存在的 **`gateway.sendControl`**,不再调用不存在的 `sendSwitchCommand`(否则日志会出现 `sendSwitchCommand is not a function` 且校准失败)。
699
- - **MQTT 节点防崩**:修复 `simi-mqtt` MQTT `connack timeout` 阶段可能触发 `Uncaught Exception` 的问题;通过保留 `error` 监听与增加 `connectTimeout` 兜底,避免单次握手失败导致 Node-RED 侧异常退出。
700
- - **「自动同步状态」勾选与运行一致**:`symi-knx-bridge` 运行时与编辑器对 **`autoSyncEnabled`** `true` / `1` / `"1"` / `"on"` 等同开启,避免界面看似关闭仍执行 DelayedSync。
701
- - **现场日志说明**:某项目 1.9.8 原始日志与问题列表见 **`docs/日志.md`**(含客户未主动描述但在日志中可见的现象)。
702
-
703
- ### v1.9.8 (2026-03-23)
704
-
705
- #### 稳定性、协议兼容性与合规性增强
706
- - **[引擎升级]** 锁定 Node.js >= 22.x、NPM >= 10.x、Node-RED >= 4.x LTS 版本,保证系统底层性能和安全性。
707
- - **[协议兼容与高并发]** 经过与 AWS IoT Core、Azure IoT Hub、Aliyun IoT、Matter 1.3、Thread 1.4 的双向通信回归测试,通过了 10,000 次高并发消息验证,实现 100% 一致性与 QoS 2 幂等性。
708
- - **[抗丢包与故障恢复]** 在 5% 随机丢包与 200ms 抖动下持续 24 小时运行测试通过,节点利用指数退避、去重缓存和生命周期 ACK 实现 **零死循环、零内存泄漏、零丢失**。
709
- - **[自动同步 (DelayedSync) 增强与防死循环闭环]** 优化了 `symi-knx-bridge` 中的自动同步状态机制。在接收到 KNX 控制后,节点会在设定的延迟时间后发起 `GroupValue_Read` 检查状态,若发现 Mesh 状态异常,节点会下发带有 `isCalibration: true` 标记的补发指令。新版本完美处理了此校准指令的生命周期,既保证了最终状态一致,又严格通过时间戳拦截了因校准动作可能引发的 Mesh 反向回流,实现状态的无感自动纠错与死循环阻断。
710
- - **[双向防死循环拦截击穿修复]** 全面修复了在 `symi-knx-bridge`、`symi-ha-sync`、`symi-485-bridge`、`symi-rs485-sync` 等节点中,物理按键被异常拦截的问题。现在当检测到**本地物理按键控制(`isUserControl=true`)**时,将**强制跳过/击穿**全局及单设备的“防死循环(Anti-Loop)”和“期望状态(Last Write Wins)”锁定窗口,确保用户的最后一次实体面板操作能无阻碍地即刻同步并覆盖 HA / KNX 的状态,解决了“快速连按导致状态被错误弹回”的顽疾。
711
- - **[安全与代码规范]** 修复 5 个底层依赖 CVE 漏洞(npm audit fix),全部节点代码通过 `eslint` 严格验证(0 Error / 0 Warning),杜绝动态代码注入等安全隐患。
712
-
713
- ### v1.9.7 (2026-02-28)
714
-
715
- #### 自动状态校准 (AutoSync) 与状态查询逻辑
716
- - **KNX Bridge「自动同步状态」**:开启后,Mesh 或 KNX 动作触发可配置延迟(默认 3s);在规定时间内完成:先对涉及设备发 `0x32` 查 Mesh 状态,约 1s 后发 KNX GroupValue_Read,收到 GroupValue_Response 后与 Mesh 比对,**不一致则仅同步到 Mesh(校准),一致则仅更新缓存不下发**;校准命令带 `isCalibration`,不触发反向 KNX 控制。
717
- - **KNX 状态查询不改变总线状态**:仅发送 GroupValue_Read、接收 Response,绝不向 KNX 总线写 Write,查询不会改变 KNX 开关状态。
718
- - **GroupValue_Read 处理**:严格区分 Read/Response/Write;对 GroupValue_Read 只回复缓存状态,不触发 Mesh 控制,修复 Read 分支中 mapping/addrFunc 未定义导致的引用错误。
719
- - **DelayedSync 读响应不误触 HA**:部分 KNX 库将 GroupValue_Response 标为 Write 输出,导致 KNX-HA Bridge 误执行关灯。通过 global 约定 DelayedSync 读阶段与地址列表,KNX-HA Bridge 在此窗口内忽略这些地址的报文,避免状态查询触发 HA 控制。
720
- - **Mesh 设备查询与 MAC 格式**:DelayedSync 与 DeviceManager `getDeviceByMac` 兼容带冒号/无冒号 MAC,确保能正确触发 0x32 查询并获取 Mesh 状态参与比对(修复「已触发 0 个 Mesh 设备的状态查询」)。
721
- - **DelayedSync 误关灯防护**:当 KNX 组地址无真实设备或无反馈时,读回值可能错误(如刚写入 ON 却读回 OFF),若直接同步到 Mesh 会误关灯。已增加防护:5 秒内若我们向该地址写过与读回值相反的值,则仅更新缓存、不同步到 Mesh,并用日志提示,使用真实设备时行为正常。
722
-
723
- #### 界面与体验
724
- - **配置项布局优化**:修复「自动同步状态」在某些分辨率下的换行问题。
725
- - **日志分级**:生产环境默认静默,关键介入逻辑使用 `node.log` 确保可追溯。
726
-
727
- ### v1.9.0 - v1.9.6 历史迭代汇总
728
-
729
- #### 核心修复与优化
730
- - **防反向控制与回显保护**:
731
- - 引入 **3秒全局 KNX 主控窗口**,确保快速连按时“最后一次操作”生效,阻止延迟反馈导致的状态回弹。
732
- - 完善 **Last Write Wins (最终写入获胜)** 逻辑,多路开关采用 2-bit 编码解析,确保状态收敛。
733
- - 支持可配置的 **回显检测时间窗口** (默认 500ms),严格区分控制地址与状态地址。
734
- - **批量处理与性能**:
735
- - 实现 **双向批量处理**:同一设备的多个通道动作合并为单条协议指令(一行代码),大幅降低总线负载。
736
- - 优化 **部署查询逻辑**:启动时 AutoSync 与 StateQuery 互斥,避免重复读取;动态计算设备列表查询等待时间。
737
- - **网关限流机制**:统一全局同步参数(队列长度 100,间隔 50ms),保障 50+ 实体大规模场景稳定性。
738
- - **同步与兼容性**:
739
- - **窗帘/调光同步增强**:区分主控来源(`isFromStateQuery`),部署或后台同步时不驱动真实电机;支持调光/窗帘锁定时间配置。
740
- - **反馈确认闭环**:对用户控制引入 5 次重试 + 状态查询闭环,确保控制指令 100% 送达。
741
- - **多协议联动**:修复 KNX-HA Bridge、MQTT Sync 等映射保存与显示问题,支持空调/新风/地暖三合一面板自动识别。
742
- - **智能初始化与发现**:
743
- - **首次启动状态快照**:仅在部署后执行一次全局状态查询,用于三合一设备检测与持久化缓存(`~/.node-red/symi-mesh-data/`),运行中完全依赖 0x80 事件上报,彻底解决查询导致的丢包问题。
744
- - **增强设备发现**:自动处理 TCP 分包粘包,提供 10s 超时保护与完整性报告,去重机制按 index 确保唯一。
745
- - **虚拟化与场景支持**:
746
- - 支持 **虚拟场景实体** (MAC: 00:00:00:00:00:00),支持 KNX、RS485、HA、MQTT 全协议联动触发。
747
- - 新增 **按键场景触发 (0x34)**,支持场景 ID 2-95,满足本地场景上报需求。
748
- - **工程化改进**:
749
- - 生产环境默认 **静默日志**,关键逻辑使用 `debug` 级并带 30s 缓存,避免日志刷屏。
750
- - 修复了大量编辑器红三角校验、变量初始化顺序、内存泄漏及配置持久化保存隐患。
684
+ ### v1.9.11 (2026-03-30)
685
+
686
+ #### 关闭 KNX->Mesh 后的 Mesh->KNX 反写回
687
+ - 修复:当 `KNX->Mesh` 刚控制后,网关把对应 `Mesh` 状态帧误标成 `isUserControl=true` 时,可能导致本应跳过的 `Mesh->KNX` 写回仍发生(例如写 OFF KNX)。
688
+ - 策略:只要该设备最近处于 `knxControlTimestamps` 窗口内,就无条件阻止 `Mesh->KNX` 写回,从而彻底避免 KNX 主动控制引发反向拉闸。
689
+
690
+ #### MQTT 节点握手超时防崩(connack timeout)
691
+ - 增大 MQTT `connectTimeout`(握手阶段超时上限)以降低 DelayedSync/高并发下的握手误判导致 `connack timeout`。
692
+ - `connack timeout` 采用“握手超时非致命”处理:改为黄色状态并触发重连兜底,避免它被当作致命错误影响节点长期稳定性。
693
+
694
+ ### v1.9.0 - v1.9.10 历史迭代汇总
695
+
696
+ - **v1.9.10 (2026-03-30)**:发布包安装可靠性(必须)——修复 `package.json / package-lock.json` 中误将自身 `node-red-contrib-symi-mesh-1.9.9.tgz` 写成 `file:` 本地依赖的问题;删除该错误引用后,`npm install node-red-contrib-symi-mesh@1.9.10` 会走正常 registry 安装流程,避免客户侧 `tarball data ... corrupted` / `ENOENT ... .tgz` 安装失败。
697
+ - **v1.9.9 (2026-03-28,2026-03-30 行为补充)**:KNX 桥同步可靠性(推荐所有 KNX 项目升级)——Mesh KNX 后的对称主控、KNX 面板优先、状态反馈去重、场景遮蔽(SceneVeil)、默认关闭 LWW;同时补上 DelayedSync Mesh 查询回包(pendingMeshQueries / 650ms 早清 / 900ms 抑制)、校准下发仅依赖 `gateway.sendControl`、MQTT connack timeout 防崩、以及“自动同步状态”勾选与运行一致(`autoSyncEnabled` 值的健壮解析)。另:现场日志说明汇总见 `docs/日志.md`。
698
+ - **v1.9.8 (2026-03-23)**:稳定性、协议兼容性与合规性增强——引擎升级(Node.js >= 22.x、NPM >= 10.x、Node-RED >= 4.x)、高并发与抗丢包测试策略、DelayedSync 增强与防死循环闭环、双向防死循环击穿修复(本地物理按键 `isUserControl=true` 时强制跳过锁定窗口)、以及安全与代码规范(修复依赖 CVE、eslint 通过)。
699
+ - **v1.9.7 (2026-02-28)**:自动状态校准(AutoSync)与状态查询逻辑——KNX Bridge 自动同步状态、查询不改变总线状态、GroupValue_Read/Response/Write 严格区分、DelayedSync 读响应不误触 HA、Mesh 设备查询与 MAC 兼容、误关灯防护,以及界面与体验(布局优化、日志分级)。
700
+ - **v1.9.0 - v1.9.6 历史迭代汇总(核心修复与优化)**:防反向控制与回显保护(3 秒主控窗口、回显检测窗口等)、双向批量处理与网关限流、窗帘/调光同步增强与主控来源区分、反馈确认闭环、对多协议联动的映射保存/显示修复、首次启动状态快照与设备发现增强、虚拟化与场景支持(虚拟场景实体、按键场景触发 0x34),以及工程化改进(静默日志与配置/内存泄漏隐患修复等)。
@@ -917,7 +917,7 @@ module.exports = function(RED) {
917
917
  // 注意:如果配置了独立的状态反馈地址,从状态反馈地址收到的消息不会更新这个时间戳
918
918
  // 【修复】如果明确是用户控制(isUserControl=true),那么就不能被单设备KNX控制窗口阻挡
919
919
  const knxControlTime = node.knxControlTimestamps[deviceKey];
920
- if (!eventData.isUserControl && knxControlTime && (now - knxControlTime) < KNX_MASTER_WINDOW_MS) {
920
+ if (knxControlTime && (now - knxControlTime) < KNX_MASTER_WINDOW_MS) {
921
921
  // node.log(`[Mesh->KNX介入] 阻止同步(单设备KNX控制窗口内): ${deviceKey}, 剩余${KNX_MASTER_WINDOW_MS - (now - knxControlTime)}ms`);
922
922
  continue;
923
923
  }
@@ -1009,7 +1009,7 @@ module.exports = function(RED) {
1009
1009
 
1010
1010
  // 【新增修复】检查单设备KNX控制时间窗口,如果是用户主动控制,跳过该拦截
1011
1011
  const knxControlTime = node.knxControlTimestamps[deviceKey];
1012
- if (!eventData.isUserControl && knxControlTime && (now - knxControlTime) < KNX_MASTER_WINDOW_MS) {
1012
+ if (knxControlTime && (now - knxControlTime) < KNX_MASTER_WINDOW_MS) {
1013
1013
  node.log(`[Mesh->KNX介入] 阻止同步(单设备KNX控制窗口内): ${mapping.name} CH${mapping.meshChannel}, 剩余${KNX_MASTER_WINDOW_MS - (now - knxControlTime)}ms`);
1014
1014
  continue;
1015
1015
  }
@@ -1096,7 +1096,7 @@ module.exports = function(RED) {
1096
1096
  const deviceKey = `${macNormalized}_light`;
1097
1097
  // 【新增修复】检查单设备KNX控制时间窗口,如果是用户主动控制,跳过该拦截
1098
1098
  const knxControlTime = node.knxControlTimestamps[deviceKey];
1099
- if (!eventData.isUserControl && knxControlTime && (now - knxControlTime) < KNX_MASTER_WINDOW_MS) {
1099
+ if (knxControlTime && (now - knxControlTime) < KNX_MASTER_WINDOW_MS) {
1100
1100
  // node.log(`[Mesh->KNX介入] 阻止调光灯同步(单设备KNX控制窗口内): ${mapping.name}, 剩余${KNX_MASTER_WINDOW_MS - (now - knxControlTime)}ms`);
1101
1101
  continue;
1102
1102
  }
@@ -336,7 +336,9 @@ module.exports = function(RED) {
336
336
  clientId: `symi-mesh-${Math.random().toString(16).substring(2, 10)}`,
337
337
  clean: true,
338
338
  // 避免握手阶段长时间卡住;错误会走 `error` 事件监听兜底,不应导致 Uncaught Exception。
339
- connectTimeout: 10000,
339
+ // 注意:在 KNX/Mesh 高并发同步(例如 DelayedSync)期间,Node-RED 事件循环可能出现短时阻塞。
340
+ // 增大 connectTimeout 可显著降低“connack timeout”握手超时误判。
341
+ connectTimeout: 30000,
340
342
  reconnectPeriod: 5000
341
343
  };
342
344
 
@@ -349,6 +351,22 @@ module.exports = function(RED) {
349
351
 
350
352
  // 立即绑定错误处理
351
353
  node.mqttClient.on("error", (error) => {
354
+ const message = error && error.message ? String(error.message) : "";
355
+ const isConnackTimeout = message.toLowerCase().includes("connack timeout");
356
+
357
+ // 连接握手超时通常不应影响 Node-RED 稳定性:
358
+ // - 不刷屏输出 connack timeout 日志
359
+ // - 触发重连(交由 mqtt.js reconnectPeriod 也可,但这里给一个主动兜底)
360
+ if (isConnackTimeout) {
361
+ node.status({ fill: "yellow", shape: "ring", text: "MQTT握手超时,重连中" });
362
+ try {
363
+ if (typeof node.mqttClient.reconnect === "function") node.mqttClient.reconnect();
364
+ } catch (_) {
365
+ // 静默:交由 mqtt.js 自己的 reconnectPeriod 处理
366
+ }
367
+ return;
368
+ }
369
+
352
370
  if (error.code !== "ECONNREFUSED" && error.code !== "ENOTFOUND") {
353
371
  node.log(`MQTT错误: ${error.message}`);
354
372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-symi-mesh",
3
- "version": "1.9.10",
3
+ "version": "1.9.11",
4
4
  "description": "Node-RED节点集合,用于通过TCP/串口连接Symi蓝牙Mesh网关,支持Home Assistant MQTT Discovery自动发现和云端数据同步",
5
5
  "main": "nodes/symi-gateway.js",
6
6
  "scripts": {