homebridge-melcloud-control 4.2.3-beta.8 → 4.2.4

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.
@@ -246,10 +246,11 @@
246
246
  }
247
247
 
248
248
  // Initialize devices arrays
249
- const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
249
+ const newDevices = { ata: [], ataPresets: [], ataSchedules: [], atw: [], atwPresets: [], atwSchedules: [], erv: [], ervPresets: [], ervSchedules: [], scenes: [] };
250
250
  const devicesByType = { ata: [], atw: [], erv: [] };
251
251
 
252
252
  response.Devices.forEach(d => {
253
+ d.Scenes = response.Scenes ?? [];
253
254
  if (d.Type === 0) devicesByType.ata.push(d);
254
255
  if (d.Type === 1) devicesByType.atw.push(d);
255
256
  if (d.Type === 3) devicesByType.erv.push(d);
@@ -263,7 +264,7 @@
263
264
  const removedAtw = removeStaleDevices(account.atwDevices, devicesByType.atw);
264
265
  const removedErv = removeStaleDevices(account.ervDevices, devicesByType.erv);
265
266
 
266
- const handleDevices = (devicesInMelCloud, devicesInConfig, typeString, newArr, newPresets) => {
267
+ const handleDevices = (devicesInMelCloud, devicesInConfig, typeString, newArr, newPresets, newSchedules, newScenes) => {
267
268
  try {
268
269
  const configDevicesMap = new Map(devicesInConfig.map(dev => [String(dev.id), dev]));
269
270
  const isMelcloud = account.type === 'melcloud';
@@ -286,6 +287,7 @@
286
287
  name: device.DeviceName,
287
288
  presets: [],
288
289
  schedules: [],
290
+ scenes: [],
289
291
  buttonsSensors: []
290
292
  };
291
293
  devicesInConfig.push(deviceInConfig);
@@ -294,19 +296,18 @@
294
296
  }
295
297
 
296
298
  // === Process presets/schedules ===
297
- const presets = device[typeKey] || [];
298
- const presetsInConfig = deviceInConfig[typeKey1] || [];
299
+ const presets = device.Presets || [];
300
+ const presetsInConfig = deviceInConfig.presets || [];
299
301
  const presetIds = new Set(presetsInConfig.map(p => String(p.id)));
300
302
 
301
303
  let addedNewPreset = false;
302
-
303
304
  presets.forEach((preset, index) => {
304
- const presetId = String(preset[idKey]);
305
+ const presetId = String(preset.ID);
305
306
  if (!presetIds.has(presetId)) {
306
307
  const presetObj = {
307
308
  id: presetId,
308
309
  displayType: 0,
309
- name: preset.NumberDescription || `Schedule ${index}`,
310
+ name: preset.NumberDescription || `Preset ${index}`,
310
311
  namePrefix: false
311
312
  };
312
313
  presetsInConfig.push(presetObj);
@@ -319,13 +320,80 @@
319
320
  // === Remove placeholder presets/schedules (id === '0') if new ones were added ===
320
321
  if (addedNewPreset) {
321
322
  const beforeCount = presetsInConfig.length;
322
- deviceInConfig[typeKey1] = presetsInConfig.filter(p => String(p.id) !== '0');
323
- const removedCount = beforeCount - deviceInConfig[typeKey1].length;
323
+ deviceInConfig.presets = presetsInConfig.filter(p => String(p.id) !== '0');
324
+ const removedCount = beforeCount - deviceInConfig.presets.length;
324
325
 
325
326
  if (removedCount > 0 && removedCount < beforeCount) {
326
- updateInfo('info2', `Removed ${removedCount} placeholder ${typeKey1} from device ${device.DeviceID}`, 'yellow');
327
+ updateInfo('info2', `Removed ${removedCount} placeholder preset from device ${device.DeviceID}`, 'yellow');
327
328
  }
328
329
  }
330
+
331
+ // === Process presets/schedules ===
332
+ const schedules = device.Schedules || [];
333
+ const schedulesInConfig = deviceInConfig.schedules || [];
334
+ const scheduleIds = new Set(schedulesInConfig.map(s => String(s.id)));
335
+
336
+ let addedNewSchedule = false;
337
+ schedules.forEach((schedule, index) => {
338
+ const scheduleId = String(schedule.Id);
339
+ if (!scheduleIds.has(scheduleId)) {
340
+ const presetObj = {
341
+ id: scheduleId,
342
+ displayType: 0,
343
+ name: `Schedule ${index}`,
344
+ namePrefix: false
345
+ };
346
+ schedulesInConfig.push(presetObj);
347
+ newPresets.push(presetObj);
348
+ scheduleIds.add(scheduleId);
349
+ addedNewSchedule = true;
350
+ }
351
+ });
352
+
353
+ // === Remove placeholder schedules (id === '0') if new ones were added ===
354
+ if (addedNewSchedule) {
355
+ const beforeCount = schedulesInConfig.length;
356
+ deviceInConfig.schedules = schedulesInConfig.filter(s => String(s.id) !== '0');
357
+ const removedCount = beforeCount - deviceInConfig.schedules.length;
358
+
359
+ if (removedCount > 0 && removedCount < beforeCount) {
360
+ updateInfo('info2', `Removed ${removedCount} placeholder schedule from device ${device.DeviceID}`, 'yellow');
361
+ }
362
+ }
363
+
364
+ // === Process scenes ===
365
+ const scenes = device.Scenes || [];
366
+ const scenesInConfig = deviceInConfig.scenes || [];
367
+ const sceneIds = new Set(scenesInConfig.map(s => String(s.id)));
368
+
369
+ let addedNewScenes = false;
370
+ scenes.forEach((scene, index) => {
371
+ const sceneId = String(scene.Id);
372
+ if (!sceneIds.has(sceneId)) {
373
+ const sceneObj = {
374
+ id: sceneId,
375
+ displayType: 0,
376
+ name: scene.Name || `Scene ${index}`,
377
+ namePrefix: false
378
+ };
379
+ scenesInConfig.push(sceneObj);
380
+ newScenes.push(sceneObj);
381
+ sceneIds.add(sceneId);
382
+ addedNewScenes = true;
383
+ }
384
+ });
385
+
386
+ // === Remove placeholder scenes (id === '0') if new ones were added ===
387
+ if (addedNewScenes) {
388
+ const beforeCount = scenesInConfig.length;
389
+ deviceInConfig.scenes = scenesInConfig.filter(s => String(s.id) !== '0');
390
+ const removedCount = beforeCount - deviceInConfig.scenes.length;
391
+
392
+ if (removedCount > 0 && removedCount < beforeCount) {
393
+ updateInfo('info2', `Removed ${removedCount} placeholder scene from device ${device.DeviceID}`, 'yellow');
394
+ }
395
+ }
396
+
329
397
  });
330
398
 
331
399
  // Return filtered devicesInConfig to make sure upstream code uses it
@@ -336,21 +404,23 @@
336
404
  }
337
405
  };
338
406
 
339
- account.ataDevices = handleDevices(devicesByType.ata, account.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
340
- account.atwDevices = handleDevices(devicesByType.atw, account.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
341
- account.ervDevices = handleDevices(devicesByType.erv, account.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets);
407
+ account.ataDevices = handleDevices(devicesByType.ata, account.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets, newDevices.ataSchedules, newDevices.scenes);
408
+ account.atwDevices = handleDevices(devicesByType.atw, account.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets, newDevices.atwSchedules, newDevices.scenes);
409
+ account.ervDevices = handleDevices(devicesByType.erv, account.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets, newDevices.ervSchedules, newDevices.scenes);
342
410
 
343
411
  const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
344
412
  const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
413
+ const newSchedulesCount = newDevices.ataSchedules.length + newDevices.atwSchedules.length + newDevices.ervSchedules.length;
414
+ const newScenesCount = newDevices.scenes.length;
345
415
  const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
346
416
 
347
- if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
417
+ if (!newDevicesCount && !newPresetsCount && !newSchedulesCount && !newScenesCount && !removedDevicesCount) {
348
418
  updateInfo('info', 'No changes detected.', 'white');
349
419
  } else {
350
420
  if (newDevicesCount)
351
421
  updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
352
- if (newPresetsCount)
353
- updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
422
+ if (newPresetsCount || newScenesCount || newSchedulesCount)
423
+ updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${account.type === 'melcloud' ? newDevices.ataPresets.length : newDevices.ataSchedules.length}, ATW: ${account.type === 'melcloud' ? newDevices.atwPresets.length : newDevices.atwSchedules.length}, ERV: ${account.type === 'melcloud' ? newDevices.ervPresets.length : newDevices.ervSchedules.length}, Scenes: ${newDevices.scenes.length}.`, 'green');
354
424
  if (removedDevicesCount)
355
425
  updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
356
426
  }
package/index.js CHANGED
@@ -183,8 +183,8 @@ class MelCloudPlatform {
183
183
 
184
184
  configuredDevice.on('melCloud', async (key, value) => {
185
185
  try {
186
- accountInfo.LoginData[key] = value;
187
- await melCloud.send(accountInfo);
186
+ const accountDate = account.type === 'melcloud' ? accountInfo.LoginData[key] = value : accountInfo[key];
187
+ await melCloud.send(accountDate);
188
188
  } catch (error) {
189
189
  if (logLevel.error) log.error(`${accountName}, ${deviceTypeText}, ${deviceName}, ${error.message ?? error}.`);
190
190
  }
@@ -204,7 +204,7 @@ class MelCloudPlatform {
204
204
  //start impulse generators\
205
205
  const timmers = accountType === 'melcloudhome' ? [{ name: 'connect', sampling: 1800000 }, { name: 'checkDevicesList', sampling: deviceRefreshInterval }] : [{ name: 'checkDevicesList', sampling: refreshInterval }];
206
206
  await melCloud.impulseGenerator.state(true, timmers, false);
207
- await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval + 500 }]);
207
+ await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
208
208
 
209
209
  //stop impulse generator
210
210
  await impulseGenerator.state(false);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.2.3-beta.8",
4
+ "version": "4.2.4",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -35,7 +35,7 @@
35
35
  "node": "^20 || ^22 || ^24 || ^25"
36
36
  },
37
37
  "dependencies": {
38
- "@homebridge/plugin-ui-utils": "^2.1.0",
38
+ "@homebridge/plugin-ui-utils": "^2.1.1",
39
39
  "async-mqtt": "^2.6.3",
40
40
  "axios": "^1.13.2",
41
41
  "express": "^5.1.0",
package/src/constants.js CHANGED
@@ -20,14 +20,28 @@ export const ApiUrls = {
20
20
  export const ApiUrlsHome = {
21
21
  BaseURL: "https://melcloudhome.com",
22
22
  GetUserContext: "/api/user/context",
23
- SetAta: "/api/ataunit/deviceid",
24
- SetAtw: "/api/atwunit/deviceid",
25
- SetErv: "/api/ervunit/deviceid",
26
- PutScheduleEnable: "/api/cloudschedule/deviceid/enabled", // PUT {"enabled":true}
23
+ GetUserScenes: "/api/user/scenes",
24
+ PutAta: "/api/ataunit/deviceid",
25
+ PutAtw: "/api/atwunit/deviceid",
26
+ PutErv: "/api/ervunit/deviceid",
27
27
  PostSchedule: " /api/cloudschedule/deviceid", // POST {"days":[2],"time":"17:59:00","enabled":true,"id":"53c5e804-0663-47d0-85c2-2d8ccd2573de","power":false,"operationMode":null,"setPoint":null,"vaneVerticalDirection":null,"vaneHorizontalDirection":null,"setFanSpeed":null}
28
28
  PostProtectionFrost: "/api/protection/frost", // POST {"enabled":true,"min":13,"max":16,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
29
29
  PostProtectionOverheat: "api/protection/overheat", // POST {"enabled":true,"min":32,"max":35,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
30
- PostHolidayMode: " /api/holidaymode" // POST {"enabled":true,"startDate":"2025-11-11T17:42:24.913","endDate":"2026-06-01T09:18:00","units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
30
+ PostHolidayMode: " /api/holidaymode", // POST {"enabled":true,"startDate":"2025-11-11T17:42:24.913","endDate":"2026-06-01T09:18:00","units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
31
+ PutScheduleEnabled: "/api/cloudschedule/deviceid/enabled", // PUT {"enabled":true}
32
+ PutScene: {
33
+ Enable: "/api/scene/sceneid/enable",
34
+ Disable: "/api/scene/sceneid/disable",
35
+ },
36
+ Referers: {
37
+ GetPutScenes: "https://melcloudhome.com/scenes",
38
+ PostHolidayMode: "https://melcloudhome.com/ata/deviceid/holidaymode",
39
+ PostProtectionFrost: "https://melcloudhome.com/ata/deviceid/frostprotection",
40
+ PostProtectionOverheat: "https://melcloudhome.com/ata/deviceid/overheatprotection",
41
+ PutDeviceSettings: "https://melcloudhome.com/dashboard",
42
+ PutScheduleEnabled: "https://melcloudhome.com/ata/deviceid/schedule",
43
+ },
44
+ Origin: "https://melcloudhome.com"
31
45
  };
32
46
 
33
47
  export const DeviceType = [
@@ -163,3 +177,32 @@ export const AccessLevel = {
163
177
  Quest: 3,
164
178
  Owner: 4
165
179
  };
180
+
181
+ export const LanguageLocaleMap = {
182
+ "0": "en-US,en;q=0.9",
183
+ "1": "bg-BG,bg;q=0.9",
184
+ "2": "cs-CZ,cs;q=0.9",
185
+ "3": "da-DK,da;q=0.9",
186
+ "4": "de-DE,de;q=0.9",
187
+ "5": "et-EE,et;q=0.9",
188
+ "6": "es-ES,es;q=0.9",
189
+ "7": "fr-FR,fr;q=0.9",
190
+ "8": "hy-AM,hy;q=0.9",
191
+ "9": "lv-LV,lv;q=0.9",
192
+ "10": "lt-LT,lt;q=0.9",
193
+ "11": "hu-HU,hu;q=0.9",
194
+ "12": "nl-NL,nl;q=0.9",
195
+ "13": "no-NO,no;q=0.9",
196
+ "14": "pl-PL,pl;q=0.9",
197
+ "15": "pt-PT,pt;q=0.9",
198
+ "16": "ru-RU,ru;q=0.9",
199
+ "17": "fi-FI,fi;q=0.9",
200
+ "18": "sv-SE,sv;q=0.9",
201
+ "19": "it-IT,it;q=0.9",
202
+ "20": "uk-UA,uk;q=0.9",
203
+ "21": "tr-TR,tr;q=0.9",
204
+ "22": "el-GR,el;q=0.9",
205
+ "23": "hr-HR,hr;q=0.9",
206
+ "24": "ro-RO,ro;q=0.9",
207
+ "25": "sl-SI,sl;q=0.9"
208
+ };