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/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, displayType } = account;
35
- if (!name || accountsName.includes(name) || !user || !passwd || !language || !displayType) {
36
- log.warn(`Account ${!name ? 'name missing' : (accountsName.includes(name) ? 'name duplicated' : name)} ${user ? ', user missing' : ''}${passwd ? '' : ', password missing'},${language ? '' : ', language missing'}${!displayType ? ', type disabled' : ''} in config, will not be published in the Home app`);
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 (accountType === 'melcloudhome') {
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.3",
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.32.1",
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
- BaseURL: "https://app.melcloud.com/Mitsubishi.Wifi.Client",
6
- ClientLogin: "/Login/ClientLogin",
7
- GetUserDetails: "/User/GetUserDetails",
8
- ListDevices: "/User/ListDevices",
9
- ListDeviceUnits: "/Device/ListDeviceUnits",
10
- DeviceState: "/Device/Get?id=DID&buildingID=BID",
11
- TileState: "/Tile/Get2?id=DID&buildingID=BID",
12
- SetAta: "/Device/SetAta",
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
- DeleteSchedule: "/api/cloudschedule/deviceid/scheduleid",
39
- Referers: {
40
- GetPutScenes: "https://melcloudhome.com/scenes",
41
- PostHolidayMode: "https://melcloudhome.com/ata/deviceid/holidaymode",
42
- PostProtectionFrost: "https://melcloudhome.com/ata/deviceid/frostprotection",
43
- PostProtectionOverheat: "https://melcloudhome.com/ata/deviceid/overheatprotection",
44
- PutDeviceSettings: "https://melcloudhome.com/dashboard",
45
- PutScheduleEnabled: "https://melcloudhome.com/ata/deviceid/schedule",
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
- Origin: "https://melcloudhome.com",
48
- WebSocketURL: "wss://ws.melcloudhome.com/?hash="
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
- // Detect OS
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 isARM = arch.startsWith("arm") || arch.startsWith("aarch64") || arch.startsWith("aarch");
66
- const isMac = osName === "Darwin";
67
- const isLinux = osName === "Linux";
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("/.dockerenv");
78
+ await access('/.dockerenv');
73
79
  isDocker = true;
74
80
  } catch { }
81
+
75
82
  try {
76
- const { stdout } = await execPromise("cat /proc/1/cgroup || true");
77
- if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
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
- // macOS
87
+ const result = { path: null, arch, system: 'unknown' };
88
+
89
+ /* ===================== macOS ===================== */
81
90
  if (isMac) {
82
- const macCandidates = [
83
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
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(p, fs.constants.X_OK);
89
- return p;
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 null;
100
+ return result;
93
101
  }
94
102
 
95
- // ARM / Raspberry Pi
96
- if (isARM && isLinux) {
97
- const armCandidates = [
98
- "/usr/bin/chromium-browser",
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(p, fs.constants.X_OK);
107
- return p;
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
- // If not in Docker, try apt installation
112
- if (!isDocker) {
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 execPromise("sudo apt-get update -y");
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 execPromise("sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg || true");
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("sudo apt-get install -y chromium || true");
121
- } catch { }
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
- // Retry after installation
125
- for (const p of armCandidates) {
157
+ for (const path of armCandidates) {
126
158
  try {
127
- await access(p, fs.constants.X_OK);
128
- return p;
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
- // Synology DSM 7
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("which chromium || which chromium-browser || which google-chrome || true");
174
- const found = stdout.trim();
175
- if (found) return found;
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 p of linuxCandidates) {
179
+ for (const path of linuxCandidates) {
179
180
  try {
180
- await access(p, fs.constants.X_OK);
181
- return p;
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("apt-get update -y && apt-get install -y chromium || true");
189
- } catch { }
190
- try {
191
- await access("/usr/bin/chromium", fs.constants.X_OK);
192
- return "/usr/bin/chromium";
193
- } catch { }
194
- }
195
-
196
- // Install missing libraries
197
- const depCommands = [
198
- "apt-get update -y && apt-get install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2 || true",
199
- "yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib || true",
200
- "apk add --no-cache nspr nss libx11 libxcomposite libxdamage libxrandr atk cups libdrm libgbm alsa-lib || true"
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 null;
212
- } catch (err) {
213
- if (this.logError) this.emit("error", `Chromium detection error: ${err.message}`);
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.BaseURL,
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.BaseURL,
167
+ baseURL: ApiUrls.Base,
168
168
  timeout: 30000,
169
169
  headers: headers
170
170
  });