node-red-contrib-symi-mesh 1.6.6 → 1.6.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 +221 -429
- package/examples/knx-sync-example.json +48 -410
- package/lib/tcp-client.js +6 -2
- package/nodes/symi-485-config.js +38 -3
- package/nodes/symi-gateway.js +20 -20
- package/nodes/symi-knx-bridge.html +368 -0
- package/nodes/symi-knx-bridge.js +1065 -0
- package/nodes/symi-mqtt.js +52 -10
- package/package.json +4 -3
package/nodes/symi-mqtt.js
CHANGED
|
@@ -4,8 +4,25 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const mqtt = require('mqtt');
|
|
7
|
+
const net = require('net');
|
|
7
8
|
const { generateDiscoveryConfig, generateStateTopics, convertStateValue } = require('../lib/mqtt-helper');
|
|
8
9
|
|
|
10
|
+
// 检查TCP端口是否可用
|
|
11
|
+
function checkPort(host, port, timeout = 2000) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
const socket = new net.Socket();
|
|
14
|
+
socket.setTimeout(timeout);
|
|
15
|
+
socket.on('connect', () => { socket.destroy(); resolve(true); });
|
|
16
|
+
socket.on('timeout', () => { socket.destroy(); resolve(false); });
|
|
17
|
+
socket.on('error', () => { socket.destroy(); resolve(false); });
|
|
18
|
+
try {
|
|
19
|
+
socket.connect({ port, host, family: 4 });
|
|
20
|
+
} catch (e) {
|
|
21
|
+
resolve(false);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
module.exports = function(RED) {
|
|
10
27
|
function SymiMQTTNode(config) {
|
|
11
28
|
RED.nodes.createNode(this, config);
|
|
@@ -193,10 +210,34 @@ module.exports = function(RED) {
|
|
|
193
210
|
});
|
|
194
211
|
}
|
|
195
212
|
|
|
196
|
-
SymiMQTTNode.prototype.connectMQTT = function() {
|
|
213
|
+
SymiMQTTNode.prototype.connectMQTT = async function() {
|
|
197
214
|
const node = this;
|
|
198
215
|
|
|
199
216
|
try {
|
|
217
|
+
// 解析broker地址
|
|
218
|
+
let host = 'localhost', port = 1883;
|
|
219
|
+
try {
|
|
220
|
+
const url = new URL(node.mqttBroker);
|
|
221
|
+
host = url.hostname || 'localhost';
|
|
222
|
+
port = parseInt(url.port) || 1883;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
// 使用默认值
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 先检查端口是否可用,避免连接失败导致崩溃
|
|
228
|
+
const isAvailable = await checkPort(host, port, 3000);
|
|
229
|
+
if (!isAvailable) {
|
|
230
|
+
// 只在首次或每分钟记录一次警告,避免日志刷屏
|
|
231
|
+
if (!node._lastMqttWarn || Date.now() - node._lastMqttWarn > 60000) {
|
|
232
|
+
node.warn(`MQTT broker ${host}:${port} 不可用,每30秒重试`);
|
|
233
|
+
node._lastMqttWarn = Date.now();
|
|
234
|
+
}
|
|
235
|
+
node.status({ fill: 'yellow', shape: 'ring', text: `Broker不可用 ${host}:${port}` });
|
|
236
|
+
// 30秒后重试
|
|
237
|
+
setTimeout(() => node.connectMQTT(), 30000);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
200
241
|
const options = {
|
|
201
242
|
clientId: `symi-mesh-${Math.random().toString(16).substring(2, 10)}`,
|
|
202
243
|
clean: true,
|
|
@@ -210,6 +251,14 @@ module.exports = function(RED) {
|
|
|
210
251
|
|
|
211
252
|
node.mqttClient = mqtt.connect(node.mqttBroker, options);
|
|
212
253
|
|
|
254
|
+
// 立即绑定错误处理
|
|
255
|
+
node.mqttClient.on('error', (error) => {
|
|
256
|
+
if (error.code !== 'ECONNREFUSED' && error.code !== 'ENOTFOUND') {
|
|
257
|
+
node.error(`MQTT错误: ${error.message}`);
|
|
258
|
+
}
|
|
259
|
+
node.status({ fill: 'red', shape: 'ring', text: '连接失败' });
|
|
260
|
+
});
|
|
261
|
+
|
|
213
262
|
node.mqttClient.on('message', (topic, message) => {
|
|
214
263
|
node.log(`[MQTT消息] topic=${topic}, message=${message.toString()}`);
|
|
215
264
|
node.handleMQTTMessage(topic, message);
|
|
@@ -236,14 +285,6 @@ module.exports = function(RED) {
|
|
|
236
285
|
node.debug('MQTT正在重连...');
|
|
237
286
|
node.status({ fill: 'yellow', shape: 'ring', text: '重连中' });
|
|
238
287
|
});
|
|
239
|
-
|
|
240
|
-
node.mqttClient.on('error', (error) => {
|
|
241
|
-
// 只记录非连接错误,避免重连时大量日志
|
|
242
|
-
if (error.code !== 'ECONNREFUSED' && error.code !== 'ENOTFOUND') {
|
|
243
|
-
node.error(`MQTT错误: ${error.message}`);
|
|
244
|
-
}
|
|
245
|
-
node.status({ fill: 'red', shape: 'ring', text: '错误' });
|
|
246
|
-
});
|
|
247
288
|
|
|
248
289
|
node.mqttClient.on('offline', () => {
|
|
249
290
|
node.status({ fill: 'yellow', shape: 'ring', text: '离线' });
|
|
@@ -552,7 +593,8 @@ module.exports = function(RED) {
|
|
|
552
593
|
});
|
|
553
594
|
node.debug(`[状态发布] ${device.name} 第${i}路: ${switchState} (value=${value})`);
|
|
554
595
|
} else {
|
|
555
|
-
|
|
596
|
+
// 部分路未使用是正常情况,不需要警告
|
|
597
|
+
node.debug(`[状态发布] ${device.name} 第${i}路状态未定义`);
|
|
556
598
|
}
|
|
557
599
|
}
|
|
558
600
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-symi-mesh",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"description": "Node-RED节点集合,用于通过TCP/串口连接Symi蓝牙Mesh网关,支持Home Assistant MQTT Discovery自动发现和云端数据同步",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "nodes/symi-gateway.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
8
|
},
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"symi-cloud-sync": "nodes/symi-cloud-sync.js",
|
|
35
35
|
"symi-485-config": "nodes/symi-485-config.js",
|
|
36
36
|
"symi-rs485-bridge": "nodes/symi-485-bridge.js",
|
|
37
|
-
"rs485-debug": "nodes/rs485-debug.js"
|
|
37
|
+
"rs485-debug": "nodes/rs485-debug.js",
|
|
38
|
+
"symi-knx-bridge": "nodes/symi-knx-bridge.js"
|
|
38
39
|
}
|
|
39
40
|
},
|
|
40
41
|
"dependencies": {
|