node-red-contrib-symi-mesh 1.6.5 → 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.
@@ -2,12 +2,12 @@
2
2
  {
3
3
  "id": "knx_sync_flow",
4
4
  "type": "tab",
5
- "label": "Symi Mesh KNX 双向同步",
5
+ "label": "Symi Mesh - KNX Sync",
6
6
  "disabled": false,
7
- "info": "展示Symi Mesh设备与KNX系统的完整双向同步,包括开关、调光灯、窗帘、温控器等所有设备类型"
7
+ "info": "Mesh设备与KNX系统双向同步示例"
8
8
  },
9
9
  {
10
- "id": "symi_gateway",
10
+ "id": "symi_gateway_knx",
11
11
  "type": "symi-gateway",
12
12
  "name": "Symi网关",
13
13
  "connectionType": "tcp",
@@ -17,449 +17,87 @@
17
17
  "baudRate": "115200"
18
18
  },
19
19
  {
20
- "id": "knx_gateway",
20
+ "id": "knx_gateway_config",
21
21
  "type": "knxUltimate-config",
22
22
  "host": "192.168.1.50",
23
23
  "port": "3671",
24
24
  "physAddr": "15.15.22",
25
25
  "hostProtocol": "TunnelUDP",
26
- "suppress_ack_ldatareq": false,
27
26
  "loglevel": "error",
28
- "name": "KNX网关",
29
- "localEchoInTunneling": true,
30
- "KNXEthInterface": "Auto",
31
- "KNXEthInterfaceManuallyInput": ""
27
+ "name": "KNX Gateway",
28
+ "delaybetweentelegrams": 25,
29
+ "autoReconnect": "yes"
32
30
  },
33
31
  {
34
- "id": "comment_switch_sync",
35
- "type": "comment",
36
- "z": "knx_sync_flow",
37
- "name": "开关双向同步 (Symi ↔ KNX)",
38
- "info": "Symi开关与KNX开关执行器双向同步",
39
- "x": 150,
40
- "y": 60,
41
- "wires": []
42
- },
43
- {
44
- "id": "symi_switch",
45
- "type": "symi-device",
46
- "z": "knx_sync_flow",
47
- "name": "三路开关",
48
- "gateway": "symi_gateway",
49
- "deviceMac": "14:27:c9:20:da:cc",
50
- "outputFormat": "full",
51
- "x": 130,
52
- "y": 120,
53
- "wires": [["switch_to_knx"]]
54
- },
55
- {
56
- "id": "switch_to_knx",
57
- "type": "function",
58
- "z": "knx_sync_flow",
59
- "name": "Symi→KNX转换",
60
- "func": "// Symi开关状态转换为KNX格式\nconst channel = msg.channel || 1;\nconst value = msg.payload ? 1 : 0;\n\n// 根据通道设置KNX组地址\nconst gaMap = {\n 1: '1/1/1',\n 2: '1/1/2',\n 3: '1/1/3'\n};\n\nreturn {\n payload: value,\n destination: gaMap[channel]\n};",
61
- "outputs": 1,
62
- "noerr": 0,
63
- "x": 330,
64
- "y": 120,
65
- "wires": [["knx_out_switch"]]
66
- },
67
- {
68
- "id": "knx_out_switch",
69
- "type": "knxUltimate-out",
32
+ "id": "knx_input_node",
33
+ "type": "knxUltimate",
70
34
  "z": "knx_sync_flow",
71
- "server": "knx_gateway",
72
- "name": "KNX开关输出",
35
+ "server": "knx_gateway_config",
73
36
  "topic": "",
74
37
  "dpt": "1.001",
75
- "initialread": false,
76
- "notifyreadrequest": false,
77
- "notifyresponse": false,
78
- "notifywrite": true,
79
- "notifyreadresponse": false,
80
- "notifyreadrequestalsorespondtobus": false,
81
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
82
- "listenallga": false,
38
+ "name": "KNX Input",
83
39
  "outputtype": "write",
84
- "outputRBE": true,
85
- "inputRBE": true,
86
- "x": 560,
87
- "y": 120,
88
- "wires": []
89
- },
90
- {
91
- "id": "knx_in_switch",
92
- "type": "knxUltimate-in",
93
- "z": "knx_sync_flow",
94
- "server": "knx_gateway",
95
- "name": "KNX开关输入",
96
- "topic": "1/1/1",
97
- "dpt": "1.001",
98
- "initialread": false,
40
+ "outputRBE": "false",
41
+ "inputRBE": "false",
99
42
  "notifyreadrequest": false,
100
43
  "notifyresponse": true,
101
44
  "notifywrite": true,
102
- "notifyreadresponse": false,
103
- "notifyreadrequestalsorespondtobus": false,
104
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
105
- "listenallga": false,
106
- "x": 140,
107
- "y": 180,
108
- "wires": [["knx_to_switch"]]
109
- },
110
- {
111
- "id": "knx_to_switch",
112
- "type": "function",
113
- "z": "knx_sync_flow",
114
- "name": "KNX→Symi转换",
115
- "func": "// KNX状态转换为Symi格式\n// 根据组地址确定通道\nconst gaChannelMap = {\n '1/1/1': 1,\n '1/1/2': 2,\n '1/1/3': 3\n};\n\nconst channel = gaChannelMap[msg.destination] || 1;\n\nreturn {\n payload: msg.payload === 1,\n channel: channel,\n deviceMac: '14:27:c9:20:da:cc'\n};",
116
- "outputs": 1,
117
- "noerr": 0,
118
- "x": 330,
119
- "y": 180,
120
- "wires": [["symi_switch_control"]]
121
- },
122
- {
123
- "id": "symi_switch_control",
124
- "type": "symi-device",
125
- "z": "knx_sync_flow",
126
- "name": "控制Symi开关",
127
- "gateway": "symi_gateway",
128
- "deviceMac": "",
129
- "outputFormat": "full",
130
- "x": 560,
131
- "y": 180,
132
- "wires": []
133
- },
134
- {
135
- "id": "comment_light_sync",
136
- "type": "comment",
137
- "z": "knx_sync_flow",
138
- "name": "调光灯双向同步 (Symi ↔ KNX)",
139
- "info": "Symi调光灯与KNX调光执行器双向同步",
140
- "x": 160,
141
- "y": 260,
142
- "wires": []
143
- },
144
- {
145
- "id": "symi_light",
146
- "type": "symi-device",
147
- "z": "knx_sync_flow",
148
- "name": "双色调光灯",
149
- "gateway": "symi_gateway",
150
- "deviceMac": "e4:53:4c:93:46:84",
151
- "outputFormat": "full",
152
- "x": 140,
153
- "y": 320,
154
- "wires": [["light_to_knx"]]
155
- },
156
- {
157
- "id": "light_to_knx",
158
- "type": "function",
159
- "z": "knx_sync_flow",
160
- "name": "Symi灯光→KNX",
161
- "func": "// Symi灯光状态转换为KNX DPT 1(开关) + DPT 5(亮度)\nconst msgs = [];\n\n// 开关状态\nif (msg.payload.switch !== undefined) {\n msgs.push({\n payload: msg.payload.switch ? 1 : 0,\n destination: '2/1/1', // KNX开关组地址\n dpt: '1.001'\n });\n}\n\n// 亮度\nif (msg.payload.brightness !== undefined) {\n msgs.push({\n payload: Math.round(msg.payload.brightness * 255 / 100),\n destination: '2/1/2', // KNX亮度组地址\n dpt: '5.001'\n });\n}\n\n// 色温(可选)\nif (msg.payload.colorTemp !== undefined) {\n msgs.push({\n payload: msg.payload.colorTemp,\n destination: '2/1/3', // KNX色温组地址\n dpt: '5.001'\n });\n}\n\nreturn [msgs];",
162
- "outputs": 1,
163
- "noerr": 0,
164
- "x": 340,
165
- "y": 320,
166
- "wires": [["knx_out_light"]]
167
- },
168
- {
169
- "id": "knx_out_light",
170
- "type": "knxUltimate-out",
171
- "z": "knx_sync_flow",
172
- "server": "knx_gateway",
173
- "name": "KNX调光输出",
174
- "topic": "",
175
- "dpt": "1.001",
176
- "initialread": false,
177
- "notifyreadrequest": false,
178
- "notifyresponse": false,
179
- "notifywrite": true,
180
- "notifyreadresponse": false,
181
- "notifyreadrequestalsorespondtobus": false,
182
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
183
- "listenallga": false,
184
- "outputtype": "write",
185
- "outputRBE": true,
186
- "inputRBE": true,
187
- "x": 560,
188
- "y": 320,
189
- "wires": []
190
- },
191
- {
192
- "id": "knx_in_light_switch",
193
- "type": "knxUltimate-in",
194
- "z": "knx_sync_flow",
195
- "server": "knx_gateway",
196
- "name": "KNX灯光开关",
197
- "topic": "2/1/1",
198
- "dpt": "1.001",
199
- "initialread": false,
200
- "notifyreadrequest": false,
201
- "notifyresponse": true,
202
- "notifywrite": true,
203
- "notifyreadresponse": false,
204
- "notifyreadrequestalsorespondtobus": false,
205
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
206
- "listenallga": false,
45
+ "listenallga": true,
207
46
  "x": 150,
208
- "y": 380,
209
- "wires": [["knx_light_to_symi"]]
47
+ "y": 100,
48
+ "wires": [["knx_bridge_node"]]
210
49
  },
211
50
  {
212
- "id": "knx_in_light_brightness",
213
- "type": "knxUltimate-in",
51
+ "id": "knx_bridge_node",
52
+ "type": "symi-knx-bridge",
214
53
  "z": "knx_sync_flow",
215
- "server": "knx_gateway",
216
- "name": "KNX亮度",
217
- "topic": "2/1/2",
218
- "dpt": "5.001",
219
- "initialread": false,
220
- "notifyreadrequest": false,
221
- "notifyresponse": true,
222
- "notifywrite": true,
223
- "notifyreadresponse": false,
224
- "notifyreadrequestalsorespondtobus": false,
225
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
226
- "listenallga": false,
227
- "x": 130,
228
- "y": 420,
229
- "wires": [["knx_light_to_symi"]]
54
+ "name": "KNX Bridge",
55
+ "gateway": "symi_gateway_knx",
56
+ "mappings": "[]",
57
+ "knxEntities": "[]",
58
+ "x": 350,
59
+ "y": 100,
60
+ "wires": [["knx_output_node"], ["debug_node"]]
230
61
  },
231
62
  {
232
- "id": "knx_light_to_symi",
233
- "type": "function",
63
+ "id": "knx_output_node",
64
+ "type": "knxUltimate",
234
65
  "z": "knx_sync_flow",
235
- "name": "KNX→Symi灯光",
236
- "func": "// KNX状态转换为Symi MQTT格式\nconst mac = 'e4534c934684'; // 双色调光灯MAC(无冒号)\n\n// 根据DPT类型处理\nif (msg.dpt === '1.001') {\n // 开关控制\n return {\n topic: `symi_mesh/${mac}/light/set`,\n payload: JSON.stringify({\n state: msg.payload === 1 ? 'ON' : 'OFF'\n })\n };\n} else if (msg.dpt === '5.001') {\n // 亮度控制\n return {\n topic: `symi_mesh/${mac}/light/set`,\n payload: JSON.stringify({\n brightness: msg.payload // 0-255\n })\n };\n}\n\nreturn null;",
237
- "outputs": 1,
238
- "noerr": 0,
239
- "x": 340,
240
- "y": 400,
241
- "wires": [["mqtt_publish"]]
242
- },
243
- {
244
- "id": "comment_cover_sync",
245
- "type": "comment",
246
- "z": "knx_sync_flow",
247
- "name": "窗帘双向同步 (Symi ↔ KNX)",
248
- "info": "Symi窗帘与KNX窗帘执行器双向同步",
249
- "x": 150,
250
- "y": 480,
251
- "wires": []
252
- },
253
- {
254
- "id": "symi_cover",
255
- "type": "symi-device",
256
- "z": "knx_sync_flow",
257
- "name": "智能窗帘",
258
- "gateway": "symi_gateway",
259
- "deviceMac": "d4:86:c0:81:d8:14",
260
- "outputFormat": "full",
261
- "x": 130,
262
- "y": 540,
263
- "wires": [["cover_to_knx"]]
264
- },
265
- {
266
- "id": "cover_to_knx",
267
- "type": "function",
268
- "z": "knx_sync_flow",
269
- "name": "Symi窗帘→KNX",
270
- "func": "// Symi窗帘状态转换为KNX DPT 1(上下) + DPT 5(位置)\nconst msgs = [];\n\n// 运行状态转换\nif (msg.payload.curtainStatus !== undefined) {\n const status = msg.payload.curtainStatus;\n // 1=opening, 2=closing, 0/3=stopped\n if (status === 1) {\n msgs.push({\n payload: 1, // 上\n destination: '3/1/1',\n dpt: '1.008'\n });\n } else if (status === 2) {\n msgs.push({\n payload: 0, // 下\n destination: '3/1/1',\n dpt: '1.008'\n });\n }\n}\n\n// 位置\nif (msg.payload.curtainPosition !== undefined) {\n msgs.push({\n payload: Math.round(msg.payload.curtainPosition * 255 / 100),\n destination: '3/1/2', // KNX位置组地址\n dpt: '5.001'\n });\n}\n\nreturn [msgs];",
271
- "outputs": 1,
272
- "noerr": 0,
273
- "x": 330,
274
- "y": 540,
275
- "wires": [["knx_out_cover"]]
276
- },
277
- {
278
- "id": "knx_out_cover",
279
- "type": "knxUltimate-out",
280
- "z": "knx_sync_flow",
281
- "server": "knx_gateway",
282
- "name": "KNX窗帘输出",
66
+ "server": "knx_gateway_config",
283
67
  "topic": "",
284
- "dpt": "1.008",
285
- "initialread": false,
286
- "notifyreadrequest": false,
287
- "notifyresponse": false,
288
- "notifywrite": true,
289
- "notifyreadresponse": false,
290
- "notifyreadrequestalsorespondtobus": false,
291
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
292
- "listenallga": false,
68
+ "dpt": "0",
69
+ "name": "KNX Output (Universal Mode)",
293
70
  "outputtype": "write",
294
- "outputRBE": true,
295
- "inputRBE": true,
296
- "x": 550,
297
- "y": 540,
298
- "wires": []
299
- },
300
- {
301
- "id": "knx_in_cover",
302
- "type": "knxUltimate-in",
303
- "z": "knx_sync_flow",
304
- "server": "knx_gateway",
305
- "name": "KNX窗帘控制",
306
- "topic": "3/1/1",
307
- "dpt": "1.008",
308
- "initialread": false,
309
- "notifyreadrequest": false,
310
- "notifyresponse": true,
311
- "notifywrite": true,
312
- "notifyreadresponse": false,
313
- "notifyreadrequestalsorespondtobus": false,
314
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
315
- "listenallga": false,
316
- "x": 150,
317
- "y": 600,
318
- "wires": [["knx_cover_to_symi"]]
319
- },
320
- {
321
- "id": "knx_cover_to_symi",
322
- "type": "function",
323
- "z": "knx_sync_flow",
324
- "name": "KNX→Symi窗帘",
325
- "func": "// KNX窗帘控制转换为Symi MQTT格式\nconst mac = 'd486c081d814';\n\nif (msg.dpt === '1.008') {\n // 上下控制\n return {\n topic: `symi_mesh/${mac}/cover/set`,\n payload: msg.payload === 1 ? 'OPEN' : 'CLOSE'\n };\n} else if (msg.dpt === '5.001') {\n // 位置控制\n return {\n topic: `symi_mesh/${mac}/cover/position/set`,\n payload: Math.round(msg.payload * 100 / 255).toString()\n };\n}\n\nreturn null;",
326
- "outputs": 1,
327
- "noerr": 0,
328
- "x": 340,
329
- "y": 600,
330
- "wires": [["mqtt_publish"]]
331
- },
332
- {
333
- "id": "comment_climate_sync",
334
- "type": "comment",
335
- "z": "knx_sync_flow",
336
- "name": "温控器双向同步 (Symi ↔ KNX)",
337
- "info": "Symi温控器与KNX空调控制器双向同步",
338
- "x": 160,
339
- "y": 680,
340
- "wires": []
341
- },
342
- {
343
- "id": "symi_climate",
344
- "type": "symi-device",
345
- "z": "knx_sync_flow",
346
- "name": "温控器",
347
- "gateway": "symi_gateway",
348
- "deviceMac": "14:2e:68:46:8c:ac",
349
- "outputFormat": "full",
350
- "x": 120,
351
- "y": 740,
352
- "wires": [["climate_to_knx"]]
353
- },
354
- {
355
- "id": "climate_to_knx",
356
- "type": "function",
357
- "z": "knx_sync_flow",
358
- "name": "Symi温控→KNX",
359
- "func": "// Symi温控器状态转换为KNX DPT 9(温度) + DPT 20(模式)\nconst msgs = [];\n\n// 开关状态\nif (msg.payload.switch !== undefined) {\n msgs.push({\n payload: msg.payload.switch ? 1 : 0,\n destination: '4/1/1', // KNX空调开关\n dpt: '1.001'\n });\n}\n\n// 目标温度\nif (msg.payload.targetTemp !== undefined) {\n msgs.push({\n payload: msg.payload.targetTemp,\n destination: '4/1/2', // KNX温度设定\n dpt: '9.001'\n });\n}\n\n// 模式\nif (msg.payload.climateMode !== undefined) {\n // 1=cool, 2=heat, 3=fan_only, 4=dry\n msgs.push({\n payload: msg.payload.climateMode,\n destination: '4/1/3', // KNX模式\n dpt: '20.102'\n });\n}\n\n// 风速\nif (msg.payload.fanMode !== undefined) {\n msgs.push({\n payload: msg.payload.fanMode,\n destination: '4/1/4', // KNX风速\n dpt: '5.001'\n });\n}\n\nreturn [msgs];",
360
- "outputs": 1,
361
- "noerr": 0,
362
- "x": 320,
363
- "y": 740,
364
- "wires": [["knx_out_climate"]]
365
- },
366
- {
367
- "id": "knx_out_climate",
368
- "type": "knxUltimate-out",
369
- "z": "knx_sync_flow",
370
- "server": "knx_gateway",
371
- "name": "KNX温控输出",
372
- "topic": "",
373
- "dpt": "9.001",
374
- "initialread": false,
375
- "notifyreadrequest": false,
376
- "notifyresponse": false,
71
+ "outputRBE": "false",
72
+ "inputRBE": "false",
377
73
  "notifywrite": true,
378
- "notifyreadresponse": false,
379
- "notifyreadrequestalsorespondtobus": false,
380
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
381
- "listenallga": false,
382
- "outputtype": "write",
383
- "outputRBE": true,
384
- "inputRBE": true,
385
- "x": 540,
386
- "y": 740,
74
+ "listenallga": true,
75
+ "x": 550,
76
+ "y": 100,
387
77
  "wires": []
388
78
  },
389
79
  {
390
- "id": "mqtt_publish",
391
- "type": "mqtt out",
80
+ "id": "debug_node",
81
+ "type": "debug",
392
82
  "z": "knx_sync_flow",
393
- "name": "MQTT发布",
394
- "topic": "",
395
- "qos": "0",
396
- "retain": "false",
397
- "respTopic": "",
398
- "contentType": "",
399
- "userProps": "",
400
- "correl": "",
401
- "expiry": "",
402
- "broker": "",
83
+ "name": "Sync Debug",
84
+ "active": true,
85
+ "tosidebar": true,
86
+ "console": false,
87
+ "tostatus": false,
88
+ "complete": "payload",
403
89
  "x": 550,
404
- "y": 600,
90
+ "y": 160,
405
91
  "wires": []
406
92
  },
407
93
  {
408
- "id": "comment_sensor_sync",
94
+ "id": "comment_usage",
409
95
  "type": "comment",
410
96
  "z": "knx_sync_flow",
411
- "name": "传感器同步 (Symi → KNX)",
412
- "info": "Symi传感器状态同步到KNX(单向)",
413
- "x": 150,
414
- "y": 820,
415
- "wires": []
416
- },
417
- {
418
- "id": "symi_motion",
419
- "type": "symi-device",
420
- "z": "knx_sync_flow",
421
- "name": "人体感应器",
422
- "gateway": "symi_gateway",
423
- "deviceMac": "f3:cf:7d:3d:44:9c",
424
- "outputFormat": "full",
97
+ "name": "使用说明",
98
+ "info": "1. 修改symi_gateway_knx的IP为网关实际地址\n2. 修改knx_gateway_config的IP为KNX网关地址\n3. 双击KNX Bridge节点配置映射:\n - 导入KNX实体(组地址)\n - 添加Mesh设备与KNX实体的映射\n4. 部署后等待20秒初始化完成\n5. 测试双向同步",
425
99
  "x": 140,
426
- "y": 880,
427
- "wires": [["motion_to_knx"]]
428
- },
429
- {
430
- "id": "motion_to_knx",
431
- "type": "function",
432
- "z": "knx_sync_flow",
433
- "name": "人体感应→KNX",
434
- "func": "// Symi人体感应转换为KNX DPT 1\nif (msg.payload.motion !== undefined) {\n return {\n payload: msg.payload.motion ? 1 : 0,\n destination: '5/1/1', // KNX人体感应组地址\n dpt: '1.002'\n };\n}\nreturn null;",
435
- "outputs": 1,
436
- "noerr": 0,
437
- "x": 330,
438
- "y": 880,
439
- "wires": [["knx_out_motion"]]
440
- },
441
- {
442
- "id": "knx_out_motion",
443
- "type": "knxUltimate-out",
444
- "z": "knx_sync_flow",
445
- "server": "knx_gateway",
446
- "name": "KNX人体感应",
447
- "topic": "",
448
- "dpt": "1.002",
449
- "initialread": false,
450
- "notifyreadrequest": false,
451
- "notifyresponse": false,
452
- "notifywrite": true,
453
- "notifyreadresponse": false,
454
- "notifyreadrequestalsorespondtobus": false,
455
- "notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized": "",
456
- "listenallga": false,
457
- "outputtype": "write",
458
- "outputRBE": true,
459
- "inputRBE": true,
460
- "x": 540,
461
- "y": 880,
100
+ "y": 40,
462
101
  "wires": []
463
102
  }
464
103
  ]
465
-
@@ -100,9 +100,36 @@ class DeviceInfo {
100
100
  break;
101
101
  case 0x05:
102
102
  // CURT_RUN_STATUS - 窗帘运行状态
103
- // 0=到头/停止, 1=打开中, 2=关闭中, 3=暂停
103
+ // 【兼容两种协议】:
104
+ // 米家协议: 0=打开中, 1=关闭中, 2=停止
105
+ // 小程序协议: 1=打开, 2=关闭, 3=停止
106
+ // 通过status=0(仅米家)和status=3(仅小程序)来区分协议类型
104
107
  if (parameters.length > 0) {
105
- this.state.curtainStatus = parameters[0];
108
+ const status = parameters[0];
109
+ this.state.curtainStatus = status;
110
+
111
+ // 检测协议类型:status=0只在米家出现,status=3只在小程序出现
112
+ if (status === 0) {
113
+ this.state.curtainProtocol = 'mijia';
114
+ this.state.curtainAction = 'opening';
115
+ } else if (status === 3) {
116
+ this.state.curtainProtocol = 'miniprogram';
117
+ this.state.curtainAction = 'stopped';
118
+ } else if (status === 1) {
119
+ // status=1: 米家=关闭中, 小程序=打开
120
+ if (this.state.curtainProtocol === 'mijia') {
121
+ this.state.curtainAction = 'closing';
122
+ } else {
123
+ this.state.curtainAction = 'opening';
124
+ }
125
+ } else if (status === 2) {
126
+ // status=2: 米家=停止, 小程序=关闭
127
+ if (this.state.curtainProtocol === 'mijia') {
128
+ this.state.curtainAction = 'stopped';
129
+ } else {
130
+ this.state.curtainAction = 'closing';
131
+ }
132
+ }
106
133
  }
107
134
  break;
108
135
  case 0x06:
@@ -161,7 +188,7 @@ class DeviceInfo {
161
188
  // 目标温度TMPC_TEMP:1字节,直接温度值(16-30°C)
162
189
  if (parameters.length > 0) {
163
190
  const temp = parameters[0];
164
- if (temp >= 5 && temp <= 35) {
191
+ if (temp >= 16 && temp <= 30) {
165
192
  this.state.targetTemp = temp;
166
193
  // 不推断开关状态,完全由0x02消息决定
167
194
  }
@@ -627,11 +627,11 @@ function convertStateValue(entityType, attrType, value, deviceType = null) {
627
627
 
628
628
  case 'climate':
629
629
  if (attrType === 0x16) {
630
- // 当前温度:温度*100 -> 温度
630
+ // 当前温度:温度*100 -> 温度 (2字节小端序)
631
631
  return (value / 100).toFixed(1);
632
632
  } else if (attrType === 0x1B) {
633
- // 目标温度:温度*100 -> 温度
634
- return (value / 100).toFixed(1);
633
+ // 目标温度:直接温度值 (1字节,16-30°C)
634
+ return value.toString();
635
635
  } else if (attrType === 0x1C) {
636
636
  // 风速:1=high, 2=medium, 3=low, 4=auto
637
637
  const fanModes = { 1: 'high', 2: 'medium', 3: 'low', 4: 'auto' };
package/lib/tcp-client.js CHANGED
@@ -57,21 +57,37 @@ class TCPClient extends EventEmitter {
57
57
  }
58
58
  }, 10000);
59
59
 
60
- this.client.connect(this.port, this.host, () => {
60
+ // 使用family:4强制IPv4,避免Node.js 18+ Happy Eyeballs导致AggregateError
61
+ try {
62
+ this.client.connect({
63
+ port: this.port,
64
+ host: this.host,
65
+ family: 4 // 强制IPv4
66
+ }, () => {
67
+ if (!resolved && !rejected) {
68
+ resolved = true;
69
+ clearTimeout(timeout);
70
+ this.connected = true;
71
+ this.logger.log(`Connected to gateway at ${this.host}:${this.port}`);
72
+ this.emit('connected');
73
+
74
+ if (!this.processingQueue) {
75
+ this.startQueueProcessor();
76
+ }
77
+
78
+ resolve();
79
+ }
80
+ });
81
+ } catch (connectError) {
82
+ // 捕获同步错误(包括AggregateError)
61
83
  if (!resolved && !rejected) {
62
- resolved = true;
84
+ rejected = true;
63
85
  clearTimeout(timeout);
64
- this.connected = true;
65
- this.logger.log(`Connected to gateway at ${this.host}:${this.port}`);
66
- this.emit('connected');
67
-
68
- if (!this.processingQueue) {
69
- this.startQueueProcessor();
70
- }
71
-
72
- resolve();
86
+ this.logger.warn(`TCP连接同步错误: ${connectError.message || connectError}`);
87
+ this.handleDisconnect();
88
+ reject(connectError);
73
89
  }
74
- });
90
+ }
75
91
 
76
92
  this.client.on('data', (data) => {
77
93
  try {
@@ -88,9 +104,13 @@ class TCPClient extends EventEmitter {
88
104
 
89
105
  this.client.on('error', (error) => {
90
106
  clearTimeout(timeout);
107
+ // AggregateError特殊处理(Node.js 18+的IPv4/IPv6连接失败)
108
+ if (error.name === 'AggregateError' || error.errors) {
109
+ this.logger.warn(`TCP连接失败: 无法连接到 ${this.host}:${this.port}`);
110
+ }
91
111
  // 只在首次连接或重要错误时记录,避免大量重复日志
92
112
  // ECONNRESET通常是网络波动,不记录错误
93
- if (!this.connected && error.code !== 'ECONNREFUSED' && error.code !== 'ECONNRESET') {
113
+ else if (!this.connected && error.code !== 'ECONNREFUSED' && error.code !== 'ECONNRESET') {
94
114
  this.logger.error('TCP client error:', error.message);
95
115
  }
96
116