node-red-contrib-symi-mesh 1.7.1 → 1.7.2
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 +245 -20
- package/examples/knx-sync-example.json +122 -58
- package/examples/rs485-sync-example.json +76 -0
- package/lib/device-manager.js +96 -51
- package/nodes/rs485-debug.html +2 -1
- package/nodes/symi-485-bridge.html +233 -32
- package/nodes/symi-485-bridge.js +874 -98
- package/nodes/symi-485-config.js +49 -11
- package/nodes/symi-cloud-sync.html +2 -0
- package/nodes/symi-device.html +5 -3
- package/nodes/symi-gateway.js +10 -3
- package/nodes/symi-knx-bridge.html +3 -2
- package/nodes/symi-knx-bridge.js +3 -3
- package/nodes/symi-knx-ha-bridge.html +4 -3
- package/nodes/symi-knx-ha-bridge.js +2 -2
- package/nodes/symi-rs485-sync.html +361 -0
- package/nodes/symi-rs485-sync.js +765 -0
- package/package.json +3 -2
package/lib/device-manager.js
CHANGED
|
@@ -76,10 +76,41 @@ class DeviceInfo {
|
|
|
76
76
|
this.lastSeen = Date.now();
|
|
77
77
|
|
|
78
78
|
switch (attrType) {
|
|
79
|
+
case 0x01:
|
|
79
80
|
case 0x02:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
case 0x03:
|
|
82
|
+
case 0x04:
|
|
83
|
+
// 特殊协议:attrType直接表示风速值(无参数)
|
|
84
|
+
// 53 80 06 03 E3 01 01 35 - 高风(01)
|
|
85
|
+
// 53 80 06 03 E3 01 02 36 - 中风(02)
|
|
86
|
+
// 53 80 06 03 E3 01 03 37 - 低风(03)
|
|
87
|
+
// 53 80 06 03 E3 01 04 30 - 自动(04)
|
|
88
|
+
if (this.deviceType === 10 && parameters.length === 0) {
|
|
89
|
+
const oldFanMode = this.state.fanMode;
|
|
90
|
+
this.state.fanMode = attrType;
|
|
91
|
+
if (oldFanMode !== this.state.fanMode && this.manager) {
|
|
92
|
+
this.manager.emit('device-state-changed', {
|
|
93
|
+
device: this,
|
|
94
|
+
state: { fanMode: this.state.fanMode, acFanSpeed: this.state.fanMode },
|
|
95
|
+
attrType: 0x1C,
|
|
96
|
+
parameters: [],
|
|
97
|
+
subOpcode: 0x06,
|
|
98
|
+
isUserControl: true,
|
|
99
|
+
isSceneExecution: false
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return; // 已处理,直接返回
|
|
103
|
+
}
|
|
104
|
+
// 非温控器设备,继续处理其他逻辑
|
|
105
|
+
if (attrType === 0x03 && parameters.length > 0) {
|
|
106
|
+
this.state.brightness = parameters[0];
|
|
107
|
+
this.state.switch = parameters[0] > 0;
|
|
108
|
+
}
|
|
109
|
+
if (attrType === 0x04 && parameters.length > 0) {
|
|
110
|
+
this.state.colorTemp = parameters[0];
|
|
111
|
+
}
|
|
112
|
+
// 0x02用于开关状态,如果是温控器且无参数已在上面处理为风速
|
|
113
|
+
if (attrType === 0x02 && parameters.length > 0 && this.deviceType !== 5) {
|
|
83
114
|
this.handleSwitchState(parameters);
|
|
84
115
|
}
|
|
85
116
|
break;
|
|
@@ -92,15 +123,6 @@ class DeviceInfo {
|
|
|
92
123
|
this.handleSwitchState(parameters);
|
|
93
124
|
}
|
|
94
125
|
break;
|
|
95
|
-
case 0x03:
|
|
96
|
-
if (parameters.length > 0) {
|
|
97
|
-
this.state.brightness = parameters[0];
|
|
98
|
-
this.state.switch = parameters[0] > 0;
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
case 0x04:
|
|
102
|
-
if (parameters.length > 0) this.state.colorTemp = parameters[0];
|
|
103
|
-
break;
|
|
104
126
|
case 0x05:
|
|
105
127
|
// CURT_RUN_STATUS - 窗帘运行状态
|
|
106
128
|
// 【兼容两种协议】:
|
|
@@ -109,6 +131,8 @@ class DeviceInfo {
|
|
|
109
131
|
// 通过status=0(仅米家)和status=3(仅小程序)来区分协议类型
|
|
110
132
|
if (parameters.length > 0) {
|
|
111
133
|
const status = parameters[0];
|
|
134
|
+
const oldStatus = this.state.curtainStatus;
|
|
135
|
+
const oldAction = this.state.curtainAction;
|
|
112
136
|
this.state.curtainStatus = status;
|
|
113
137
|
|
|
114
138
|
// 检测协议类型:status=0只在米家出现,status=3只在小程序出现
|
|
@@ -133,6 +157,19 @@ class DeviceInfo {
|
|
|
133
157
|
this.state.curtainAction = 'closing';
|
|
134
158
|
}
|
|
135
159
|
}
|
|
160
|
+
|
|
161
|
+
// 触发状态变化事件
|
|
162
|
+
if ((oldStatus !== this.state.curtainStatus || oldAction !== this.state.curtainAction) && this.manager) {
|
|
163
|
+
this.manager.emit('device-state-changed', {
|
|
164
|
+
device: this,
|
|
165
|
+
state: { curtainStatus: this.state.curtainStatus, curtainAction: this.state.curtainAction },
|
|
166
|
+
attrType: attrType,
|
|
167
|
+
parameters: parameters,
|
|
168
|
+
subOpcode: 0x06,
|
|
169
|
+
isUserControl: true,
|
|
170
|
+
isSceneExecution: false
|
|
171
|
+
});
|
|
172
|
+
}
|
|
136
173
|
}
|
|
137
174
|
break;
|
|
138
175
|
case 0x06:
|
|
@@ -202,12 +239,16 @@ class DeviceInfo {
|
|
|
202
239
|
if (parameters.length > 0) {
|
|
203
240
|
const oldFanMode = this.state.fanMode;
|
|
204
241
|
this.state.fanMode = parameters[0];
|
|
205
|
-
//
|
|
206
|
-
if (oldFanMode !== this.state.fanMode) {
|
|
207
|
-
this.emit('
|
|
242
|
+
// 触发状态变化事件(通过DeviceManager)
|
|
243
|
+
if (oldFanMode !== this.state.fanMode && this.manager) {
|
|
244
|
+
this.manager.emit('device-state-changed', {
|
|
208
245
|
device: this,
|
|
209
|
-
state: { fanMode: this.state.fanMode },
|
|
210
|
-
attrType: attrType
|
|
246
|
+
state: { fanMode: this.state.fanMode, acFanSpeed: this.state.fanMode },
|
|
247
|
+
attrType: attrType,
|
|
248
|
+
parameters: [],
|
|
249
|
+
subOpcode: 0x06,
|
|
250
|
+
isUserControl: true,
|
|
251
|
+
isSceneExecution: false
|
|
211
252
|
});
|
|
212
253
|
}
|
|
213
254
|
}
|
|
@@ -399,40 +440,25 @@ class DeviceInfo {
|
|
|
399
440
|
if (this.channels === 1) {
|
|
400
441
|
// 单路开关
|
|
401
442
|
const value = parameters[0];
|
|
402
|
-
|
|
403
|
-
//
|
|
443
|
+
// 温控器设备:0x02消息的值含义
|
|
444
|
+
// 0x01=关, 0x02=开 (开关控制)
|
|
445
|
+
// 但某些协议中 1-4 表示风速(需要通过 attrType=0x1C 来区分)
|
|
446
|
+
// 这里只处理开关控制,风速控制由 0x1C 消息处理
|
|
404
447
|
if (this.deviceType === 10) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
attrType: 0x1C
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
} else {
|
|
423
|
-
// 否则是开关控制
|
|
424
|
-
console.log(`[DeviceManager] 开关控制: value=${value}`);
|
|
425
|
-
const oldSwitch = this.state.switch;
|
|
426
|
-
this.state.switch = value === 0x02;
|
|
427
|
-
// 触发开关状态变化事件
|
|
428
|
-
if (oldSwitch !== this.state.switch && this.manager) {
|
|
429
|
-
console.log(`[DeviceManager] 触发switch事件: ${oldSwitch} -> ${this.state.switch}`);
|
|
430
|
-
this.manager.emit('device-state-changed', {
|
|
431
|
-
device: this,
|
|
432
|
-
state: { switch: this.state.switch },
|
|
433
|
-
attrType: 0x02
|
|
434
|
-
});
|
|
435
|
-
}
|
|
448
|
+
// 温控器开关:0x01=关, 0x02=开
|
|
449
|
+
const oldSwitch = this.state.switch;
|
|
450
|
+
this.state.switch = value === 0x02;
|
|
451
|
+
// 触发开关状态变化事件
|
|
452
|
+
if (oldSwitch !== this.state.switch && this.manager) {
|
|
453
|
+
this.manager.emit('device-state-changed', {
|
|
454
|
+
device: this,
|
|
455
|
+
state: { switch: this.state.switch, acSwitch: this.state.switch },
|
|
456
|
+
attrType: 0x02,
|
|
457
|
+
parameters: [],
|
|
458
|
+
subOpcode: 0x06,
|
|
459
|
+
isUserControl: true,
|
|
460
|
+
isSceneExecution: false
|
|
461
|
+
});
|
|
436
462
|
}
|
|
437
463
|
} else {
|
|
438
464
|
this.state.switch = value === 0x02;
|
|
@@ -500,6 +526,25 @@ class DeviceInfo {
|
|
|
500
526
|
// 存储变化的按键信息,供外部使用
|
|
501
527
|
this.lastChangedButtons = changedButtons;
|
|
502
528
|
|
|
529
|
+
// 触发状态变化事件(多路开关)
|
|
530
|
+
// 注意:这个事件会被symi-gateway.js转发给symi-485-bridge.js
|
|
531
|
+
if (changedButtons.length > 0 && this.manager) {
|
|
532
|
+
const changedState = {};
|
|
533
|
+
for (const btn of changedButtons) {
|
|
534
|
+
changedState[`switch_${btn.button}`] = btn.newState;
|
|
535
|
+
}
|
|
536
|
+
// 使用与symi-gateway.js相同的事件格式
|
|
537
|
+
this.manager.emit('device-state-changed', {
|
|
538
|
+
device: this,
|
|
539
|
+
state: changedState,
|
|
540
|
+
attrType: 0x02,
|
|
541
|
+
parameters: [],
|
|
542
|
+
subOpcode: 0x06,
|
|
543
|
+
isUserControl: true,
|
|
544
|
+
isSceneExecution: false
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
503
548
|
return changedButtons;
|
|
504
549
|
}
|
|
505
550
|
|
|
@@ -584,7 +629,7 @@ class DeviceManager extends EventEmitter {
|
|
|
584
629
|
const addrMap = this.context.get(`symi_addr_map_${this.gatewayId}`) || {};
|
|
585
630
|
|
|
586
631
|
Object.entries(saved).forEach(([mac, data]) => {
|
|
587
|
-
const device = new DeviceInfo(data);
|
|
632
|
+
const device = new DeviceInfo(data, this); // 传递manager引用
|
|
588
633
|
// 恢复三合一标记
|
|
589
634
|
if (data.isThreeInOne) {
|
|
590
635
|
device.isThreeInOne = true;
|