node-red-contrib-symi-mesh 1.8.6 → 1.8.7

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
@@ -558,6 +558,8 @@ npm install node-red-contrib-home-assistant-websocket
558
558
 
559
559
  | 节点 | 用途 |
560
560
  |-----|------|
561
+ | **Symi RS485 Sync** | RS485多机批量同步 |
562
+ | **RS485调试** | 原始485字节流抓取显示 |
561
563
  | **Symi Gateway** | 网关连接(TCP/串口) |
562
564
  | **Symi MQTT** | MQTT桥接,设备发布到HA |
563
565
  | **Symi Device** | Flow中单设备控制/监听 |
@@ -702,6 +704,14 @@ node-red-contrib-symi-mesh/
702
704
 
703
705
  ## 更新日志
704
706
 
707
+ ### v1.8.7 (2026-01-08)
708
+
709
+ **生产环境日志优化与稳定性增强**:
710
+ - **错误日志节流 (Throttling)**:在网关连接和 RS485 配置中引入 60 秒节流机制,同类网络错误(如 `ECONNREFUSED`)每分钟仅记录一次,彻底解决离线时的日志刷屏问题。
711
+ - **日志级别降级**:将所有节点的 `node.error` 和 `node.warn` 统一降级为 `node.log` (Info 级别),保持 Node-RED 控制台整洁,仅在调试模式下显示详细信息。
712
+ - **TCP 客户端优化**:在 `tcp-client` 库级别拦截常见的网络波动报错,提升系统在高频重连场景下的静默稳定性。
713
+ - **全量节点适配**:完成 MQTT、HA同步、云端同步、RS485、KNX 等所有功能节点的日志规范化清理。
714
+
705
715
  ### v1.8.6 (2026-01-07)
706
716
 
707
717
  **核心修复与同步增强**:
package/lib/tcp-client.js CHANGED
@@ -111,7 +111,7 @@ class TCPClient extends EventEmitter {
111
111
  // 只在首次连接或重要错误时记录,避免大量重复日志
112
112
  // ECONNRESET通常是网络波动,不记录错误
113
113
  else if (!this.connected && error.code !== 'ECONNREFUSED' && error.code !== 'ECONNRESET') {
114
- this.logger.error('TCP client error:', error.message);
114
+ this.logger.log('TCP client error: ' + error.message);
115
115
  }
116
116
 
117
117
  // 确保错误不会导致uncaught exception
@@ -145,12 +145,13 @@ module.exports = function(RED) {
145
145
  });
146
146
 
147
147
  node.rs485Config.on('disconnected', function() {
148
- node.warn('RS485连接已断开');
148
+ node.log('RS485连接已断开');
149
149
  updateStatus();
150
150
  });
151
151
 
152
152
  node.rs485Config.on('error', function(err) {
153
- node.error('RS485错误: ' + err.message);
153
+ // 降级为 log,避免控制台报错
154
+ node.log('RS485错误: ' + err.message);
154
155
  node.status({ fill: 'red', shape: 'ring', text: '错误: ' + err.message });
155
156
  });
156
157
 
@@ -66,6 +66,17 @@ module.exports = function(RED) {
66
66
  node.receiveBuffer = Buffer.alloc(0);
67
67
  node.users = [];
68
68
 
69
+ // 限流错误日志
70
+ node._lastErrorLog = 0;
71
+ node._ERROR_LOG_INTERVAL = 60000;
72
+ node.logErrorThrottled = function(msg) {
73
+ const now = Date.now();
74
+ if (now - node._lastErrorLog > node._ERROR_LOG_INTERVAL) {
75
+ node._lastErrorLog = now;
76
+ node.log(msg);
77
+ }
78
+ };
79
+
69
80
  // 注册使用者
70
81
  node.register = function(userNode) {
71
82
  if (!node.users.includes(userNode)) {
@@ -122,7 +133,7 @@ module.exports = function(RED) {
122
133
  });
123
134
 
124
135
  node.client.on('error', (err) => {
125
- node.error(`RS485串口错误: ${err.message}`);
136
+ node.logErrorThrottled(`RS485串口错误: ${err.message}`);
126
137
  node.emit('error', err);
127
138
  });
128
139
 
@@ -170,7 +181,7 @@ module.exports = function(RED) {
170
181
  if (err.name === 'AggregateError' || err.errors) {
171
182
  node.debug(`RS485 TCP连接失败: 无法连接到 ${node.host}:${node.port}`);
172
183
  } else {
173
- node.error(`RS485 TCP错误: ${err.message}`);
184
+ node.logErrorThrottled(`RS485 TCP错误: ${err.message}`);
174
185
  }
175
186
  node.emit('error', err);
176
187
  });
@@ -332,7 +332,7 @@ module.exports = function(RED) {
332
332
  msg.payload = { scene_id: scene.scene_id };
333
333
  node.send(msg);
334
334
  } else {
335
- node.warn(`场景ID ${msg.payload.scene_id} 不存在`);
335
+ node.log(`场景ID ${msg.payload.scene_id} 不存在`);
336
336
  }
337
337
  }
338
338
  });
@@ -99,21 +99,21 @@ module.exports = function(RED) {
99
99
  node.on('input', async function(msg) {
100
100
  try {
101
101
  if (!node.gateway.connected) {
102
- node.error('网关未连接');
102
+ node.log('网关未连接');
103
103
  return;
104
104
  }
105
105
 
106
106
  if (!node.device) {
107
107
  updateDevice();
108
108
  if (!node.device) {
109
- node.error('设备未找到');
109
+ node.log('设备未找到');
110
110
  return;
111
111
  }
112
112
  }
113
113
 
114
114
  const command = node.parseInputCommand(msg);
115
115
  if (!command) {
116
- node.warn('无效的命令格式');
116
+ node.log('无效的命令格式');
117
117
  return;
118
118
  }
119
119
 
@@ -122,7 +122,7 @@ module.exports = function(RED) {
122
122
  node.status({ fill: 'green', shape: 'dot', text: '命令已发送' });
123
123
 
124
124
  } catch (error) {
125
- node.error(`控制失败: ${error.message}`);
125
+ node.log(`控制失败: ${error.message}`);
126
126
  node.status({ fill: 'red', shape: 'dot', text: '控制失败' });
127
127
  }
128
128
  });
@@ -70,6 +70,17 @@ module.exports = function(RED) {
70
70
  this.sceneExecutionInProgress = false; // 场景执行中标志
71
71
  this.sceneExecutionTimer = null; // 场景执行超时定时器
72
72
 
73
+ // 限流错误日志
74
+ this._lastErrorLog = 0;
75
+ this._ERROR_LOG_INTERVAL = 60000;
76
+ this.logErrorThrottled = function(msg) {
77
+ const now = Date.now();
78
+ if (now - this._lastErrorLog > this._ERROR_LOG_INTERVAL) {
79
+ this._lastErrorLog = now;
80
+ this.log(msg);
81
+ }
82
+ };
83
+
73
84
  this.log(`Initializing Symi Gateway: ${this.connectionType === 'tcp' ? `${this.host}:${this.port}` : this.serialPort}`);
74
85
 
75
86
  // 三合一设备检测已在 queryAllDeviceStates 中实现,无需额外事件监听
@@ -128,7 +139,7 @@ module.exports = function(RED) {
128
139
 
129
140
  } catch (error) {
130
141
  // 初始连接失败,但自动重连会继续尝试
131
- this.error(`Initial connection failed: ${error.message}, will retry automatically`);
142
+ this.logErrorThrottled(`Initial connection failed: ${error.message}, will retry automatically`);
132
143
  }
133
144
 
134
145
  // 节点关闭时清理资源
@@ -117,7 +117,7 @@ module.exports = function(RED) {
117
117
  }
118
118
  } catch (e) {
119
119
  node.mappings = [];
120
- node.error('映射配置解析失败: ' + e.message);
120
+ node.log('映射配置解析失败: ' + e.message);
121
121
  }
122
122
 
123
123
  node.commandQueue = [];
@@ -162,7 +162,7 @@ module.exports = function(RED) {
162
162
 
163
163
  if (configError) {
164
164
  node.status({ fill: 'red', shape: 'ring', text: configError });
165
- node.warn(`[HA同步] ${configError}`);
165
+ node.log(`[HA同步] ${configError}`);
166
166
  // 不要return,继续注册input监听器
167
167
  } else {
168
168
  // 初始状态:只有Mesh→HA方向,等待HA输入连接
@@ -1145,12 +1145,12 @@ module.exports = function(RED) {
1145
1145
  node.publishCommandFeedback(device, command, payload, topic);
1146
1146
 
1147
1147
  } catch(err) {
1148
- node.error(`[MQTT发送] 失败: ${err.message}`);
1148
+ node.log(`[MQTT发送] 失败: ${err.message}`);
1149
1149
  }
1150
1150
  }
1151
1151
  })();
1152
1152
  } else {
1153
- node.warn(`[MQTT解析] 无法解析命令 - topic: ${topic}, payload: ${payload}`);
1153
+ node.log(`[MQTT解析] 无法解析命令 - topic: ${topic}, payload: ${payload}`);
1154
1154
  }
1155
1155
  };
1156
1156
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-symi-mesh",
3
- "version": "1.8.6",
3
+ "version": "1.8.7",
4
4
  "description": "Node-RED节点集合,用于通过TCP/串口连接Symi蓝牙Mesh网关,支持Home Assistant MQTT Discovery自动发现和云端数据同步",
5
5
  "main": "nodes/symi-gateway.js",
6
6
  "scripts": {