homebridge-melcloud-control 4.0.0-beta.413 → 4.0.0-beta.414
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/melcloud.js +19 -19
- package/src/melcloudhometoken.js +15 -15
- package/src/restful.js +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.0.0-beta.
|
|
4
|
+
"version": "4.0.0-beta.414",
|
|
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/melcloud.js
CHANGED
|
@@ -73,7 +73,7 @@ class MelCloud extends EventEmitter {
|
|
|
73
73
|
const listDevicesData = await axiosInstance.get(ApiUrls.ListDevices);
|
|
74
74
|
|
|
75
75
|
if (!listDevicesData || !listDevicesData.data) {
|
|
76
|
-
this.emit('warn', `Invalid or empty response from MELCloud API`);
|
|
76
|
+
if (this.logWarn) this.emit('warn', `Invalid or empty response from MELCloud API`);
|
|
77
77
|
return null;
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -83,7 +83,7 @@ class MelCloud extends EventEmitter {
|
|
|
83
83
|
this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
|
|
84
84
|
|
|
85
85
|
if (!Array.isArray(buildingsList) || buildingsList.length === 0) {
|
|
86
|
-
this.emit('warn', `No buildings found in MELCloud account`);
|
|
86
|
+
if (this.logWarn) this.emit('warn', `No buildings found in MELCloud account`);
|
|
87
87
|
return null;
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -129,7 +129,7 @@ class MelCloud extends EventEmitter {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
if (devices.length === 0) {
|
|
132
|
-
this.emit('warn', `No devices found in any building`);
|
|
132
|
+
if (this.logWarn) this.emit('warn', `No devices found in any building`);
|
|
133
133
|
return null;
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -342,10 +342,10 @@ class MelCloud extends EventEmitter {
|
|
|
342
342
|
|
|
343
343
|
const page = await browser.newPage();
|
|
344
344
|
|
|
345
|
-
page.on('error', err => this.emit('
|
|
346
|
-
page.on('pageerror', err => this.emit('
|
|
347
|
-
page.on('close', () => this.emit('
|
|
348
|
-
browser.on('disconnected', () => this.emit('
|
|
345
|
+
page.on('error', err => this.emit('error', `Page crashed: ${err.message}`));
|
|
346
|
+
page.on('pageerror', err => this.emit('error', `Browser error: ${err.message}`));
|
|
347
|
+
page.on('close', () => this.emit('debug', 'Page was closed unexpectedly'));
|
|
348
|
+
browser.on('disconnected', () => this.emit('debug', 'Browser disconnected unexpectedly'));
|
|
349
349
|
|
|
350
350
|
page.setDefaultTimeout(30000);
|
|
351
351
|
page.setDefaultNavigationTimeout(30000);
|
|
@@ -362,12 +362,12 @@ class MelCloud extends EventEmitter {
|
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
364
|
if (!loginBtn) {
|
|
365
|
-
this.emit('warn', 'Login button not found');
|
|
365
|
+
if (this.logWarn) this.emit('warn', 'Login button not found');
|
|
366
366
|
return null;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
if (page.isClosed()) {
|
|
370
|
-
this.emit('warn', 'Page closed before login click');
|
|
370
|
+
if (this.logWarn) this.emit('warn', 'Page closed before login click');
|
|
371
371
|
return null;
|
|
372
372
|
}
|
|
373
373
|
|
|
@@ -382,12 +382,12 @@ class MelCloud extends EventEmitter {
|
|
|
382
382
|
const usernameInput = await page.$('input[name="username"]');
|
|
383
383
|
const passwordInput = await page.$('input[name="password"]');
|
|
384
384
|
if (!usernameInput || !passwordInput) {
|
|
385
|
-
this.emit('warn', 'Username or password input not found');
|
|
385
|
+
if (this.logWarn) this.emit('warn', 'Username or password input not found');
|
|
386
386
|
return null;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
if (page.isClosed()) {
|
|
390
|
-
this.emit('warn', 'Page closed before typing credentials');
|
|
390
|
+
if (this.logWarn) this.emit('warn', 'Page closed before typing credentials');
|
|
391
391
|
return null;
|
|
392
392
|
}
|
|
393
393
|
|
|
@@ -396,12 +396,12 @@ class MelCloud extends EventEmitter {
|
|
|
396
396
|
|
|
397
397
|
const submitButton = await page.$('input[type="submit"], button[type="submit"]');
|
|
398
398
|
if (!submitButton) {
|
|
399
|
-
this.emit('warn', 'Submit button not found on login form');
|
|
399
|
+
if (this.logWarn) this.emit('warn', 'Submit button not found on login form');
|
|
400
400
|
return null;
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
if (page.isClosed()) {
|
|
404
|
-
this.emit('warn', 'Page closed before submitting form');
|
|
404
|
+
if (this.logWarn) this.emit('warn', 'Page closed before submitting form');
|
|
405
405
|
return null;
|
|
406
406
|
}
|
|
407
407
|
|
|
@@ -415,13 +415,13 @@ class MelCloud extends EventEmitter {
|
|
|
415
415
|
|
|
416
416
|
const pageText = await page.content();
|
|
417
417
|
if (pageText.includes('incorrect') || pageText.includes('Invalid') || pageText.includes('nieprawidłowe')) {
|
|
418
|
-
this.emit('warn', 'Login failed: incorrect email or password');
|
|
418
|
+
if (this.logWarn) this.emit('warn', 'Login failed: incorrect email or password');
|
|
419
419
|
return null;
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
const captchaPresent = await page.$('iframe[src*="captcha"], div[class*="captcha"]');
|
|
423
423
|
if (captchaPresent) {
|
|
424
|
-
this.emit('warn', 'Login blocked by CAPTCHA. Manual verification required.');
|
|
424
|
+
if (this.logWarn) this.emit('warn', 'Login blocked by CAPTCHA. Manual verification required.');
|
|
425
425
|
return null;
|
|
426
426
|
}
|
|
427
427
|
|
|
@@ -435,7 +435,7 @@ class MelCloud extends EventEmitter {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
if (!c1 || !c2) {
|
|
438
|
-
this.emit('warn', 'Cookies C1/C2 missing after login');
|
|
438
|
+
if (this.logWarn) this.emit('warn', 'Cookies C1/C2 missing after login');
|
|
439
439
|
return null;
|
|
440
440
|
}
|
|
441
441
|
|
|
@@ -456,7 +456,7 @@ class MelCloud extends EventEmitter {
|
|
|
456
456
|
// Puppeteer / system error handling
|
|
457
457
|
if (error.message.includes('libnspr4.so') || error.message.includes('Failed to launch the browser process')) {
|
|
458
458
|
const inDocker = await this.functions.isRunningInDocker();
|
|
459
|
-
this.emit('
|
|
459
|
+
if (this.logError) this.emit('error', `Missing system libraries detected.`);
|
|
460
460
|
|
|
461
461
|
const installCmd =
|
|
462
462
|
'apt-get update && apt-get install -y ' +
|
|
@@ -465,7 +465,7 @@ class MelCloud extends EventEmitter {
|
|
|
465
465
|
'libgbm1 libasound2 libxshmfence1 fonts-liberation libappindicator3-1 libu2f-udev';
|
|
466
466
|
|
|
467
467
|
if (inDocker) {
|
|
468
|
-
this.emit('warn', `Running in Docker — attempting automatic fix...`);
|
|
468
|
+
if (this.logWarn) this.emit('warn', `Running in Docker — attempting automatic fix...`);
|
|
469
469
|
try {
|
|
470
470
|
const { execSync } = require('child_process');
|
|
471
471
|
execSync(installCmd, { stdio: 'inherit' });
|
|
@@ -486,7 +486,7 @@ class MelCloud extends EventEmitter {
|
|
|
486
486
|
try {
|
|
487
487
|
await browser.close();
|
|
488
488
|
} catch (closeErr) {
|
|
489
|
-
this.emit('
|
|
489
|
+
if (this.logError) this.emit('error', `Failed to close Puppeteer browser: ${closeErr.message}`);
|
|
490
490
|
}
|
|
491
491
|
}
|
|
492
492
|
}
|
package/src/melcloudhometoken.js
CHANGED
|
@@ -59,7 +59,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
59
59
|
const csrf = dom.window.document.querySelector('input[name="_csrf"]')?.value;
|
|
60
60
|
|
|
61
61
|
if (!csrf) {
|
|
62
|
-
this.emit('warn', 'CSRF token not found');
|
|
62
|
+
if (this.logWarn) this.emit('warn', 'CSRF token not found');
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -85,7 +85,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
if (response.status === 400) {
|
|
88
|
-
this.emit('warn', `Login failed: ${response.data}`);
|
|
88
|
+
if (this.logWarn) this.emit('warn', `Login failed: ${response.data}`);
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -106,7 +106,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
106
106
|
return code || null;
|
|
107
107
|
|
|
108
108
|
} catch (err) {
|
|
109
|
-
this.emit('warn', `loginToMelCloudHome error: ${err}`);
|
|
109
|
+
if (this.logWarn) this.emit('warn', `loginToMelCloudHome error: ${err}`);
|
|
110
110
|
return null;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -120,7 +120,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
120
120
|
if (locationHeader && locationHeader.startsWith('melcloudhome://')) {
|
|
121
121
|
const match = locationHeader.match(/[?&]code=([^&]+)/);
|
|
122
122
|
if (match) {
|
|
123
|
-
this.emit('warn', `Found code in Location header: ${match[1]}`);
|
|
123
|
+
if (this.logWarn) this.emit('warn', `Found code in Location header: ${match[1]}`);
|
|
124
124
|
resolve(match[1]);
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
@@ -132,14 +132,14 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
132
132
|
const formActionMatch = data.match(/action="([^"]+)"/);
|
|
133
133
|
|
|
134
134
|
if (formCodeMatch && formStateMatch && formActionMatch) {
|
|
135
|
-
this.emit('warn', 'Found form_post, submitting...');
|
|
135
|
+
if (this.logWarn) this.emit('warn', 'Found form_post, submitting...');
|
|
136
136
|
try {
|
|
137
137
|
const code = await this.submitFormPost(formActionMatch[1], formCodeMatch[1], formStateMatch[1]);
|
|
138
|
-
this.emit('warn', `submitFormPost returned code: ${code}`);
|
|
138
|
+
if (this.logWarn) this.emit('warn', `submitFormPost returned code: ${code}`);
|
|
139
139
|
resolve(code);
|
|
140
140
|
return;
|
|
141
141
|
} catch (err) {
|
|
142
|
-
this.emit('warn', `submitFormPost failed: ${err}`);
|
|
142
|
+
if (this.logWarn) this.emit('warn', `submitFormPost failed: ${err}`);
|
|
143
143
|
reject(err);
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
@@ -148,29 +148,29 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
148
148
|
// 3️⃣ JS redirect in body
|
|
149
149
|
const bodyCodeMatch = data.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
|
|
150
150
|
if (bodyCodeMatch) {
|
|
151
|
-
this.emit('warn', `Found code in body: ${bodyCodeMatch[1]}`);
|
|
151
|
+
if (this.logWarn) this.emit('warn', `Found code in body: ${bodyCodeMatch[1]}`);
|
|
152
152
|
resolve(bodyCodeMatch[1]);
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// 4️⃣ Follow redirect
|
|
157
157
|
if (locationHeader && ['301', '302', '303'].includes(headers['status'] || '')) {
|
|
158
|
-
this.emit('warn', `Following redirect to ${locationHeader}`);
|
|
158
|
+
if (this.logWarn) this.emit('warn', `Following redirect to ${locationHeader}`);
|
|
159
159
|
try {
|
|
160
160
|
const code = await followRedirect(locationHeader);
|
|
161
161
|
resolve(code);
|
|
162
162
|
return;
|
|
163
163
|
} catch (err) {
|
|
164
|
-
this.emit('warn', `Follow redirect failed: ${err}`);
|
|
164
|
+
if (this.logWarn) this.emit('warn', `Follow redirect failed: ${err}`);
|
|
165
165
|
reject(err);
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
this.emit('warn', 'Authorization code not found in response');
|
|
170
|
+
if (this.logWarn) this.emit('warn', 'Authorization code not found in response');
|
|
171
171
|
reject(new Error('Authorization code not found'));
|
|
172
172
|
} catch (err) {
|
|
173
|
-
this.emit('warn', `extractCodeFromResponse error: ${err}`);
|
|
173
|
+
if (this.logWarn) this.emit('warn', `extractCodeFromResponse error: ${err}`);
|
|
174
174
|
reject(err);
|
|
175
175
|
}
|
|
176
176
|
});
|
|
@@ -178,7 +178,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
178
178
|
|
|
179
179
|
async submitFormPost(actionUrl, code, state) {
|
|
180
180
|
const formData = new URLSearchParams({ code, state });
|
|
181
|
-
this.emit('warn', `Submitting form_post to ${actionUrl}`);
|
|
181
|
+
if (this.logWarn) this.emit('warn', `Submitting form_post to ${actionUrl}`);
|
|
182
182
|
const res = await this.client.post(actionUrl, formData.toString(), {
|
|
183
183
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
184
184
|
maxRedirects: 0,
|
|
@@ -191,7 +191,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
191
191
|
if (match) return match[1];
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
this.emit('warn', 'Code not found after form_post submission');
|
|
194
|
+
if (this.logWarn) this.emit('warn', 'Code not found after form_post submission');
|
|
195
195
|
throw new Error('Code not found after form_post submission');
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -213,7 +213,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
213
213
|
});
|
|
214
214
|
|
|
215
215
|
const tokens = tokenResponse.data;
|
|
216
|
-
this.emit('warn', `Token obtained: ${JSON.stringify(tokens)}`);
|
|
216
|
+
if (this.logWarn) this.emit('warn', `Token obtained: ${JSON.stringify(tokens)}`);
|
|
217
217
|
return tokens;
|
|
218
218
|
|
|
219
219
|
} catch (err) {
|
package/src/restful.js
CHANGED
|
@@ -65,7 +65,7 @@ class RestFul extends EventEmitter {
|
|
|
65
65
|
|
|
66
66
|
// Start the server
|
|
67
67
|
app.listen(this.port, () => {
|
|
68
|
-
this.emit('connected', `RESTful started on port: ${this.
|
|
68
|
+
this.emit('connected', `RESTful started on port: ${this.port}`);
|
|
69
69
|
});
|
|
70
70
|
} catch (error) {
|
|
71
71
|
if (this.logWarn) this.emit('warn', `RESTful Connect error: ${error}`);
|