homebridge-melcloud-control 4.1.2-beta.3 → 4.1.2-beta.30
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 +9 -0
- package/config.schema.json +353 -26
- package/homebridge-ui/public/index.html +39 -22
- package/package.json +1 -1
- package/src/constants.js +4 -0
- package/src/deviceata.js +77 -18
- package/src/deviceatw.js +69 -5
- package/src/deviceerv.js +71 -8
- package/src/melcloud.js +30 -12
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
|
package/config.schema.json
CHANGED
|
@@ -234,8 +234,9 @@
|
|
|
234
234
|
"type": "object",
|
|
235
235
|
"properties": {
|
|
236
236
|
"id": {
|
|
237
|
-
"title": "
|
|
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
|
-
"
|
|
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": "
|
|
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": "
|
|
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
|
-
"
|
|
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": "Preset",
|
|
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": "
|
|
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
|
-
"
|
|
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": "
|
|
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
|
-
"
|
|
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": "Preset",
|
|
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": "
|
|
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
|
-
"
|
|
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": "
|
|
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
|
-
"
|
|
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
|
|
2051
|
+
"title": "Client Id",
|
|
1803
2052
|
"type": "string",
|
|
1804
2053
|
"placeholder": "client id",
|
|
1805
|
-
"description": "Here optional set the Client
|
|
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 !==
|
|
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 !==
|
|
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 !==
|
|
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:
|
|
264
|
+
presets: [],
|
|
265
|
+
schedules: [],
|
|
267
266
|
buttonsSensors: []
|
|
268
267
|
};
|
|
269
268
|
|
|
270
|
-
if (!devicesInConfig.some(
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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((preset, index) => {
|
|
295
|
+
const presetObj = {
|
|
296
|
+
id: preset.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.presets.some(pres => String(pres.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.
|
|
4
|
+
"version": "4.1.2-beta.30",
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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?.
|
|
1038
|
-
const frostProtectionActive = deviceData.FrostProtection?.
|
|
1039
|
-
const overheatProtectionEnabled = deviceData.OverheatProtection?.
|
|
1040
|
-
const overheatProtectionActive = deviceData.OverheatProtection?.
|
|
1041
|
-
const holidayModeEnabled = deviceData.HolidayMode?.
|
|
1042
|
-
const holidayModeActive = deviceData.HolidayMode?.
|
|
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
|
|
1338
|
+
&& presetData.SetTemperature === setTemperature
|
|
1291
1339
|
&& presetData.OperationMode === operationMode
|
|
1292
1340
|
&& presetData.VaneHorizontalDirection === vaneHorizontalDirection
|
|
1293
1341
|
&& presetData.VaneVerticalDirection === vaneVerticalDirection
|
|
1294
|
-
&& presetData
|
|
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
|
|
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 =
|
|
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
|
|
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.
|
|
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.
|
|
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 =
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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(
|
|
291
|
-
...(building.airToWaterUnits || []).map(d => createDevice(
|
|
292
|
-
...(building.airToVentilationUnits || []).map(d => createDevice(
|
|
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
|
|