homebridge-melcloud-control 4.3.3-beta.9 → 4.3.3

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 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
@@ -2640,24 +2640,24 @@
2640
2640
  }
2641
2641
  },
2642
2642
  {
2643
- "title": "Schedules",
2643
+ "title": "Scenes",
2644
2644
  "type": "section",
2645
- "description": "Section for setup device schedules",
2645
+ "description": "Section for setup device scenes",
2646
2646
  "expandable": true,
2647
2647
  "expanded": false,
2648
2648
  "items": [
2649
2649
  {
2650
- "key": "accounts[].ataDevices[].schedules",
2650
+ "key": "accounts[].ataDevices[].scenes",
2651
2651
  "type": "tabarray",
2652
2652
  "title": "{{ value.name }}",
2653
2653
  "items": [
2654
2654
  {
2655
- "key": "accounts[].ataDevices[].schedules[].id",
2655
+ "key": "accounts[].ataDevices[].scenes[].id",
2656
2656
  "readonly": true
2657
2657
  },
2658
- "accounts[].ataDevices[].schedules[].displayType",
2659
- "accounts[].ataDevices[].schedules[].name",
2660
- "accounts[].ataDevices[].schedules[].namePrefix"
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": "Scenes",
2669
+ "title": "Schedules",
2670
2670
  "type": "section",
2671
- "description": "Section for setup device scenes",
2671
+ "description": "Section for setup device schedules",
2672
2672
  "expandable": true,
2673
2673
  "expanded": false,
2674
2674
  "items": [
2675
2675
  {
2676
- "key": "accounts[].ataDevices[].scenes",
2676
+ "key": "accounts[].ataDevices[].schedules",
2677
2677
  "type": "tabarray",
2678
2678
  "title": "{{ value.name }}",
2679
2679
  "items": [
2680
2680
  {
2681
- "key": "accounts[].ataDevices[].scenes[].id",
2681
+ "key": "accounts[].ataDevices[].schedules[].id",
2682
2682
  "readonly": true
2683
2683
  },
2684
- "accounts[].ataDevices[].scenes[].displayType",
2685
- "accounts[].ataDevices[].scenes[].name",
2686
- "accounts[].ataDevices[].scenes[].namePrefix"
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": "Schedules",
2816
+ "title": "Scenes",
2817
2817
  "type": "section",
2818
- "description": "Section for setup device schedules",
2818
+ "description": "Section for setup device scenes",
2819
2819
  "expandable": true,
2820
2820
  "expanded": false,
2821
2821
  "items": [
2822
2822
  {
2823
- "key": "accounts[].atwDevices[].schedules",
2823
+ "key": "accounts[].atwDevices[].scenes",
2824
2824
  "type": "tabarray",
2825
2825
  "title": "{{ value.name }}",
2826
2826
  "items": [
2827
2827
  {
2828
- "key": "accounts[].atwDevices[].schedules[].id",
2828
+ "key": "accounts[].atwDevices[].scenes[].id",
2829
2829
  "readonly": true
2830
2830
  },
2831
- "accounts[].atwDevices[].schedules[].displayType",
2832
- "accounts[].atwDevices[].schedules[].name",
2833
- "accounts[].atwDevices[].schedules[].namePrefix"
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": "Scenes",
2842
+ "title": "Schedules",
2843
2843
  "type": "section",
2844
- "description": "Section for setup device scenes",
2844
+ "description": "Section for setup device schedules",
2845
2845
  "expandable": true,
2846
2846
  "expanded": false,
2847
2847
  "items": [
2848
2848
  {
2849
- "key": "accounts[].atwDevices[].scenes",
2849
+ "key": "accounts[].atwDevices[].schedules",
2850
2850
  "type": "tabarray",
2851
2851
  "title": "{{ value.name }}",
2852
2852
  "items": [
2853
2853
  {
2854
- "key": "accounts[].atwDevices[].scenes[].id",
2854
+ "key": "accounts[].atwDevices[].schedules[].id",
2855
2855
  "readonly": true
2856
2856
  },
2857
- "accounts[].atwDevices[].scenes[].displayType",
2858
- "accounts[].atwDevices[].scenes[].name",
2859
- "accounts[].atwDevices[].scenes[].namePrefix"
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": "Schedules",
2994
+ "title": "Scenes",
2995
2995
  "type": "section",
2996
- "description": "Section for setup device schedules",
2996
+ "description": "Section for setup device scenes",
2997
2997
  "expandable": true,
2998
2998
  "expanded": false,
2999
2999
  "items": [
3000
3000
  {
3001
- "key": "accounts[].ervDevices[].schedules",
3001
+ "key": "accounts[].ervDevices[].scenes",
3002
3002
  "type": "tabarray",
3003
3003
  "title": "{{ value.name }}",
3004
3004
  "items": [
3005
3005
  {
3006
- "key": "accounts[].ervDevices[].schedules[].id",
3006
+ "key": "accounts[].ervDevices[].scenes[].id",
3007
3007
  "readonly": true
3008
3008
  },
3009
- "accounts[].ervDevices[].schedules[].displayType",
3010
- "accounts[].ervDevices[].schedules[].name",
3011
- "accounts[].ervDevices[].schedules[].namePrefix"
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": "Scenes",
3020
+ "title": "Schedules",
3021
3021
  "type": "section",
3022
- "description": "Section for setup device scenes",
3022
+ "description": "Section for setup device schedules",
3023
3023
  "expandable": true,
3024
3024
  "expanded": false,
3025
3025
  "items": [
3026
3026
  {
3027
- "key": "accounts[].ervDevices[].scenes",
3027
+ "key": "accounts[].ervDevices[].schedules",
3028
3028
  "type": "tabarray",
3029
3029
  "title": "{{ value.name }}",
3030
3030
  "items": [
3031
3031
  {
3032
- "key": "accounts[].ervDevices[].scenes[].id",
3032
+ "key": "accounts[].ervDevices[].schedules[].id",
3033
3033
  "readonly": true
3034
3034
  },
3035
- "accounts[].ervDevices[].scenes[].displayType",
3036
- "accounts[].ervDevices[].scenes[].name",
3037
- "accounts[].ervDevices[].scenes[].namePrefix"
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(d => {
253
- d.Scenes = response.Scenes ?? [];
254
- if (d.Type === 0) devicesByType.ata.push(d);
255
- if (d.Type === 1) devicesByType.atw.push(d);
256
- if (d.Type === 3) devicesByType.erv.push(d);
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
- // === Process presets/schedules ===
299
- const presets = device.Presets || [];
300
- const presetsInConfig = deviceInConfig.presets || [];
301
- const presetIds = new Set(presetsInConfig.map(p => String(p.id)));
302
-
303
- let addedNewPreset = false;
304
- presets.forEach((preset, index) => {
305
- const presetId = String(preset.ID);
306
- if (!presetIds.has(presetId)) {
307
- const presetObj = {
308
- id: presetId,
309
- displayType: 0,
310
- name: preset.NumberDescription || `Preset ${index}`,
311
- namePrefix: false
312
- };
313
- presetsInConfig.push(presetObj);
314
- newPresets.push(presetObj);
315
- presetIds.add(presetId);
316
- addedNewPreset = true;
317
- }
318
- });
319
-
320
- // === Remove placeholder presets/schedules (id === '0') if new ones were added ===
321
- if (addedNewPreset) {
322
- const beforeCount = presetsInConfig.length;
323
- deviceInConfig.presets = presetsInConfig.filter(p => String(p.id) !== '0');
324
- const removedCount = beforeCount - deviceInConfig.presets.length;
325
-
326
- if (removedCount > 0 && removedCount < beforeCount) {
327
- updateInfo('info2', `Removed ${removedCount} placeholder preset from device ${device.DeviceID}`, 'yellow');
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
- // === Process presets/schedules ===
332
- const schedules = device.Schedules || [];
333
- const schedulesInConfig = deviceInConfig.schedules || [];
334
- const scheduleIds = new Set(schedulesInConfig.map(s => String(s.id)));
335
-
336
- let addedNewSchedule = false;
337
- schedules.forEach((schedule, index) => {
338
- const scheduleId = String(schedule.Id);
339
- if (!scheduleIds.has(scheduleId)) {
340
- const presetObj = {
341
- id: scheduleId,
342
- displayType: 0,
343
- name: `Schedule ${index}`,
344
- namePrefix: false
345
- };
346
- schedulesInConfig.push(presetObj);
347
- newPresets.push(presetObj);
348
- scheduleIds.add(scheduleId);
349
- addedNewSchedule = true;
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
- // === Remove placeholder schedules (id === '0') if new ones were added ===
354
- if (addedNewSchedule) {
355
- const beforeCount = schedulesInConfig.length;
356
- deviceInConfig.schedules = schedulesInConfig.filter(s => String(s.id) !== '0');
357
- const removedCount = beforeCount - deviceInConfig.schedules.length;
358
-
359
- if (removedCount > 0 && removedCount < beforeCount) {
360
- updateInfo('info2', `Removed ${removedCount} placeholder schedule from device ${device.DeviceID}`, 'yellow');
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}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
422
- if (newPresetsCount || newScenesCount || newSchedulesCount)
423
- updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${account.type === 'melcloud' ? newDevices.ataPresets.length : newDevices.ataSchedules.length}, ATW: ${account.type === 'melcloud' ? newDevices.atwPresets.length : newDevices.atwSchedules.length}, ERV: ${account.type === 'melcloud' ? newDevices.ervPresets.length : newDevices.ervSchedules.length}, Scenes: ${newDevices.scenes.length}.`, 'green');
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}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
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', `Connect error ${JSON.stringify(error)}`, 'red');
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.3-beta.9",
4
+ "version": "4.3.3",
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.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 = this.accessory.scheduleEnabled;
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 = this.accessory.scheduleEnabled;
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 = this.accessory.scheduleEnabled;
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 = {
@@ -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
- const settings = Object.fromEntries(
205
- messageData.settings.map(({ name, value }) => {
206
- let parsedValue = this.functions.convertValue(value);
207
- return [name, parsedValue];
208
- })
209
- );
210
- Object.assign(deviceData.Device, settings);
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 (!this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
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 (!this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
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
- this.socket.send(JSON.stringify([{ "messageType": "unitStateChanged", "Data": { "id": "ef333525-2699-4290-af5a-2922566676da", "unitType": "ata", "settings": [{ "name": "Power", "value": deviceData.Device.Power }] } }]));
372
- return;
389
+ break;
373
390
  }
374
391
 
375
392
  //sens payload
@@ -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
@@ -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
 
@@ -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);