homebridge-melcloud-control 4.4.1-beta.32 → 4.4.1-beta.34
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/homebridge-ui/public/index.html +225 -132
- package/package.json +2 -2
- package/src/melcloudata.js +1 -1
- package/src/melcloudatw.js +0 -1
- package/src/melclouderv.js +0 -1
|
@@ -204,19 +204,22 @@
|
|
|
204
204
|
}
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
function
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
for (let i =
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
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(
|
|
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,23 @@
|
|
|
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
|
-
//
|
|
248
|
-
const newInMelCloud = {
|
|
252
|
+
// Prepare MELCloud data
|
|
253
|
+
const newInMelCloud = {
|
|
254
|
+
ata: [], ataPresets: [], ataSchedules: [],
|
|
255
|
+
atw: [], atwPresets: [], atwSchedules: [],
|
|
256
|
+
erv: [], ervPresets: [], ervSchedules: [],
|
|
257
|
+
scenes: []
|
|
258
|
+
};
|
|
259
|
+
|
|
249
260
|
const devicesInMelCloudByType = { ata: [], atw: [], erv: [] };
|
|
250
|
-
const scenesInMelCloud = response.Scenes ?? []
|
|
261
|
+
const scenesInMelCloud = response.Scenes ?? [];
|
|
251
262
|
|
|
252
263
|
response.Devices.forEach(device => {
|
|
253
264
|
if (device.Type === 0) devicesInMelCloudByType.ata.push(device);
|
|
@@ -259,140 +270,222 @@
|
|
|
259
270
|
account.atwDevices = (account.atwDevices ?? []).filter(d => String(d.id) !== '0');
|
|
260
271
|
account.ervDevices = (account.ervDevices ?? []).filter(d => String(d.id) !== '0');
|
|
261
272
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
273
|
+
// ================================
|
|
274
|
+
// Remove stale DEVICES
|
|
275
|
+
// ================================
|
|
276
|
+
const removedAta = removeStaleEntities(
|
|
277
|
+
account.ataDevices,
|
|
278
|
+
devicesInMelCloudByType.ata,
|
|
279
|
+
d => d.id,
|
|
280
|
+
d => d.DeviceID
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const removedAtw = removeStaleEntities(
|
|
284
|
+
account.atwDevices,
|
|
285
|
+
devicesInMelCloudByType.atw,
|
|
286
|
+
d => d.id,
|
|
287
|
+
d => d.DeviceID
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const removedErv = removeStaleEntities(
|
|
291
|
+
account.ervDevices,
|
|
292
|
+
devicesInMelCloudByType.erv,
|
|
293
|
+
d => d.id,
|
|
294
|
+
d => d.DeviceID
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
// Remove stale SCENES (global)
|
|
298
|
+
account.scenes = account.scenes ?? [];
|
|
299
|
+
|
|
300
|
+
const removedScenes = removeStaleEntities(
|
|
301
|
+
account.scenes,
|
|
302
|
+
scenesInMelCloud,
|
|
303
|
+
s => s.id,
|
|
304
|
+
s => s.Id
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
removedScenes.forEach(s =>
|
|
308
|
+
newInMelCloud.scenes.push({ ...s, removed: true })
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Handle Devices
|
|
312
|
+
const handleDevices = (
|
|
313
|
+
devicesInMelCloud,
|
|
314
|
+
devicesInConfig,
|
|
315
|
+
deviceTypeString,
|
|
316
|
+
newDevices,
|
|
317
|
+
newPresets,
|
|
318
|
+
newSchedules
|
|
319
|
+
) => {
|
|
320
|
+
const configDevicesMap = new Map(
|
|
321
|
+
devicesInConfig.map(dev => [String(dev.id), dev])
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
devicesInMelCloud.forEach(device => {
|
|
325
|
+
const deviceId = String(device.DeviceID);
|
|
326
|
+
let deviceInConfig = configDevicesMap.get(deviceId);
|
|
327
|
+
|
|
328
|
+
// === Create missing device ===
|
|
329
|
+
if (!deviceInConfig) {
|
|
330
|
+
deviceInConfig = {
|
|
331
|
+
id: deviceId,
|
|
332
|
+
type: device.Type,
|
|
333
|
+
deviceTypeString,
|
|
334
|
+
displayType: 0,
|
|
335
|
+
name: device.DeviceName
|
|
336
|
+
};
|
|
337
|
+
devicesInConfig.push(deviceInConfig);
|
|
338
|
+
newDevices.push(deviceInConfig);
|
|
339
|
+
configDevicesMap.set(deviceId, deviceInConfig);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// PRESETS (melcloud)
|
|
343
|
+
if (account.type === 'melcloud') {
|
|
344
|
+
deviceInConfig.presets = deviceInConfig.presets ?? [];
|
|
345
|
+
const presetsInMelCloud = device.Presets || [];
|
|
346
|
+
|
|
347
|
+
const removedPresets = removeStaleEntities(
|
|
348
|
+
deviceInConfig.presets,
|
|
349
|
+
presetsInMelCloud,
|
|
350
|
+
p => p.id,
|
|
351
|
+
p => p.ID
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
removedPresets.forEach(p =>
|
|
355
|
+
newPresets.push({ ...p, removed: true })
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
const presetIds = new Set(
|
|
359
|
+
deviceInConfig.presets.map(p => String(p.id))
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
presetsInMelCloud.forEach((preset, index) => {
|
|
363
|
+
const presetId = String(preset.ID);
|
|
364
|
+
if (!presetIds.has(presetId)) {
|
|
365
|
+
const presetObj = {
|
|
366
|
+
id: presetId,
|
|
367
|
+
displayType: 0,
|
|
368
|
+
name: preset.NumberDescription || `Preset ${index}`,
|
|
369
|
+
namePrefix: false
|
|
370
|
+
};
|
|
371
|
+
deviceInConfig.presets.push(presetObj);
|
|
372
|
+
newPresets.push(presetObj);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// SCHEDULES (melcloudhome)
|
|
378
|
+
if (account.type === 'melcloudhome') {
|
|
379
|
+
deviceInConfig.schedules = deviceInConfig.schedules ?? [];
|
|
380
|
+
const schedulesInMelCloud = device.Schedule || [];
|
|
381
|
+
|
|
382
|
+
const removedSchedules = removeStaleEntities(
|
|
383
|
+
deviceInConfig.schedules,
|
|
384
|
+
schedulesInMelCloud,
|
|
385
|
+
s => s.id,
|
|
386
|
+
s => s.Id
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
removedSchedules.forEach(s =>
|
|
390
|
+
newSchedules.push({ ...s, removed: true })
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const scheduleIds = new Set(
|
|
394
|
+
deviceInConfig.schedules.map(s => String(s.id))
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
schedulesInMelCloud.forEach((schedule, index) => {
|
|
398
|
+
const scheduleId = String(schedule.Id);
|
|
399
|
+
if (!scheduleIds.has(scheduleId)) {
|
|
400
|
+
const scheduleObj = {
|
|
401
|
+
id: scheduleId,
|
|
402
|
+
displayType: 0,
|
|
403
|
+
name: `Schedule ${index}`,
|
|
404
|
+
namePrefix: false
|
|
405
|
+
};
|
|
406
|
+
deviceInConfig.schedules.push(scheduleObj);
|
|
407
|
+
newSchedules.push(scheduleObj);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return devicesInConfig;
|
|
361
414
|
};
|
|
362
415
|
|
|
363
|
-
|
|
364
|
-
account.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
416
|
+
// Execute device handlers
|
|
417
|
+
account.ataDevices = handleDevices(
|
|
418
|
+
devicesInMelCloudByType.ata,
|
|
419
|
+
account.ataDevices,
|
|
420
|
+
"Air Conditioner",
|
|
421
|
+
newInMelCloud.ata,
|
|
422
|
+
newInMelCloud.ataPresets,
|
|
423
|
+
newInMelCloud.ataSchedules
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
account.atwDevices = handleDevices(
|
|
427
|
+
devicesInMelCloudByType.atw,
|
|
428
|
+
account.atwDevices,
|
|
429
|
+
"Heat Pump",
|
|
430
|
+
newInMelCloud.atw,
|
|
431
|
+
newInMelCloud.atwPresets,
|
|
432
|
+
newInMelCloud.atwSchedules
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
account.ervDevices = handleDevices(
|
|
436
|
+
devicesInMelCloudByType.erv,
|
|
437
|
+
account.ervDevices,
|
|
438
|
+
"Energy Recovery Ventilation",
|
|
439
|
+
newInMelCloud.erv,
|
|
440
|
+
newInMelCloud.ervPresets,
|
|
441
|
+
newInMelCloud.ervSchedules
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
// Summary
|
|
445
|
+
const newDevicesCount =
|
|
446
|
+
newInMelCloud.ata.length +
|
|
447
|
+
newInMelCloud.atw.length +
|
|
448
|
+
newInMelCloud.erv.length;
|
|
449
|
+
|
|
450
|
+
const newPresetsCount =
|
|
451
|
+
newInMelCloud.ataPresets.length +
|
|
452
|
+
newInMelCloud.atwPresets.length +
|
|
453
|
+
newInMelCloud.ervPresets.length;
|
|
454
|
+
|
|
455
|
+
const newSchedulesCount =
|
|
456
|
+
newInMelCloud.ataSchedules.length +
|
|
457
|
+
newInMelCloud.atwSchedules.length +
|
|
458
|
+
newInMelCloud.ervSchedules.length;
|
|
459
|
+
|
|
460
|
+
const removedDevicesCount =
|
|
461
|
+
removedAta.length +
|
|
462
|
+
removedAtw.length +
|
|
463
|
+
removedErv.length;
|
|
464
|
+
|
|
465
|
+
if (!newDevicesCount && !newPresetsCount && !newSchedulesCount && !removedDevicesCount) {
|
|
374
466
|
updateInfo('info', 'No changes detected.', 'white');
|
|
375
467
|
} else {
|
|
376
468
|
if (newDevicesCount)
|
|
377
|
-
updateInfo('info', `Found new devices: ${
|
|
469
|
+
updateInfo('info', `Found new devices: ${newDevicesCount}`, 'green');
|
|
378
470
|
if (newPresetsCount)
|
|
379
|
-
updateInfo('info1', `Found new presets: ${
|
|
380
|
-
if (
|
|
381
|
-
updateInfo('info1', `Found new
|
|
471
|
+
updateInfo('info1', `Found new presets: ${newPresetsCount}`, 'green');
|
|
472
|
+
if (newSchedulesCount)
|
|
473
|
+
updateInfo('info1', `Found new schedules: ${newSchedulesCount}`, 'green');
|
|
382
474
|
if (removedDevicesCount)
|
|
383
|
-
updateInfo('info2', `Removed devices: ${
|
|
475
|
+
updateInfo('info2', `Removed devices: ${removedDevicesCount}`, 'orange');
|
|
384
476
|
}
|
|
385
477
|
|
|
386
478
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
387
479
|
await homebridge.savePluginConfig(pluginConfig);
|
|
480
|
+
|
|
388
481
|
} catch (error) {
|
|
389
482
|
updateInfo('info', `Prepare config error ${JSON.stringify(error)}`, 'red');
|
|
390
|
-
document.getElementById('logIn').className = "btn btn-secondary";
|
|
391
483
|
} finally {
|
|
392
484
|
document.getElementById('logIn').className = "btn btn-secondary";
|
|
393
485
|
homebridge.hideSpinner();
|
|
394
486
|
}
|
|
395
487
|
});
|
|
396
488
|
|
|
489
|
+
|
|
397
490
|
})();
|
|
398
491
|
</script>
|
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.
|
|
4
|
+
"version": "4.4.1-beta.34",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"mqtt": "^5.14.1",
|
|
40
40
|
"axios": "^1.13.2",
|
|
41
41
|
"express": "^5.2.1",
|
|
42
|
-
"puppeteer": "^24.33.
|
|
42
|
+
"puppeteer": "^24.33.1",
|
|
43
43
|
"ws": "^8.18.3"
|
|
44
44
|
},
|
|
45
45
|
"keywords": [
|
package/src/melcloudata.js
CHANGED
|
@@ -72,7 +72,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
if (this.logDebug) this.emit('debug', `WS update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
|
|
76
75
|
updateState = true;
|
|
77
76
|
break;
|
|
78
77
|
case 'ataUnitFrostProtectionTriggered':
|
|
@@ -86,6 +85,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
86
85
|
deviceData.Device[key] = value;
|
|
87
86
|
}
|
|
88
87
|
}
|
|
88
|
+
|
|
89
89
|
updateState = true;
|
|
90
90
|
break;
|
|
91
91
|
case 'ataUnitOverheatProtectionTriggered':
|
package/src/melcloudatw.js
CHANGED
package/src/melclouderv.js
CHANGED