homebridge-melcloud-control 4.4.1-beta.0 → 4.4.1-beta.10
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/README.md +29 -29
- package/config.schema.json +64 -64
- package/index.js +33 -41
- 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 +25 -30
- package/src/functions.js +120 -94
- package/src/melcloud.js +4 -4
- package/src/melcloudata.js +15 -11
- package/src/melcloudatw.js +14 -10
- package/src/melclouderv.js +20 -8
- package/src/melcloudhome.js +39 -22
package/src/melcloudhome.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
1
|
import axios from 'axios';
|
|
3
2
|
import WebSocket from 'ws';
|
|
4
3
|
import { exec } from 'child_process';
|
|
@@ -7,7 +6,7 @@ import EventEmitter from 'events';
|
|
|
7
6
|
import puppeteer from 'puppeteer';
|
|
8
7
|
import ImpulseGenerator from './impulsegenerator.js';
|
|
9
8
|
import Functions from './functions.js';
|
|
10
|
-
import {
|
|
9
|
+
import { ApiUrls, LanguageLocaleMap } from './constants.js';
|
|
11
10
|
const execPromise = promisify(exec);
|
|
12
11
|
|
|
13
12
|
class MelCloudHome extends EventEmitter {
|
|
@@ -17,7 +16,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
17
16
|
this.user = account.user;
|
|
18
17
|
this.passwd = account.passwd;
|
|
19
18
|
this.language = account.language;
|
|
20
|
-
this.logSuccess = account.log?.success;
|
|
21
19
|
this.logWarn = account.log?.warn;
|
|
22
20
|
this.logError = account.log?.error;
|
|
23
21
|
this.logDebug = account.log?.debug;
|
|
@@ -79,7 +77,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
79
77
|
async checkScenesList() {
|
|
80
78
|
try {
|
|
81
79
|
if (this.logDebug) this.emit('debug', `Scanning for scenes`);
|
|
82
|
-
const listScenesData = await this.client(
|
|
80
|
+
const listScenesData = await this.client(ApiUrls.Home.Get.Scenes, { method: 'GET', });
|
|
83
81
|
|
|
84
82
|
const scenesList = listScenesData.data;
|
|
85
83
|
if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
|
|
@@ -111,7 +109,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
111
109
|
try {
|
|
112
110
|
const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
|
|
113
111
|
if (this.logDebug) this.emit('debug', `Scanning for devices`);
|
|
114
|
-
const listDevicesData = await this.client(
|
|
112
|
+
const listDevicesData = await this.client(ApiUrls.Home.Get.ListDevices, { method: 'GET' });
|
|
115
113
|
|
|
116
114
|
const userContext = listDevicesData.data;
|
|
117
115
|
const buildings = userContext.buildings ?? [];
|
|
@@ -225,26 +223,45 @@ class MelCloudHome extends EventEmitter {
|
|
|
225
223
|
try {
|
|
226
224
|
const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false };
|
|
227
225
|
|
|
228
|
-
// Get Chromium path
|
|
226
|
+
// Get Chromium path from resolver
|
|
229
227
|
let chromiumPath = await this.functions.ensureChromiumInstalled();
|
|
230
228
|
|
|
231
|
-
//
|
|
229
|
+
// Detect architecture again (cheap & explicit)
|
|
230
|
+
const { stdout: archOut } = await execPromise('uname -m');
|
|
231
|
+
const arch = archOut.trim();
|
|
232
|
+
const isARM =
|
|
233
|
+
arch.startsWith('arm') ||
|
|
234
|
+
arch.startsWith('aarch64') ||
|
|
235
|
+
arch.startsWith('aarch');
|
|
236
|
+
|
|
237
|
+
// Detect QNAP
|
|
238
|
+
const isQnap =
|
|
239
|
+
fs.existsSync('/etc/config/uLinux.conf') ||
|
|
240
|
+
fs.existsSync('/etc/config/qpkg.conf');
|
|
241
|
+
|
|
242
|
+
// Conditional Puppeteer fallback
|
|
232
243
|
if (!chromiumPath) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
244
|
+
if (!isARM && !isQnap) {
|
|
245
|
+
try {
|
|
246
|
+
const puppeteerPath = puppeteer.executablePath();
|
|
247
|
+
if (!puppeteerPath) {
|
|
248
|
+
accountInfo.Info = 'Puppeteer returned empty Chromium path';
|
|
249
|
+
return accountInfo;
|
|
250
|
+
}
|
|
251
|
+
|
|
236
252
|
chromiumPath = puppeteerPath;
|
|
237
253
|
if (this.logDebug) this.emit('debug', `Using Puppeteer bundled Chromium at ${chromiumPath}`);
|
|
238
|
-
}
|
|
239
|
-
accountInfo.Info = `
|
|
254
|
+
} catch (error) {
|
|
255
|
+
accountInfo.Info = `Failed to get Puppeteer Chromium path: ${error.message}`;
|
|
240
256
|
return accountInfo;
|
|
241
257
|
}
|
|
242
|
-
}
|
|
243
|
-
accountInfo.Info =
|
|
258
|
+
} else {
|
|
259
|
+
accountInfo.Info = 'Chromium not available for this platform. Install system Chromium.';
|
|
244
260
|
return accountInfo;
|
|
245
261
|
}
|
|
246
262
|
}
|
|
247
263
|
|
|
264
|
+
|
|
248
265
|
// Verify Chromium executable
|
|
249
266
|
try {
|
|
250
267
|
const { stdout } = await execPromise(`"${chromiumPath}" --version`);
|
|
@@ -282,7 +299,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
282
299
|
await client.send('Network.enable')
|
|
283
300
|
client.on('Network.webSocketCreated', ({ url }) => {
|
|
284
301
|
try {
|
|
285
|
-
if (url.startsWith(`${
|
|
302
|
+
if (url.startsWith(`${ApiUrls.Home.WebSocket}`)) {
|
|
286
303
|
const params = new URL(url).searchParams;
|
|
287
304
|
const hash = params.get('hash');
|
|
288
305
|
if (this.logDebug) this.emit('debug', `Web socket hash detected: ${hash}`);
|
|
@@ -293,11 +310,11 @@ class MelCloudHome extends EventEmitter {
|
|
|
293
310
|
|
|
294
311
|
try {
|
|
295
312
|
const headers = {
|
|
296
|
-
'Origin':
|
|
313
|
+
'Origin': ApiUrls.Home.Base,
|
|
297
314
|
'Pragma': 'no-cache',
|
|
298
315
|
'Cache-Control': 'no-cache'
|
|
299
316
|
};
|
|
300
|
-
const webSocket = new WebSocket(`${
|
|
317
|
+
const webSocket = new WebSocket(`${ApiUrls.Home.WebSocket}${hash}`, { headers: headers })
|
|
301
318
|
.on('error', (error) => {
|
|
302
319
|
if (this.logError) this.emit('error', `Web socket error: ${error}`);
|
|
303
320
|
try {
|
|
@@ -326,7 +343,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
326
343
|
})
|
|
327
344
|
.on('message', (message) => {
|
|
328
345
|
const parsedMessage = JSON.parse(message);
|
|
329
|
-
if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
|
|
346
|
+
if (!this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
|
|
330
347
|
if (parsedMessage.message === 'Forbidden') return;
|
|
331
348
|
|
|
332
349
|
this.emit('webSocket', parsedMessage);
|
|
@@ -343,9 +360,9 @@ class MelCloudHome extends EventEmitter {
|
|
|
343
360
|
});
|
|
344
361
|
|
|
345
362
|
try {
|
|
346
|
-
await page.goto(
|
|
363
|
+
await page.goto(ApiUrls.Home.Base, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
|
|
347
364
|
} catch (error) {
|
|
348
|
-
accountInfo.Info = `Navigation to ${
|
|
365
|
+
accountInfo.Info = `Navigation to ${ApiUrls.Home.Base} failed: ${error.message}`;
|
|
349
366
|
return accountInfo;
|
|
350
367
|
}
|
|
351
368
|
|
|
@@ -407,7 +424,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
407
424
|
'Accept-Language': LanguageLocaleMap[this.language],
|
|
408
425
|
'Cookie': cookies,
|
|
409
426
|
'Priority': 'u=3, i',
|
|
410
|
-
'Referer':
|
|
427
|
+
'Referer': ApiUrls.Home.Dashboard,
|
|
411
428
|
'Sec-Fetch-Dest': 'empty',
|
|
412
429
|
'Sec-Fetch-Mode': 'cors',
|
|
413
430
|
'Sec-Fetch-Site': 'same-origin',
|
|
@@ -416,7 +433,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
416
433
|
};
|
|
417
434
|
|
|
418
435
|
this.client = axios.create({
|
|
419
|
-
baseURL:
|
|
436
|
+
baseURL: ApiUrls.Home.Base,
|
|
420
437
|
timeout: 30000,
|
|
421
438
|
headers: headers
|
|
422
439
|
})
|