homebridge-melcloud-control 4.0.0-beta.9 → 4.0.0-beta.90

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
@@ -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
@@ -204,9 +204,10 @@ 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 MELCloud username. |
208
- | `passwd` | Here set the MELCloud password. |
209
- | `language` | Here select the MELCloud language. |
207
+ | `user` | Here set the account username. |
208
+ | `passwd` | Here set the account password. |
209
+ | `language` | Here select the account language. |
210
+ | `displayMode` | 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. |
@@ -201,6 +201,32 @@
201
201
  }
202
202
  ]
203
203
  },
204
+ "displayType": {
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": "integer",
213
- "default": 0,
238
+ "type": "string",
239
+ "default": "0",
214
240
  "readonly": true
215
241
  },
216
242
  "type": {
@@ -778,8 +804,8 @@
778
804
  "properties": {
779
805
  "id": {
780
806
  "title": "ID",
781
- "type": "integer",
782
- "default": 0,
807
+ "type": "string",
808
+ "default": "0",
783
809
  "readonly": true
784
810
  },
785
811
  "type": {
@@ -1313,8 +1339,8 @@
1313
1339
  "properties": {
1314
1340
  "id": {
1315
1341
  "title": "ID",
1316
- "type": "integer",
1317
- "default": 0,
1342
+ "type": "string",
1343
+ "default": "0",
1318
1344
  "readonly": true
1319
1345
  },
1320
1346
  "type": {
@@ -1798,7 +1824,8 @@
1798
1824
  "name",
1799
1825
  "user",
1800
1826
  "passwd",
1801
- "language"
1827
+ "language",
1828
+ "displayType"
1802
1829
  ]
1803
1830
  }
1804
1831
  }
@@ -1817,13 +1844,14 @@
1817
1844
  "type": "password"
1818
1845
  },
1819
1846
  "accounts[].language",
1847
+ "accounts[].displayType",
1820
1848
  {
1821
1849
  "key": "accounts[]",
1822
1850
  "type": "tabarray",
1823
1851
  "title": "{{ value.title }}",
1824
1852
  "items": [
1825
1853
  {
1826
- "key": "accounts[]",
1854
+ "key": "accounts[].ataDevices",
1827
1855
  "title": "Air Conditioner",
1828
1856
  "items": [
1829
1857
  {
@@ -76,41 +76,53 @@
76
76
  </select>
77
77
  </div>
78
78
 
79
+ <div class="mb-2">
80
+ <label for="displayType" class="form-label">Account Type</label>
81
+ <select id="displayType" name="displayType" 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
99
  const pluginConfig = await homebridge.getPluginConfig();
100
+
101
+ // Ensure proper structure on load
92
102
  if (!pluginConfig.length) {
93
- pluginConfig.push({});
103
+ pluginConfig.push({ accounts: [] });
94
104
  await homebridge.updatePluginConfig(pluginConfig);
95
105
  homebridge.showSchemaForm();
96
106
  return;
97
107
  }
108
+ pluginConfig[0].accounts ??= [];
98
109
 
99
- this.deviceIndex = 0;
100
-
110
+ this.accountIndex = 0;
101
111
  const accountsCount = pluginConfig[0].accounts.length;
112
+
102
113
  for (let i = 0; i < accountsCount; i++) {
114
+ const acc = pluginConfig[0].accounts[i];
103
115
  const button = document.createElement("button");
104
116
  button.type = "button";
105
117
  button.id = `button${i}`;
106
118
  button.className = "btn btn-primary";
107
119
  button.style.textTransform = 'none';
108
- button.innerText = pluginConfig[0].accounts[i].name;
120
+ button.innerText = acc.name;
109
121
  document.getElementById("accountButton").appendChild(button);
110
122
 
111
123
  button.addEventListener('click', async () => {
112
124
  for (let j = 0; j < accountsCount; j++) {
113
- document.getElementById(`button${j}`).className = (j === i ? 'btn btn-secondary' : 'btn btn-primary');
125
+ document.getElementById(`button${j}`).className = (j === i ? 'btn btn-primary' : 'btn btn-secondary');
114
126
  }
115
127
 
116
128
  const acc = pluginConfig[0].accounts[i];
@@ -118,24 +130,30 @@
118
130
  document.getElementById('name').value = acc.name || '';
119
131
  document.getElementById('user').value = acc.user || '';
120
132
  document.getElementById('passwd').value = acc.passwd || '';
121
- document.getElementById('language').value = acc.language || '';
122
- document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
123
- this.deviceIndex = i;
133
+ document.getElementById('language').value = acc.language || '0';
134
+ document.getElementById('displayType').value = acc.displayType || 'disabled';
135
+ document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language && acc.displayType);
124
136
  });
125
137
 
126
- if (i === accountsCount - 1) document.getElementById(`button0`).click();
138
+ if (i === accountsCount - 1 && accountsCount > 0)
139
+ document.getElementById(`button0`).click();
140
+
141
+ this.accountIndex = i;
127
142
  }
128
143
 
129
144
  document.getElementById('melCloudAccount').style.display = 'block';
130
145
 
146
+ // Safe update
131
147
  document.getElementById('configForm').addEventListener('input', async () => {
132
- const acc = pluginConfig[0].accounts[this.deviceIndex];
148
+ const acc = pluginConfig[0].accounts[this.accountIndex];
149
+ if (!acc) return;
133
150
  acc.name = document.querySelector('#name').value;
134
151
  acc.user = document.querySelector('#user').value;
135
152
  acc.passwd = document.querySelector('#passwd').value;
136
153
  acc.language = document.querySelector('#language').value;
154
+ acc.displayType = document.querySelector('#displayType').value;
137
155
 
138
- document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
156
+ document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language && acc.displayType);
139
157
 
140
158
  await homebridge.updatePluginConfig(pluginConfig);
141
159
  await homebridge.savePluginConfig(pluginConfig);
@@ -167,9 +185,10 @@
167
185
  function removeStaleDevices(configDevices, melcloudDevices) {
168
186
  const melcloudIds = melcloudDevices.map(d => d.DeviceID);
169
187
  const removedDevices = [];
188
+
170
189
  for (let i = configDevices.length - 1; i >= 0; i--) {
171
190
  const device = configDevices[i];
172
- if (device.id !== 0 && !melcloudIds.includes(device.id)) {
191
+ if (device.id !== "0" && !melcloudIds.includes(device.id)) {
173
192
  removedDevices.push(device);
174
193
  configDevices.splice(i, 1);
175
194
  }
@@ -191,8 +210,8 @@
191
210
  updateInfo('info', 'Connecting...', 'yellow');
192
211
 
193
212
  try {
194
- const acc = pluginConfig[0].accounts[this.deviceIndex];
195
- const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language };
213
+ const acc = pluginConfig[0].accounts[this.accountIndex];
214
+ const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language, displayType: acc.displayType };
196
215
  const devicesInMelCloud = await homebridge.request('/connect', payload);
197
216
 
198
217
  // Initialize devices arrays
@@ -213,24 +232,36 @@
213
232
  const removedAtw = removeStaleDevices(acc.atwDevices, devicesByType.atw);
214
233
  const removedErv = removeStaleDevices(acc.ervDevices, devicesByType.erv);
215
234
 
216
- // Function to handle device & presets
217
235
  const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
218
236
  devicesInCloud.forEach(device => {
219
237
  const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
220
- const devObj = { id, type, typeString, name, displayMode: 1, presets: [], buttonsSensors: [] };
238
+ const devObj = structuredClone({
239
+ id,
240
+ type,
241
+ typeString,
242
+ name,
243
+ displayMode: 1,
244
+ presets: [],
245
+ buttonsSensors: []
246
+ });
247
+
221
248
  if (!devicesInConfig.some(d => d.id === id)) {
222
249
  devicesInConfig.push(devObj);
223
- newArr.push(devObj);
250
+ newArr.push(structuredClone(devObj));
224
251
  }
225
- presets.forEach(p => {
226
- p.id = p.ID;
227
- p.name = p.NumberDescription;
228
- p.displayType = 0;
229
- p.namePrefix = false;
252
+
253
+ presets.forEach(preset => {
254
+ const p = structuredClone({
255
+ id: preset.ID,
256
+ name: preset.NumberDescription,
257
+ displayType: 0,
258
+ namePrefix: false
259
+ });
260
+
230
261
  const devConfig = devicesInConfig.find(d => d.id === id);
231
- if (devConfig && !devConfig.presets.some(x => x.id === p.ID)) {
262
+ if (devConfig && !devConfig.presets.some(x => x.id === p.id)) {
232
263
  devConfig.presets.push(p);
233
- newPresets.push(p);
264
+ newPresets.push(structuredClone(p));
234
265
  }
235
266
  });
236
267
  });
@@ -238,9 +269,8 @@
238
269
 
239
270
  handleDevices(devicesByType.ata, acc.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
240
271
  handleDevices(devicesByType.atw, acc.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
241
- handleDevices(devicesByType.erv, acc.ervDevices, "Energy Recovery ventiltion", newDevices.erv, newDevices.ervPresets);
272
+ handleDevices(devicesByType.erv, acc.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets);
242
273
 
243
- // Display summary
244
274
  const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
245
275
  const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
246
276
  const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
@@ -248,9 +278,12 @@
248
278
  if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
249
279
  updateInfo('info', 'No changes detected.', 'white');
250
280
  } else {
251
- if (newDevicesCount) updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
252
- if (newPresetsCount) updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
253
- if (removedDevicesCount) updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
281
+ if (newDevicesCount)
282
+ updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
283
+ if (newPresetsCount)
284
+ updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
285
+ if (removedDevicesCount)
286
+ updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
254
287
  }
255
288
 
256
289
  await homebridge.updatePluginConfig(pluginConfig);
@@ -259,7 +292,7 @@
259
292
 
260
293
  } catch (error) {
261
294
  updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
262
- updateInfo('info1', `Error: ${error}`, 'red');
295
+ updateInfo('info1', `Error: ${JSON.stringify(error)}`, 'red');
263
296
  document.getElementById('logIn').className = "btn btn-secondary";
264
297
  } finally {
265
298
  homebridge.hideSpinner();
@@ -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 displayType = payload.displayType;
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(displayType, user, passwd, language, accountFile, buildingsFile, devicesFile, false, true);
24
25
 
25
26
  try {
26
- const response = await melCloud.connect();
27
- const devices = await melCloud.checkDevicesList(response.contextKey);
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}.`);
package/index.js CHANGED
@@ -30,6 +30,9 @@ class MelCloudPlatform {
30
30
  api.on('didFinishLaunching', async () => {
31
31
  //loop through accounts
32
32
  for (const account of config.accounts) {
33
+ const displayType = account.displayType || 'disabled';
34
+ if (displayType === 'disabled') continue;
35
+
33
36
  const accountName = account.name;
34
37
  const user = account.user;
35
38
  const passwd = account.passwd;
@@ -64,7 +67,7 @@ class MelCloudPlatform {
64
67
  passwd: 'removed',
65
68
  mqtt: {
66
69
  auth: {
67
- ...device.mqtt?.auth,
70
+ ...account.mqtt?.auth,
68
71
  passwd: 'removed',
69
72
  }
70
73
  },
@@ -76,7 +79,6 @@ class MelCloudPlatform {
76
79
  const accountFile = `${prefDir}/${accountName}_Account`;
77
80
  const buildingsFile = `${prefDir}/${accountName}_Buildings`;
78
81
  const devicesFile = `${prefDir}/${accountName}_Devices`;
79
- const cookiesFile = `${prefDir}/${accountName}cookies`;
80
82
 
81
83
 
82
84
  //set account refresh interval
@@ -88,31 +90,26 @@ class MelCloudPlatform {
88
90
  .on('start', async () => {
89
91
  try {
90
92
  //melcloud account
91
- const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile, cookiesFile, logLevel.warn, logLevel.debug, false)
93
+ const melCloud = new MelCloud(displayType, user, passwd, language, accountFile, buildingsFile, devicesFile, logLevel.warn, logLevel.debug, false)
92
94
  .on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
93
95
  .on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
94
96
  .on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
95
97
  .on('warn', (msg) => logLevel.warn && log.warn(`${accountName}, ${msg}`))
96
98
  .on('error', (msg) => logLevel.error && log.error(`${accountName}, ${msg}`));
97
99
 
98
- await melCloud.connectHomeCookies()
99
- return;
100
-
101
-
102
100
  //connect
103
- let response;
101
+ let accountInfo;
104
102
  try {
105
- response = await melCloud.connect();
103
+ accountInfo = await melCloud.connect();
106
104
  } catch (error) {
107
105
  if (logLevel.error) log.error(`${accountName}, Connect error: ${error.message ?? error}`);
108
106
  return;
109
107
  }
110
108
 
111
- const accountInfo = response.accountInfo ?? false;
112
- const contextKey = response.contextKey ?? false;
113
- const useFahrenheit = response.useFahrenheit ?? false;
109
+ const contextKey = accountInfo.ContextKey;
110
+ const useFahrenheit = accountInfo.UseFahrenheit;
114
111
 
115
- if (contextKey === false) {
112
+ if (!contextKey) {
116
113
  return;
117
114
  }
118
115
 
@@ -135,9 +132,9 @@ class MelCloudPlatform {
135
132
 
136
133
  for (const device of devices) {
137
134
  //chack device from config exist on melcloud
138
- const deviceId = device.id;
135
+ device.id = displayType === 'melcloud' ? parseInt(device.id) : device.id;
139
136
  const displayMode = device.displayMode > 0;
140
- const deviceExistInMelCloud = devicesInMelcloud.some(dev => dev.DeviceID === deviceId);
137
+ const deviceExistInMelCloud = devicesInMelcloud.some(dev => dev.DeviceID === device.id);
141
138
  if (!deviceExistInMelCloud || !displayMode) {
142
139
  continue;
143
140
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.0.0-beta.9",
4
+ "version": "4.0.0-beta.90",
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
@@ -18,7 +18,7 @@ export const ApiUrls = {
18
18
  };
19
19
 
20
20
  export const ApiUrlsHome = {
21
- LoginUrl:"https://live-melcloudhome.auth.eu-west-1.amazoncognito.com/login?client_id=3g4d5l5kivuqi7oia68gib7uso&redirect_uri=https%3A%2F%2Fauth.melcloudhome.com%2Fsignin-oidc-meu&response_type=code&scope=openid%20profile&response_mode=form_post",
21
+ LoginUrl: "https://live-melcloudhome.auth.eu-west-1.amazoncognito.com/login?client_id=3g4d5l5kivuqi7oia68gib7uso&redirect_uri=https%3A%2F%2Fauth.melcloudhome.com%2Fsignin-oidc-meu&response_type=code&scope=openid%20profile&response_mode=form_post",
22
22
  BaseURL: 'https://melcloudhome.com',
23
23
  GetUserContext: "/api/user/context",
24
24
  SetAta: "/api/ataunit/deviceid",
@@ -37,7 +37,7 @@ export const TemperatureDisplayUnits = ["°C", "°F"];
37
37
 
38
38
  export const AirConditioner = {
39
39
  System: ["AIR CONDITIONER OFF", "AIR CONDITIONER ON", "AIR CONDITIONER OFFLINE"],
40
- DriveMode: [
40
+ OperationMode: [
41
41
  "0", "HEAT", "DRY", "COOL", "4", "5", "6", "FAN", "AUTO",
42
42
  "ISEE HEAT", "ISEE DRY", "ISEE COOL"
43
43
  ],
@@ -74,7 +74,8 @@ export const AirConditioner = {
74
74
  Presets: 287,
75
75
  HolidayMode: 131072,
76
76
  All: 281483566710825
77
- }
77
+ },
78
+ OperationModeMap: { "0": 0, "Heat": 1, "Dry": 2, "Cool": 3, "4": 4, "5": 5, "6": 6, "Fan": 7, "Auto": 8, "Isee Heat": 9, "Isee Dry": 10, "Isee Cool": 11 },
78
79
  };
79
80
 
80
81
  export const HeatPump = {
package/src/deviceata.js CHANGED
@@ -16,6 +16,7 @@ class DeviceAta extends EventEmitter {
16
16
  AccessoryUUID = api.hap.uuid;
17
17
 
18
18
  //account config
19
+ this.accountMelcloud = account.displayType === 'melcloud' ? true : false;
19
20
  this.device = device;
20
21
  this.displayMode = device.displayMode;
21
22
  this.temperatureSensor = device.temperatureSensor || false;
@@ -327,7 +328,7 @@ class DeviceAta extends EventEmitter {
327
328
 
328
329
  deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.OperationModeSetTemperature;
329
330
  await this.melCloudAta.send(deviceData, this.displayMode);
330
- const operationModeText = AirConditioner.DriveMode[deviceData.Device.OperationMode];
331
+ const operationModeText = AirConditioner.OperationMode[deviceData.Device.OperationMode];
331
332
  if (this.logInfo) this.emit('info', `Set operation mode: ${operationModeText}`);
332
333
  } catch (error) {
333
334
  if (this.logWarn) this.emit('warn', `Set operation mode error: ${error}`);
@@ -522,7 +523,7 @@ class DeviceAta extends EventEmitter {
522
523
  };
523
524
 
524
525
  await this.melCloudAta.send(deviceData, this.displayMode);
525
- const operationModeText = AirConditioner.DriveMode[deviceData.Device.OperationMode];
526
+ const operationModeText = AirConditioner.OperationMode[deviceData.Device.OperationMode];
526
527
  if (this.logInfo) this.emit('info', `Set operation mode: ${operationModeText}`);
527
528
  } catch (error) {
528
529
  if (this.logWarn) this.emit('warn', `Set operation mode error: ${error}`);
@@ -925,8 +926,8 @@ class DeviceAta extends EventEmitter {
925
926
  this.emit('devInfo', `Account: ${this.accountName}`);
926
927
  if (modelIndoor) this.emit('devInfo', `Indoor: ${modelIndoor}`);
927
928
  if (modelOutdoor) this.emit('devInfo', `Outdoor: ${modelOutdoor}`);
928
- this.emit('devInfo', `Serial: ${serialNumber}`);
929
- this.emit('devInfo', `Firmware: ${firmwareAppVersion}`);
929
+ if (serialNumber) this.emit('devInfo', `Serial: ${serialNumber}`);
930
+ if (firmwareAppVersion) this.emit('devInfo', `Firmware: ${firmwareAppVersion}`);
930
931
  this.emit('devInfo', `Manufacturer: ${manufacturer}`);
931
932
  this.emit('devInfo', '----------------------------------');
932
933
  this.displayDeviceInfo = false;
@@ -952,16 +953,16 @@ class DeviceAta extends EventEmitter {
952
953
 
953
954
  //device info
954
955
  const hasAutomaticFanSpeed = deviceData.Device.HasAutomaticFanSpeed ?? false;
955
- const airDirectionFunction = deviceData.Device.AirDirectionFunction ?? false;
956
- const swingFunction = deviceData.Device.SwingFunction ?? false;
956
+ const airDirectionFunction = this.accountMelcloud ? deviceData.Device.AirDirectionFunction : deviceData.Device.HasAirDirectionFunction;;
957
+ const swingFunction = this.accountMelcloud ? deviceData.Device.SwingFunction : deviceData.Device.HasSwing;
957
958
  const hasOutdoorTemperature = deviceData.Device.HasOutdoorTemperature ?? false;
958
- const numberOfFanSpeeds = deviceData.Device.NumberOfFanSpeeds ?? 0;
959
- const modelSupportsFanSpeed = deviceData.Device.ModelSupportsFanSpeed ?? false;
960
- const modelSupportsAuto1 = deviceData.Device.ModelSupportsAuto ?? false;
959
+ const modelSupportsFanSpeed = deviceData.Device.ModelSupportsFanSpeed || deviceData.Device.NumberOfFanSpeeds > 0;
960
+ const numberOfFanSpeeds = modelSupportsFanSpeed ? deviceData.Device.NumberOfFanSpeeds : 0;
961
+ const modelSupportsAuto1 = this.accountMelcloud ? deviceData.Device.ModelSupportsAuto : deviceData.Device.HasAutoOperationMode;
961
962
  const modelSupportsAuto = this.autoDryFanMode >= 1 && modelSupportsAuto1
962
- const modelSupportsHeat1 = deviceData.Device.ModelSupportsHeat ?? false;
963
+ const modelSupportsHeat1 = this.accountMelcloud ? deviceData.Device.ModelSupportsHeat : deviceData.Device.HasHeatOperationMode
963
964
  const modelSupportsHeat = this.heatDryFanMode >= 1 && modelSupportsHeat1;
964
- const modelSupportsDry = deviceData.Device.ModelSupportsDry ?? false;
965
+ const modelSupportsDry = this.accountMelcloud ? deviceData.Device.ModelSupportsDry : deviceData.Device.HasDryOperationMode;
965
966
  const modelSupportsCool = this.coolDryFanMode >= 1;
966
967
  const minTempHeat = 10;
967
968
  const maxTempHeat = 31;
@@ -977,8 +978,8 @@ class DeviceAta extends EventEmitter {
977
978
  const defaultCoolingSetTemperature = deviceData.Device.DefaultCoolingSetTemperature ?? 23;
978
979
  const actualFanSpeed = deviceData.Device.ActualFanSpeed;
979
980
  const automaticFanSpeed = deviceData.Device.AutomaticFanSpeed;
980
- const fanSpeed = deviceData.Device.FanSpeed ?? 0;
981
- const operationMode = deviceData.Device.OperationMode;
981
+ const fanSpeed = this.accountMelcloud ? deviceData.Device.FanSpeed : deviceData.Device.SetFanSpeed;
982
+ const operationMode = this.accountMelcloud ? deviceData.Device.OperationMode : AirConditioner.OperationModeMap[deviceData.Device.OperationMode];
982
983
  const vaneVerticalDirection = deviceData.Device.VaneVerticalDirection;
983
984
  const vaneVerticalSwing = deviceData.Device.VaneVerticalSwing;
984
985
  const vaneHorizontalDirection = deviceData.Device.VaneHorizontalDirection;
@@ -1108,9 +1109,9 @@ class DeviceAta extends EventEmitter {
1108
1109
  .updateCharacteristic(Characteristic.LockPhysicalControls, obj.lockPhysicalControl)
1109
1110
  .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit)
1110
1111
  .updateCharacteristic(Characteristic.CoolingThresholdTemperature, defaultCoolingSetTemperature);
1111
- const updateDefHeat = modelSupportsHeat ? this.melCloudService?.updateCharacteristic(Characteristic.HeatingThresholdTemperature, defaultHeatingSetTemperature) : false;
1112
- const updateRS = modelSupportsFanSpeed ? this.melCloudService?.updateCharacteristic(Characteristic.RotationSpeed, obj.fanSpeed) : false;
1113
- const updateSM = swingFunction ? this.melCloudService?.updateCharacteristic(Characteristic.SwingMode, obj.swingMode) : false;
1112
+ if (modelSupportsHeat) this.melCloudService?.updateCharacteristic(Characteristic.HeatingThresholdTemperature, defaultHeatingSetTemperature);
1113
+ if (modelSupportsFanSpeed) this.melCloudService?.updateCharacteristic(Characteristic.RotationSpeed, obj.fanSpeed);
1114
+ if (swingFunction) this.melCloudService?.updateCharacteristic(Characteristic.SwingMode, obj.swingMode);
1114
1115
  break;
1115
1116
  case 2: //Thermostat
1116
1117
  switch (operationMode) {
@@ -1312,7 +1313,7 @@ class DeviceAta extends EventEmitter {
1312
1313
  //log current state
1313
1314
  if (this.logInfo) {
1314
1315
  this.emit('info', `Power: ${power ? 'ON' : 'OFF'}`);
1315
- this.emit('info', `Target operation mode: ${AirConditioner.DriveMode[operationMode]}`);
1316
+ this.emit('info', `Target operation mode: ${AirConditioner.OperationMode[operationMode]}`);
1316
1317
  this.emit('info', `Current operation mode: ${this.displayMode === 1 ? AirConditioner.CurrentOperationModeHeatherCooler[obj.currentOperationMode] : AirConditioner.CurrentOperationModeThermostat[obj.currentOperationMode]}`);
1317
1318
  this.emit('info', `Target temperature: ${setTemperature}${obj.temperatureUnit}`);
1318
1319
  this.emit('info', `Current temperature: ${roomTemperature}${obj.temperatureUnit}`);