homebridge-melcloud-control 4.9.2-beta.8 → 4.10.0-beta.0
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/package.json +1 -1
- package/src/melcloudhome.js +112 -112
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
|
+
"version": "4.10.0-beta.0",
|
|
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/melcloudhome.js
CHANGED
|
@@ -424,9 +424,9 @@ class MelCloudHome extends EventEmitter {
|
|
|
424
424
|
// attachTokenInterceptors() dodaje interceptory tylko przy pierwszym wywołaniu.
|
|
425
425
|
this.ensureClient();
|
|
426
426
|
this.attachTokenInterceptors();
|
|
427
|
-
this.emit('client', this.client);
|
|
428
427
|
|
|
429
428
|
if (this.pluginStart) {
|
|
429
|
+
this.emit('client', this.client);
|
|
430
430
|
await this.connectSocket().catch(err => {
|
|
431
431
|
if (this.logError) this.emit('error', `Initial WebSocket connect failed: ${err.message}`);
|
|
432
432
|
});
|
|
@@ -439,117 +439,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
439
439
|
return connectInfo;
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
-
// ── Scenes & Devices ──────────────────────────────────────────────────────
|
|
443
|
-
|
|
444
|
-
async checkScenesList() {
|
|
445
|
-
try {
|
|
446
|
-
if (this.logDebug) this.emit('debug', 'Scanning for scenes');
|
|
447
|
-
|
|
448
|
-
const resp = await this.client.get(ApiUrls.Home.Get.Scenes);
|
|
449
|
-
const scenesList = resp.data;
|
|
450
|
-
|
|
451
|
-
if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
|
|
452
|
-
|
|
453
|
-
return this.capitalizeKeysDeep(scenesList);
|
|
454
|
-
} catch (error) {
|
|
455
|
-
throw new Error(`Check scenes list error: ${error.message}`);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
async checkDevicesList() {
|
|
460
|
-
try {
|
|
461
|
-
const result = { State: false, Status: null, Buildings: {}, Devices: [], Scenes: [] };
|
|
462
|
-
if (this.logDebug) this.emit('debug', 'Scanning for devices');
|
|
463
|
-
|
|
464
|
-
const resp = await this.client.get(ApiUrls.Home.Get.Context);
|
|
465
|
-
const userContext = resp.data;
|
|
466
|
-
//if (this.logDebug) this.emit('debug', `User Context: ${JSON.stringify(userContext, null, 2)}`);
|
|
467
|
-
|
|
468
|
-
const buildings = userContext.buildings ?? [];
|
|
469
|
-
const guestBuildings = userContext.guestBuildings ?? [];
|
|
470
|
-
const buildingsList = [...buildings, ...guestBuildings];
|
|
471
|
-
|
|
472
|
-
if (this.logDebug) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
|
|
473
|
-
|
|
474
|
-
if (buildingsList.length === 0) {
|
|
475
|
-
result.Status = 'No buildings found';
|
|
476
|
-
return result;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const capitalizeKeys = obj => Object.fromEntries(
|
|
480
|
-
Object.entries(obj).map(([k, v]) => [k.charAt(0).toUpperCase() + k.slice(1), v])
|
|
481
|
-
);
|
|
482
|
-
|
|
483
|
-
const createDevice = (device, type) => {
|
|
484
|
-
const settingsObject = Object.fromEntries(
|
|
485
|
-
(device.Settings || []).map(({ name, value }) => [
|
|
486
|
-
name.charAt(0).toUpperCase() + name.slice(1),
|
|
487
|
-
this.functions.convertValue(value),
|
|
488
|
-
])
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
const deviceObject = {
|
|
492
|
-
...capitalizeKeys(device.Capabilities || {}),
|
|
493
|
-
...settingsObject,
|
|
494
|
-
DeviceType: type,
|
|
495
|
-
FirmwareAppVersion: device.ConnectedInterfaceIdentifier,
|
|
496
|
-
IsConnected: device.IsConnected,
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
if (device.FrostProtection) device.FrostProtection = capitalizeKeys(device.FrostProtection);
|
|
500
|
-
if (device.OverheatProtection) device.OverheatProtection = capitalizeKeys(device.OverheatProtection);
|
|
501
|
-
if (device.HolidayMode) device.HolidayMode = capitalizeKeys(device.HolidayMode);
|
|
502
|
-
if (Array.isArray(device.Schedule)) device.Schedule = device.Schedule.map(s => this.capitalizeKeysDeep(s));
|
|
503
|
-
|
|
504
|
-
const { Settings, Capabilities, Id, GivenDisplayName, ...rest } = device;
|
|
505
|
-
|
|
506
|
-
return {
|
|
507
|
-
...rest,
|
|
508
|
-
Type: type,
|
|
509
|
-
DeviceID: Id,
|
|
510
|
-
DeviceName: GivenDisplayName,
|
|
511
|
-
SerialNumber: Id,
|
|
512
|
-
Device: deviceObject,
|
|
513
|
-
};
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
const devices = buildingsList.flatMap(building => [
|
|
517
|
-
...(building.airToAirUnits || []).map(d => createDevice(capitalizeKeys(d), 0)),
|
|
518
|
-
...(building.airToWaterUnits || []).map(d => createDevice(capitalizeKeys(d), 1)),
|
|
519
|
-
...(building.airToVentilationUnits || []).map(d => createDevice(capitalizeKeys(d), 3)),
|
|
520
|
-
]);
|
|
521
|
-
|
|
522
|
-
if (devices.length === 0) {
|
|
523
|
-
result.Status = 'No devices found';
|
|
524
|
-
return result;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Sceny
|
|
528
|
-
let scenes = [];
|
|
529
|
-
try {
|
|
530
|
-
scenes = await this.checkScenesList();
|
|
531
|
-
if (this.logDebug) this.emit('debug', `Found ${scenes.length} scenes`);
|
|
532
|
-
} catch (error) {
|
|
533
|
-
if (this.logError) this.emit('error', `Get scenes error: ${error}`);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
result.State = true;
|
|
537
|
-
result.Status = `Found ${devices.length} devices${scenes.length > 0 ? ` and ${scenes.length} scenes` : ''}`;
|
|
538
|
-
result.Buildings = userContext;
|
|
539
|
-
result.Devices = devices;
|
|
540
|
-
result.Scenes = scenes;
|
|
541
|
-
|
|
542
|
-
for (const deviceData of result.Devices) {
|
|
543
|
-
deviceData.Scenes = result.Scenes;
|
|
544
|
-
this.emit(deviceData.DeviceID, 'request', deviceData);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
return result;
|
|
548
|
-
} catch (error) {
|
|
549
|
-
throw new Error(`Check devices list error: ${error.message}`);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
442
|
// ── Connect ───────────────────────────────────────────────────────────────
|
|
554
443
|
|
|
555
444
|
async connect() {
|
|
@@ -779,6 +668,117 @@ class MelCloudHome extends EventEmitter {
|
|
|
779
668
|
throw new Error(`Connect error: ${error.message}`);
|
|
780
669
|
}
|
|
781
670
|
}
|
|
671
|
+
|
|
672
|
+
// ── Scenes & Devices ──────────────────────────────────────────────────────
|
|
673
|
+
|
|
674
|
+
async checkScenesList() {
|
|
675
|
+
try {
|
|
676
|
+
if (this.logDebug) this.emit('debug', 'Scanning for scenes');
|
|
677
|
+
|
|
678
|
+
const resp = await this.client.get(ApiUrls.Home.Get.Scenes);
|
|
679
|
+
const scenesList = resp.data;
|
|
680
|
+
|
|
681
|
+
if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
|
|
682
|
+
|
|
683
|
+
return this.capitalizeKeysDeep(scenesList);
|
|
684
|
+
} catch (error) {
|
|
685
|
+
throw new Error(`Check scenes list error: ${error.message}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
async checkDevicesList() {
|
|
690
|
+
try {
|
|
691
|
+
const result = { State: false, Status: null, Buildings: {}, Devices: [], Scenes: [] };
|
|
692
|
+
if (this.logDebug) this.emit('debug', 'Scanning for devices');
|
|
693
|
+
|
|
694
|
+
const resp = await this.client.get(ApiUrls.Home.Get.Context);
|
|
695
|
+
const userContext = resp.data;
|
|
696
|
+
//if (this.logDebug) this.emit('debug', `User Context: ${JSON.stringify(userContext, null, 2)}`);
|
|
697
|
+
|
|
698
|
+
const buildings = userContext.buildings ?? [];
|
|
699
|
+
const guestBuildings = userContext.guestBuildings ?? [];
|
|
700
|
+
const buildingsList = [...buildings, ...guestBuildings];
|
|
701
|
+
|
|
702
|
+
if (this.logDebug) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
|
|
703
|
+
|
|
704
|
+
if (buildingsList.length === 0) {
|
|
705
|
+
result.Status = 'No buildings found';
|
|
706
|
+
return result;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const capitalizeKeys = obj => Object.fromEntries(
|
|
710
|
+
Object.entries(obj).map(([k, v]) => [k.charAt(0).toUpperCase() + k.slice(1), v])
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
const createDevice = (device, type) => {
|
|
714
|
+
const settingsObject = Object.fromEntries(
|
|
715
|
+
(device.Settings || []).map(({ name, value }) => [
|
|
716
|
+
name.charAt(0).toUpperCase() + name.slice(1),
|
|
717
|
+
this.functions.convertValue(value),
|
|
718
|
+
])
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
const deviceObject = {
|
|
722
|
+
...capitalizeKeys(device.Capabilities || {}),
|
|
723
|
+
...settingsObject,
|
|
724
|
+
DeviceType: type,
|
|
725
|
+
FirmwareAppVersion: device.ConnectedInterfaceIdentifier,
|
|
726
|
+
IsConnected: device.IsConnected,
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
if (device.FrostProtection) device.FrostProtection = capitalizeKeys(device.FrostProtection);
|
|
730
|
+
if (device.OverheatProtection) device.OverheatProtection = capitalizeKeys(device.OverheatProtection);
|
|
731
|
+
if (device.HolidayMode) device.HolidayMode = capitalizeKeys(device.HolidayMode);
|
|
732
|
+
if (Array.isArray(device.Schedule)) device.Schedule = device.Schedule.map(s => this.capitalizeKeysDeep(s));
|
|
733
|
+
|
|
734
|
+
const { Settings, Capabilities, Id, GivenDisplayName, ...rest } = device;
|
|
735
|
+
|
|
736
|
+
return {
|
|
737
|
+
...rest,
|
|
738
|
+
Type: type,
|
|
739
|
+
DeviceID: Id,
|
|
740
|
+
DeviceName: GivenDisplayName,
|
|
741
|
+
SerialNumber: Id,
|
|
742
|
+
Device: deviceObject,
|
|
743
|
+
};
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
const devices = buildingsList.flatMap(building => [
|
|
747
|
+
...(building.airToAirUnits || []).map(d => createDevice(capitalizeKeys(d), 0)),
|
|
748
|
+
...(building.airToWaterUnits || []).map(d => createDevice(capitalizeKeys(d), 1)),
|
|
749
|
+
...(building.airToVentilationUnits || []).map(d => createDevice(capitalizeKeys(d), 3)),
|
|
750
|
+
]);
|
|
751
|
+
|
|
752
|
+
if (devices.length === 0) {
|
|
753
|
+
result.Status = 'No devices found';
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Sceny
|
|
758
|
+
let scenes = [];
|
|
759
|
+
try {
|
|
760
|
+
scenes = await this.checkScenesList();
|
|
761
|
+
if (this.logDebug) this.emit('debug', `Found ${scenes.length} scenes`);
|
|
762
|
+
} catch (error) {
|
|
763
|
+
if (this.logError) this.emit('error', `Get scenes error: ${error}`);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
result.State = true;
|
|
767
|
+
result.Status = `Found ${devices.length} devices${scenes.length > 0 ? ` and ${scenes.length} scenes` : ''}`;
|
|
768
|
+
result.Buildings = userContext;
|
|
769
|
+
result.Devices = devices;
|
|
770
|
+
result.Scenes = scenes;
|
|
771
|
+
|
|
772
|
+
for (const deviceData of result.Devices) {
|
|
773
|
+
deviceData.Scenes = result.Scenes;
|
|
774
|
+
this.emit(deviceData.DeviceID, 'request', deviceData);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
return result;
|
|
778
|
+
} catch (error) {
|
|
779
|
+
throw new Error(`Check devices list error: ${error.message}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
export default MelCloudHome;
|