homebridge-melcloud-control 4.4.1-beta.33 → 4.4.1-beta.35

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.
@@ -204,19 +204,22 @@
204
204
  }
205
205
  });
206
206
 
207
- // Device Handling & Login Logic
208
- function removeStaleDevices(configDevices, melcloudDevices) {
209
- const melcloudIds = melcloudDevices.map(d => d.DeviceID);
210
- const removedDevices = [];
211
-
212
- for (let i = configDevices.length - 1; i >= 0; i--) {
213
- const device = configDevices[i];
214
- if (!melcloudIds.includes(device.id)) {
215
- removedDevices.push(device);
216
- configDevices.splice(i, 1);
207
+ // Generic remove function
208
+ function removeStaleEntities(configList, serverList, getConfigId, getServerId) {
209
+ const serverIds = new Set(serverList.map(item => String(getServerId(item))));
210
+ const removedEntities = [];
211
+
212
+ for (let i = configList.length - 1; i >= 0; i--) {
213
+ const entity = configList[i];
214
+ const entityId = String(getConfigId(entity));
215
+
216
+ if (!serverIds.has(entityId)) {
217
+ removedEntities.push(entity);
218
+ configList.splice(i, 1);
217
219
  }
218
220
  }
219
- return removedDevices;
221
+
222
+ return removedEntities;
220
223
  }
221
224
 
222
225
  function updateInfo(id, text, color) {
@@ -227,10 +230,11 @@
227
230
  }
228
231
  }
229
232
 
233
+ // Login & Sync Logic
230
234
  document.getElementById('logIn').addEventListener('click', async () => {
231
235
  homebridge.showSpinner();
232
236
 
233
- document.getElementById(`logIn`).className = "btn btn-primary";
237
+ document.getElementById('logIn').className = "btn btn-primary";
234
238
  updateInfo('info', '', 'white');
235
239
  updateInfo('info1', '', 'white');
236
240
  updateInfo('info2', '', 'white');
@@ -238,16 +242,17 @@
238
242
  try {
239
243
  const account = this.account;
240
244
  const response = await homebridge.request('/connect', account);
245
+
241
246
  if (!response.State) {
242
247
  homebridge.hideSpinner();
243
- updateInfo('info', response.Info);
248
+ updateInfo('info', response.Info, 'red');
244
249
  return;
245
250
  }
246
251
 
247
- // Initialize devices arrays
252
+ // Prepare MELCloud data
248
253
  const newInMelCloud = { ata: [], ataPresets: [], ataSchedules: [], atw: [], atwPresets: [], atwSchedules: [], erv: [], ervPresets: [], ervSchedules: [], scenes: [] };
249
254
  const devicesInMelCloudByType = { ata: [], atw: [], erv: [] };
250
- const scenesInMelCloud = response.Scenes ?? []
255
+ const scenesInMelCloud = response.Scenes ?? [];
251
256
 
252
257
  response.Devices.forEach(device => {
253
258
  if (device.Type === 0) devicesInMelCloudByType.ata.push(device);
@@ -259,135 +264,116 @@
259
264
  account.atwDevices = (account.atwDevices ?? []).filter(d => String(d.id) !== '0');
260
265
  account.ervDevices = (account.ervDevices ?? []).filter(d => String(d.id) !== '0');
261
266
 
262
- const removedAta = removeStaleDevices(account.ataDevices, devicesInMelCloudByType.ata);
263
- const removedAtw = removeStaleDevices(account.atwDevices, devicesInMelCloudByType.atw);
264
- const removedErv = removeStaleDevices(account.ervDevices, devicesInMelCloudByType.erv);
265
-
266
- const handleDevices = (devicesInMelCloud, devicesInConfig, deviceTypeString, newDevices, newPresets, newSchedules, newScenes) => {
267
- try {
268
- const configDevicesMap = new Map(devicesInConfig.map(dev => [String(dev.id), dev]));
269
-
270
- devicesInMelCloud.forEach(device => {
271
- const deviceId = String(device.DeviceID);
272
- let deviceInConfig = configDevicesMap.get(deviceId);
273
-
274
- // === Create device if missing ===
275
- if (!deviceInConfig) {
276
- deviceInConfig = {
277
- id: deviceId,
278
- type: device.Type,
279
- deviceTypeString,
280
- displayType: 0,
281
- name: device.DeviceName
282
- };
283
- devicesInConfig.push(deviceInConfig);
284
- newDevices.push(deviceInConfig);
285
- configDevicesMap.set(deviceId, deviceInConfig);
286
- }
287
-
288
- //only for melcloud
289
- if (account.type === 'melcloud') {
290
-
291
- // === Process presets ===
292
- const presetsInMelCloud = device.Presets || [];
293
- const presetsInConfig = (deviceInConfig.presets ?? []).filter(p => String(p.id) !== '0');
294
- const presetIds = new Set(presetsInConfig.map(p => String(p.id)));
295
-
296
- presetsInMelCloud.forEach((preset, index) => {
297
- const presetId = String(preset.ID);
298
- if (!presetIds.has(presetId)) {
299
- const presetObj = {
300
- id: presetId,
301
- displayType: 0,
302
- name: preset.NumberDescription || `Preset ${index}`,
303
- namePrefix: false
304
- };
305
- presetsInConfig.push(presetObj);
306
- newPresets.push(presetObj);
307
- presetIds.add(presetId);
308
- }
309
- });
310
- }
311
-
312
- //only for melcloudhome
313
- if (account.type === 'melcloudhome') {
314
-
315
- // === Process schedules ===
316
- const schedulesInMelCloud = device.Schedule || [];
317
- const schedulesInConfig = (deviceInConfig.schedules ?? []).filter(s => String(s.id) !== '0');
318
- const scheduleIds = new Set(schedulesInConfig.map(s => String(s.id)));
319
-
320
- schedulesInMelCloud.forEach((schedule, index) => {
321
- const scheduleId = String(schedule.Id);
322
- if (!scheduleIds.has(scheduleId)) {
323
- const scheduleObj = {
324
- id: scheduleId,
325
- displayType: 0,
326
- name: `Schedule ${index}`,
327
- namePrefix: false
328
- };
329
- schedulesInConfig.push(scheduleObj);
330
- newSchedules.push(scheduleObj);
331
- scheduleIds.add(scheduleId);
332
- }
333
- });
334
-
335
- // === Process scenes ===
336
- const scenesInConfig = (deviceInConfig.scenes ?? []).filter(s => String(s.id) !== '0');
337
- const sceneIds = new Set(scenesInConfig.map(s => String(s.id)));
338
-
339
- scenesInMelCloud.forEach((scene, index) => {
340
- const sceneId = String(scene.Id);
341
- if (!sceneIds.has(sceneId)) {
342
- const sceneObj = {
343
- id: sceneId,
344
- displayType: 0,
345
- name: scene.Name || `Scene ${index}`,
346
- namePrefix: false
347
- };
348
- scenesInConfig.push(sceneObj);
349
- newScenes.push(sceneObj);
350
- sceneIds.add(sceneId);
351
- }
352
- });
353
- }
354
- });
355
-
356
- // Return filtered devicesInConfig to make sure upstream code uses it
357
- return devicesInConfig;
358
- } catch (error) {
359
- updateInfo('info', `Error while processing device: ${JSON.stringify(error)}`, 'red');
360
- }
267
+ // Remove stale DEVICES
268
+ const removedAta = removeStaleEntities(account.ataDevices, devicesInMelCloudByType.ata, d => d.id, d => d.DeviceID);
269
+ const removedAtw = removeStaleEntities(account.atwDevices, devicesInMelCloudByType.atw, d => d.id, d => d.DeviceID);
270
+ const removedErv = removeStaleEntities(account.ervDevices, devicesInMelCloudByType.erv, d => d.id, d => d.DeviceID);
271
+
272
+ // Remove stale SCENES (global)
273
+ account.scenes = account.scenes ?? [];
274
+ removeStaleEntities(account.scenes, scenesInMelCloud, s => s.id, s => s.Id);
275
+
276
+ // Handle Devices
277
+ const handleDevices = (devicesInMelCloud, devicesInConfig, deviceTypeString, newDevices, newPresets, newSchedules) => {
278
+ const configDevicesMap = new Map(devicesInConfig.map(dev => [String(dev.id), dev]));
279
+
280
+ devicesInMelCloud.forEach(device => {
281
+ const deviceId = String(device.DeviceID);
282
+ let deviceInConfig = configDevicesMap.get(deviceId);
283
+
284
+ // === Create missing device ===
285
+ if (!deviceInConfig) {
286
+ deviceInConfig = {
287
+ id: deviceId,
288
+ type: device.Type,
289
+ deviceTypeString,
290
+ displayType: 0,
291
+ name: device.DeviceName
292
+ };
293
+ devicesInConfig.push(deviceInConfig);
294
+ newDevices.push(deviceInConfig);
295
+ configDevicesMap.set(deviceId, deviceInConfig);
296
+ }
297
+
298
+ // PRESETS (melcloud)
299
+ if (account.type === 'melcloud') {
300
+ deviceInConfig.presets = deviceInConfig.presets ?? [];
301
+
302
+ const presetsInMelCloud = device.Presets || [];
303
+ removeStaleEntities(deviceInConfig.presets, presetsInMelCloud, p => p.id, p => p.ID);
304
+
305
+ const presetIds = new Set(deviceInConfig.presets.map(p => String(p.id)));
306
+ presetsInMelCloud.forEach((preset, index) => {
307
+ const presetId = String(preset.ID);
308
+ if (!presetIds.has(presetId)) {
309
+ const presetObj = {
310
+ id: presetId,
311
+ displayType: 0,
312
+ name: preset.NumberDescription || `Preset ${index}`,
313
+ namePrefix: false
314
+ };
315
+ deviceInConfig.presets.push(presetObj);
316
+ newPresets.push(presetObj);
317
+ }
318
+ });
319
+ }
320
+
321
+ // SCHEDULES (melcloudhome)
322
+ if (account.type === 'melcloudhome') {
323
+ deviceInConfig.schedules = deviceInConfig.schedules ?? [];
324
+
325
+ const schedulesInMelCloud = device.Schedule || [];
326
+ removeStaleEntities(deviceInConfig.schedules, schedulesInMelCloud, s => s.id, s => s.Id);
327
+
328
+ const scheduleIds = new Set(deviceInConfig.schedules.map(s => String(s.id)));
329
+ schedulesInMelCloud.forEach((schedule, index) => {
330
+ const scheduleId = String(schedule.Id);
331
+ if (!scheduleIds.has(scheduleId)) {
332
+ const scheduleObj = {
333
+ id: scheduleId,
334
+ displayType: 0,
335
+ name: `Schedule ${index}`,
336
+ namePrefix: false
337
+ };
338
+ deviceInConfig.schedules.push(scheduleObj);
339
+ newSchedules.push(scheduleObj);
340
+ }
341
+ });
342
+ }
343
+ });
344
+
345
+ return devicesInConfig;
361
346
  };
362
347
 
363
- account.ataDevices = handleDevices(devicesInMelCloudByType.ata, account.ataDevices, "Air Conditioner", newInMelCloud.ata, newInMelCloud.ataPresets, newInMelCloud.ataSchedules, newInMelCloud.scenes);
364
- account.atwDevices = handleDevices(devicesInMelCloudByType.atw, account.atwDevices, "Heat Pump", newInMelCloud.atw, newInMelCloud.atwPresets, newInMelCloud.atwSchedules, newInMelCloud.scenes);
365
- account.ervDevices = handleDevices(devicesInMelCloudByType.erv, account.ervDevices, "Energy Recovery Ventilation", newInMelCloud.erv, newInMelCloud.ervPresets, newInMelCloud.ervSchedules, newInMelCloud.scenes);
348
+ // Execute device handlers
349
+ account.ataDevices = handleDevices(devicesInMelCloudByType.ata, account.ataDevices, "Air Conditioner", newInMelCloud.ata, newInMelCloud.ataPresets, newInMelCloud.ataSchedules);
350
+ account.atwDevices = handleDevices(devicesInMelCloudByType.atw, account.atwDevices, "Heat Pump", newInMelCloud.atw, newInMelCloud.atwPresets, newInMelCloud.atwSchedules);
351
+ account.ervDevices = handleDevices(devicesInMelCloudByType.erv, account.ervDevices, "Energy Recovery Ventilation", newInMelCloud.erv, newInMelCloud.ervPresets, newInMelCloud.ervSchedules);
366
352
 
353
+ // Summary
367
354
  const newDevicesCount = newInMelCloud.ata.length + newInMelCloud.atw.length + newInMelCloud.erv.length;
368
355
  const newPresetsCount = newInMelCloud.ataPresets.length + newInMelCloud.atwPresets.length + newInMelCloud.ervPresets.length;
369
356
  const newSchedulesCount = newInMelCloud.ataSchedules.length + newInMelCloud.atwSchedules.length + newInMelCloud.ervSchedules.length;
370
- const newScenesCount = newInMelCloud.scenes.length;
371
357
  const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
372
358
 
373
- if (!newDevicesCount && !newPresetsCount && !newSchedulesCount && !newScenesCount && !removedDevicesCount) {
359
+ if (!newDevicesCount && !newPresetsCount && !newSchedulesCount && !removedDevicesCount) {
374
360
  updateInfo('info', 'No changes detected.', 'white');
375
361
  } else {
376
362
  if (newDevicesCount)
377
- updateInfo('info', `Found new devices: ${newInMelCloud.ata.length ? `ATA: ${newInMelCloud.ata.length},` : ''} ${newInMelCloud.atw.length ? `ATW: ${newInMelCloud.atw.length},` : ''} ${newInMelCloud.erv.length ? `ERV: ${newInMelCloud.erv.length},` : ''}.`, 'green');
363
+ updateInfo('info', `Found new devices: ${newDevicesCount}`, 'green');
378
364
  if (newPresetsCount)
379
- updateInfo('info1', `Found new presets: ${newInMelCloud.ataPresets.length ? `ATA: ${newInMelCloud.ataPresets.length},` : ''} ${newInMelCloud.atwPresets.length ? `ATW: ${newInMelCloud.atwPresets.length},` : ''} ${newInMelCloud.ervPresets.length ? `ERV: ${newInMelCloud.ervPresets.length}` : ''}.`, 'green');
380
- if (newScenesCount || newSchedulesCount)
381
- updateInfo('info1', `Found new ${newSchedulesCount ? `schedules:` : ''} ${newInMelCloud.ataSchedules.length ? `ATA: ${newInMelCloud.ataSchedules.length},` : ''} ${newInMelCloud.atwSchedules.length ? `ATW: ${newInMelCloud.atwSchedules.length},` : ''} ${newInMelCloud.ervSchedules.length ? `ERV: ${newInMelCloud.ervSchedules.length},` : ''} ${newScenesCount ? `scenes: ${newScenesCount}` : ''}.`, 'green');
365
+ updateInfo('info1', `Found new presets: ${newPresetsCount}`, 'green');
366
+ if (newSchedulesCount)
367
+ updateInfo('info1', `Found new schedules: ${newSchedulesCount}`, 'green');
382
368
  if (removedDevicesCount)
383
- updateInfo('info2', `Removed devices: ${removedAta.length ? `ATA: ${removedAta.length},` : ''} ${removedAtw.length ? `ATW: ${removedAtw.length},` : ''} ${removedErv.length ? `ERV: ${removedErv.length}` : ''}.`, 'orange');
369
+ updateInfo('info2', `Removed devices: ${removedDevicesCount}`, 'orange');
384
370
  }
385
371
 
386
372
  await homebridge.updatePluginConfig(pluginConfig);
387
373
  await homebridge.savePluginConfig(pluginConfig);
374
+
388
375
  } catch (error) {
389
376
  updateInfo('info', `Prepare config error ${JSON.stringify(error)}`, 'red');
390
- document.getElementById('logIn').className = "btn btn-secondary";
391
377
  } finally {
392
378
  document.getElementById('logIn').className = "btn btn-secondary";
393
379
  homebridge.hideSpinner();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.4.1-beta.33",
4
+ "version": "4.4.1-beta.35",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",