homebridge-melcloud-control 4.4.1-beta.3 → 4.4.1-beta.30
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 +10 -0
- package/README.md +34 -34
- package/config.schema.json +54 -54
- package/index.js +4 -4
- package/package.json +2 -2
- package/src/constants.js +62 -50
- package/src/deviceata.js +2 -6
- package/src/deviceatw.js +2 -6
- package/src/deviceerv.js +2 -6
- package/src/functions.js +105 -111
- package/src/melcloud.js +4 -4
- package/src/melcloudata.js +9 -9
- package/src/melcloudatw.js +8 -8
- package/src/melclouderv.js +7 -7
- package/src/melcloudhome.js +34 -32
package/index.js
CHANGED
|
@@ -31,9 +31,9 @@ class MelCloudPlatform {
|
|
|
31
31
|
api.on('didFinishLaunching', async () => {
|
|
32
32
|
//loop through accounts
|
|
33
33
|
for (const account of config.accounts) {
|
|
34
|
-
const { name, user, passwd, language,
|
|
35
|
-
if (!name || accountsName.includes(name) || !user || !passwd || !language || !
|
|
36
|
-
log.warn(`Account ${!name ? 'name missing' : (accountsName.includes(name) ? 'name duplicated' : name)} ${user ? ', user missing' : ''}${passwd ? '
|
|
34
|
+
const { name, user, passwd, language, type } = account;
|
|
35
|
+
if (!name || accountsName.includes(name) || !user || !passwd || !language || !type) {
|
|
36
|
+
log.warn(`Account ${!name ? 'name missing' : (accountsName.includes(name) ? 'name duplicated' : name)} ${!user ? ', user missing' : ''}${!passwd ? ', password missing' : ''}${!language ? ', language missing' : ''}${!type ? ', type disabled' : ''} in config, will not be published in the Home app`);
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
39
39
|
accountsName.push(name);
|
|
@@ -146,7 +146,7 @@ class MelCloudPlatform {
|
|
|
146
146
|
// set rest ful port
|
|
147
147
|
account.restFul.port = (device.id).slice(-4).replace(/^0/, '9');
|
|
148
148
|
|
|
149
|
-
if (
|
|
149
|
+
if (type === 'melcloudhome') {
|
|
150
150
|
account.restFul.port = `${3000}${index}`;
|
|
151
151
|
|
|
152
152
|
try {
|
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.30",
|
|
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.
|
|
42
|
+
"puppeteer": "^24.33.0",
|
|
43
43
|
"ws": "^8.18.3"
|
|
44
44
|
},
|
|
45
45
|
"keywords": [
|
package/src/constants.js
CHANGED
|
@@ -2,60 +2,67 @@ export const PlatformName = "melcloudcontrol";
|
|
|
2
2
|
export const PluginName = "homebridge-melcloud-control";
|
|
3
3
|
|
|
4
4
|
export const ApiUrls = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
SetAtw: "/Device/SetAtw",
|
|
14
|
-
SetErv: "/Device/SetErv",
|
|
15
|
-
GetRefreshUnit: "/Device/RequestRefresh?id=deviceid",
|
|
16
|
-
UpdateApplicationOptions: "/User/UpdateApplicationOptions",
|
|
17
|
-
HolidayModeUpdate: "/HolidayMode/Update",
|
|
18
|
-
EnergyCostReport: "/EnergyCost/Report",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const ApiUrlsHome = {
|
|
22
|
-
BaseURL: "https://melcloudhome.com",
|
|
23
|
-
GetConfiguration: "https://melcloudhome.com/api/configuration",
|
|
24
|
-
GetUserContext: "/api/user/context",
|
|
25
|
-
GetUserScenes: "/api/user/scenes",
|
|
26
|
-
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}
|
|
27
|
-
PostProtectionFrost: "/api/protection/frost", // POST {"enabled":true,"min":13,"max":16,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
28
|
-
PostProtectionOverheat: "/api/protection/overheat", // POST {"enabled":true,"min":32,"max":35,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
29
|
-
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
|
-
PutAta: "/api/ataunit/deviceid",
|
|
31
|
-
PutAtw: "/api/atwunit/deviceid",
|
|
32
|
-
PutErv: "/api/ervunit/deviceid",
|
|
33
|
-
PutScheduleEnabled: "/api/cloudschedule/deviceid/enabled", // PUT {"enabled":true}
|
|
34
|
-
PutScene: {
|
|
35
|
-
Enable: "/api/scene/sceneid/enable",
|
|
36
|
-
Disable: "/api/scene/sceneid/disable",
|
|
5
|
+
Base: "https://app.melcloud.com/Mitsubishi.Wifi.Client",
|
|
6
|
+
Get: {
|
|
7
|
+
UserDetails: "/User/GetUserDetails",
|
|
8
|
+
ListDevices: "/User/ListDevices",
|
|
9
|
+
ListDeviceUnits: "/Device/ListDeviceUnits",
|
|
10
|
+
RefreshUnit: "/Device/RequestRefresh?id=deviceid",
|
|
11
|
+
DeviceState: "/Device/Get?id=DID&buildingID=BID",
|
|
12
|
+
TileState: "/Tile/Get2?id=DID&buildingID=BID",
|
|
37
13
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
14
|
+
Post: {
|
|
15
|
+
ClientLogin: "/Login/ClientLogin",
|
|
16
|
+
Ata: "/Device/SetAta",
|
|
17
|
+
Atw: "/Device/SetAtw",
|
|
18
|
+
Erv: "/Device/SetErv",
|
|
19
|
+
UpdateApplicationOptions: "/User/UpdateApplicationOptions",
|
|
20
|
+
HolidayMode: "/HolidayMode/Update",
|
|
21
|
+
EnergyCostReport: "/EnergyCost/Report",
|
|
46
22
|
},
|
|
47
|
-
|
|
48
|
-
|
|
23
|
+
Home: {
|
|
24
|
+
Base: "https://melcloudhome.com",
|
|
25
|
+
WebSocket: "wss://ws.melcloudhome.com/?hash=",
|
|
26
|
+
Get: {
|
|
27
|
+
Configuration: "/api/configuration",
|
|
28
|
+
ListDevices: "/api/user/context",
|
|
29
|
+
Scenes: "/api/user/scenes",
|
|
30
|
+
},
|
|
31
|
+
Post: {
|
|
32
|
+
ProtectionFrost: "/api/protection/frost", //{"enabled":true,"min":13,"max":16,"units":{"ATA":["deviceid"]}}
|
|
33
|
+
ProtectionOverheat: "/api/protection/overheat", //{"enabled":true,"min":32,"max":35,"units":{"ATA":["deviceid"]}}
|
|
34
|
+
HolidayMode: "/api/holidaymode", //{"enabled":true,"startDate":"2025-11-11T17:42:24.913","endDate":"2026-06-01T09:18:00","units":{"ATA":["deviceid"]}}
|
|
35
|
+
Schedule: "/api/cloudschedule/deviceid", //{"days":[2],"time":"17:59:00","enabled":true,"id":"scheduleid","power":false,"operationMode":null,"setPoint":null,"vaneVerticalDirection":null,"vaneHorizontalDirection":null,"setFanSpeed":null}
|
|
36
|
+
Scene: "/api/scene", //{"id": "sceneid", "userId": "userid","name": "Poza domem","enabled": false,"icon": "AwayIcon","ataSceneSettings": [{"unitId": "deviceid","ataSettings": { "power": false, "operationMode": "heat","setFanSpeed": "auto","vaneHorizontalDirection": "auto", "vaneVerticalDirection": "auto", "setTemperature": 21,"temperatureIncrementOverride": null,"inStandbyMode": null},"previousSettings": null}],"atwSceneSettings": []}
|
|
37
|
+
},
|
|
38
|
+
Put: {
|
|
39
|
+
Ata: "/api/ataunit/deviceid", //{ power: true,setTemperature: 22, setFanSpeed: "auto", operationMode: "heat", vaneHorizontalDirection: "auto",vaneVerticalDirection: "auto", temperatureIncrementOverride: null, inStandbyMode: null}
|
|
40
|
+
Atw: "/api/atwunit/deviceid",
|
|
41
|
+
Erv: "/api/ervunit/deviceid",
|
|
42
|
+
ScheduleEnableDisable: "/api/cloudschedule/deviceid/enabled", // {"enabled": true}
|
|
43
|
+
SceneEnableDisable: "/api/scene/sceneid/enabledisable",
|
|
44
|
+
},
|
|
45
|
+
Delete: {
|
|
46
|
+
Schedule: "/api/cloudschedule/deviceid/scheduleid",
|
|
47
|
+
Scene: "/api/scene/sceneid"
|
|
48
|
+
},
|
|
49
|
+
Referers: {
|
|
50
|
+
GetPutScenes: "https://melcloudhome.com/scenes",
|
|
51
|
+
PostHolidayMode: "https://melcloudhome.com/ata/deviceid/holidaymode",
|
|
52
|
+
PostProtectionFrost: "https://melcloudhome.com/ata/deviceid/frostprotection",
|
|
53
|
+
PostProtectionOverheat: "https://melcloudhome.com/ata/deviceid/overheatprotection",
|
|
54
|
+
PutDeviceSettings: "https://melcloudhome.com/dashboard",
|
|
55
|
+
PutScheduleEnabled: "https://melcloudhome.com/ata/deviceid/schedule",
|
|
56
|
+
}
|
|
57
|
+
}
|
|
49
58
|
};
|
|
50
59
|
|
|
51
|
-
export const DeviceType =
|
|
52
|
-
"Air Conditioner",
|
|
53
|
-
"Heat Pump",
|
|
54
|
-
"Unknown",
|
|
55
|
-
"Energy Recovery Ventilation"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
export const TemperatureDisplayUnits = ["°C", "°F"];
|
|
60
|
+
export const DeviceType = {
|
|
61
|
+
0: "Air Conditioner",
|
|
62
|
+
1: "Heat Pump",
|
|
63
|
+
2: "Unknown",
|
|
64
|
+
3: "Energy Recovery Ventilation"
|
|
65
|
+
};
|
|
59
66
|
|
|
60
67
|
export const AirConditioner = {
|
|
61
68
|
SystemMapEnumToString: { 0: "Air Conditioner Off", 1: "Air Conditioner On", 2: "Air Conditioner Offline" },
|
|
@@ -183,6 +190,11 @@ export const Ventilation = {
|
|
|
183
190
|
}
|
|
184
191
|
};
|
|
185
192
|
|
|
193
|
+
export const TemperatureDisplayUnits = {
|
|
194
|
+
0: "°C",
|
|
195
|
+
1: "°F"
|
|
196
|
+
};
|
|
197
|
+
|
|
186
198
|
export const AccessLevel = {
|
|
187
199
|
Quest: 3,
|
|
188
200
|
Owner: 4
|
package/src/deviceata.js
CHANGED
|
@@ -65,7 +65,6 @@ class DeviceAta extends EventEmitter {
|
|
|
65
65
|
|
|
66
66
|
//presets configured
|
|
67
67
|
for (const preset of this.presets) {
|
|
68
|
-
preset.name = preset.name;
|
|
69
68
|
preset.serviceType = serviceType[preset.displayType];
|
|
70
69
|
preset.characteristicType = characteristicType[preset.displayType];
|
|
71
70
|
preset.state = false;
|
|
@@ -74,7 +73,6 @@ class DeviceAta extends EventEmitter {
|
|
|
74
73
|
|
|
75
74
|
//schedules configured
|
|
76
75
|
for (const schedule of this.schedules) {
|
|
77
|
-
schedule.name = schedule.name;
|
|
78
76
|
schedule.serviceType = serviceType[schedule.displayType];
|
|
79
77
|
schedule.characteristicType = characteristicType[schedule.displayType];
|
|
80
78
|
schedule.state = false;
|
|
@@ -82,7 +80,6 @@ class DeviceAta extends EventEmitter {
|
|
|
82
80
|
|
|
83
81
|
//scenes configured
|
|
84
82
|
for (const scene of this.scenes) {
|
|
85
|
-
scene.name = scene.name;
|
|
86
83
|
scene.serviceType = serviceType[scene.displayType];
|
|
87
84
|
scene.characteristicType = characteristicType[scene.displayType];
|
|
88
85
|
scene.state = false;
|
|
@@ -90,7 +87,6 @@ class DeviceAta extends EventEmitter {
|
|
|
90
87
|
|
|
91
88
|
//buttons configured
|
|
92
89
|
for (const button of this.buttons) {
|
|
93
|
-
button.name = button.name;
|
|
94
90
|
button.serviceType = serviceType[button.displayType];
|
|
95
91
|
button.characteristicType = characteristicType[button.displayType];
|
|
96
92
|
button.state = false;
|
|
@@ -522,8 +518,8 @@ class DeviceAta extends EventEmitter {
|
|
|
522
518
|
|
|
523
519
|
try {
|
|
524
520
|
this.accessory.useFahrenheit = value ? true : false;
|
|
525
|
-
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
526
521
|
this.accountInfo.UseFahrenheit = value ? true : false;
|
|
522
|
+
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
527
523
|
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'account', this.accountInfo);
|
|
528
524
|
} catch (error) {
|
|
529
525
|
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
|
|
@@ -615,8 +611,8 @@ class DeviceAta extends EventEmitter {
|
|
|
615
611
|
|
|
616
612
|
try {
|
|
617
613
|
this.accessory.useFahrenheit = value ? true : false;
|
|
618
|
-
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
619
614
|
this.accountInfo.UseFahrenheit = value ? true : false;
|
|
615
|
+
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
620
616
|
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'account', this.accountInfo);
|
|
621
617
|
} catch (error) {
|
|
622
618
|
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
|
package/src/deviceatw.js
CHANGED
|
@@ -69,7 +69,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
69
69
|
|
|
70
70
|
//presets configured
|
|
71
71
|
for (const preset of this.presets) {
|
|
72
|
-
preset.name = preset.name;
|
|
73
72
|
preset.serviceType = serviceType[preset.displayType];
|
|
74
73
|
preset.characteristicType = characteristicType[preset.displayType];
|
|
75
74
|
preset.state = false;
|
|
@@ -78,7 +77,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
78
77
|
|
|
79
78
|
//schedules configured
|
|
80
79
|
for (const schedule of this.schedules) {
|
|
81
|
-
schedule.name = schedule.name;
|
|
82
80
|
schedule.serviceType = serviceType[schedule.displayType];
|
|
83
81
|
schedule.characteristicType = characteristicType[schedule.displayType];
|
|
84
82
|
schedule.state = false;
|
|
@@ -86,7 +84,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
86
84
|
|
|
87
85
|
//scenes configured
|
|
88
86
|
for (const scene of this.scenes) {
|
|
89
|
-
scene.name = scene.name;
|
|
90
87
|
scene.serviceType = serviceType[scene.displayType];
|
|
91
88
|
scene.characteristicType = characteristicType[scene.displayType];
|
|
92
89
|
scene.state = false;
|
|
@@ -94,7 +91,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
94
91
|
|
|
95
92
|
//buttons configured
|
|
96
93
|
for (const button of this.buttons) {
|
|
97
|
-
button.name = button.name;
|
|
98
94
|
button.serviceType = serviceType[button.displayType];
|
|
99
95
|
button.characteristicType = characteristicType[button.displayType];
|
|
100
96
|
button.state = false;
|
|
@@ -645,8 +641,8 @@ class DeviceAtw extends EventEmitter {
|
|
|
645
641
|
|
|
646
642
|
try {
|
|
647
643
|
this.accessory.useFahrenheit = value ? true : false;
|
|
648
|
-
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
649
644
|
this.accountInfo.UseFahrenheit = value ? true : false;
|
|
645
|
+
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
650
646
|
await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, 'account', this.accountInfo);
|
|
651
647
|
} catch (error) {
|
|
652
648
|
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
|
|
@@ -826,8 +822,8 @@ class DeviceAtw extends EventEmitter {
|
|
|
826
822
|
|
|
827
823
|
try {
|
|
828
824
|
this.accessory.useFahrenheit = value ? true : false;
|
|
829
|
-
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
830
825
|
this.accountInfo.UseFahrenheit = value ? true : false;
|
|
826
|
+
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
831
827
|
await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, 'account', this.accountInfo);
|
|
832
828
|
} catch (error) {
|
|
833
829
|
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
|
package/src/deviceerv.js
CHANGED
|
@@ -61,7 +61,6 @@ class DeviceErv extends EventEmitter {
|
|
|
61
61
|
|
|
62
62
|
//presets configured
|
|
63
63
|
for (const preset of this.presets) {
|
|
64
|
-
preset.name = preset.name;
|
|
65
64
|
preset.serviceType = serviceType[preset.displayType];
|
|
66
65
|
preset.characteristicType = characteristicType[preset.displayType];
|
|
67
66
|
preset.state = false;
|
|
@@ -70,7 +69,6 @@ class DeviceErv extends EventEmitter {
|
|
|
70
69
|
|
|
71
70
|
//schedules configured
|
|
72
71
|
for (const schedule of this.schedules) {
|
|
73
|
-
schedule.name = schedule.name;
|
|
74
72
|
schedule.serviceType = serviceType[schedule.displayType];
|
|
75
73
|
schedule.characteristicType = characteristicType[schedule.displayType];
|
|
76
74
|
schedule.state = false;
|
|
@@ -78,7 +76,6 @@ class DeviceErv extends EventEmitter {
|
|
|
78
76
|
|
|
79
77
|
//scenes configured
|
|
80
78
|
for (const scene of this.scenes) {
|
|
81
|
-
scene.name = scene.name;
|
|
82
79
|
scene.serviceType = serviceType[scene.displayType];
|
|
83
80
|
scene.characteristicType = characteristicType[scene.displayType];
|
|
84
81
|
scene.state = false;
|
|
@@ -86,7 +83,6 @@ class DeviceErv extends EventEmitter {
|
|
|
86
83
|
|
|
87
84
|
//buttons configured
|
|
88
85
|
for (const button of this.buttons) {
|
|
89
|
-
button.name = button.name;
|
|
90
86
|
button.serviceType = serviceType[button.displayType];
|
|
91
87
|
button.characteristicType = characteristicType[button.displayType];
|
|
92
88
|
button.state = false;
|
|
@@ -454,8 +450,8 @@ class DeviceErv extends EventEmitter {
|
|
|
454
450
|
|
|
455
451
|
try {
|
|
456
452
|
this.accessory.useFahrenheit = value ? true : false;
|
|
457
|
-
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
458
453
|
this.accountInfo.UseFahrenheit = value ? true : false;
|
|
454
|
+
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
459
455
|
await this.melCloudErv.send(this.accountType, this.displayType, deviceData, 'account', this.accountInfo);
|
|
460
456
|
} catch (error) {
|
|
461
457
|
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
|
|
@@ -548,8 +544,8 @@ class DeviceErv extends EventEmitter {
|
|
|
548
544
|
|
|
549
545
|
try {
|
|
550
546
|
this.accessory.useFahrenheit = value ? true : false;
|
|
551
|
-
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
552
547
|
this.accountInfo.UseFahrenheit = value ? true : false;
|
|
548
|
+
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
|
|
553
549
|
await this.melCloudErv.send(this.accountType, this.displayType, deviceData, 'account', this.accountInfo);
|
|
554
550
|
} catch (error) {
|
|
555
551
|
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
|
package/src/functions.js
CHANGED
|
@@ -56,162 +56,157 @@ class Functions extends EventEmitter {
|
|
|
56
56
|
|
|
57
57
|
async ensureChromiumInstalled() {
|
|
58
58
|
try {
|
|
59
|
-
|
|
60
|
-
const { stdout: osOut } = await execPromise("uname -s");
|
|
59
|
+
const { stdout: osOut } = await execPromise('uname -s');
|
|
61
60
|
const osName = osOut.trim();
|
|
62
|
-
const { stdout: archOut } = await execPromise("uname -m");
|
|
63
|
-
const arch = archOut.trim();
|
|
64
61
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
const { stdout: archOut } = await execPromise('uname -m');
|
|
63
|
+
let arch = archOut.trim() || 'unknown';
|
|
64
|
+
|
|
65
|
+
// Normalizacja architektury
|
|
66
|
+
if (arch.startsWith('arm') || arch.startsWith('aarch')) arch = 'arm';
|
|
67
|
+
else if (arch.includes('64')) arch = 'x64';
|
|
68
|
+
else arch = 'x86';
|
|
69
|
+
|
|
70
|
+
const isARM = arch === 'arm';
|
|
71
|
+
const isMac = osName === 'Darwin';
|
|
72
|
+
const isLinux = osName === 'Linux';
|
|
73
|
+
const isQnap = fs.existsSync('/etc/config/uLinux.conf') || fs.existsSync('/etc/config/qpkg.conf');
|
|
68
74
|
|
|
69
75
|
// Detect Docker
|
|
70
76
|
let isDocker = false;
|
|
71
77
|
try {
|
|
72
|
-
await access(
|
|
78
|
+
await access('/.dockerenv');
|
|
73
79
|
isDocker = true;
|
|
74
80
|
} catch { }
|
|
81
|
+
|
|
75
82
|
try {
|
|
76
|
-
const { stdout } = await execPromise(
|
|
77
|
-
if (stdout.includes(
|
|
83
|
+
const { stdout } = await execPromise('cat /proc/1/cgroup');
|
|
84
|
+
if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
|
|
78
85
|
} catch { }
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
const result = { path: null, arch, system: 'unknown' };
|
|
88
|
+
|
|
89
|
+
/* ===================== macOS ===================== */
|
|
81
90
|
if (isMac) {
|
|
82
|
-
const macCandidates = [
|
|
83
|
-
|
|
84
|
-
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
|
85
|
-
];
|
|
86
|
-
for (const p of macCandidates) {
|
|
91
|
+
const macCandidates = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '/Applications/Chromium.app/Contents/MacOS/Chromium'];
|
|
92
|
+
for (const path of macCandidates) {
|
|
87
93
|
try {
|
|
88
|
-
await access(
|
|
89
|
-
|
|
94
|
+
await access(path, fs.constants.X_OK);
|
|
95
|
+
result.path = path;
|
|
96
|
+
result.system = 'macOS';
|
|
97
|
+
return result;
|
|
90
98
|
} catch { }
|
|
91
99
|
}
|
|
92
|
-
return
|
|
100
|
+
return result;
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
"/usr/bin/chromium",
|
|
100
|
-
"/snap/bin/chromium"
|
|
101
|
-
];
|
|
102
|
-
|
|
103
|
-
// Try existing
|
|
104
|
-
for (const p of armCandidates) {
|
|
103
|
+
/* ===================== QNAP ===================== */
|
|
104
|
+
if (isQnap) {
|
|
105
|
+
const qnapCandidates = ['/opt/bin/chromium', '/opt/bin/chromium-browser'];
|
|
106
|
+
for (const path of qnapCandidates) {
|
|
105
107
|
try {
|
|
106
|
-
await access(
|
|
107
|
-
|
|
108
|
+
await access(path, fs.constants.X_OK);
|
|
109
|
+
result.path = path;
|
|
110
|
+
result.system = 'Qnap';
|
|
111
|
+
return result;
|
|
108
112
|
} catch { }
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
try {
|
|
116
|
+
await access('/opt/bin/opkg', fs.constants.X_OK);
|
|
117
|
+
await execPromise('/opt/bin/opkg update');
|
|
118
|
+
await execPromise('opkg install chromium nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib');
|
|
119
|
+
process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ''}`;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (this.logError) this.emit('error', `Install package for Qnap error: ${error}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const path of qnapCandidates) {
|
|
113
125
|
try {
|
|
114
|
-
await
|
|
126
|
+
await access(path, fs.constants.X_OK);
|
|
127
|
+
result.path = path;
|
|
128
|
+
result.system = 'Qnap';
|
|
129
|
+
return result;
|
|
115
130
|
} catch { }
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ===================== Linux ARM ===================== */
|
|
136
|
+
if (isLinux && isARM) {
|
|
137
|
+
const armCandidates = ['/usr/bin/chromium-browser', '/usr/bin/chromium', '/snap/bin/chromium'];
|
|
138
|
+
for (const path of armCandidates) {
|
|
116
139
|
try {
|
|
117
|
-
await
|
|
140
|
+
await access(path, fs.constants.X_OK);
|
|
141
|
+
result.path = path;
|
|
142
|
+
result.system = 'Linux';
|
|
143
|
+
return result;
|
|
118
144
|
} catch { }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!isDocker) {
|
|
119
148
|
try {
|
|
120
|
-
await execPromise(
|
|
121
|
-
|
|
149
|
+
await execPromise('sudo apt update -y');
|
|
150
|
+
await execPromise('sudo apt install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2');
|
|
151
|
+
await execPromise('sudo apt install -y chromium chromium-browser chromium-codecs-ffmpeg');
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (this.logError) this.emit('error', `Install package for Linux ARM error: ${error}`);
|
|
154
|
+
}
|
|
122
155
|
}
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
for (const p of armCandidates) {
|
|
157
|
+
for (const path of armCandidates) {
|
|
126
158
|
try {
|
|
127
|
-
await access(
|
|
128
|
-
|
|
159
|
+
await access(path, fs.constants.X_OK);
|
|
160
|
+
result.path = path;
|
|
161
|
+
result.system = 'Linux';
|
|
162
|
+
return result;
|
|
129
163
|
} catch { }
|
|
130
164
|
}
|
|
131
|
-
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// QNAP / Entware
|
|
136
|
-
let entwareExists = false;
|
|
137
|
-
try {
|
|
138
|
-
await access("/opt/bin/opkg", fs.constants.X_OK);
|
|
139
|
-
entwareExists = true;
|
|
140
|
-
} catch { }
|
|
141
|
-
|
|
142
|
-
if (entwareExists) {
|
|
143
|
-
try {
|
|
144
|
-
await execPromise("/opt/bin/opkg update");
|
|
145
|
-
await execPromise("/opt/bin/opkg install nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib");
|
|
146
|
-
process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ""}`;
|
|
147
|
-
} catch { }
|
|
165
|
+
return result;
|
|
148
166
|
}
|
|
149
167
|
|
|
150
|
-
|
|
151
|
-
const synoCandidates = [
|
|
152
|
-
"/var/packages/Chromium/target/usr/bin/chromium",
|
|
153
|
-
"/usr/local/chromium/bin/chromium"
|
|
154
|
-
];
|
|
155
|
-
for (const p of synoCandidates) {
|
|
156
|
-
try {
|
|
157
|
-
await access(p, fs.constants.X_OK);
|
|
158
|
-
return p;
|
|
159
|
-
} catch { }
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Linux x64
|
|
168
|
+
/* ===================== Linux x64 ===================== */
|
|
163
169
|
if (isLinux) {
|
|
164
|
-
const linuxCandidates = [
|
|
165
|
-
"/usr/bin/chromium",
|
|
166
|
-
"/usr/bin/chromium-browser",
|
|
167
|
-
"/usr/bin/google-chrome",
|
|
168
|
-
"/snap/bin/chromium",
|
|
169
|
-
"/usr/local/bin/chromium"
|
|
170
|
-
];
|
|
171
|
-
|
|
170
|
+
const linuxCandidates = ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', '/snap/bin/chromium', '/usr/local/bin/chromium'];
|
|
172
171
|
try {
|
|
173
|
-
const { stdout } = await execPromise(
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome');
|
|
173
|
+
if (stdout.trim()) {
|
|
174
|
+
result.path = stdout.trim();
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
176
177
|
} catch { }
|
|
177
178
|
|
|
178
|
-
for (const
|
|
179
|
+
for (const path of linuxCandidates) {
|
|
179
180
|
try {
|
|
180
|
-
await access(
|
|
181
|
-
|
|
181
|
+
await access(path, fs.constants.X_OK);
|
|
182
|
+
result.path = path;
|
|
183
|
+
result.system = 'Linux';
|
|
184
|
+
return result;
|
|
182
185
|
} catch { }
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
// Docker: try installing chromium inside container (if allowed)
|
|
186
188
|
if (isDocker) {
|
|
187
189
|
try {
|
|
188
|
-
await execPromise(
|
|
189
|
-
} catch {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
];
|
|
202
|
-
for (const cmd of depCommands) {
|
|
203
|
-
try {
|
|
204
|
-
await execPromise(`sudo ${cmd}`);
|
|
205
|
-
} catch { }
|
|
190
|
+
await execPromise('apt update -y && apt install -y chromium');
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (this.logError) this.emit('error', `Install package for Linux Docker error: ${error}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const path of linuxCandidates) {
|
|
196
|
+
try {
|
|
197
|
+
await access(path, fs.constants.X_OK);
|
|
198
|
+
result.path = path;
|
|
199
|
+
result.system = 'Linux Docker';
|
|
200
|
+
return result;
|
|
201
|
+
} catch { }
|
|
202
|
+
}
|
|
206
203
|
}
|
|
207
|
-
|
|
208
|
-
return null;
|
|
209
204
|
}
|
|
210
205
|
|
|
211
|
-
return
|
|
212
|
-
} catch (
|
|
213
|
-
if (this.logError) this.emit(
|
|
214
|
-
return null;
|
|
206
|
+
return result;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (this.logError) this.emit('error', `Chromium detection error: ${error.message}`);
|
|
209
|
+
return { path: null, arch: 'unknown', system: 'unknown' };
|
|
215
210
|
}
|
|
216
211
|
}
|
|
217
212
|
|
|
@@ -280,7 +275,6 @@ class Functions extends EventEmitter {
|
|
|
280
275
|
|
|
281
276
|
return { min, max };
|
|
282
277
|
}
|
|
283
|
-
|
|
284
278
|
}
|
|
285
279
|
|
|
286
280
|
export default Functions
|
package/src/melcloud.js
CHANGED
|
@@ -56,7 +56,7 @@ class MelCloud extends EventEmitter {
|
|
|
56
56
|
try {
|
|
57
57
|
const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
|
|
58
58
|
if (this.logDebug) this.emit('debug', `Scanning for devices...`);
|
|
59
|
-
const listDevicesData = await this.client(ApiUrls.ListDevices, { method: 'GET', });
|
|
59
|
+
const listDevicesData = await this.client(ApiUrls.Get.ListDevices, { method: 'GET', });
|
|
60
60
|
|
|
61
61
|
if (!listDevicesData || !listDevicesData.data) {
|
|
62
62
|
devicesList.Info = 'Invalid or empty response from MELCloud API'
|
|
@@ -132,9 +132,9 @@ class MelCloud extends EventEmitter {
|
|
|
132
132
|
CaptchaResponse: '',
|
|
133
133
|
Persist: true
|
|
134
134
|
};
|
|
135
|
-
const accountData = await axios(ApiUrls.ClientLogin, {
|
|
135
|
+
const accountData = await axios(ApiUrls.Post.ClientLogin, {
|
|
136
136
|
method: 'POST',
|
|
137
|
-
baseURL: ApiUrls.
|
|
137
|
+
baseURL: ApiUrls.Base,
|
|
138
138
|
timeout: 15000,
|
|
139
139
|
data: payload
|
|
140
140
|
});
|
|
@@ -164,7 +164,7 @@ class MelCloud extends EventEmitter {
|
|
|
164
164
|
};
|
|
165
165
|
|
|
166
166
|
this.client = axios.create({
|
|
167
|
-
baseURL: ApiUrls.
|
|
167
|
+
baseURL: ApiUrls.Base,
|
|
168
168
|
timeout: 30000,
|
|
169
169
|
headers: headers
|
|
170
170
|
});
|