homebridge-melcloud-control 4.0.0-beta.22 → 4.0.0-beta.221
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/README.md +12 -11
- package/config.schema.json +62 -46
- package/homebridge-ui/public/index.html +72 -35
- package/homebridge-ui/server.js +4 -3
- package/index.js +40 -24
- package/package.json +2 -2
- package/src/constants.js +15 -22
- package/src/deviceata.js +199 -188
- package/src/deviceatw.js +7 -5
- package/src/deviceerv.js +28 -26
- package/src/functions.js +18 -3
- package/src/melcloud.js +209 -106
- package/src/melcloudata.js +139 -308
- package/src/melcloudatw.js +3 -228
- package/src/melclouderv.js +5 -162
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
16
|
- do not configure it manually, always using Config UI X
|
|
17
17
|
- required Homebridge v2.0.0 and above
|
|
18
18
|
|
|
19
|
+
## [4.0.0] - (xx.10.2025)
|
|
20
|
+
|
|
21
|
+
## Changes
|
|
22
|
+
|
|
23
|
+
- added support for MELCloud Home [#215](https://github.com/grzegorz914/homebridge-melcloud-control/issues/215)
|
|
24
|
+
- redme updated
|
|
25
|
+
- config schema updated
|
|
26
|
+
- cleanup
|
|
27
|
+
|
|
19
28
|
## [3.9.5] - (02.09.2025)
|
|
20
29
|
|
|
21
30
|
## Changes
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](https://github.com/grzegorz914/homebridge-melcloud-control/pulls)
|
|
14
14
|
[](https://github.com/grzegorz914/homebridge-melcloud-control/issues)
|
|
15
15
|
|
|
16
|
-
Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation Lossnay, manufactured by Mistsubishi and connected to MELCloud.
|
|
16
|
+
Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation Lossnay, manufactured by Mistsubishi and connected to MELCloud or MELCloud Home.
|
|
17
17
|
|
|
18
18
|
</span>
|
|
19
19
|
|
|
@@ -21,14 +21,14 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
|
|
|
21
21
|
|
|
22
22
|
| Package | Installation | Role | Required |
|
|
23
23
|
| --- | --- | --- | --- |
|
|
24
|
-
| [Homebridge
|
|
25
|
-
| [
|
|
24
|
+
| [Homebridge](https://github.com/homebridge/homebridge) | [Homebridge Wiki](https://github.com/homebridge/homebridge/wiki) | HomeKit Bridge | Required |
|
|
25
|
+
| [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x) | [Config UI X Wiki](https://github.com/homebridge/homebridge-config-ui-x/wiki) | Homebridge Web User Interface | Required |
|
|
26
26
|
| [MELCloud](https://github.com/grzegorz914/homebridge-melcloud-control) | [Plug-In Wiki](https://github.com/grzegorz914/homebridge-melcloud-control/wiki) | Homebridge Plug-In | Required |
|
|
27
27
|
|
|
28
28
|
### About The Plugin
|
|
29
29
|
|
|
30
30
|
* All devices are detected automatically.
|
|
31
|
-
* Control devices over MELCloud
|
|
31
|
+
* Control devices over MELCloud and MELCloud Home.
|
|
32
32
|
* Support multiple accounts, buildings, floors, areas.
|
|
33
33
|
* Support temperature display units `Celsius/Fahrenheit`.
|
|
34
34
|
* Support assing inividual operating mode for `Heat/Cool/Auto`.
|
|
@@ -187,7 +187,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
|
|
|
187
187
|
### Configuration
|
|
188
188
|
|
|
189
189
|
* Run this plugin as a [Child Bridge](https://github.com/homebridge/homebridge/wiki/Child-Bridges) (Highly Recommended), this prevent crash Homebridge if plugin crashes.
|
|
190
|
-
* Install and use [Homebridge
|
|
190
|
+
* Install and use [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x/wiki) to configure this plugin, required for version v3 and above.
|
|
191
191
|
* The `sample-config.json` can be edited and used as an alternative.
|
|
192
192
|
|
|
193
193
|
<p align="center">
|
|
@@ -204,15 +204,16 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
|
|
|
204
204
|
| Key | Description |
|
|
205
205
|
| --- | --- |
|
|
206
206
|
| `name` | Here set the own account name. |
|
|
207
|
-
| `user` | Here set the
|
|
208
|
-
| `passwd` | Here set the
|
|
209
|
-
| `language` | Here select the
|
|
207
|
+
| `user` | Here set the account username. |
|
|
208
|
+
| `passwd` | Here set the account password. |
|
|
209
|
+
| `language` | Here select the account language. |
|
|
210
|
+
| `type` | Here select the account type `None/Disabled`, `MELCloud`, `MELCloud Home`. |
|
|
210
211
|
| `ataDevices[]` | Array of ATA devices created automatically after login to MELCloud from plugin config UI. |
|
|
211
212
|
| `ataDevices[].id` | Read only data, do not change it. |
|
|
212
213
|
| `ataDevices[].type` | Read only data, do not change it. |
|
|
213
214
|
| `ataDevices[].typeString` | Read only data, do not change it. |
|
|
214
215
|
| `ataDevices[].name` | Here You can schange the `Accessory Name` which is exposed to the `Homebridge/HomeKit`. |
|
|
215
|
-
| `ataDevices[].
|
|
216
|
+
| `ataDevices[].displayType` | Here select device control mode `None/Disabled`, `Heater/Cooler`, `Thermostat`. |
|
|
216
217
|
| `ataDevices[].heatDryFanMode` | Here select the operatiing mode for `Heat`, if this mode is not supported, it will be disabled. |
|
|
217
218
|
| `ataDevices[].coolDryFanMode` | Here select the operatiing mode for `Cool`, if this mode is not supported, it will be disabled. |
|
|
218
219
|
| `ataDevices[].autoDryFanMode` | Here select the operatiing mode for `Auto`, if this mode is not supported, it will be disabled.. |
|
|
@@ -233,7 +234,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
|
|
|
233
234
|
| `atwDevices[].type` | Read only data, do not change it. |
|
|
234
235
|
| `atwDevices[].typeString` | Read only data, do not change it. |
|
|
235
236
|
| `atwDevices[].name` | Here You can schange the `Accessory Name` which is exposed to the `Homebridge/HomeKit`. |
|
|
236
|
-
| `atwDevices[].
|
|
237
|
+
| `atwDevices[].displayType` | Here select main control mode `None/Disabled`, `Heater/Cooler`, `Thermostat`. |
|
|
237
238
|
| `atwDevices[].hideZone` | Here select which zone need to be hidden `None/Disabled`, `Heat Pump`, `Heat Pump / Zone 1`, `Heat Pump / Zone 1 / Hot Water`, `Heat Pump / Zone 1 / Zone 2`, `Heat Pump / Hot Water`,`Heat Pump / Hot Water / Zone 2`, `Heat Pump / Zone 2`, `Zone 1`, `Zone 1 / Hot Water`, `Zone 1 / Hot Water / Zone 2`, `Zone 1 / Zone 2`, `Hot Water`, `Hot Water / Zone 2`, `Zone 2`, `All`. |
|
|
238
239
|
| `atwDevices[].temperatureSensor` | This enable extra `Room` temperature sensors to use with automations in HomeKit app. |
|
|
239
240
|
| `atwDevices[].temperatureSensorFlow` | This enable extra `Flow` temperature sensors to use with automations in HomeKit app. |
|
|
@@ -259,7 +260,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
|
|
|
259
260
|
| `ervDevices[].type` | Read only data, do not change it. |
|
|
260
261
|
| `ervDevices[].typeString` | Read only data, do not change it. |
|
|
261
262
|
| `ervDevices[].name` | Here You can schange the `Accessory Name` which is exposed to the `Homebridge/HomeKit`. |
|
|
262
|
-
| `ervDevices[].
|
|
263
|
+
| `ervDevices[].displayType` | Here select main control mode `None/Disabled`, `Heater/Cooler`, `Thermostat`. |
|
|
263
264
|
| `ervDevices[].temperatureSensor` | This enable extra `Room` temperature sensors to use with automations in HomeKit app. |
|
|
264
265
|
| `ervDevices[].temperatureSensorOutdoor` | This enable extra `Outdoor` temperature sensors to use with automations in HomeKit app. |
|
|
265
266
|
| `ervDevices[].temperatureSensorSupply` | This enable extra `Supply` temperature sensors to use with automations in HomeKit app. |
|
package/config.schema.json
CHANGED
|
@@ -201,6 +201,32 @@
|
|
|
201
201
|
}
|
|
202
202
|
]
|
|
203
203
|
},
|
|
204
|
+
"type": {
|
|
205
|
+
"title": "Account Type",
|
|
206
|
+
"type": "string",
|
|
207
|
+
"default": "disabled",
|
|
208
|
+
"description": "Here select the language used in MELCloud account.",
|
|
209
|
+
"oneOf": [
|
|
210
|
+
{
|
|
211
|
+
"title": "None/Disabled",
|
|
212
|
+
"enum": [
|
|
213
|
+
"disabled"
|
|
214
|
+
]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"title": "MELCLoud",
|
|
218
|
+
"enum": [
|
|
219
|
+
"melcloud"
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"title": "MELCLoud Home",
|
|
224
|
+
"enum": [
|
|
225
|
+
"melcloudhome"
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
},
|
|
204
230
|
"ataDevices": {
|
|
205
231
|
"title": "Devices ATA",
|
|
206
232
|
"type": "array",
|
|
@@ -209,8 +235,8 @@
|
|
|
209
235
|
"properties": {
|
|
210
236
|
"id": {
|
|
211
237
|
"title": "ID",
|
|
212
|
-
"type": "
|
|
213
|
-
"default": 0,
|
|
238
|
+
"type": "string",
|
|
239
|
+
"default": "0",
|
|
214
240
|
"readonly": true
|
|
215
241
|
},
|
|
216
242
|
"type": {
|
|
@@ -225,12 +251,12 @@
|
|
|
225
251
|
"default": "Air Conditioner",
|
|
226
252
|
"readonly": true
|
|
227
253
|
},
|
|
228
|
-
"
|
|
254
|
+
"displayType": {
|
|
229
255
|
"title": "Display Type",
|
|
230
256
|
"type": "integer",
|
|
231
257
|
"minimum": 0,
|
|
232
258
|
"maximum": 2,
|
|
233
|
-
"default":
|
|
259
|
+
"default": 0,
|
|
234
260
|
"description": "Select accessory control type displayed in HomeKit app.",
|
|
235
261
|
"oneOf": [
|
|
236
262
|
{
|
|
@@ -760,14 +786,14 @@
|
|
|
760
786
|
}
|
|
761
787
|
},
|
|
762
788
|
"required": [
|
|
763
|
-
"
|
|
789
|
+
"displayType",
|
|
764
790
|
"heatDryFanMode",
|
|
765
791
|
"coolDryFanMode",
|
|
766
792
|
"autoDryFanMode"
|
|
767
793
|
]
|
|
768
794
|
},
|
|
769
795
|
"condition": {
|
|
770
|
-
"functionBody": "const
|
|
796
|
+
"functionBody": "const id = model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].id; return id && id !== '0';"
|
|
771
797
|
}
|
|
772
798
|
},
|
|
773
799
|
"atwDevices": {
|
|
@@ -778,8 +804,8 @@
|
|
|
778
804
|
"properties": {
|
|
779
805
|
"id": {
|
|
780
806
|
"title": "ID",
|
|
781
|
-
"type": "
|
|
782
|
-
"default": 0,
|
|
807
|
+
"type": "string",
|
|
808
|
+
"default": "0",
|
|
783
809
|
"readonly": true
|
|
784
810
|
},
|
|
785
811
|
"type": {
|
|
@@ -799,12 +825,12 @@
|
|
|
799
825
|
"type": "string",
|
|
800
826
|
"default": "Heat Pump"
|
|
801
827
|
},
|
|
802
|
-
"
|
|
828
|
+
"displayType": {
|
|
803
829
|
"title": "Display Type",
|
|
804
830
|
"type": "integer",
|
|
805
831
|
"minimum": 0,
|
|
806
832
|
"maximum": 2,
|
|
807
|
-
"default":
|
|
833
|
+
"default": 0,
|
|
808
834
|
"description": "Select accessory control type displayed in HomeKit app.",
|
|
809
835
|
"oneOf": [
|
|
810
836
|
{
|
|
@@ -1297,12 +1323,9 @@
|
|
|
1297
1323
|
}
|
|
1298
1324
|
},
|
|
1299
1325
|
"required": [
|
|
1300
|
-
"
|
|
1326
|
+
"displayType",
|
|
1301
1327
|
"hideZone"
|
|
1302
1328
|
]
|
|
1303
|
-
},
|
|
1304
|
-
"condition": {
|
|
1305
|
-
"functionBody": "const accounts = model.accounts[arrayIndices]?.atwDevices || []; return accounts.length > 0 && accounts.every(device => device.id != null);"
|
|
1306
1329
|
}
|
|
1307
1330
|
},
|
|
1308
1331
|
"ervDevices": {
|
|
@@ -1313,8 +1336,8 @@
|
|
|
1313
1336
|
"properties": {
|
|
1314
1337
|
"id": {
|
|
1315
1338
|
"title": "ID",
|
|
1316
|
-
"type": "
|
|
1317
|
-
"default": 0,
|
|
1339
|
+
"type": "string",
|
|
1340
|
+
"default": "0",
|
|
1318
1341
|
"readonly": true
|
|
1319
1342
|
},
|
|
1320
1343
|
"type": {
|
|
@@ -1334,12 +1357,12 @@
|
|
|
1334
1357
|
"type": "string",
|
|
1335
1358
|
"default": "Ventilation"
|
|
1336
1359
|
},
|
|
1337
|
-
"
|
|
1360
|
+
"displayType": {
|
|
1338
1361
|
"title": "Display Type",
|
|
1339
1362
|
"type": "integer",
|
|
1340
1363
|
"minimum": 0,
|
|
1341
1364
|
"maximum": 2,
|
|
1342
|
-
"default":
|
|
1365
|
+
"default": 0,
|
|
1343
1366
|
"description": "Select accessory control type displayed in HomeKit app.",
|
|
1344
1367
|
"oneOf": [
|
|
1345
1368
|
{
|
|
@@ -1646,11 +1669,11 @@
|
|
|
1646
1669
|
}
|
|
1647
1670
|
},
|
|
1648
1671
|
"required": [
|
|
1649
|
-
"
|
|
1672
|
+
"displayType"
|
|
1650
1673
|
]
|
|
1651
1674
|
},
|
|
1652
1675
|
"condition": {
|
|
1653
|
-
"functionBody": "const
|
|
1676
|
+
"functionBody": "const id = model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].id; return id;"
|
|
1654
1677
|
}
|
|
1655
1678
|
},
|
|
1656
1679
|
"refreshInterval": {
|
|
@@ -1798,7 +1821,8 @@
|
|
|
1798
1821
|
"name",
|
|
1799
1822
|
"user",
|
|
1800
1823
|
"passwd",
|
|
1801
|
-
"language"
|
|
1824
|
+
"language",
|
|
1825
|
+
"type"
|
|
1802
1826
|
]
|
|
1803
1827
|
}
|
|
1804
1828
|
}
|
|
@@ -1817,6 +1841,7 @@
|
|
|
1817
1841
|
"type": "password"
|
|
1818
1842
|
},
|
|
1819
1843
|
"accounts[].language",
|
|
1844
|
+
"accounts[].type",
|
|
1820
1845
|
{
|
|
1821
1846
|
"key": "accounts[]",
|
|
1822
1847
|
"type": "tabarray",
|
|
@@ -1839,7 +1864,7 @@
|
|
|
1839
1864
|
"key": "accounts[].ataDevices[].typeString",
|
|
1840
1865
|
"readonly": true
|
|
1841
1866
|
},
|
|
1842
|
-
"accounts[].ataDevices[].
|
|
1867
|
+
"accounts[].ataDevices[].displayType",
|
|
1843
1868
|
{
|
|
1844
1869
|
"key": "accounts[].ataDevices[]",
|
|
1845
1870
|
"title": "Settings",
|
|
@@ -1854,7 +1879,7 @@
|
|
|
1854
1879
|
"accounts[].ataDevices[].refreshInterval"
|
|
1855
1880
|
],
|
|
1856
1881
|
"condition": {
|
|
1857
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].
|
|
1882
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
|
|
1858
1883
|
}
|
|
1859
1884
|
},
|
|
1860
1885
|
{
|
|
@@ -1868,7 +1893,7 @@
|
|
|
1868
1893
|
"accounts[].ataDevices[].temperatureSensorOutdoor"
|
|
1869
1894
|
],
|
|
1870
1895
|
"condition": {
|
|
1871
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].
|
|
1896
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
|
|
1872
1897
|
}
|
|
1873
1898
|
},
|
|
1874
1899
|
{
|
|
@@ -1894,7 +1919,7 @@
|
|
|
1894
1919
|
}
|
|
1895
1920
|
],
|
|
1896
1921
|
"condition": {
|
|
1897
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].
|
|
1922
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
|
|
1898
1923
|
}
|
|
1899
1924
|
},
|
|
1900
1925
|
{
|
|
@@ -1917,21 +1942,12 @@
|
|
|
1917
1942
|
}
|
|
1918
1943
|
],
|
|
1919
1944
|
"condition": {
|
|
1920
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].
|
|
1945
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].displayType > 0;"
|
|
1921
1946
|
}
|
|
1922
1947
|
}
|
|
1923
1948
|
],
|
|
1924
1949
|
"condition": {
|
|
1925
|
-
"functionBody": "return model.accounts[arrayIndices].ataDevices.
|
|
1926
|
-
}
|
|
1927
|
-
},
|
|
1928
|
-
{
|
|
1929
|
-
"key": "accounts[]",
|
|
1930
|
-
"type": "tabarray",
|
|
1931
|
-
"title": "{{ value.title }}",
|
|
1932
|
-
"items": [],
|
|
1933
|
-
"condition": {
|
|
1934
|
-
"functionBody": "return model.accounts[arrayIndices].ataDevices.length === 0;"
|
|
1950
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ataDevices[arrayIndices[1]].id !== '0';"
|
|
1935
1951
|
}
|
|
1936
1952
|
}
|
|
1937
1953
|
]
|
|
@@ -1953,7 +1969,7 @@
|
|
|
1953
1969
|
"key": "accounts[].atwDevices[].typeString",
|
|
1954
1970
|
"readonly": true
|
|
1955
1971
|
},
|
|
1956
|
-
"accounts[].atwDevices[].
|
|
1972
|
+
"accounts[].atwDevices[].displayType",
|
|
1957
1973
|
{
|
|
1958
1974
|
"key": "accounts[].atwDevices[]",
|
|
1959
1975
|
"type": "section",
|
|
@@ -1966,7 +1982,7 @@
|
|
|
1966
1982
|
"accounts[].atwDevices[].refreshInterval"
|
|
1967
1983
|
],
|
|
1968
1984
|
"condition": {
|
|
1969
|
-
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].
|
|
1985
|
+
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
|
|
1970
1986
|
}
|
|
1971
1987
|
},
|
|
1972
1988
|
{
|
|
@@ -1987,7 +2003,7 @@
|
|
|
1987
2003
|
"accounts[].atwDevices[].temperatureSensorReturnZone2"
|
|
1988
2004
|
],
|
|
1989
2005
|
"condition": {
|
|
1990
|
-
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].
|
|
2006
|
+
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
|
|
1991
2007
|
}
|
|
1992
2008
|
},
|
|
1993
2009
|
{
|
|
@@ -2013,7 +2029,7 @@
|
|
|
2013
2029
|
}
|
|
2014
2030
|
],
|
|
2015
2031
|
"condition": {
|
|
2016
|
-
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].
|
|
2032
|
+
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
|
|
2017
2033
|
}
|
|
2018
2034
|
},
|
|
2019
2035
|
{
|
|
@@ -2036,7 +2052,7 @@
|
|
|
2036
2052
|
}
|
|
2037
2053
|
],
|
|
2038
2054
|
"condition": {
|
|
2039
|
-
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].
|
|
2055
|
+
"functionBody": "return model.accounts[arrayIndices[0]].atwDevices[arrayIndices[1]].displayType > 0;"
|
|
2040
2056
|
}
|
|
2041
2057
|
}
|
|
2042
2058
|
],
|
|
@@ -2072,7 +2088,7 @@
|
|
|
2072
2088
|
"key": "accounts[].ervDevices[].typeString",
|
|
2073
2089
|
"readonly": true
|
|
2074
2090
|
},
|
|
2075
|
-
"accounts[].ervDevices[].
|
|
2091
|
+
"accounts[].ervDevices[].displayType",
|
|
2076
2092
|
{
|
|
2077
2093
|
"key": "accounts[].ervDevices[]",
|
|
2078
2094
|
"title": "Settings",
|
|
@@ -2084,7 +2100,7 @@
|
|
|
2084
2100
|
"accounts[].ervDevices[].refreshInterval"
|
|
2085
2101
|
],
|
|
2086
2102
|
"condition": {
|
|
2087
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].
|
|
2103
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
|
|
2088
2104
|
}
|
|
2089
2105
|
},
|
|
2090
2106
|
{
|
|
@@ -2099,7 +2115,7 @@
|
|
|
2099
2115
|
"accounts[].ervDevices[].temperatureSensorSupply"
|
|
2100
2116
|
],
|
|
2101
2117
|
"condition": {
|
|
2102
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].
|
|
2118
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
|
|
2103
2119
|
}
|
|
2104
2120
|
},
|
|
2105
2121
|
{
|
|
@@ -2125,7 +2141,7 @@
|
|
|
2125
2141
|
}
|
|
2126
2142
|
],
|
|
2127
2143
|
"condition": {
|
|
2128
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].
|
|
2144
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
|
|
2129
2145
|
}
|
|
2130
2146
|
},
|
|
2131
2147
|
{
|
|
@@ -2148,7 +2164,7 @@
|
|
|
2148
2164
|
}
|
|
2149
2165
|
],
|
|
2150
2166
|
"condition": {
|
|
2151
|
-
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].
|
|
2167
|
+
"functionBody": "return model.accounts[arrayIndices[0]].ervDevices[arrayIndices[1]].displayType > 0;"
|
|
2152
2168
|
}
|
|
2153
2169
|
}
|
|
2154
2170
|
],
|
|
@@ -76,41 +76,56 @@
|
|
|
76
76
|
</select>
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
|
+
<div class="mb-2">
|
|
80
|
+
<label for="accountType" class="form-label">Account Type</label>
|
|
81
|
+
<select id="accountType" name="accountType" class="form-control">
|
|
82
|
+
<option value="disabled">None/Disabled</option>
|
|
83
|
+
<option value="melcloud">MELCloud</option>
|
|
84
|
+
<option value="melcloudhome">MELCloud Home</option>
|
|
85
|
+
</select>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
79
88
|
<div class="text-center">
|
|
80
89
|
<button id="logIn" type="button" class="btn btn-secondary">Connect to MELCloud</button>
|
|
81
90
|
<button id="configButton" type="button" class="btn btn-secondary"><i class="fas fa-gear"></i></button>
|
|
82
91
|
</div>
|
|
83
92
|
</form>
|
|
93
|
+
<div id="accountButton" class="text-center mt-2"></div>
|
|
84
94
|
</div>
|
|
85
|
-
|
|
86
|
-
<div id="accountButton" class="mt-2"></div>
|
|
87
95
|
</div>
|
|
88
96
|
|
|
89
97
|
<script>
|
|
90
98
|
(async () => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
const config = await homebridge.getPluginConfig();
|
|
100
|
+
|
|
101
|
+
if (!config.length) {
|
|
102
|
+
config.push({ accounts: [] });
|
|
103
|
+
await homebridge.updatePluginConfig(config);
|
|
95
104
|
homebridge.showSchemaForm();
|
|
96
105
|
return;
|
|
97
106
|
}
|
|
98
107
|
|
|
99
|
-
|
|
108
|
+
config[0].accounts ??= [];
|
|
100
109
|
|
|
110
|
+
// 🧩 Deep clone — UI-safe copy (JSON method resets references)
|
|
111
|
+
const pluginConfig = JSON.parse(JSON.stringify(config));
|
|
112
|
+
|
|
113
|
+
this.accountIndex = 0;
|
|
101
114
|
const accountsCount = pluginConfig[0].accounts.length;
|
|
115
|
+
|
|
102
116
|
for (let i = 0; i < accountsCount; i++) {
|
|
117
|
+
const acc = pluginConfig[0].accounts[i];
|
|
103
118
|
const button = document.createElement("button");
|
|
104
119
|
button.type = "button";
|
|
105
120
|
button.id = `button${i}`;
|
|
106
121
|
button.className = "btn btn-primary";
|
|
107
122
|
button.style.textTransform = 'none';
|
|
108
|
-
button.innerText =
|
|
123
|
+
button.innerText = acc.name;
|
|
109
124
|
document.getElementById("accountButton").appendChild(button);
|
|
110
125
|
|
|
111
126
|
button.addEventListener('click', async () => {
|
|
112
127
|
for (let j = 0; j < accountsCount; j++) {
|
|
113
|
-
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-
|
|
128
|
+
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-primary' : 'btn btn-secondary');
|
|
114
129
|
}
|
|
115
130
|
|
|
116
131
|
const acc = pluginConfig[0].accounts[i];
|
|
@@ -118,24 +133,30 @@
|
|
|
118
133
|
document.getElementById('name').value = acc.name || '';
|
|
119
134
|
document.getElementById('user').value = acc.user || '';
|
|
120
135
|
document.getElementById('passwd').value = acc.passwd || '';
|
|
121
|
-
document.getElementById('language').value = acc.language || '';
|
|
122
|
-
document.getElementById('
|
|
123
|
-
|
|
136
|
+
document.getElementById('language').value = acc.language || '0';
|
|
137
|
+
document.getElementById('accountType').value = acc.type || 'disabled';
|
|
138
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language && acc.type);
|
|
124
139
|
});
|
|
125
140
|
|
|
126
|
-
if (i === accountsCount - 1
|
|
141
|
+
if (i === accountsCount - 1 && accountsCount > 0)
|
|
142
|
+
document.getElementById(`button0`).click();
|
|
143
|
+
|
|
144
|
+
this.accountIndex = i;
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
document.getElementById('melCloudAccount').style.display = 'block';
|
|
130
148
|
|
|
149
|
+
// Safe update
|
|
131
150
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
132
|
-
const acc = pluginConfig[0].accounts[this.
|
|
151
|
+
const acc = pluginConfig[0].accounts[this.accountIndex];
|
|
152
|
+
if (!acc) return;
|
|
133
153
|
acc.name = document.querySelector('#name').value;
|
|
134
154
|
acc.user = document.querySelector('#user').value;
|
|
135
155
|
acc.passwd = document.querySelector('#passwd').value;
|
|
136
156
|
acc.language = document.querySelector('#language').value;
|
|
157
|
+
acc.type = document.querySelector('#accountType').value;
|
|
137
158
|
|
|
138
|
-
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
159
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language && acc.type);
|
|
139
160
|
|
|
140
161
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
141
162
|
await homebridge.savePluginConfig(pluginConfig);
|
|
@@ -167,9 +188,10 @@
|
|
|
167
188
|
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
168
189
|
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
169
190
|
const removedDevices = [];
|
|
191
|
+
|
|
170
192
|
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
171
193
|
const device = configDevices[i];
|
|
172
|
-
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
194
|
+
if (device.id !== "0" && !melcloudIds.includes(device.id)) {
|
|
173
195
|
removedDevices.push(device);
|
|
174
196
|
configDevices.splice(i, 1);
|
|
175
197
|
}
|
|
@@ -186,13 +208,14 @@
|
|
|
186
208
|
}
|
|
187
209
|
|
|
188
210
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
189
|
-
homebridge.showSpinner();
|
|
190
211
|
document.getElementById(`logIn`).className = "btn btn-primary";
|
|
212
|
+
|
|
191
213
|
updateInfo('info', 'Connecting...', 'yellow');
|
|
214
|
+
homebridge.showSpinner();
|
|
192
215
|
|
|
193
216
|
try {
|
|
194
|
-
const acc = pluginConfig[0].accounts[this.
|
|
195
|
-
const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language };
|
|
217
|
+
const acc = pluginConfig[0].accounts[this.accountIndex];
|
|
218
|
+
const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language, accountType: acc.type };
|
|
196
219
|
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
197
220
|
|
|
198
221
|
// Initialize devices arrays
|
|
@@ -213,24 +236,36 @@
|
|
|
213
236
|
const removedAtw = removeStaleDevices(acc.atwDevices, devicesByType.atw);
|
|
214
237
|
const removedErv = removeStaleDevices(acc.ervDevices, devicesByType.erv);
|
|
215
238
|
|
|
216
|
-
// Function to handle device & presets
|
|
217
239
|
const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
218
240
|
devicesInCloud.forEach(device => {
|
|
219
241
|
const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
|
|
220
|
-
const devObj = {
|
|
242
|
+
const devObj = structuredClone({
|
|
243
|
+
id,
|
|
244
|
+
type,
|
|
245
|
+
typeString,
|
|
246
|
+
name,
|
|
247
|
+
displayType: 0,
|
|
248
|
+
presets: [],
|
|
249
|
+
buttonsSensors: []
|
|
250
|
+
});
|
|
251
|
+
|
|
221
252
|
if (!devicesInConfig.some(d => d.id === id)) {
|
|
222
253
|
devicesInConfig.push(devObj);
|
|
223
|
-
newArr.push(devObj);
|
|
254
|
+
newArr.push(structuredClone(devObj));
|
|
224
255
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
p
|
|
228
|
-
|
|
229
|
-
|
|
256
|
+
|
|
257
|
+
presets.forEach(preset => {
|
|
258
|
+
const p = structuredClone({
|
|
259
|
+
id: preset.ID,
|
|
260
|
+
name: preset.NumberDescription,
|
|
261
|
+
displayType: 0,
|
|
262
|
+
namePrefix: false
|
|
263
|
+
});
|
|
264
|
+
|
|
230
265
|
const devConfig = devicesInConfig.find(d => d.id === id);
|
|
231
|
-
if (devConfig && !devConfig.presets.some(x => x.id === p.
|
|
266
|
+
if (devConfig && !devConfig.presets.some(x => x.id === p.id)) {
|
|
232
267
|
devConfig.presets.push(p);
|
|
233
|
-
newPresets.push(p);
|
|
268
|
+
newPresets.push(structuredClone(p));
|
|
234
269
|
}
|
|
235
270
|
});
|
|
236
271
|
});
|
|
@@ -238,9 +273,8 @@
|
|
|
238
273
|
|
|
239
274
|
handleDevices(devicesByType.ata, acc.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
240
275
|
handleDevices(devicesByType.atw, acc.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
241
|
-
handleDevices(devicesByType.erv, acc.ervDevices, "Energy Recovery
|
|
276
|
+
handleDevices(devicesByType.erv, acc.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets);
|
|
242
277
|
|
|
243
|
-
// Display summary
|
|
244
278
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
245
279
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
246
280
|
const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
@@ -248,9 +282,12 @@
|
|
|
248
282
|
if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
|
|
249
283
|
updateInfo('info', 'No changes detected.', 'white');
|
|
250
284
|
} else {
|
|
251
|
-
if (newDevicesCount)
|
|
252
|
-
|
|
253
|
-
if (
|
|
285
|
+
if (newDevicesCount)
|
|
286
|
+
updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
287
|
+
if (newPresetsCount)
|
|
288
|
+
updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
289
|
+
if (removedDevicesCount)
|
|
290
|
+
updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
254
291
|
}
|
|
255
292
|
|
|
256
293
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -259,7 +296,7 @@
|
|
|
259
296
|
|
|
260
297
|
} catch (error) {
|
|
261
298
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
262
|
-
updateInfo('info1', `Error: ${error}`, 'red');
|
|
299
|
+
updateInfo('info1', `Error: ${JSON.stringify(error)}`, 'red');
|
|
263
300
|
document.getElementById('logIn').className = "btn btn-secondary";
|
|
264
301
|
} finally {
|
|
265
302
|
homebridge.hideSpinner();
|
package/homebridge-ui/server.js
CHANGED
|
@@ -17,14 +17,15 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
17
17
|
const user = payload.user;
|
|
18
18
|
const passwd = payload.passwd;
|
|
19
19
|
const language = payload.language;
|
|
20
|
+
const accountType = payload.accountType;
|
|
20
21
|
const accountFile = `${this.homebridgeStoragePath}/melcloud/${accountName}_Account`;
|
|
21
22
|
const buildingsFile = `${this.homebridgeStoragePath}/melcloud/${accountName}_Buildings`;
|
|
22
23
|
const devicesFile = `${this.homebridgeStoragePath}/melcloud/${accountName}_Devices`;
|
|
23
|
-
const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile, false, true);
|
|
24
|
+
const melCloud = new MelCloud(accountType, user, passwd, language, accountFile, buildingsFile, devicesFile, false, true);
|
|
24
25
|
|
|
25
26
|
try {
|
|
26
|
-
const
|
|
27
|
-
const devices = await melCloud.checkDevicesList(
|
|
27
|
+
const accountInfo = await melCloud.connect();
|
|
28
|
+
const devices = await melCloud.checkDevicesList(accountInfo.ContextKey);
|
|
28
29
|
return devices;
|
|
29
30
|
} catch (error) {
|
|
30
31
|
throw new Error(`MELCloud error: ${error.message ?? error}.`);
|