homebridge-melcloud-control 4.3.3-beta.9 → 4.3.4-beta.0
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/CHANGELOG.md +11 -0
- package/config.schema.json +42 -42
- package/homebridge-ui/public/index.html +111 -102
- package/package.json +2 -2
- package/src/deviceata.js +1 -1
- package/src/deviceatw.js +1 -1
- package/src/deviceerv.js +1 -1
- package/src/functions.js +11 -0
- package/src/melcloud.js +1 -1
- package/src/melcloudata.js +31 -14
- package/src/melcloudatw.js +1 -0
- package/src/melclouderv.js +1 -0
- package/src/melcloudhome.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
22
22
|
|
|
23
23
|
- Do not use Homebridge UI > v5.5.0 because of break config.json
|
|
24
24
|
|
|
25
|
+
# [4.3.3] - (22.11.2025)
|
|
26
|
+
|
|
27
|
+
## Changes
|
|
28
|
+
|
|
29
|
+
- fix [#224](https://github.com/grzegorz914/homebridge-melcloud-control/issues/224)
|
|
30
|
+
- fix presets report wrong state
|
|
31
|
+
- fix schedules assign to the config
|
|
32
|
+
- stability improvements
|
|
33
|
+
- bump dependencies
|
|
34
|
+
- cleanup
|
|
35
|
+
|
|
25
36
|
# [4.3.2] - (21.11.2025)
|
|
26
37
|
|
|
27
38
|
## Changes
|
package/config.schema.json
CHANGED
|
@@ -2640,24 +2640,24 @@
|
|
|
2640
2640
|
}
|
|
2641
2641
|
},
|
|
2642
2642
|
{
|
|
2643
|
-
"title": "
|
|
2643
|
+
"title": "Scenes",
|
|
2644
2644
|
"type": "section",
|
|
2645
|
-
"description": "Section for setup device
|
|
2645
|
+
"description": "Section for setup device scenes",
|
|
2646
2646
|
"expandable": true,
|
|
2647
2647
|
"expanded": false,
|
|
2648
2648
|
"items": [
|
|
2649
2649
|
{
|
|
2650
|
-
"key": "accounts[].ataDevices[].
|
|
2650
|
+
"key": "accounts[].ataDevices[].scenes",
|
|
2651
2651
|
"type": "tabarray",
|
|
2652
2652
|
"title": "{{ value.name }}",
|
|
2653
2653
|
"items": [
|
|
2654
2654
|
{
|
|
2655
|
-
"key": "accounts[].ataDevices[].
|
|
2655
|
+
"key": "accounts[].ataDevices[].scenes[].id",
|
|
2656
2656
|
"readonly": true
|
|
2657
2657
|
},
|
|
2658
|
-
"accounts[].ataDevices[].
|
|
2659
|
-
"accounts[].ataDevices[].
|
|
2660
|
-
"accounts[].ataDevices[].
|
|
2658
|
+
"accounts[].ataDevices[].scenes[].displayType",
|
|
2659
|
+
"accounts[].ataDevices[].scenes[].name",
|
|
2660
|
+
"accounts[].ataDevices[].scenes[].namePrefix"
|
|
2661
2661
|
]
|
|
2662
2662
|
}
|
|
2663
2663
|
],
|
|
@@ -2666,24 +2666,24 @@
|
|
|
2666
2666
|
}
|
|
2667
2667
|
},
|
|
2668
2668
|
{
|
|
2669
|
-
"title": "
|
|
2669
|
+
"title": "Schedules",
|
|
2670
2670
|
"type": "section",
|
|
2671
|
-
"description": "Section for setup device
|
|
2671
|
+
"description": "Section for setup device schedules",
|
|
2672
2672
|
"expandable": true,
|
|
2673
2673
|
"expanded": false,
|
|
2674
2674
|
"items": [
|
|
2675
2675
|
{
|
|
2676
|
-
"key": "accounts[].ataDevices[].
|
|
2676
|
+
"key": "accounts[].ataDevices[].schedules",
|
|
2677
2677
|
"type": "tabarray",
|
|
2678
2678
|
"title": "{{ value.name }}",
|
|
2679
2679
|
"items": [
|
|
2680
2680
|
{
|
|
2681
|
-
"key": "accounts[].ataDevices[].
|
|
2681
|
+
"key": "accounts[].ataDevices[].schedules[].id",
|
|
2682
2682
|
"readonly": true
|
|
2683
2683
|
},
|
|
2684
|
-
"accounts[].ataDevices[].
|
|
2685
|
-
"accounts[].ataDevices[].
|
|
2686
|
-
"accounts[].ataDevices[].
|
|
2684
|
+
"accounts[].ataDevices[].schedules[].displayType",
|
|
2685
|
+
"accounts[].ataDevices[].schedules[].name",
|
|
2686
|
+
"accounts[].ataDevices[].schedules[].namePrefix"
|
|
2687
2687
|
]
|
|
2688
2688
|
}
|
|
2689
2689
|
],
|
|
@@ -2813,24 +2813,24 @@
|
|
|
2813
2813
|
}
|
|
2814
2814
|
},
|
|
2815
2815
|
{
|
|
2816
|
-
"title": "
|
|
2816
|
+
"title": "Scenes",
|
|
2817
2817
|
"type": "section",
|
|
2818
|
-
"description": "Section for setup device
|
|
2818
|
+
"description": "Section for setup device scenes",
|
|
2819
2819
|
"expandable": true,
|
|
2820
2820
|
"expanded": false,
|
|
2821
2821
|
"items": [
|
|
2822
2822
|
{
|
|
2823
|
-
"key": "accounts[].atwDevices[].
|
|
2823
|
+
"key": "accounts[].atwDevices[].scenes",
|
|
2824
2824
|
"type": "tabarray",
|
|
2825
2825
|
"title": "{{ value.name }}",
|
|
2826
2826
|
"items": [
|
|
2827
2827
|
{
|
|
2828
|
-
"key": "accounts[].atwDevices[].
|
|
2828
|
+
"key": "accounts[].atwDevices[].scenes[].id",
|
|
2829
2829
|
"readonly": true
|
|
2830
2830
|
},
|
|
2831
|
-
"accounts[].atwDevices[].
|
|
2832
|
-
"accounts[].atwDevices[].
|
|
2833
|
-
"accounts[].atwDevices[].
|
|
2831
|
+
"accounts[].atwDevices[].scenes[].displayType",
|
|
2832
|
+
"accounts[].atwDevices[].scenes[].name",
|
|
2833
|
+
"accounts[].atwDevices[].scenes[].namePrefix"
|
|
2834
2834
|
]
|
|
2835
2835
|
}
|
|
2836
2836
|
],
|
|
@@ -2839,24 +2839,24 @@
|
|
|
2839
2839
|
}
|
|
2840
2840
|
},
|
|
2841
2841
|
{
|
|
2842
|
-
"title": "
|
|
2842
|
+
"title": "Schedules",
|
|
2843
2843
|
"type": "section",
|
|
2844
|
-
"description": "Section for setup device
|
|
2844
|
+
"description": "Section for setup device schedules",
|
|
2845
2845
|
"expandable": true,
|
|
2846
2846
|
"expanded": false,
|
|
2847
2847
|
"items": [
|
|
2848
2848
|
{
|
|
2849
|
-
"key": "accounts[].atwDevices[].
|
|
2849
|
+
"key": "accounts[].atwDevices[].schedules",
|
|
2850
2850
|
"type": "tabarray",
|
|
2851
2851
|
"title": "{{ value.name }}",
|
|
2852
2852
|
"items": [
|
|
2853
2853
|
{
|
|
2854
|
-
"key": "accounts[].atwDevices[].
|
|
2854
|
+
"key": "accounts[].atwDevices[].schedules[].id",
|
|
2855
2855
|
"readonly": true
|
|
2856
2856
|
},
|
|
2857
|
-
"accounts[].atwDevices[].
|
|
2858
|
-
"accounts[].atwDevices[].
|
|
2859
|
-
"accounts[].atwDevices[].
|
|
2857
|
+
"accounts[].atwDevices[].schedules[].displayType",
|
|
2858
|
+
"accounts[].atwDevices[].schedules[].name",
|
|
2859
|
+
"accounts[].atwDevices[].schedules[].namePrefix"
|
|
2860
2860
|
]
|
|
2861
2861
|
}
|
|
2862
2862
|
],
|
|
@@ -2991,24 +2991,24 @@
|
|
|
2991
2991
|
}
|
|
2992
2992
|
},
|
|
2993
2993
|
{
|
|
2994
|
-
"title": "
|
|
2994
|
+
"title": "Scenes",
|
|
2995
2995
|
"type": "section",
|
|
2996
|
-
"description": "Section for setup device
|
|
2996
|
+
"description": "Section for setup device scenes",
|
|
2997
2997
|
"expandable": true,
|
|
2998
2998
|
"expanded": false,
|
|
2999
2999
|
"items": [
|
|
3000
3000
|
{
|
|
3001
|
-
"key": "accounts[].ervDevices[].
|
|
3001
|
+
"key": "accounts[].ervDevices[].scenes",
|
|
3002
3002
|
"type": "tabarray",
|
|
3003
3003
|
"title": "{{ value.name }}",
|
|
3004
3004
|
"items": [
|
|
3005
3005
|
{
|
|
3006
|
-
"key": "accounts[].ervDevices[].
|
|
3006
|
+
"key": "accounts[].ervDevices[].scenes[].id",
|
|
3007
3007
|
"readonly": true
|
|
3008
3008
|
},
|
|
3009
|
-
"accounts[].ervDevices[].
|
|
3010
|
-
"accounts[].ervDevices[].
|
|
3011
|
-
"accounts[].ervDevices[].
|
|
3009
|
+
"accounts[].ervDevices[].scenes[].displayType",
|
|
3010
|
+
"accounts[].ervDevices[].scenes[].name",
|
|
3011
|
+
"accounts[].ervDevices[].scenes[].namePrefix"
|
|
3012
3012
|
]
|
|
3013
3013
|
}
|
|
3014
3014
|
],
|
|
@@ -3017,24 +3017,24 @@
|
|
|
3017
3017
|
}
|
|
3018
3018
|
},
|
|
3019
3019
|
{
|
|
3020
|
-
"title": "
|
|
3020
|
+
"title": "Schedules",
|
|
3021
3021
|
"type": "section",
|
|
3022
|
-
"description": "Section for setup device
|
|
3022
|
+
"description": "Section for setup device schedules",
|
|
3023
3023
|
"expandable": true,
|
|
3024
3024
|
"expanded": false,
|
|
3025
3025
|
"items": [
|
|
3026
3026
|
{
|
|
3027
|
-
"key": "accounts[].ervDevices[].
|
|
3027
|
+
"key": "accounts[].ervDevices[].schedules",
|
|
3028
3028
|
"type": "tabarray",
|
|
3029
3029
|
"title": "{{ value.name }}",
|
|
3030
3030
|
"items": [
|
|
3031
3031
|
{
|
|
3032
|
-
"key": "accounts[].ervDevices[].
|
|
3032
|
+
"key": "accounts[].ervDevices[].schedules[].id",
|
|
3033
3033
|
"readonly": true
|
|
3034
3034
|
},
|
|
3035
|
-
"accounts[].ervDevices[].
|
|
3036
|
-
"accounts[].ervDevices[].
|
|
3037
|
-
"accounts[].ervDevices[].
|
|
3035
|
+
"accounts[].ervDevices[].schedules[].displayType",
|
|
3036
|
+
"accounts[].ervDevices[].schedules[].name",
|
|
3037
|
+
"accounts[].ervDevices[].schedules[].namePrefix"
|
|
3038
3038
|
]
|
|
3039
3039
|
}
|
|
3040
3040
|
],
|
|
@@ -249,11 +249,11 @@
|
|
|
249
249
|
const newDevices = { ata: [], ataPresets: [], ataSchedules: [], atw: [], atwPresets: [], atwSchedules: [], erv: [], ervPresets: [], ervSchedules: [], scenes: [] };
|
|
250
250
|
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
251
251
|
|
|
252
|
-
response.Devices.forEach(
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
if (
|
|
256
|
-
if (
|
|
252
|
+
response.Devices.forEach(device => {
|
|
253
|
+
device.Scenes = response.Scenes ?? [];
|
|
254
|
+
if (device.Type === 0) devicesByType.ata.push(device);
|
|
255
|
+
if (device.Type === 1) devicesByType.atw.push(device);
|
|
256
|
+
if (device.Type === 3) devicesByType.erv.push(device);
|
|
257
257
|
});
|
|
258
258
|
|
|
259
259
|
account.ataDevices ??= [];
|
|
@@ -295,105 +295,112 @@
|
|
|
295
295
|
configDevicesMap.set(deviceId, deviceInConfig);
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
298
|
+
//only for melcloud
|
|
299
|
+
if (account.type === 'melcloud') {
|
|
300
|
+
|
|
301
|
+
// === Process presets ===
|
|
302
|
+
const presetsInMelCloud = device.Presets || [];
|
|
303
|
+
const presetsInConfig = deviceInConfig.presets || [];
|
|
304
|
+
const presetIds = new Set(presetsInConfig.map(p => String(p.id)));
|
|
305
|
+
|
|
306
|
+
let addedNewPreset = false;
|
|
307
|
+
presetsInMelCloud.forEach((preset, index) => {
|
|
308
|
+
const presetId = String(preset.ID);
|
|
309
|
+
if (!presetIds.has(presetId)) {
|
|
310
|
+
const presetObj = {
|
|
311
|
+
id: presetId,
|
|
312
|
+
displayType: 0,
|
|
313
|
+
name: preset.NumberDescription || `Preset ${index}`,
|
|
314
|
+
namePrefix: false
|
|
315
|
+
};
|
|
316
|
+
presetsInConfig.push(presetObj);
|
|
317
|
+
newPresets.push(presetObj);
|
|
318
|
+
presetIds.add(presetId);
|
|
319
|
+
addedNewPreset = true;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// === Remove placeholder presets/schedules (id === '0') if new ones were added ===
|
|
324
|
+
if (addedNewPreset) {
|
|
325
|
+
const beforeCount = presetsInConfig.length;
|
|
326
|
+
deviceInConfig.presets = presetsInConfig.filter(p => String(p.id) !== '0');
|
|
327
|
+
const removedCount = beforeCount - deviceInConfig.presets.length;
|
|
328
|
+
|
|
329
|
+
if (removedCount > 0 && removedCount < beforeCount) {
|
|
330
|
+
updateInfo('info2', `Removed ${removedCount} placeholder preset from device ${device.DeviceID}`, 'yellow');
|
|
331
|
+
}
|
|
328
332
|
}
|
|
329
333
|
}
|
|
330
334
|
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
335
|
+
//only for melcloudhome
|
|
336
|
+
if (account.type === 'melcloudhome') {
|
|
337
|
+
|
|
338
|
+
// === Process schedules ===
|
|
339
|
+
const schedulesInMelCloud = device.Schedule || [];
|
|
340
|
+
const schedulesInConfig = deviceInConfig.schedules || [];
|
|
341
|
+
const scheduleIds = new Set(schedulesInConfig.map(s => String(s.id)));
|
|
342
|
+
|
|
343
|
+
let addedNewSchedule = false;
|
|
344
|
+
schedulesInMelCloud.forEach((schedule, index) => {
|
|
345
|
+
const scheduleId = String(schedule.Id);
|
|
346
|
+
if (!scheduleIds.has(scheduleId)) {
|
|
347
|
+
const scheduleObj = {
|
|
348
|
+
id: scheduleId,
|
|
349
|
+
displayType: 0,
|
|
350
|
+
name: `Schedule ${index}`,
|
|
351
|
+
namePrefix: false
|
|
352
|
+
};
|
|
353
|
+
schedulesInConfig.push(scheduleObj);
|
|
354
|
+
newSchedules.push(scheduleObj);
|
|
355
|
+
scheduleIds.add(scheduleId);
|
|
356
|
+
addedNewSchedule = true;
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// === Remove placeholder schedules (id === '0') if new ones were added ===
|
|
361
|
+
if (addedNewSchedule) {
|
|
362
|
+
const beforeCount = schedulesInConfig.length;
|
|
363
|
+
deviceInConfig.schedules = schedulesInConfig.filter(s => String(s.id) !== '0');
|
|
364
|
+
const removedCount = beforeCount - deviceInConfig.schedules.length;
|
|
365
|
+
|
|
366
|
+
if (removedCount > 0 && removedCount < beforeCount) {
|
|
367
|
+
updateInfo('info2', `Removed ${removedCount} placeholder schedule from device ${device.DeviceID}`, 'yellow');
|
|
368
|
+
}
|
|
350
369
|
}
|
|
351
|
-
});
|
|
352
370
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
371
|
+
// === Process scenes ===
|
|
372
|
+
const scenesInMelCloud = device.Scenes || [];
|
|
373
|
+
const scenesInConfig = deviceInConfig.scenes || [];
|
|
374
|
+
const sceneIds = new Set(scenesInConfig.map(s => String(s.id)));
|
|
375
|
+
|
|
376
|
+
let addedNewScenes = false;
|
|
377
|
+
scenesInMelCloud.forEach((scene, index) => {
|
|
378
|
+
const sceneId = String(scene.Id);
|
|
379
|
+
if (!sceneIds.has(sceneId)) {
|
|
380
|
+
const sceneObj = {
|
|
381
|
+
id: sceneId,
|
|
382
|
+
displayType: 0,
|
|
383
|
+
name: scene.Name || `Scene ${index}`,
|
|
384
|
+
namePrefix: false
|
|
385
|
+
};
|
|
386
|
+
scenesInConfig.push(sceneObj);
|
|
387
|
+
newScenes.push(sceneObj);
|
|
388
|
+
sceneIds.add(sceneId);
|
|
389
|
+
addedNewScenes = true;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// === Remove placeholder scenes (id === '0') if new ones were added ===
|
|
394
|
+
if (addedNewScenes) {
|
|
395
|
+
const beforeCount = scenesInConfig.length;
|
|
396
|
+
deviceInConfig.scenes = scenesInConfig.filter(s => String(s.id) !== '0');
|
|
397
|
+
const removedCount = beforeCount - deviceInConfig.scenes.length;
|
|
398
|
+
|
|
399
|
+
if (removedCount > 0 && removedCount < beforeCount) {
|
|
400
|
+
updateInfo('info2', `Removed ${removedCount} placeholder scene from device ${device.DeviceID}`, 'yellow');
|
|
401
|
+
}
|
|
361
402
|
}
|
|
362
403
|
}
|
|
363
|
-
|
|
364
|
-
// === Process scenes ===
|
|
365
|
-
const scenes = device.Scenes || [];
|
|
366
|
-
const scenesInConfig = deviceInConfig.scenes || [];
|
|
367
|
-
const sceneIds = new Set(scenesInConfig.map(s => String(s.id)));
|
|
368
|
-
|
|
369
|
-
let addedNewScenes = false;
|
|
370
|
-
scenes.forEach((scene, index) => {
|
|
371
|
-
const sceneId = String(scene.Id);
|
|
372
|
-
if (!sceneIds.has(sceneId)) {
|
|
373
|
-
const sceneObj = {
|
|
374
|
-
id: sceneId,
|
|
375
|
-
displayType: 0,
|
|
376
|
-
name: scene.Name || `Scene ${index}`,
|
|
377
|
-
namePrefix: false
|
|
378
|
-
};
|
|
379
|
-
scenesInConfig.push(sceneObj);
|
|
380
|
-
newScenes.push(sceneObj);
|
|
381
|
-
sceneIds.add(sceneId);
|
|
382
|
-
addedNewScenes = true;
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
// === Remove placeholder scenes (id === '0') if new ones were added ===
|
|
387
|
-
if (addedNewScenes) {
|
|
388
|
-
const beforeCount = scenesInConfig.length;
|
|
389
|
-
deviceInConfig.scenes = scenesInConfig.filter(s => String(s.id) !== '0');
|
|
390
|
-
const removedCount = beforeCount - deviceInConfig.scenes.length;
|
|
391
|
-
|
|
392
|
-
if (removedCount > 0 && removedCount < beforeCount) {
|
|
393
|
-
updateInfo('info2', `Removed ${removedCount} placeholder scene from device ${device.DeviceID}`, 'yellow');
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
404
|
});
|
|
398
405
|
|
|
399
406
|
// Return filtered devicesInConfig to make sure upstream code uses it
|
|
@@ -418,17 +425,19 @@
|
|
|
418
425
|
updateInfo('info', 'No changes detected.', 'white');
|
|
419
426
|
} else {
|
|
420
427
|
if (newDevicesCount)
|
|
421
|
-
updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}
|
|
422
|
-
if (newPresetsCount
|
|
423
|
-
updateInfo('info1', `Found new
|
|
428
|
+
updateInfo('info', `Found new devices: ${newDevices.ata.length ? `ATA: ${newDevices.ata.length},` : ''} ${newDevices.atw.length ? `ATW: ${newDevices.atw.length},` : ''} ${newDevices.erv.length ? `ERV: ${newDevices.erv.length},` : ''}.`, 'green');
|
|
429
|
+
if (newPresetsCount)
|
|
430
|
+
updateInfo('info1', `Found new presets: ${newDevices.ataPresets.length ? `ATA: ${newDevices.ataPresets.length},` : ''} ${newDevices.atwPresets.length ? `ATW: ${newDevices.atwPresets.length},` : ''} ${newDevices.ervPresets.length ? `ERV: ${newDevices.ervPresets.length}` : ''}.`, 'green');
|
|
431
|
+
if (newScenesCount || newSchedulesCount)
|
|
432
|
+
updateInfo('info1', `Found new ${newSchedulesCount ? `schedules:` : ''} ${newDevices.ataSchedules.length ? `ATA: ${newDevices.ataSchedules.length},` : ''} ${newDevices.atwSchedules.length ? `ATW: ${newDevices.atwSchedules.length},` : ''} ${newDevices.ervSchedules.length ? `ERV: ${newDevices.ervSchedules.length},` : ''} ${newScenesCount ? `scenes: ${newScenesCount}` : ''}.`, 'green');
|
|
424
433
|
if (removedDevicesCount)
|
|
425
|
-
updateInfo('info2', `Removed devices: ATA: ${removedAta.length}
|
|
434
|
+
updateInfo('info2', `Removed devices: ${removedAta.length ? `ATA: ${removedAta.length},` : ''} ${removedAtw.length ? `ATW: ${removedAtw.length},` : ''} ${removedErv.length ? `ERV: ${removedErv.length}` : ''}.`, 'orange');
|
|
426
435
|
}
|
|
427
436
|
|
|
428
437
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
429
438
|
await homebridge.savePluginConfig(pluginConfig);
|
|
430
439
|
} catch (error) {
|
|
431
|
-
updateInfo('info', `
|
|
440
|
+
updateInfo('info', `Prepare config error ${JSON.stringify(error)}`, 'red');
|
|
432
441
|
document.getElementById('logIn').className = "btn btn-secondary";
|
|
433
442
|
} finally {
|
|
434
443
|
document.getElementById('logIn').className = "btn btn-secondary";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.4-beta.0",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"node": "^20 || ^22 || ^24 || ^25"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@homebridge/plugin-ui-utils": "^2.1.
|
|
38
|
+
"@homebridge/plugin-ui-utils": "^2.1.2",
|
|
39
39
|
"async-mqtt": "^2.6.3",
|
|
40
40
|
"axios": "^1.13.2",
|
|
41
41
|
"express": "^5.1.0",
|
package/src/deviceata.js
CHANGED
|
@@ -915,7 +915,7 @@ class DeviceAta extends EventEmitter {
|
|
|
915
915
|
presetControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${serviceName1} Control`);
|
|
916
916
|
presetControlSensorService.getCharacteristic(characteristicType)
|
|
917
917
|
.onGet(async () => {
|
|
918
|
-
const state =
|
|
918
|
+
const state = preset.state;
|
|
919
919
|
return state;
|
|
920
920
|
})
|
|
921
921
|
this.presetControlSensorServices.push(presetControlSensorService);
|
package/src/deviceatw.js
CHANGED
|
@@ -1230,7 +1230,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1230
1230
|
presetControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${serviceName1} Control`);
|
|
1231
1231
|
presetControlSensorService.getCharacteristic(characteristicType)
|
|
1232
1232
|
.onGet(async () => {
|
|
1233
|
-
const state =
|
|
1233
|
+
const state = preset.state;
|
|
1234
1234
|
return state;
|
|
1235
1235
|
})
|
|
1236
1236
|
this.presetControlSensorServices.push(presetControlSensorService);
|
package/src/deviceerv.js
CHANGED
|
@@ -842,7 +842,7 @@ class DeviceErv extends EventEmitter {
|
|
|
842
842
|
presetControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${serviceName1} Control`);
|
|
843
843
|
presetControlSensorService.getCharacteristic(characteristicType)
|
|
844
844
|
.onGet(async () => {
|
|
845
|
-
const state =
|
|
845
|
+
const state = preset.state;
|
|
846
846
|
return state;
|
|
847
847
|
})
|
|
848
848
|
this.presetControlSensorServices.push(presetControlSensorService);
|
package/src/functions.js
CHANGED
|
@@ -167,5 +167,16 @@ class Functions extends EventEmitter {
|
|
|
167
167
|
else if (!isNaN(v) && v !== "") parsedValue = Number(v);
|
|
168
168
|
return parsedValue;
|
|
169
169
|
}
|
|
170
|
+
|
|
171
|
+
parseArrayNameValue(data) {
|
|
172
|
+
if (!Array.isArray(data)) return {};
|
|
173
|
+
|
|
174
|
+
return Object.fromEntries(
|
|
175
|
+
data.map(({ name, value }) => {
|
|
176
|
+
const parsedValue = this.convertValue(value);
|
|
177
|
+
return [name, parsedValue];
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
}
|
|
170
181
|
}
|
|
171
182
|
export default Functions
|
package/src/melcloud.js
CHANGED
|
@@ -143,7 +143,7 @@ class MelCloud extends EventEmitter {
|
|
|
143
143
|
data: payload
|
|
144
144
|
});
|
|
145
145
|
const account = accountData.data;
|
|
146
|
-
const loginData = account.LoginData ??
|
|
146
|
+
const loginData = account.LoginData ?? {};
|
|
147
147
|
const contextKey = loginData.ContextKey;
|
|
148
148
|
|
|
149
149
|
const safeConfig = {
|
package/src/melcloudata.js
CHANGED
|
@@ -76,8 +76,8 @@ class MelCloudAta extends EventEmitter {
|
|
|
76
76
|
deviceData.Device.OperationMode = AirConditioner.OperationModeMapStringToEnum[deviceData.Device.OperationMode] ?? deviceData.Device.OperationMode;
|
|
77
77
|
deviceData.Device.ActualFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.ActualFanSpeed] ?? deviceData.Device.ActualFanSpeed;
|
|
78
78
|
deviceData.Device.SetFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.SetFanSpeed] ?? deviceData.Device.SetFanSpeed;
|
|
79
|
-
deviceData.Device.VaneVerticalDirection = AirConditioner.VaneVerticalDirectionMapStringToEnum[deviceData.Device.VaneVerticalDirection] ?? deviceData.Device.VaneVerticalDirection;
|
|
80
79
|
deviceData.Device.VaneHorizontalDirection = AirConditioner.VaneHorizontalDirectionMapStringToEnum[deviceData.Device.VaneHorizontalDirection] ?? deviceData.Device.VaneHorizontalDirection
|
|
80
|
+
deviceData.Device.VaneVerticalDirection = AirConditioner.VaneVerticalDirectionMapStringToEnum[deviceData.Device.VaneVerticalDirection] ?? deviceData.Device.VaneVerticalDirection;
|
|
81
81
|
|
|
82
82
|
//read default temps
|
|
83
83
|
const temps = await this.functions.readData(this.defaultTempsFile, true);
|
|
@@ -151,14 +151,15 @@ class MelCloudAta extends EventEmitter {
|
|
|
151
151
|
|
|
152
152
|
this.headers = devicesData.Headers;
|
|
153
153
|
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
154
|
+
if (!deviceData) return;
|
|
154
155
|
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
155
156
|
|
|
156
157
|
//web cocket connection
|
|
157
158
|
if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
|
|
158
159
|
this.connecting = true;
|
|
159
160
|
|
|
160
|
-
const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
|
|
161
161
|
try {
|
|
162
|
+
const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
|
|
162
163
|
const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
|
|
163
164
|
.on('error', (error) => {
|
|
164
165
|
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
@@ -188,7 +189,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
188
189
|
.on('message', async (message) => {
|
|
189
190
|
const parsedMessage = JSON.parse(message);
|
|
190
191
|
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
191
|
-
if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
192
192
|
if (parsedMessage.message === 'Forbidden') return;
|
|
193
193
|
|
|
194
194
|
const messageData = parsedMessage?.[0]?.Data;
|
|
@@ -198,16 +198,33 @@ class MelCloudAta extends EventEmitter {
|
|
|
198
198
|
const unitId = messageData?.id;
|
|
199
199
|
switch (unitId) {
|
|
200
200
|
case this.deviceId:
|
|
201
|
+
if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
201
202
|
const messageType = parsedMessage[0].messageType;
|
|
203
|
+
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
202
204
|
switch (messageType) {
|
|
203
205
|
case 'unitStateChanged':
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
|
|
207
|
+
//update values
|
|
208
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
209
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
210
|
+
|
|
211
|
+
//update holiday mode
|
|
212
|
+
if (key === 'HolidayMode') {
|
|
213
|
+
deviceData.HolidayMode.Enabled = value;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
//update device settings
|
|
218
|
+
if (key in deviceData.Device) {
|
|
219
|
+
deviceData.Device[key] = value;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
updateState = true;
|
|
223
|
+
break;
|
|
224
|
+
case 'unitHolidayModeTriggered':
|
|
225
|
+
deviceData.Device.Power = settings.Power;
|
|
226
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
227
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
211
228
|
updateState = true;
|
|
212
229
|
break;
|
|
213
230
|
case 'unitWifiSignalChanged':
|
|
@@ -215,12 +232,12 @@ class MelCloudAta extends EventEmitter {
|
|
|
215
232
|
updateState = true;
|
|
216
233
|
break;
|
|
217
234
|
default:
|
|
218
|
-
if (
|
|
235
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
219
236
|
return;
|
|
220
237
|
}
|
|
221
238
|
break;
|
|
222
239
|
default:
|
|
223
|
-
if (
|
|
240
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
224
241
|
return;
|
|
225
242
|
}
|
|
226
243
|
|
|
@@ -329,6 +346,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
329
346
|
method = 'POST';
|
|
330
347
|
path = ApiUrlsHome.PostHolidayMode;
|
|
331
348
|
headers.Referer = ApiUrlsHome.Referers.PostHolidayMode.replace('deviceid', deviceData.DeviceID);
|
|
349
|
+
updateState = false;
|
|
332
350
|
break;
|
|
333
351
|
case 'schedule':
|
|
334
352
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
@@ -368,8 +386,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
368
386
|
path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
|
|
369
387
|
headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings;
|
|
370
388
|
updateState = false;
|
|
371
|
-
|
|
372
|
-
return;
|
|
389
|
+
break;
|
|
373
390
|
}
|
|
374
391
|
|
|
375
392
|
//sens payload
|
package/src/melcloudatw.js
CHANGED
|
@@ -145,6 +145,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
145
145
|
|
|
146
146
|
this.headers = devicesData.Headers;
|
|
147
147
|
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
148
|
+
if (!deviceData) return;
|
|
148
149
|
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
149
150
|
|
|
150
151
|
//web cocket connection
|
package/src/melclouderv.js
CHANGED
|
@@ -60,6 +60,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
60
60
|
|
|
61
61
|
this.headers = devicesData.Headers;
|
|
62
62
|
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
63
|
+
if (!deviceData) return;
|
|
63
64
|
if (this.accountType === 'melcloudhome') {
|
|
64
65
|
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
65
66
|
|
package/src/melcloudhome.js
CHANGED
|
@@ -142,7 +142,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
142
142
|
const createDevice = (device, type) => {
|
|
143
143
|
// Settings już kapitalizowane w nazwach
|
|
144
144
|
const settingsArray = device.Settings || [];
|
|
145
|
-
|
|
146
145
|
const settingsObject = Object.fromEntries(
|
|
147
146
|
settingsArray.map(({ name, value }) => {
|
|
148
147
|
let parsedValue = this.functions.convertValue(value);
|