node-red-contrib-symi-modbus 1.6.0 → 1.6.1

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
@@ -1235,7 +1235,156 @@ python -m pymodbus.server tcp --port 502
1235
1235
 
1236
1236
  ## 更新日志
1237
1237
 
1238
- ### v1.6.0 (2025-10-18) - 智能MQTT连接和Docker/容器环境完美兼容 ✅最新稳定版
1238
+ ### v1.6.1 (2025-10-18) - 增强日志和故障诊断 ✅最新稳定版
1239
+
1240
+ #### 核心改进
1241
+
1242
+ **1. 详细的连接状态日志**
1243
+ - ✅ 使用emoji图标(✅❌)清晰显示连接状态
1244
+ - ✅ Modbus连接成功/失败日志:`✅ Modbus已连接` 或 `❌ Modbus连接失败`
1245
+ - ✅ MQTT连接成功日志:`✅ MQTT已连接`
1246
+ - ✅ 节点状态栏显示:`Modbus✓ MQTT✓`(绿色圆点)或 `Modbus✗ MQTT✗`(红色圆环)
1247
+
1248
+ **2. 完整的状态发布日志**
1249
+ - ✅ 每次发布状态到MQTT都显示日志:`📤 发布状态: modbus/relay/10/0/state = ON`
1250
+ - ✅ 首次轮询日志:`📊 从站10首次轮询成功,发布32个状态到MQTT`
1251
+ - ✅ 状态变化日志:`📊 从站10状态变化,发布5个更新到MQTT`
1252
+ - ✅ MQTT未连接警告:`无法发布状态: MQTT未连接 (从站10 线圈0)`
1253
+
1254
+ **3. 轮询详情日志**
1255
+ - ✅ 开始轮询日志:`🔄 开始轮询 1 个从站设备: 从站10(线圈0-31)`
1256
+ - ✅ 连接状态检查:`📡 Modbus连接: 已连接✅, MQTT连接: 已连接✅`
1257
+ - ✅ 帮助用户快速定位问题(Modbus连接失败?MQTT连接失败?轮询失败?)
1258
+
1259
+ **4. 故障诊断指南**
1260
+
1261
+ #### HA实体不可用问题排查步骤
1262
+
1263
+ **步骤1:检查Node-RED日志(最重要!)**
1264
+
1265
+ ```
1266
+ ✅ 正常日志(一切正常):
1267
+ ✅ Modbus已连接: 127.0.0.1:502
1268
+ ✅ MQTT已连接: mqtt://localhost:1883
1269
+ 🔄 开始轮询 1 个从站设备: 从站10(线圈0-31)
1270
+ 📡 Modbus连接: 已连接✅, MQTT连接: 已连接✅
1271
+ 📊 从站10首次轮询成功,发布32个状态到MQTT
1272
+ 📤 发布状态: modbus/relay/10/0/state = OFF
1273
+ 📤 发布状态: modbus/relay/10/1/state = OFF
1274
+ ... (共32个)
1275
+
1276
+ ❌ 问题1:Modbus未连接
1277
+ ❌ Modbus连接失败: connect ECONNREFUSED 127.0.0.1:502
1278
+ 解决:检查Modbus设备是否运行,地址端口是否正确
1279
+
1280
+ ❌ 问题2:MQTT未连接
1281
+ MQTT错误: connect ECONNREFUSED 127.0.0.1:1883
1282
+ 所有MQTT broker候选地址都无法连接
1283
+ 解决:启动MQTT broker (mosquitto)
1284
+
1285
+ ❌ 问题3:Modbus已连接,但无状态发布
1286
+ ✅ Modbus已连接
1287
+ ✅ MQTT已连接
1288
+ 🔄 开始轮询...
1289
+ 轮询从站10失败: Timed out
1290
+ 解决:检查从站地址是否正确,设备是否在线
1291
+ ```
1292
+
1293
+ **步骤2:检查mosquitto日志**
1294
+
1295
+ ```bash
1296
+ # 查看mosquitto日志,确认是否收到状态消息
1297
+ tail -f /var/log/mosquitto/mosquitto.log
1298
+
1299
+ # 应该看到类似输出:
1300
+ Received PUBLISH from modbus_master_xxx (d0, q1, r1, ..., 'modbus/relay/10/0/state', ... (2 bytes))
1301
+ Received PUBLISH from modbus_master_xxx (d0, q1, r1, ..., 'modbus/relay/10/1/state', ... (3 bytes))
1302
+ ```
1303
+
1304
+ 如果**只看到discovery消息,没有state消息**,说明Modbus轮询失败或状态未发布。
1305
+
1306
+ **步骤3:使用MQTT客户端测试**
1307
+
1308
+ ```bash
1309
+ # 订阅所有状态主题
1310
+ mosquitto_sub -h localhost -t 'modbus/relay/#' -v
1311
+
1312
+ # 应该看到:
1313
+ modbus/relay/10/availability online
1314
+ modbus/relay/10/0/state OFF
1315
+ modbus/relay/10/1/state OFF
1316
+ ...
1317
+ ```
1318
+
1319
+ 如果**只看到availability,没有state**,说明状态未发布。
1320
+
1321
+ **步骤4:检查Home Assistant日志**
1322
+
1323
+ 在HA中查看日志,如果看到类似错误:
1324
+ ```
1325
+ Payload is not supported: {...}
1326
+ Invalid fan_modes mode: 0
1327
+ ```
1328
+
1329
+ **这些错误与本节点无关**,这是其他MQTT设备的问题。本节点只发布switch类型实体。
1330
+
1331
+ #### 常见问题和解决方案
1332
+
1333
+ | 症状 | 原因 | 解决方案 |
1334
+ |-----|------|---------|
1335
+ | HA实体不可用(unavailable) | 1) Modbus未连接<br>2) MQTT未连接<br>3) 未发布状态 | 查看Node-RED日志,按上述步骤排查 |
1336
+ | Node-RED日志无Modbus连接 | Modbus设备未运行或地址错误 | 检查TCP地址/端口或串口配置 |
1337
+ | Node-RED日志无MQTT连接 | MQTT broker未运行 | 启动mosquitto服务 |
1338
+ | Modbus和MQTT都已连接,但无状态发布 | 1) 轮询失败<br>2) 从站地址错误 | 检查从站地址配置(默认10) |
1339
+ | mosquitto日志只有discovery | 状态未发布到MQTT | 检查Modbus轮询是否成功 |
1340
+
1341
+ #### 技术改进
1342
+
1343
+ **节点状态显示优化**:
1344
+ ```javascript
1345
+ // 新增状态更新函数
1346
+ node.updateNodeStatus = function() {
1347
+ const modbusStatus = node.isConnected ? "Modbus✓" : "Modbus✗";
1348
+ const mqttStatus = node.mqttConnected ? "MQTT✓" : "MQTT✗";
1349
+
1350
+ if (node.isConnected && node.mqttConnected) {
1351
+ node.status({fill: "green", shape: "dot", text: `${modbusStatus} ${mqttStatus}`});
1352
+ } else if (node.isConnected || node.mqttConnected) {
1353
+ node.status({fill: "yellow", shape: "ring", text: `${modbusStatus} ${mqttStatus}`});
1354
+ } else {
1355
+ node.status({fill: "red", shape: "ring", text: `${modbusStatus} ${mqttStatus}`});
1356
+ }
1357
+ };
1358
+ ```
1359
+
1360
+ **状态发布日志**:
1361
+ ```javascript
1362
+ node.mqttClient.publish(stateTopic, payload, { qos: 1, retain: true }, (err) => {
1363
+ if (err) {
1364
+ node.warn(`❌ 发布状态失败: ${stateTopic} - ${err.message}`);
1365
+ } else {
1366
+ node.log(`📤 发布状态: ${stateTopic} = ${payload}`);
1367
+ }
1368
+ });
1369
+ ```
1370
+
1371
+ #### 升级建议
1372
+
1373
+ **从v1.6.0升级到v1.6.1**:
1374
+ ```bash
1375
+ cd ~/.node-red
1376
+ npm install node-red-contrib-symi-modbus@latest
1377
+ # 重启Node-RED
1378
+ ```
1379
+
1380
+ **新功能**:
1381
+ - 无需修改配置,升级后自动获得详细日志
1382
+ - 在Node-RED界面中查看节点状态栏,一目了然
1383
+ - 出现问题时,日志会清晰指出问题所在
1384
+
1385
+ ---
1386
+
1387
+ ### v1.6.0 (2025-10-18) - 智能MQTT连接和Docker/容器环境完美兼容
1239
1388
 
1240
1389
  #### 核心功能
1241
1390
 
@@ -94,11 +94,26 @@ module.exports = function(RED) {
94
94
  node.currentSlaveIndex = 0;
95
95
  node.deviceStates = {}; // 存储每个设备的状态
96
96
  node.mqttClient = null;
97
+ node.mqttConnected = false; // MQTT连接状态
97
98
  node.isClosing = false;
98
99
  node.lastErrorLog = {}; // 记录每个从站的最后错误日志时间
99
100
  node.lastMqttErrorLog = 0; // MQTT错误日志时间
100
101
  node.errorLogInterval = 10 * 60 * 1000; // 错误日志间隔:10分钟
101
102
 
103
+ // 更新节点状态显示
104
+ node.updateNodeStatus = function() {
105
+ const modbusStatus = node.isConnected ? "Modbus✓" : "Modbus✗";
106
+ const mqttStatus = node.mqttConnected ? "MQTT✓" : "MQTT✗";
107
+
108
+ if (node.isConnected && node.mqttConnected) {
109
+ node.status({fill: "green", shape: "dot", text: `${modbusStatus} ${mqttStatus}`});
110
+ } else if (node.isConnected || node.mqttConnected) {
111
+ node.status({fill: "yellow", shape: "ring", text: `${modbusStatus} ${mqttStatus}`});
112
+ } else {
113
+ node.status({fill: "red", shape: "ring", text: `${modbusStatus} ${mqttStatus}`});
114
+ }
115
+ };
116
+
102
117
  // 初始化设备状态(基于从站配置列表)
103
118
  node.config.slaves.forEach((slave) => {
104
119
  const slaveId = slave.address;
@@ -131,7 +146,8 @@ module.exports = function(RED) {
131
146
 
132
147
  node.client.setTimeout(5000);
133
148
  node.isConnected = true;
134
- node.status({fill: "green", shape: "dot", text: "已连接"});
149
+ node.log(`✅ Modbus已连接: ${node.config.connectionType === "tcp" ? `${node.config.tcpHost}:${node.config.tcpPort}` : node.config.serialPort}`);
150
+ node.updateNodeStatus();
135
151
 
136
152
  // 清除错误日志记录(重新部署或重连时允许再次显示错误)
137
153
  node.lastErrorLog = {};
@@ -141,9 +157,9 @@ module.exports = function(RED) {
141
157
  node.startPolling();
142
158
 
143
159
  } catch (err) {
144
- node.error(`Modbus连接失败: ${err.message}`);
145
- node.status({fill: "red", shape: "ring", text: "连接失败"});
160
+ node.error(`❌ Modbus连接失败: ${err.message}`);
146
161
  node.isConnected = false;
162
+ node.updateNodeStatus();
147
163
 
148
164
  // 5秒后重试连接(使用定时器避免递归)
149
165
  if (!node.isClosing && !node.reconnectTimer) {
@@ -262,7 +278,8 @@ module.exports = function(RED) {
262
278
  lastConnectAttempt = Date.now();
263
279
 
264
280
  node.mqttClient.on('connect', () => {
265
- node.log(`MQTT已连接: ${brokerUrl}`);
281
+ node.mqttConnected = true;
282
+ node.log(`✅ MQTT已连接: ${brokerUrl}`);
266
283
 
267
284
  // 成功连接后,更新配置的broker地址(下次优先使用成功的地址)
268
285
  if (brokerUrl !== brokerCandidates[0]) {
@@ -274,6 +291,9 @@ module.exports = function(RED) {
274
291
 
275
292
  // 订阅命令主题
276
293
  node.subscribeCommands();
294
+
295
+ // 更新状态显示
296
+ node.updateNodeStatus();
277
297
  });
278
298
 
279
299
  node.mqttClient.on('error', (err) => {
@@ -469,6 +489,7 @@ module.exports = function(RED) {
469
489
  // 发布MQTT状态
470
490
  node.publishMqttState = function(slaveId, coil, value) {
471
491
  if (!node.mqttClient || !node.mqttClient.connected) {
492
+ node.warn(`无法发布状态: MQTT未连接 (从站${slaveId} 线圈${coil})`);
472
493
  return;
473
494
  }
474
495
 
@@ -478,7 +499,9 @@ module.exports = function(RED) {
478
499
  // 使用QoS=1确保消息送达,retain=true确保断线重连后可获取最新状态
479
500
  node.mqttClient.publish(stateTopic, payload, { qos: 1, retain: true }, (err) => {
480
501
  if (err) {
481
- node.warn(`发布状态失败: ${stateTopic} - ${err.message}`);
502
+ node.warn(`❌ 发布状态失败: ${stateTopic} - ${err.message}`);
503
+ } else {
504
+ node.log(`📤 发布状态: ${stateTopic} = ${payload}`);
482
505
  }
483
506
  });
484
507
  };
@@ -493,7 +516,9 @@ module.exports = function(RED) {
493
516
  node.lastErrorLog = {};
494
517
  node.lastMqttErrorLog = 0;
495
518
 
496
- node.log(`开始轮询 ${node.config.slaves.length} 个从站设备`);
519
+ const slaveList = node.config.slaves.map(s => `从站${s.address}(线圈${s.coilStart}-${s.coilEnd})`).join(', ');
520
+ node.log(`🔄 开始轮询 ${node.config.slaves.length} 个从站设备: ${slaveList}`);
521
+ node.log(`📡 Modbus连接: ${node.isConnected ? '已连接✅' : '未连接❌'}, MQTT连接: ${node.mqttConnected ? '已连接✅' : '未连接❌'}`);
497
522
  node.currentSlaveIndex = 0;
498
523
 
499
524
  // 使用最小的轮询间隔
@@ -540,6 +565,7 @@ module.exports = function(RED) {
540
565
 
541
566
  // 更新设备状态
542
567
  const isFirstPoll = !node.deviceStates[slaveId].initialPublished;
568
+ let publishCount = 0;
543
569
 
544
570
  for (let i = 0; i < coilCount; i++) {
545
571
  const coilIndex = slave.coilStart + i;
@@ -551,6 +577,7 @@ module.exports = function(RED) {
551
577
  // 第一次轮询或状态改变时,发布到MQTT和触发事件
552
578
  if (isFirstPoll || oldValue !== newValue) {
553
579
  node.publishMqttState(slaveId, coilIndex, newValue);
580
+ publishCount++;
554
581
  node.emit('stateUpdate', {
555
582
  slave: slaveId,
556
583
  coil: coilIndex,
@@ -559,6 +586,12 @@ module.exports = function(RED) {
559
586
  }
560
587
  }
561
588
 
589
+ if (isFirstPoll) {
590
+ node.log(`📊 从站${slaveId}首次轮询成功,发布${publishCount}个状态到MQTT`);
591
+ } else if (publishCount > 0) {
592
+ node.log(`📊 从站${slaveId}状态变化,发布${publishCount}个更新到MQTT`);
593
+ }
594
+
562
595
  node.deviceStates[slaveId].lastUpdate = Date.now();
563
596
  node.deviceStates[slaveId].error = null;
564
597
  node.deviceStates[slaveId].initialPublished = true; // 标记已发布初始状态
@@ -578,11 +611,7 @@ module.exports = function(RED) {
578
611
  node.send(output);
579
612
 
580
613
  // 更新状态显示
581
- node.status({
582
- fill: "green",
583
- shape: "dot",
584
- text: `从站${slaveId} 正常`
585
- });
614
+ node.updateNodeStatus();
586
615
 
587
616
  } catch (err) {
588
617
  node.deviceStates[slaveId].error = err.message;
@@ -598,13 +627,7 @@ module.exports = function(RED) {
598
627
  }
599
628
 
600
629
  // 更新状态显示
601
- const failedCount = Object.values(node.deviceStates).filter(s => s.error).length;
602
- const totalCount = node.config.slaves.length;
603
- node.status({
604
- fill: failedCount === totalCount ? "red" : "yellow",
605
- shape: "ring",
606
- text: `正常: ${totalCount - failedCount}/${totalCount}`
607
- });
630
+ node.updateNodeStatus();
608
631
 
609
632
  // 仅当全部从站都失败时,检测连接是否断开
610
633
  if (failedCount === totalCount && err.message &&
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-red-contrib-symi-modbus",
3
- "version": "1.6.0",
4
- "description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、智能MQTT连接(自动fallback到host.docker.internal等)、Home Assistant自动发现和多品牌开关面板,完美兼容Docker/容器环境",
3
+ "version": "1.6.1",
4
+ "description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、智能MQTT连接(自动fallback到host.docker.internal等)、Home Assistant自动发现和多品牌开关面板,增强日志和状态显示",
5
5
  "main": "nodes/modbus-master.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"