homebridge-melcloud-control 4.1.2-beta.3 → 4.1.2-beta.31

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,15 @@ 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.1.2] - (10.11.2025)
26
+
27
+ ## Changes
28
+
29
+ - added presets/schedules support for MELCLoud Home ATA devices
30
+ - config schema updated
31
+ - readme updated
32
+ - cleanup
33
+
25
34
  ## [4.1.1] - (10.11.2025)
26
35
 
27
36
  ## Changes
@@ -234,8 +234,9 @@
234
234
  "type": "object",
235
235
  "properties": {
236
236
  "id": {
237
- "title": "ID",
238
- "type": "string"
237
+ "title": "Id",
238
+ "type": "string",
239
+ "default": "0"
239
240
  },
240
241
  "type": {
241
242
  "title": "Type",
@@ -278,7 +279,7 @@
278
279
  "name": {
279
280
  "title": "Name",
280
281
  "type": "string",
281
- "placeholder": "Device name"
282
+ "default": "Air Conditioner"
282
283
  },
283
284
  "heatDryFanMode": {
284
285
  "title": "Heat",
@@ -422,7 +423,7 @@
422
423
  }
423
424
  },
424
425
  "scheduleSensor": {
425
- "title": "Shedule",
426
+ "title": "Schedule",
426
427
  "type": "boolean",
427
428
  "default": false,
428
429
  "description": "This enable extra schedule sensor to use with automations in HomeKit app.",
@@ -451,8 +452,9 @@
451
452
  "type": "object",
452
453
  "properties": {
453
454
  "id": {
454
- "title": "ID",
455
- "type": "string"
455
+ "title": "Id",
456
+ "type": "string",
457
+ "default": "0"
456
458
  },
457
459
  "displayType": {
458
460
  "title": "Display Type",
@@ -503,7 +505,7 @@
503
505
  "name": {
504
506
  "title": "Name",
505
507
  "type": "string",
506
- "placeholder": "Preset name",
508
+ "default": "Preset",
507
509
  "description": "Your own name displayed in Homebridge/HomeKit app.",
508
510
  "condition": {
509
511
  "functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].presets[arrayIndices[2]].displayType > 0;"
@@ -524,6 +526,87 @@
524
526
  ]
525
527
  }
526
528
  },
529
+ "schedules": {
530
+ "title": "Schedules",
531
+ "type": "array",
532
+ "items": {
533
+ "type": "object",
534
+ "properties": {
535
+ "id": {
536
+ "title": "Id",
537
+ "type": "string",
538
+ "default": "0"
539
+ },
540
+ "displayType": {
541
+ "title": "Display Type",
542
+ "type": "integer",
543
+ "minimum": 0,
544
+ "maximum": 5,
545
+ "default": 0,
546
+ "description": "Select the characteristic type to be displayed in HomeKit app.",
547
+ "anyOf": [
548
+ {
549
+ "title": "None/Disabled",
550
+ "enum": [
551
+ 0
552
+ ]
553
+ },
554
+ {
555
+ "title": "Outlet",
556
+ "enum": [
557
+ 1
558
+ ]
559
+ },
560
+ {
561
+ "title": "Switch",
562
+ "enum": [
563
+ 2
564
+ ]
565
+ },
566
+ {
567
+ "title": "Motion Sensor",
568
+ "enum": [
569
+ 3
570
+ ]
571
+ },
572
+ {
573
+ "title": "Occupancy Sensor",
574
+ "enum": [
575
+ 4
576
+ ]
577
+ },
578
+ {
579
+ "title": "Contact Sensor",
580
+ "enum": [
581
+ 5
582
+ ]
583
+ }
584
+ ]
585
+ },
586
+ "name": {
587
+ "title": "Name",
588
+ "type": "string",
589
+ "default": "Schedule",
590
+ "description": "Your own name displayed in Homebridge/HomeKit app.",
591
+ "condition": {
592
+ "functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].schedules[arrayIndices[2]].displayType > 0;"
593
+ }
594
+ },
595
+ "namePrefix": {
596
+ "title": "Prefix",
597
+ "type": "boolean",
598
+ "default": false,
599
+ "description": "Enable/disable the accessory name as a prefix for button/sensor name.",
600
+ "condition": {
601
+ "functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].schedules[arrayIndices[2]].displayType > 0;"
602
+ }
603
+ }
604
+ },
605
+ "required": [
606
+ "displayType"
607
+ ]
608
+ }
609
+ },
527
610
  "buttonsSensors": {
528
611
  "title": "Button / Sensor",
529
612
  "type": "array",
@@ -838,8 +921,9 @@
838
921
  "type": "object",
839
922
  "properties": {
840
923
  "id": {
841
- "title": "ID",
842
- "type": "string"
924
+ "title": "Id",
925
+ "type": "string",
926
+ "default": "0"
843
927
  },
844
928
  "type": {
845
929
  "title": "Type",
@@ -854,7 +938,7 @@
854
938
  "name": {
855
939
  "title": "Name",
856
940
  "type": "string",
857
- "placeholder": "Device name"
941
+ "default": "Heat Pump"
858
942
  },
859
943
  "displayType": {
860
944
  "title": "Display Type",
@@ -1065,8 +1149,9 @@
1065
1149
  "type": "object",
1066
1150
  "properties": {
1067
1151
  "id": {
1068
- "title": "ID",
1069
- "type": "string"
1152
+ "title": "Id",
1153
+ "type": "string",
1154
+ "default": "0"
1070
1155
  },
1071
1156
  "displayType": {
1072
1157
  "title": "Display Type",
@@ -1117,7 +1202,7 @@
1117
1202
  "name": {
1118
1203
  "title": "Name",
1119
1204
  "type": "string",
1120
- "placeholder": "Preset name",
1205
+ "default": "Preset",
1121
1206
  "description": "Your own name displayed in Homebridge/HomeKit app.",
1122
1207
  "condition": {
1123
1208
  "functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].presets[arrayIndices[2]].displayType > 0;"
@@ -1138,6 +1223,87 @@
1138
1223
  ]
1139
1224
  }
1140
1225
  },
1226
+ "schedules": {
1227
+ "title": "Schedules",
1228
+ "type": "array",
1229
+ "items": {
1230
+ "type": "object",
1231
+ "properties": {
1232
+ "id": {
1233
+ "title": "Id",
1234
+ "type": "string",
1235
+ "default": "0"
1236
+ },
1237
+ "displayType": {
1238
+ "title": "Display Type",
1239
+ "type": "integer",
1240
+ "minimum": 0,
1241
+ "maximum": 5,
1242
+ "default": 0,
1243
+ "description": "Select the characteristic type to be displayed in HomeKit app.",
1244
+ "anyOf": [
1245
+ {
1246
+ "title": "None/Disabled",
1247
+ "enum": [
1248
+ 0
1249
+ ]
1250
+ },
1251
+ {
1252
+ "title": "Outlet",
1253
+ "enum": [
1254
+ 1
1255
+ ]
1256
+ },
1257
+ {
1258
+ "title": "Switch",
1259
+ "enum": [
1260
+ 2
1261
+ ]
1262
+ },
1263
+ {
1264
+ "title": "Motion Sensor",
1265
+ "enum": [
1266
+ 3
1267
+ ]
1268
+ },
1269
+ {
1270
+ "title": "Occupancy Sensor",
1271
+ "enum": [
1272
+ 4
1273
+ ]
1274
+ },
1275
+ {
1276
+ "title": "Contact Sensor",
1277
+ "enum": [
1278
+ 5
1279
+ ]
1280
+ }
1281
+ ]
1282
+ },
1283
+ "name": {
1284
+ "title": "Name",
1285
+ "type": "string",
1286
+ "default": "Schedule",
1287
+ "description": "Your own name displayed in Homebridge/HomeKit app.",
1288
+ "condition": {
1289
+ "functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].schedules[arrayIndices[2]].displayType > 0;"
1290
+ }
1291
+ },
1292
+ "namePrefix": {
1293
+ "title": "Prefix",
1294
+ "type": "boolean",
1295
+ "default": false,
1296
+ "description": "Enable/disable the accessory name as a prefix for button/sensor name.",
1297
+ "condition": {
1298
+ "functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].schedules[arrayIndices[2]].displayType > 0;"
1299
+ }
1300
+ }
1301
+ },
1302
+ "required": [
1303
+ "displayType"
1304
+ ]
1305
+ }
1306
+ },
1141
1307
  "buttonsSensors": {
1142
1308
  "title": "Button",
1143
1309
  "type": "array",
@@ -1372,8 +1538,9 @@
1372
1538
  "type": "object",
1373
1539
  "properties": {
1374
1540
  "id": {
1375
- "title": "ID",
1376
- "type": "string"
1541
+ "title": "Id",
1542
+ "type": "string",
1543
+ "default": "0"
1377
1544
  },
1378
1545
  "type": {
1379
1546
  "title": "Type",
@@ -1388,7 +1555,7 @@
1388
1555
  "name": {
1389
1556
  "title": "Name",
1390
1557
  "type": "string",
1391
- "placeholder": "Device name"
1558
+ "default": "Ventilation"
1392
1559
  },
1393
1560
  "displayType": {
1394
1561
  "title": "Display Type",
@@ -1457,8 +1624,9 @@
1457
1624
  "type": "object",
1458
1625
  "properties": {
1459
1626
  "id": {
1460
- "title": "ID",
1627
+ "title": "Id",
1461
1628
  "type": "string",
1629
+ "default": "0",
1462
1630
  "readonly": true
1463
1631
  },
1464
1632
  "displayType": {
@@ -1513,7 +1681,7 @@
1513
1681
  "name": {
1514
1682
  "title": "Name",
1515
1683
  "type": "string",
1516
- "placeholder": "Preset name",
1684
+ "default": "Preset",
1517
1685
  "description": "Your own name displayed in Homebridge/HomeKit app.",
1518
1686
  "condition": {
1519
1687
  "functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].presets[arrayIndices[2]].displayType > 0;"
@@ -1534,6 +1702,87 @@
1534
1702
  ]
1535
1703
  }
1536
1704
  },
1705
+ "schedules": {
1706
+ "title": "Schedules",
1707
+ "type": "array",
1708
+ "items": {
1709
+ "type": "object",
1710
+ "properties": {
1711
+ "id": {
1712
+ "title": "Id",
1713
+ "type": "string",
1714
+ "default": "0"
1715
+ },
1716
+ "displayType": {
1717
+ "title": "Display Type",
1718
+ "type": "integer",
1719
+ "minimum": 0,
1720
+ "maximum": 5,
1721
+ "default": 0,
1722
+ "description": "Select the characteristic type to be displayed in HomeKit app.",
1723
+ "anyOf": [
1724
+ {
1725
+ "title": "None/Disabled",
1726
+ "enum": [
1727
+ 0
1728
+ ]
1729
+ },
1730
+ {
1731
+ "title": "Outlet",
1732
+ "enum": [
1733
+ 1
1734
+ ]
1735
+ },
1736
+ {
1737
+ "title": "Switch",
1738
+ "enum": [
1739
+ 2
1740
+ ]
1741
+ },
1742
+ {
1743
+ "title": "Motion Sensor",
1744
+ "enum": [
1745
+ 3
1746
+ ]
1747
+ },
1748
+ {
1749
+ "title": "Occupancy Sensor",
1750
+ "enum": [
1751
+ 4
1752
+ ]
1753
+ },
1754
+ {
1755
+ "title": "Contact Sensor",
1756
+ "enum": [
1757
+ 5
1758
+ ]
1759
+ }
1760
+ ]
1761
+ },
1762
+ "name": {
1763
+ "title": "Name",
1764
+ "type": "string",
1765
+ "default": "Preset",
1766
+ "description": "Your own name displayed in Homebridge/HomeKit app.",
1767
+ "condition": {
1768
+ "functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].schedules[arrayIndices[2]].displayType > 0;"
1769
+ }
1770
+ },
1771
+ "namePrefix": {
1772
+ "title": "Prefix",
1773
+ "type": "boolean",
1774
+ "default": false,
1775
+ "description": "Enable/disable the accessory name as a prefix for button/sensor name.",
1776
+ "condition": {
1777
+ "functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].schedules[arrayIndices[2]].displayType > 0;"
1778
+ }
1779
+ }
1780
+ },
1781
+ "required": [
1782
+ "displayType"
1783
+ ]
1784
+ }
1785
+ },
1537
1786
  "buttonsSensors": {
1538
1787
  "title": "Buttons",
1539
1788
  "type": "array",
@@ -1799,10 +2048,10 @@
1799
2048
  }
1800
2049
  },
1801
2050
  "clientId": {
1802
- "title": "Client ID",
2051
+ "title": "Client Id",
1803
2052
  "type": "string",
1804
2053
  "placeholder": "client id",
1805
- "description": "Here optional set the Client ID of MQTT Broker.",
2054
+ "description": "Here optional set the Client Id of MQTT Broker.",
1806
2055
  "condition": {
1807
2056
  "functionBody": "return model.accounts[arrayIndices].mqtt.enable === true"
1808
2057
  }
@@ -1943,7 +2192,33 @@
1943
2192
  }
1944
2193
  ],
1945
2194
  "condition": {
1946
- "functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
2195
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloud' && model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
2196
+ }
2197
+ },
2198
+ {
2199
+ "title": "Schedules",
2200
+ "type": "section",
2201
+ "description": "Section for setup device schedules",
2202
+ "expandable": true,
2203
+ "expanded": false,
2204
+ "items": [
2205
+ {
2206
+ "key": "accounts[].ataDevices[].schedules",
2207
+ "type": "tabarray",
2208
+ "title": "{{ value.name }}",
2209
+ "items": [
2210
+ {
2211
+ "key": "accounts[].ataDevices[].schedules[].id",
2212
+ "readonly": true
2213
+ },
2214
+ "accounts[].ataDevices[].schedules[].displayType",
2215
+ "accounts[].ataDevices[].schedules[].name",
2216
+ "accounts[].ataDevices[].schedules[].namePrefix"
2217
+ ]
2218
+ }
2219
+ ],
2220
+ "condition": {
2221
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloudhome' && model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
1947
2222
  }
1948
2223
  },
1949
2224
  {
@@ -2004,7 +2279,7 @@
2004
2279
  }
2005
2280
  ],
2006
2281
  "condition": {
2007
- "functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].id !== null;"
2282
+ "functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].id !== '0';"
2008
2283
  }
2009
2284
  }
2010
2285
  ]
@@ -2069,7 +2344,33 @@
2069
2344
  }
2070
2345
  ],
2071
2346
  "condition": {
2072
- "functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
2347
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloud' && model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
2348
+ }
2349
+ },
2350
+ {
2351
+ "title": "Schedules",
2352
+ "type": "section",
2353
+ "description": "Section for setup device schedules",
2354
+ "expandable": true,
2355
+ "expanded": false,
2356
+ "items": [
2357
+ {
2358
+ "key": "accounts[].atwDevices[].schedules",
2359
+ "type": "tabarray",
2360
+ "title": "{{ value.name }}",
2361
+ "items": [
2362
+ {
2363
+ "key": "accounts[].atwDevices[].schedules[].id",
2364
+ "readonly": true
2365
+ },
2366
+ "accounts[].atwDevices[].schedules[].displayType",
2367
+ "accounts[].atwDevices[].schedules[].name",
2368
+ "accounts[].atwDevices[].schedules[].namePrefix"
2369
+ ]
2370
+ }
2371
+ ],
2372
+ "condition": {
2373
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloudhome' && model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
2073
2374
  }
2074
2375
  },
2075
2376
  {
@@ -2124,7 +2425,7 @@
2124
2425
  }
2125
2426
  ],
2126
2427
  "condition": {
2127
- "functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].id !== null;"
2428
+ "functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].id !== '0';"
2128
2429
  }
2129
2430
  }
2130
2431
  ]
@@ -2188,7 +2489,33 @@
2188
2489
  }
2189
2490
  ],
2190
2491
  "condition": {
2191
- "functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
2492
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloud' && model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
2493
+ }
2494
+ },
2495
+ {
2496
+ "title": "Schedules",
2497
+ "type": "section",
2498
+ "description": "Section for setup device schedules",
2499
+ "expandable": true,
2500
+ "expanded": false,
2501
+ "items": [
2502
+ {
2503
+ "key": "accounts[].ervDevices[].schedules",
2504
+ "type": "tabarray",
2505
+ "title": "{{ value.name }}",
2506
+ "items": [
2507
+ {
2508
+ "key": "accounts[].ervDevices[].schedules[].id",
2509
+ "readonly": true
2510
+ },
2511
+ "accounts[].ervDevices[].schedules[].displayType",
2512
+ "accounts[].ervDevices[].schedules[].name",
2513
+ "accounts[].ervDevices[].schedules[].namePrefix"
2514
+ ]
2515
+ }
2516
+ ],
2517
+ "condition": {
2518
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloudhome' && model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
2192
2519
  }
2193
2520
  },
2194
2521
  {
@@ -2237,7 +2564,7 @@
2237
2564
  }
2238
2565
  ],
2239
2566
  "condition": {
2240
- "functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].id !== null;"
2567
+ "functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].id !== '0';"
2241
2568
  }
2242
2569
  }
2243
2570
  ]
@@ -224,8 +224,6 @@
224
224
 
225
225
  document.getElementById('logIn').addEventListener('click', async () => {
226
226
  document.getElementById(`logIn`).className = "btn btn-primary";
227
-
228
- updateInfo('info', 'Connecting...', 'yellow');
229
227
  homebridge.showSpinner();
230
228
 
231
229
  try {
@@ -263,32 +261,51 @@
263
261
  typeString,
264
262
  displayType: 0,
265
263
  name: device.DeviceName,
266
- presets: device.Presets ?? [],
264
+ presets: [],
265
+ schedules: [],
267
266
  buttonsSensors: []
268
267
  };
269
268
 
270
- if (!devicesInConfig.some(device => String(device.id) === deviceObj.id)) {
269
+ if (!devicesInConfig.some(dev => String(dev.id) === deviceObj.id)) {
271
270
  devicesInConfig.push(deviceObj);
272
271
  newArr.push(deviceObj);
273
272
  }
274
273
 
275
- const presetsKey = account.type === 'melcloud' ? 'Presets' : 'Schedule';
276
- const presetsIdKey = account.type === 'melcloud' ? 'ID' : 'Id';
277
- const presets = device[presetsKey] || [];
278
- presets.forEach((preset, index) => {
279
- const presetObj = {
280
- id: preset[presetsIdKey],
281
- displayType: 0,
282
- name: preset.NumberDescription || `Preset ${index}`,
283
- namePrefix: false
284
- };
285
-
286
- const deviceInConfig = devicesInConfig.find(device => String(device.id) === deviceObj.id);
287
- if (deviceInConfig && !deviceInConfig.presets.some(preset => String(preset.id) === presetObj.id)) {
288
- deviceInConfig.presets.push(presetObj);
289
- newPresets.push(presetObj);
290
- }
291
- });
274
+ if (account.type === 'melcloud') {
275
+ const presets = device.Presets || [];
276
+ presets.forEach((preset, index) => {
277
+ const presetObj = {
278
+ id: preset.ID,
279
+ displayType: 0,
280
+ name: preset.NumberDescription || `Preset ${index}`,
281
+ namePrefix: false
282
+ };
283
+
284
+ const deviceInConfig = devicesInConfig.find(dev => String(dev.id) === deviceObj.id);
285
+ if (deviceInConfig && !deviceInConfig.presets.some(pres => String(pres.id) === presetObj.id)) {
286
+ deviceInConfig.presets.push(presetObj);
287
+ newPresets.push(presetObj);
288
+ }
289
+ });
290
+ }
291
+
292
+ if (account.type === 'melcloudhome') {
293
+ const schedule = device.Schedule || [];
294
+ schedule.forEach((schedule, index) => {
295
+ const presetObj = {
296
+ id: schedule.Id,
297
+ displayType: 0,
298
+ name: `Schedule ${index}`,
299
+ namePrefix: false
300
+ };
301
+
302
+ const deviceInConfig = devicesInConfig.find(dev => String(dev.id) === deviceObj.id);
303
+ if (deviceInConfig && !deviceInConfig.schedules.some(sched => String(sched.id) === presetObj.id)) {
304
+ deviceInConfig.schedules.push(presetObj);
305
+ newPresets.push(presetObj);
306
+ }
307
+ });
308
+ }
292
309
  });
293
310
  };
294
311
 
@@ -306,7 +323,7 @@
306
323
  if (newDevicesCount)
307
324
  updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
308
325
  if (newPresetsCount)
309
- updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
326
+ updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
310
327
  if (removedDevicesCount)
311
328
  updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
312
329
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.1.2-beta.3",
4
+ "version": "4.1.2-beta.31",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/constants.js CHANGED
@@ -21,8 +21,12 @@ export const ApiUrlsHome = {
21
21
  BaseURL: "https://melcloudhome.com",
22
22
  GetUserContext: "/api/user/context",
23
23
  SetAta: "/api/ataunit/deviceid",
24
+ GetAtaSchedule: "/ata/deviceid/schedule",
24
25
  SetAtw: "/api/atwunit/deviceid",
26
+ GetAtwSchedule: "/atw/deviceid/schedule",
25
27
  SetErv: "/api/ervunit/deviceid",
28
+ GetErvSchedule: "/erv/deviceid/schedule",
29
+ SetSchedule: "/api/cloudschedule/deviceid/enabled",
26
30
  };
27
31
 
28
32
  export const DeviceType = [
package/src/deviceata.js CHANGED
@@ -37,7 +37,8 @@ class DeviceAta extends EventEmitter {
37
37
  this.heatDryFanMode = device.heatDryFanMode || 1; //NONE, HEAT, DRY, FAN
38
38
  this.coolDryFanMode = device.coolDryFanMode || 1; //NONE, COOL, DRY, FAN
39
39
  this.autoDryFanMode = device.autoDryFanMode || 1; //NONE, AUTO, DRY, FAN
40
- this.presets = (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0);
40
+ this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
41
+ this.schedules = this.accountType === 'melcloudhome' ? (device.presets || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
41
42
  this.buttons = (device.buttonsSensors || []).filter(sensor => (sensor.displayType ?? 0) > 0);
42
43
  this.deviceId = device.id;
43
44
  this.deviceName = device.name;
@@ -61,6 +62,14 @@ class DeviceAta extends EventEmitter {
61
62
  preset.previousSettings = {};
62
63
  }
63
64
 
65
+ //schedules configured
66
+ for (const schedule of this.schedules) {
67
+ schedule.name = schedule.name || 'Schedule'
68
+ schedule.serviceType = [null, Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][schedule.displayType];
69
+ schedule.characteristicType = [null, Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][schedule.displayType];
70
+ schedule.state = false;
71
+ }
72
+
64
73
  //buttons configured
65
74
  for (const button of this.buttons) {
66
75
  button.name = button.name || 'Button'
@@ -244,6 +253,7 @@ class DeviceAta extends EventEmitter {
244
253
  const deviceName = this.deviceName;
245
254
  const accountName = this.accountName;
246
255
  const presetsOnServer = this.accessory.presets;
256
+ const schedulesOnServer = this.accessory.schedules;
247
257
  const supportsHeat = this.accessory.supportsHeat;
248
258
  const supportsDry = this.accessory.supportsDry;
249
259
  const supportsCool = this.accessory.supportsCool;
@@ -705,8 +715,6 @@ class DeviceAta extends EventEmitter {
705
715
  })
706
716
  .onSet(async (state) => {
707
717
  try {
708
- const setTempKey = this.accountType === 'melcloud' ? 'SetTemperature' : 'SetPoint';
709
- const fanKey = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
710
718
  switch (state) {
711
719
  case true:
712
720
  preset.previousSettings = deviceData.Device;
@@ -715,7 +723,7 @@ class DeviceAta extends EventEmitter {
715
723
  deviceData.Device.SetTemperature = presetData.SetTemperature;
716
724
  deviceData.Device.VaneHorizontalDirection = presetData.VaneHorizontalDirection;
717
725
  deviceData.Device.VaneVerticalDirection = presetData.VaneVerticalDirection;
718
- deviceData.Device[fanKey] = presetData[fanKey];
726
+ deviceData.Device.SetFanSpeed = presetData.SetFanSpeed;
719
727
  break;
720
728
  case false:
721
729
  deviceData.Device.Power = preset.previousSettings.Power;
@@ -723,7 +731,7 @@ class DeviceAta extends EventEmitter {
723
731
  deviceData.Device.SetTemperature = preset.previousSettings.SetTemperature;
724
732
  deviceData.Device.VaneHorizontalDirection = preset.previousSettings.VaneHorizontalDirection;
725
733
  deviceData.Device.VaneVerticalDirection = preset.previousSettings.VaneVerticalDirection;
726
- deviceData.Device[fanKey] = preset.previousSettings[fanKey];
734
+ deviceData.Device.SetFanSpeed = preset.previousSettings.SetFanSpeed;
727
735
  break;
728
736
  };
729
737
 
@@ -738,6 +746,44 @@ class DeviceAta extends EventEmitter {
738
746
  });
739
747
  };
740
748
 
749
+ //schedules services
750
+ if (this.schedules.length > 0) {
751
+ if (this.logDebug) this.emit('debug', `Prepare schedules services`);
752
+ this.schedulesServices = [];
753
+ this.schedules.forEach((schedule, i) => {
754
+ const scheduleData = schedulesOnServer.find(p => p.Id === schedule.id);
755
+
756
+ //get preset name
757
+ const name = schedule.name;
758
+
759
+ //get preset name prefix
760
+ const namePrefix = schedule.namePrefix;
761
+
762
+ const serviceName = namePrefix ? `${accessoryName} ${name}` : name;
763
+ const serviceType = schedule.serviceType;
764
+ const characteristicType = schedule.characteristicType;
765
+ const scheduleService = new serviceType(serviceName, `Schedule ${deviceId} ${i}`);
766
+ scheduleService.addOptionalCharacteristic(Characteristic.ConfiguredName);
767
+ scheduleService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
768
+ scheduleService.getCharacteristic(characteristicType)
769
+ .onGet(async () => {
770
+ const state = schedule.state;
771
+ return state;
772
+ })
773
+ .onSet(async (state) => {
774
+ try {
775
+ deviceData.Schedule = state;
776
+ await this.melCloudAta.send(this.accountType, this.displayType, deviceData);
777
+ if (this.logInfo) this.emit('info', `${state ? 'Set:' : 'Unset:'} ${name}`);
778
+ } catch (error) {
779
+ if (this.logWarn) this.emit('warn', `Set schedule error: ${error}`);
780
+ };
781
+ });
782
+ this.schedulesServices.push(scheduleService);
783
+ accessory.addService(scheduleService);
784
+ });
785
+ };
786
+
741
787
  //buttons services
742
788
  if (this.buttons.length > 0) {
743
789
  if (this.logDebug) this.emit('debug', `Prepare buttons/sensors services`);
@@ -765,7 +811,7 @@ class DeviceAta extends EventEmitter {
765
811
  })
766
812
  .onSet(async (state) => {
767
813
  try {
768
- const key = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
814
+ const fanKey = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
769
815
  let effectiveFlags = null;
770
816
  switch (mode) {
771
817
  case 0: //POWER ON,OFF
@@ -1016,8 +1062,7 @@ class DeviceAta extends EventEmitter {
1016
1062
  this.deviceData = deviceData;
1017
1063
 
1018
1064
  //keys
1019
- const presetsKey = this.accountType === 'melcloud' ? 'Presets' : 'Schedule';
1020
- const presetsIdKey = account.type === 'melcloud' ? 'ID' : 'Id';
1065
+ const presetsIdKey = this.accountType === 'melcloud' ? 'ID' : 'Id';
1021
1066
  const setTempKey = this.accountType === 'melcloud' ? 'SetTemperature' : 'SetPoint';
1022
1067
  const fanKey = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
1023
1068
  const tempStepKey = this.accountType === 'melcloud' ? 'TemperatureIncrement' : 'HasHalfDegreeIncrements';
@@ -1031,16 +1076,18 @@ class DeviceAta extends EventEmitter {
1031
1076
  const supportCoolKey = this.accountType === 'melcloud' ? 'ModelSupportsCool' : 'HasCoolOperationMode';
1032
1077
 
1033
1078
  //presets schedule
1034
- const presetsOnServer = deviceData[presetsKey] ?? [];
1079
+ const scheduleEnabled = deviceData.ScheduleEnabled;
1080
+ const schedulesOnServer = deviceData.Schedule ?? [];
1081
+ const presetsOnServer = deviceData.Presets ?? [];
1082
+
1035
1083
 
1036
1084
  //protection
1037
- const frostProtectionEnabled = deviceData.FrostProtection?.enabled;
1038
- const frostProtectionActive = deviceData.FrostProtection?.active;
1039
- const overheatProtectionEnabled = deviceData.OverheatProtection?.enabled;
1040
- const overheatProtectionActive = deviceData.OverheatProtection?.active;
1041
- const holidayModeEnabled = deviceData.HolidayMode?.enabled;
1042
- const holidayModeActive = deviceData.HolidayMode?.active;
1043
- const scheduleEnabled = deviceData.ScheduleEnabled;
1085
+ const frostProtectionEnabled = deviceData.FrostProtection?.Enabled;
1086
+ const frostProtectionActive = deviceData.FrostProtection?.Active;
1087
+ const overheatProtectionEnabled = deviceData.OverheatProtection?.Enabled;
1088
+ const overheatProtectionActive = deviceData.OverheatProtection?.Active;
1089
+ const holidayModeEnabled = deviceData.HolidayMode?.Enabled;
1090
+ const holidayModeActive = deviceData.HolidayMode?.Active;
1044
1091
 
1045
1092
  //device control
1046
1093
  const hideVaneControls = deviceData.HideVaneControls ?? false;
@@ -1092,6 +1139,7 @@ class DeviceAta extends EventEmitter {
1092
1139
  //accessory
1093
1140
  const obj = {
1094
1141
  presets: presetsOnServer,
1142
+ schedules: schedulesOnServer,
1095
1143
  supportsAutomaticFanSpeed: supportsAutomaticFanSpeed,
1096
1144
  supportsAirDirectionFunction: supportsAirDirectionFunction,
1097
1145
  supportsSwingFunction: supportsSwingFunction,
@@ -1287,17 +1335,28 @@ class DeviceAta extends EventEmitter {
1287
1335
  const presetData = presetsOnServer.find(p => p[presetsIdKey] === preset.id);
1288
1336
 
1289
1337
  preset.state = presetData ? (presetData.Power === power
1290
- && presetData[setTempKey] === setTemperature
1338
+ && presetData.SetTemperature === setTemperature
1291
1339
  && presetData.OperationMode === operationMode
1292
1340
  && presetData.VaneHorizontalDirection === vaneHorizontalDirection
1293
1341
  && presetData.VaneVerticalDirection === vaneVerticalDirection
1294
- && presetData[fanKey] === setFanSpeed) : false;
1342
+ && presetData.FanSpeed === setFanSpeed) : false;
1295
1343
 
1296
1344
  const characteristicType = preset.characteristicType;
1297
1345
  this.presetsServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1298
1346
  });
1299
1347
  };
1300
1348
 
1349
+ //update schedules state
1350
+ if (this.schedules.length > 0) {
1351
+ this.schedules.forEach((schedule, i) => {
1352
+ const scheduleData = schedulesOnServer.find(p => p[presetsIdKey] === schedule.id);
1353
+ schedule.state = scheduleData ? scheduleData.Enabled : false;
1354
+
1355
+ const characteristicType = schedule.characteristicType;
1356
+ this.schedulesServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1357
+ });
1358
+ };
1359
+
1301
1360
  //update buttons state
1302
1361
  if (this.buttons.length > 0) {
1303
1362
  this.buttons.forEach((button, i) => {
package/src/deviceatw.js CHANGED
@@ -38,7 +38,8 @@ class DeviceAtw extends EventEmitter {
38
38
  this.temperatureFlowZone2Sensor = device.temperatureFlowZone2Sensor || false;
39
39
  this.temperatureReturnZone2Sensor = device.temperatureReturnZone2Sensor || false;
40
40
  this.errorSensor = device.errorSensor || false;
41
- this.presets = (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0);
41
+ this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
42
+ this.schedules = this.accountType === 'melcloudhome' ? (device.presets || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
42
43
  this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
43
44
  this.deviceId = device.id;
44
45
  this.deviceName = device.name;
@@ -62,6 +63,14 @@ class DeviceAtw extends EventEmitter {
62
63
  preset.previousSettings = {};
63
64
  }
64
65
 
66
+ //schedules configured
67
+ for (const schedule of this.schedules) {
68
+ schedule.name = schedule.name || 'Schedule'
69
+ schedule.serviceType = [null, Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][schedule.displayType];
70
+ schedule.characteristicType = [null, Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][schedule.displayType];
71
+ schedule.state = false;
72
+ }
73
+
65
74
  //buttons configured
66
75
  for (const button of this.buttons) {
67
76
  button.name = button.name || 'Button'
@@ -261,6 +270,7 @@ class DeviceAtw extends EventEmitter {
261
270
  const deviceName = this.deviceName;
262
271
  const accountName = this.accountName;
263
272
  const presetsOnServer = this.accessory.presets;
273
+ const schedulesOnServer = this.accessory.schedules;
264
274
  const zonesCount = this.accessory.zonesCount;
265
275
  const caseHeatPump = this.accessory.caseHeatPump;
266
276
  const caseZone1 = this.accessory.caseZone1;
@@ -1055,8 +1065,9 @@ class DeviceAtw extends EventEmitter {
1055
1065
  if (this.presets.length > 0) {
1056
1066
  if (this.logDebug) this.emit('debug', `Prepare presets services`);
1057
1067
  this.presetsServices = [];
1068
+ const presetsIdKey = this.accountType === 'melcloud' ? 'ID' : 'Id';
1058
1069
  this.presets.forEach((preset, i) => {
1059
- const presetData = presetsOnServer.find(p => p.ID === preset.id);
1070
+ const presetData = presetsOnServer.find(p => p[presetsIdKey] === preset.id);
1060
1071
 
1061
1072
  //get preset name
1062
1073
  const name = preset.name;
@@ -1120,6 +1131,44 @@ class DeviceAtw extends EventEmitter {
1120
1131
  });
1121
1132
  };
1122
1133
 
1134
+ //schedules services
1135
+ if (this.schedules.length > 0) {
1136
+ if (this.logDebug) this.emit('debug', `Prepare schedules services`);
1137
+ this.schedulesServices = [];
1138
+ this.schedules.forEach((schedule, i) => {
1139
+ const scheduleData = schedulesOnServer.find(p => p.Id === schedule.id);
1140
+
1141
+ //get preset name
1142
+ const name = schedule.name;
1143
+
1144
+ //get preset name prefix
1145
+ const namePrefix = schedule.namePrefix;
1146
+
1147
+ const serviceName = namePrefix ? `${accessoryName} ${name}` : name;
1148
+ const serviceType = schedule.serviceType;
1149
+ const characteristicType = schedule.characteristicType;
1150
+ const scheduleService = new serviceType(serviceName, `Schedule ${deviceId} ${i}`);
1151
+ scheduleService.addOptionalCharacteristic(Characteristic.ConfiguredName);
1152
+ scheduleService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
1153
+ scheduleService.getCharacteristic(characteristicType)
1154
+ .onGet(async () => {
1155
+ const state = schedule.state;
1156
+ return state;
1157
+ })
1158
+ .onSet(async (state) => {
1159
+ try {
1160
+ deviceData.Schedule = state;
1161
+ await this.melCloudAta.send(this.accountType, this.displayType, deviceData);
1162
+ if (this.logInfo) this.emit('info', `${state ? 'Set:' : 'Unset:'} ${name}`);
1163
+ } catch (error) {
1164
+ if (this.logWarn) this.emit('warn', `Set schedule error: ${error}`);
1165
+ };
1166
+ });
1167
+ this.schedulesServices.push(scheduleService);
1168
+ accessory.addService(scheduleService);
1169
+ });
1170
+ };
1171
+
1123
1172
  //buttons services
1124
1173
  if (this.buttons.length > 0) {
1125
1174
  if (this.logDebug) this.emit('debug', `Prepare buttons services`);
@@ -1331,12 +1380,14 @@ class DeviceAtw extends EventEmitter {
1331
1380
 
1332
1381
  //keys
1333
1382
  const presetsKey = this.accountType === 'melcloud' ? 'Presets' : 'Schedule';
1334
- const presetsIdKey = account.type === 'melcloud' ? 'ID' : 'Id';
1383
+ const presetsIdKey = accountType === 'melcloud' ? 'ID' : 'Id';
1335
1384
  const tempStepKey = this.accountType === 'melcloud' ? 'TemperatureIncrement' : 'HasHalfDegreeIncrements';
1336
1385
  const errorKey = this.accountType === 'melcloud' ? 'HasError' : 'IsInError';
1337
1386
 
1338
- //presets
1339
- const presetsOnServer = deviceData[presetsKey] ?? [];
1387
+ //presets schedule
1388
+ const scheduleEnabled = deviceData.ScheduleEnabled;
1389
+ const schedulesOnServer = deviceData.Schedule ?? [];
1390
+ const presetsOnServer = deviceData.Presets ?? [];
1340
1391
 
1341
1392
  //device info
1342
1393
  const hasHeatPump = ![1, 2, 3, 4, 5, 6, 7, 15].includes(this.hideZone);
@@ -1415,6 +1466,7 @@ class DeviceAtw extends EventEmitter {
1415
1466
  //accessory
1416
1467
  const obj = {
1417
1468
  presets: presetsOnServer,
1469
+ schedules: schedulesOnServer,
1418
1470
  power: power ? 1 : 0,
1419
1471
  unitStatus: unitStatus,
1420
1472
  idleZone1: idleZone1,
@@ -1438,6 +1490,7 @@ class DeviceAtw extends EventEmitter {
1438
1490
  useFahrenheit: this.useFahrenheit,
1439
1491
  temperatureUnit: TemperatureDisplayUnits[this.useFahrenheit],
1440
1492
  isInError: isInError,
1493
+ scheduleEnabled: scheduleEnabled,
1441
1494
  zones: [],
1442
1495
  zonesSensors: []
1443
1496
  };
@@ -1838,6 +1891,17 @@ class DeviceAtw extends EventEmitter {
1838
1891
  });
1839
1892
  };
1840
1893
 
1894
+ //update schedules state
1895
+ if (this.schedules.length > 0) {
1896
+ this.schedules.forEach((schedule, i) => {
1897
+ const scheduleData = schedulesOnServer.find(p => p[presetsIdKey] === schedule.id);
1898
+ schedule.state = scheduleData ? scheduleData.Enabled : false;
1899
+
1900
+ const characteristicType = schedule.characteristicType;
1901
+ this.schedulesServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1902
+ });
1903
+ };
1904
+
1841
1905
  //update buttons state
1842
1906
  if (this.buttons.length > 0) {
1843
1907
  this.buttons.forEach((button, i) => {
package/src/deviceerv.js CHANGED
@@ -31,7 +31,8 @@ class DeviceErv extends EventEmitter {
31
31
  this.temperatureOutdoorSensor = device.temperatureOutdoorSensor || false;
32
32
  this.temperatureSupplySensor = device.temperatureSupplySensor || false;
33
33
  this.errorSensor = device.errorSensor || false;
34
- this.presets = (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0);
34
+ this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
35
+ this.schedules = this.accountType === 'melcloudhome' ? (device.presets || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
35
36
  this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
36
37
  this.deviceId = device.id;
37
38
  this.deviceName = device.name;
@@ -55,6 +56,14 @@ class DeviceErv extends EventEmitter {
55
56
  preset.previousSettings = {};
56
57
  }
57
58
 
59
+ //schedules configured
60
+ for (const schedule of this.schedules) {
61
+ schedule.name = schedule.name || 'Schedule'
62
+ schedule.serviceType = [null, Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][schedule.displayType];
63
+ schedule.characteristicType = [null, Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][schedule.displayType];
64
+ schedule.state = false;
65
+ }
66
+
58
67
  //buttons configured
59
68
  for (const button of this.buttons) {
60
69
  button.name = button.name || 'Button'
@@ -230,6 +239,7 @@ class DeviceErv extends EventEmitter {
230
239
  const deviceName = this.deviceName;
231
240
  const accountName = this.accountName;
232
241
  const presetsOnServer = this.accessory.presets;
242
+ const schedulesOnServer = this.accessory.schedules;
233
243
  const hasRoomTemperature = this.accessory.hasRoomTemperature;
234
244
  const hasSupplyTemperature = this.accessory.hasSupplyTemperature;
235
245
  const hasOutdoorTemperature = this.accessory.hasOutdoorTemperature;
@@ -703,14 +713,14 @@ class DeviceErv extends EventEmitter {
703
713
  deviceData.Device.Power = presetData.Power;
704
714
  deviceData.Device.OperationMode = presetData.OperationMode;
705
715
  deviceData.Device.VentilationMode = presetData.VentilationMode;
706
- deviceData.Device.SetFanSpeed = presetData.FanSpeed;
716
+ deviceData.Device.SetFanSpeed = presetData.SetFanSpeed;
707
717
  break;
708
718
  case false:
709
719
  deviceData.Device.SetTemperature = preset.previousSettings.SetTemperature;
710
720
  deviceData.Device.Power = preset.previousSettings.Power;
711
721
  deviceData.Device.OperationMode = preset.previousSettings.OperationMode;
712
722
  deviceData.Device.VentilationMode = preset.previousSettings.VentilationMode;
713
- deviceData.Device.SetFanSpeed = preset.previousSettings.FanSpeed;
723
+ deviceData.Device.SetFanSpeed = preset.previousSettings.SetFanSpeed;
714
724
  break;
715
725
  };
716
726
 
@@ -725,6 +735,44 @@ class DeviceErv extends EventEmitter {
725
735
  accessory.addService(presetService);
726
736
  };
727
737
 
738
+ //schedules services
739
+ if (this.schedules.length > 0) {
740
+ if (this.logDebug) this.emit('debug', `Prepare schedules services`);
741
+ this.schedulesServices = [];
742
+ this.schedules.forEach((schedule, i) => {
743
+ const scheduleData = schedulesOnServer.find(p => p.Id === schedule.id);
744
+
745
+ //get preset name
746
+ const name = schedule.name;
747
+
748
+ //get preset name prefix
749
+ const namePrefix = schedule.namePrefix;
750
+
751
+ const serviceName = namePrefix ? `${accessoryName} ${name}` : name;
752
+ const serviceType = schedule.serviceType;
753
+ const characteristicType = schedule.characteristicType;
754
+ const scheduleService = new serviceType(serviceName, `Schedule ${deviceId} ${i}`);
755
+ scheduleService.addOptionalCharacteristic(Characteristic.ConfiguredName);
756
+ scheduleService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
757
+ scheduleService.getCharacteristic(characteristicType)
758
+ .onGet(async () => {
759
+ const state = schedule.state;
760
+ return state;
761
+ })
762
+ .onSet(async (state) => {
763
+ try {
764
+ deviceData.Schedule = state;
765
+ await this.melCloudAta.send(this.accountType, this.displayType, deviceData);
766
+ if (this.logInfo) this.emit('info', `${state ? 'Set:' : 'Unset:'} ${name}`);
767
+ } catch (error) {
768
+ if (this.logWarn) this.emit('warn', `Set schedule error: ${error}`);
769
+ };
770
+ });
771
+ this.schedulesServices.push(scheduleService);
772
+ accessory.addService(scheduleService);
773
+ });
774
+ };
775
+
728
776
  //buttons services
729
777
  if (this.buttons.length > 0) {
730
778
  if (this.logDebug) this.emit('debug', `Prepare buttons services`);
@@ -877,13 +925,15 @@ class DeviceErv extends EventEmitter {
877
925
 
878
926
  //keys
879
927
  const presetsKey = this.accountType === 'melcloud' ? 'Presets' : 'Schedule';
880
- const presetsIdKey = account.type === 'melcloud' ? 'ID' : 'Id';
928
+ const presetsIdKey = accountType === 'melcloud' ? 'ID' : 'Id';
881
929
  const fanKey = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
882
930
  const tempStepKey = this.accountType === 'melcloud' ? 'TemperatureIncrement' : 'HasHalfDegreeIncrements';
883
931
  const errorKey = this.accountType === 'melcloud' ? 'HasError' : 'IsInError';
884
932
 
885
- //presets
886
- const presetsOnServer = deviceData[presetsKey] ?? [];
933
+ //presets schedule
934
+ const scheduleEnabled = deviceData.ScheduleEnabled;
935
+ const schedulesOnServer = deviceData.Schedule ?? [];
936
+ const presetsOnServer = deviceData.Presets ?? [];
887
937
 
888
938
  //device control
889
939
  const hideRoomTemperature = deviceData.HideRoomTemperature;
@@ -934,6 +984,7 @@ class DeviceErv extends EventEmitter {
934
984
  //accessory
935
985
  const obj = {
936
986
  presets: presetsOnServer,
987
+ schedules: schedulesOnServer,
937
988
  hasRoomTemperature: hasRoomTemperature,
938
989
  hasSupplyTemperature: hasSupplyTemperature,
939
990
  hasOutdoorTemperature: hasOutdoorTemperature,
@@ -972,7 +1023,8 @@ class DeviceErv extends EventEmitter {
972
1023
  maxTempCoolDry: maxTempCoolDry,
973
1024
  useFahrenheit: this.useFahrenheit,
974
1025
  temperatureUnit: TemperatureDisplayUnits[this.useFahrenheit],
975
- isInError: isInError
1026
+ isInError: isInError,
1027
+ scheduleEnabled: scheduleEnabled
976
1028
  };
977
1029
 
978
1030
  //ventilation mode - 0, HEAT, 2, COOL, 4, 5, 6, FAN, AUTO
@@ -1121,13 +1173,24 @@ class DeviceErv extends EventEmitter {
1121
1173
  && presetData.SetTemperature === setTemperature
1122
1174
  && presetData.OperationMode === operationMode
1123
1175
  && presetData.VentilationMode === ventilationMode
1124
- && presetData.FanSpeed === setFanSpeed) : false;
1176
+ && presetData.SetFanSpeed === setFanSpeed) : false;
1125
1177
 
1126
1178
  const characteristicType = preset.characteristicType;
1127
1179
  this.presetsServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1128
1180
  });
1129
1181
  };
1130
1182
 
1183
+ //update schedules state
1184
+ if (this.schedules.length > 0) {
1185
+ this.schedules.forEach((schedule, i) => {
1186
+ const scheduleData = schedulesOnServer.find(p => p[presetsIdKey] === schedule.id);
1187
+ schedule.state = scheduleData ? scheduleData.Enabled : false;
1188
+
1189
+ const characteristicType = schedule.characteristicType;
1190
+ this.schedulesServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1191
+ });
1192
+ };
1193
+
1131
1194
  //update buttons state
1132
1195
  if (this.buttons.length > 0) {
1133
1196
  this.buttons.forEach((button, i) => {
package/src/melcloud.js CHANGED
@@ -236,11 +236,18 @@ class MelCloud extends EventEmitter {
236
236
 
237
237
  const devices = buildingsList.flatMap(building => {
238
238
  // Funkcja kapitalizująca klucze obiektu
239
- // Rekurencyjna funkcja kapitalizująca klucze w całym obiekcie (łącznie z tablicami)
239
+ const capitalizeKeys = obj =>
240
+ Object.fromEntries(
241
+ Object.entries(obj).map(([key, value]) => [
242
+ key.charAt(0).toUpperCase() + key.slice(1),
243
+ value
244
+ ])
245
+ );
246
+
247
+ // Rekurencyjna kapitalizacja kluczy w obiekcie lub tablicy
240
248
  const capitalizeKeysDeep = obj => {
241
- if (Array.isArray(obj)) {
242
- return obj.map(item => capitalizeKeysDeep(item));
243
- } else if (obj && typeof obj === 'object' && obj.constructor === Object) {
249
+ if (Array.isArray(obj)) return obj.map(capitalizeKeysDeep);
250
+ if (obj && typeof obj === 'object') {
244
251
  return Object.fromEntries(
245
252
  Object.entries(obj).map(([key, value]) => [
246
253
  key.charAt(0).toUpperCase() + key.slice(1),
@@ -252,7 +259,8 @@ class MelCloud extends EventEmitter {
252
259
  };
253
260
 
254
261
  // Funkcja tworząca finalny obiekt Device
255
- const createDevice = (device, type, headers = {}) => {
262
+ const createDevice = (device, type) => {
263
+ // Settings już kapitalizowane w nazwach
256
264
  const settingsArray = device.Settings || [];
257
265
 
258
266
  const settingsObject = Object.fromEntries(
@@ -260,23 +268,34 @@ class MelCloud extends EventEmitter {
260
268
  let parsedValue = value;
261
269
  if (value === "True") parsedValue = true;
262
270
  else if (value === "False") parsedValue = false;
263
- else if (!isNaN(Number(value)) && value.trim() !== "") parsedValue = Number(value);
271
+ else if (!isNaN(value) && value !== "") parsedValue = Number(value);
264
272
 
265
273
  const key = name.charAt(0).toUpperCase() + name.slice(1);
266
274
  return [key, parsedValue];
267
275
  })
268
276
  );
269
277
 
278
+ // Scal Capabilities + Settings + DeviceType w Device
270
279
  const deviceObject = {
271
- ...capitalizeKeysDeep(device.Capabilities || {}),
280
+ ...capitalizeKeys(device.Capabilities || {}),
272
281
  ...settingsObject,
273
282
  DeviceType: type
274
283
  };
275
284
 
285
+ // Kapitalizacja brakujących obiektów/tablic
286
+ if (device.FrostProtection) device.FrostProtection = { ...capitalizeKeys(device.FrostProtection || {}) };
287
+ if (device.OverheatProtection) device.OverheatProtection = { ...capitalizeKeys(device.OverheatProtection || {}) };
288
+ if (device.HolidayMode) device.HolidayMode = { ...capitalizeKeys(device.HolidayMode || {}) };
289
+
290
+ if (Array.isArray(device.Schedule)) {
291
+ device.Schedule = device.Schedule.map(capitalizeKeysDeep);
292
+ }
293
+
294
+ // Usuń stare pola Settings i Capabilities
276
295
  const { Settings, Capabilities, Id, GivenDisplayName, ...rest } = device;
277
296
 
278
297
  return {
279
- ...capitalizeKeysDeep(rest),
298
+ ...rest,
280
299
  Type: type,
281
300
  DeviceID: Id,
282
301
  DeviceName: GivenDisplayName,
@@ -285,11 +304,10 @@ class MelCloud extends EventEmitter {
285
304
  };
286
305
  };
287
306
 
288
- // Mapowanie urządzeń
289
307
  return [
290
- ...(building.airToAirUnits || []).map(d => createDevice(capitalizeKeysDeep(d), 0)),
291
- ...(building.airToWaterUnits || []).map(d => createDevice(capitalizeKeysDeep(d), 1)),
292
- ...(building.airToVentilationUnits || []).map(d => createDevice(capitalizeKeysDeep(d), 3))
308
+ ...(building.airToAirUnits || []).map(d => createDevice(capitalizeKeys(d), 0)),
309
+ ...(building.airToWaterUnits || []).map(d => createDevice(capitalizeKeys(d), 1)),
310
+ ...(building.airToVentilationUnits || []).map(d => createDevice(capitalizeKeys(d), 3))
293
311
  ];
294
312
  });
295
313