node-red-contrib-symi-mesh 1.2.4 → 1.3.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.
@@ -1,7 +1,6 @@
1
1
  <script type="text/javascript">
2
2
  RED.nodes.registerType('symi-mqtt', {
3
- category: 'Symi Mesh',
4
- color: '#DB4437',
3
+ category: 'config',
5
4
  defaults: {
6
5
  name: { value: '' },
7
6
  gateway: { value: '', type: 'symi-gateway', required: true },
@@ -10,44 +9,50 @@
10
9
  mqttPassword: { value: '' },
11
10
  mqttPrefix: { value: 'homeassistant' }
12
11
  },
13
- inputs: 0,
14
- outputs: 0,
15
- icon: 'bridge-dash.png',
16
12
  label: function() {
17
- return this.name || 'MQTT桥接';
13
+ return this.name || 'MQTT配置';
14
+ },
15
+ oneditprepare: function() {
16
+ // 自动填充默认值到输入框
17
+ if (!this.mqttBroker || this.mqttBroker === '') {
18
+ $('#node-config-input-mqttBroker').val('mqtt://localhost:1883');
19
+ }
20
+ if (!this.mqttPrefix || this.mqttPrefix === '') {
21
+ $('#node-config-input-mqttPrefix').val('homeassistant');
22
+ }
18
23
  }
19
24
  });
20
25
  </script>
21
26
 
22
27
  <script type="text/html" data-template-name="symi-mqtt">
23
28
  <div class="form-row">
24
- <label for="node-input-name"><i class="fa fa-tag"></i> 名称</label>
25
- <input type="text" id="node-input-name" placeholder="MQTT桥接">
29
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> 名称</label>
30
+ <input type="text" id="node-config-input-name" placeholder="MQTT配置">
26
31
  </div>
27
-
32
+
28
33
  <div class="form-row">
29
- <label for="node-input-gateway"><i class="fa fa-server"></i> 网关</label>
30
- <input type="text" id="node-input-gateway">
34
+ <label for="node-config-input-gateway"><i class="fa fa-server"></i> 网关</label>
35
+ <input type="text" id="node-config-input-gateway">
31
36
  </div>
32
-
37
+
33
38
  <div class="form-row">
34
- <label for="node-input-mqttBroker"><i class="fa fa-rss"></i> MQTT地址</label>
35
- <input type="text" id="node-input-mqttBroker" placeholder="mqtt://localhost:1883">
39
+ <label for="node-config-input-mqttBroker"><i class="fa fa-rss"></i> MQTT地址</label>
40
+ <input type="text" id="node-config-input-mqttBroker" placeholder="mqtt://localhost:1883">
36
41
  </div>
37
-
42
+
38
43
  <div class="form-row">
39
- <label for="node-input-mqttUsername"><i class="fa fa-user"></i> 用户名</label>
40
- <input type="text" id="node-input-mqttUsername" placeholder="可选">
44
+ <label for="node-config-input-mqttUsername"><i class="fa fa-user"></i> 用户名</label>
45
+ <input type="text" id="node-config-input-mqttUsername" placeholder="可选">
41
46
  </div>
42
-
47
+
43
48
  <div class="form-row">
44
- <label for="node-input-mqttPassword"><i class="fa fa-lock"></i> 密码</label>
45
- <input type="password" id="node-input-mqttPassword" placeholder="可选">
49
+ <label for="node-config-input-mqttPassword"><i class="fa fa-lock"></i> 密码</label>
50
+ <input type="password" id="node-config-input-mqttPassword" placeholder="可选">
46
51
  </div>
47
-
52
+
48
53
  <div class="form-row">
49
- <label for="node-input-mqttPrefix"><i class="fa fa-folder-open"></i> HA前缀</label>
50
- <input type="text" id="node-input-mqttPrefix" placeholder="homeassistant">
54
+ <label for="node-config-input-mqttPrefix"><i class="fa fa-folder-open"></i> HA前缀</label>
55
+ <input type="text" id="node-config-input-mqttPrefix" placeholder="homeassistant">
51
56
  </div>
52
57
  </script>
53
58
 
@@ -10,18 +10,20 @@ module.exports = function(RED) {
10
10
  function SymiMQTTNode(config) {
11
11
  RED.nodes.createNode(this, config);
12
12
  const node = this;
13
-
13
+
14
14
  node.gateway = RED.nodes.getNode(config.gateway);
15
- node.mqttBroker = config.mqttBroker;
16
- node.mqttUsername = config.mqttUsername;
17
- node.mqttPassword = config.mqttPassword;
18
- node.mqttPrefix = config.mqttPrefix || 'homeassistant';
19
-
15
+
20
16
  if (!node.gateway) {
21
17
  node.error('未配置网关');
22
18
  node.status({ fill: 'red', shape: 'ring', text: '未配置网关' });
23
19
  return;
24
20
  }
21
+
22
+ // 使用配置的MQTT参数,如果为空则使用默认值
23
+ node.mqttBroker = config.mqttBroker || 'mqtt://localhost:1883';
24
+ node.mqttUsername = config.mqttUsername;
25
+ node.mqttPassword = config.mqttPassword;
26
+ node.mqttPrefix = config.mqttPrefix || 'homeassistant';
25
27
 
26
28
  node.mqttClient = null;
27
29
  node.publishedDevices = new Set();
@@ -209,6 +211,24 @@ module.exports = function(RED) {
209
211
  });
210
212
  };
211
213
 
214
+ SymiMQTTNode.prototype.subscribeSceneTriggers = function(roomNo) {
215
+ const node = this;
216
+ const topic = `symi_mesh/room_${roomNo}/scene/+/trigger`;
217
+
218
+ if (!node.mqttClient || !node.mqttClient.connected) {
219
+ node.warn('MQTT客户端未连接,无法订阅场景触发');
220
+ return;
221
+ }
222
+
223
+ node.mqttClient.subscribe(topic, (err) => {
224
+ if (err) {
225
+ node.error(`订阅场景触发主题失败: ${err.message}`);
226
+ } else {
227
+ node.log(`已订阅场景触发主题: ${topic}`);
228
+ }
229
+ });
230
+ };
231
+
212
232
  // 发布设备初始状态
213
233
  function publishInitialDeviceState(device, node) {
214
234
  const macClean = device.macAddress.replace(/:/g, '').toLowerCase();
@@ -311,7 +331,10 @@ module.exports = function(RED) {
311
331
 
312
332
  switch (attrType) {
313
333
  case 0x02:
334
+ case 0x45:
314
335
  // 开关状态反馈 (TYPE_ON_OFF)
336
+ // 0x02: 1-4路开关状态反馈
337
+ // 0x45: 6路开关状态反馈(场景执行时使用)
315
338
  // 窗帘(type=5)不使用0x02,忽略避免误操作
316
339
  if (device.deviceType === 5) {
317
340
  node.debug(`窗帘设备忽略0x02消息(窗帘使用0x05/0x06)`);
@@ -852,13 +875,20 @@ module.exports = function(RED) {
852
875
 
853
876
  SymiMQTTNode.prototype.handleMQTTMessage = function(topic, message) {
854
877
  const node = this;
878
+
879
+ // 检查是否是场景触发消息
880
+ if (topic.match(/symi_mesh\/room_\w+\/scene\/\d+\/trigger/)) {
881
+ node.emit('scene-trigger', topic, message);
882
+ return;
883
+ }
884
+
855
885
  const deviceMac = node.subscriptions.get(topic);
856
-
886
+
857
887
  if (!deviceMac) {
858
888
  node.warn(`未找到topic订阅: ${topic}`);
859
889
  return;
860
890
  }
861
-
891
+
862
892
  const device = node.gateway.getDevice(deviceMac);
863
893
  if (!device) {
864
894
  node.warn(`设备未找到: ${deviceMac}`);
@@ -866,24 +896,24 @@ module.exports = function(RED) {
866
896
  }
867
897
 
868
898
  const payload = message.toString();
869
- node.debug(`[MQTT收到] topic=${topic}, payload=${payload}, deviceType=${device.deviceType}, isThreeInOne=${device.isThreeInOne}`);
870
-
899
+ node.log(`[MQTT收到] topic=${topic}, payload=${payload}, deviceType=${device.deviceType}, isThreeInOne=${device.isThreeInOne}`);
900
+
871
901
  const commands = node.parseMQTTCommand(topic, payload, device);
872
-
902
+
873
903
  if (commands && commands.length > 0) {
874
- node.debug(`[MQTT解析] 解析出${commands.length}个命令:`);
904
+ node.log(`[MQTT解析] 解析出${commands.length}个命令:`);
875
905
  commands.forEach((cmd, idx) => {
876
- node.debug(` 命令${idx + 1}: attrType=0x${cmd.attrType.toString(16).toUpperCase()}, param=[${Array.from(cmd.param).map(p => '0x' + p.toString(16).toUpperCase()).join(', ')}]`);
906
+ node.log(` 命令${idx + 1}: attrType=0x${cmd.attrType.toString(16).toUpperCase()}, param=[${Array.from(cmd.param).map(p => '0x' + p.toString(16).toUpperCase()).join(', ')}]`);
877
907
  });
878
-
908
+
879
909
  // 使用for循环而不是forEach以正确处理async
880
910
  (async () => {
881
911
  for (const command of commands) {
882
912
  const paramHex = Array.from(command.param).map(p => '0x' + p.toString(16).toUpperCase()).join(' ');
883
- node.debug(`[MQTT发送] → 网关: addr=0x${device.networkAddress.toString(16).toUpperCase()}, attr=0x${command.attrType.toString(16).toUpperCase()}, param=[${paramHex}]`);
913
+ node.log(`[MQTT发送] → 网关: addr=0x${device.networkAddress.toString(16).toUpperCase()}, attr=0x${command.attrType.toString(16).toUpperCase()}, param=[${paramHex}]`);
884
914
  try {
885
915
  await node.gateway.sendControl(device.networkAddress, command.attrType, command.param);
886
- node.debug(`[MQTT发送] ✓ 成功: ${device.name}`);
916
+ node.log(`[MQTT发送] ✓ 成功: ${device.name}`);
887
917
 
888
918
  // 立即发布状态更新(optimistic update)
889
919
  node.publishCommandFeedback(device, command, payload, topic);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-red-contrib-symi-mesh",
3
- "version": "1.2.4",
4
- "description": "Node-RED节点集合,用于通过TCP/串口连接Symi蓝牙Mesh网关,支持Home Assistant MQTT Discovery自动发现",
3
+ "version": "1.3.1",
4
+ "description": "Node-RED节点集合,用于通过TCP/串口连接Symi蓝牙Mesh网关,支持Home Assistant MQTT Discovery自动发现和云端数据同步",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -30,10 +30,12 @@
30
30
  "nodes": {
31
31
  "symi-gateway": "nodes/symi-gateway.js",
32
32
  "symi-device": "nodes/symi-device.js",
33
- "symi-mqtt": "nodes/symi-mqtt.js"
33
+ "symi-mqtt": "nodes/symi-mqtt.js",
34
+ "symi-cloud-sync": "nodes/symi-cloud-sync.js"
34
35
  }
35
36
  },
36
37
  "dependencies": {
38
+ "axios": "^1.13.2",
37
39
  "mqtt": "^5.3.0",
38
40
  "serialport": "^12.0.0"
39
41
  },