homebridge-melcloud-control 4.0.0-beta.3 → 4.0.0-beta.31
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 +5 -8
- package/package.json +1 -1
- package/src/melcloud.js +77 -74
package/index.js
CHANGED
|
@@ -64,7 +64,7 @@ class MelCloudPlatform {
|
|
|
64
64
|
passwd: 'removed',
|
|
65
65
|
mqtt: {
|
|
66
66
|
auth: {
|
|
67
|
-
...
|
|
67
|
+
...account.mqtt?.auth,
|
|
68
68
|
passwd: 'removed',
|
|
69
69
|
}
|
|
70
70
|
},
|
|
@@ -76,7 +76,7 @@ class MelCloudPlatform {
|
|
|
76
76
|
const accountFile = `${prefDir}/${accountName}_Account`;
|
|
77
77
|
const buildingsFile = `${prefDir}/${accountName}_Buildings`;
|
|
78
78
|
const devicesFile = `${prefDir}/${accountName}_Devices`;
|
|
79
|
-
const
|
|
79
|
+
const cookiesFile = `${prefDir}/${accountName}_Cookies`;
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
//set account refresh interval
|
|
@@ -88,21 +88,18 @@ class MelCloudPlatform {
|
|
|
88
88
|
.on('start', async () => {
|
|
89
89
|
try {
|
|
90
90
|
//melcloud account
|
|
91
|
-
const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile,
|
|
91
|
+
const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile, cookiesFile, logLevel.warn, logLevel.debug, false)
|
|
92
92
|
.on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
|
|
93
93
|
.on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
|
|
94
94
|
.on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
|
|
95
95
|
.on('warn', (msg) => logLevel.warn && log.warn(`${accountName}, ${msg}`))
|
|
96
96
|
.on('error', (msg) => logLevel.error && log.error(`${accountName}, ${msg}`));
|
|
97
97
|
|
|
98
|
-
await melCloud.connectHomeCoocies()
|
|
99
|
-
return;
|
|
100
|
-
|
|
101
|
-
|
|
102
98
|
//connect
|
|
103
99
|
let response;
|
|
104
100
|
try {
|
|
105
|
-
response = await melCloud.connect();
|
|
101
|
+
response = await melCloud.connect('home');
|
|
102
|
+
return;
|
|
106
103
|
} catch (error) {
|
|
107
104
|
if (logLevel.error) log.error(`${accountName}, Connect error: ${error.message ?? error}`);
|
|
108
105
|
return;
|
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.31",
|
|
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
|
@@ -7,7 +7,7 @@ import Functions from './functions.js';
|
|
|
7
7
|
import { ApiUrls, ApiUrlsHome } from './constants.js';
|
|
8
8
|
|
|
9
9
|
class MelCloud extends EventEmitter {
|
|
10
|
-
constructor(user, passwd, language, accountFile, buildingsFile, devicesFile,
|
|
10
|
+
constructor(user, passwd, language, accountFile, buildingsFile, devicesFile, cookiesFile, logWarn, logDebug, requestConfig) {
|
|
11
11
|
super();
|
|
12
12
|
this.user = user;
|
|
13
13
|
this.passwd = passwd;
|
|
@@ -15,7 +15,7 @@ class MelCloud extends EventEmitter {
|
|
|
15
15
|
this.accountFile = accountFile;
|
|
16
16
|
this.buildingsFile = buildingsFile;
|
|
17
17
|
this.devicesFile = devicesFile;
|
|
18
|
-
this.
|
|
18
|
+
this.cookiesFile = cookiesFile;
|
|
19
19
|
this.logWarn = logWarn;
|
|
20
20
|
this.logDebug = logDebug;
|
|
21
21
|
this.requestConfig = requestConfig;
|
|
@@ -109,7 +109,7 @@ class MelCloud extends EventEmitter {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
async
|
|
112
|
+
async connectMelCloud() {
|
|
113
113
|
if (this.logDebug) this.emit('debug', `Connecting to MELCloud`);
|
|
114
114
|
|
|
115
115
|
try {
|
|
@@ -166,14 +166,15 @@ class MelCloud extends EventEmitter {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
async
|
|
169
|
+
async connectMelCloudHome() {
|
|
170
170
|
if (this.logDebug) this.emit('debug', `Connecting to MELCloud Home`);
|
|
171
171
|
|
|
172
172
|
try {
|
|
173
|
-
const
|
|
174
|
-
const
|
|
173
|
+
const coocies = await this.getCookies();
|
|
174
|
+
const c1 = coocies.C1.trim();
|
|
175
|
+
const c2 = coocies.C2.trim();
|
|
175
176
|
|
|
176
|
-
const
|
|
177
|
+
const cookies = [
|
|
177
178
|
'__Secure-monitorandcontrol=chunks-2',
|
|
178
179
|
`__Secure-monitorandcontrolC1=${c1}`,
|
|
179
180
|
`__Secure-monitorandcontrolC2=${c2}`,
|
|
@@ -189,8 +190,8 @@ class MelCloud extends EventEmitter {
|
|
|
189
190
|
headers: {
|
|
190
191
|
'Accept': '*/*',
|
|
191
192
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
192
|
-
'Cookie':
|
|
193
|
-
'User-Agent': 'homebridge-melcloud-
|
|
193
|
+
'Cookie': cookies,
|
|
194
|
+
'User-Agent': 'homebridge-melcloud-control/4.0.0',
|
|
194
195
|
'DNT': '1',
|
|
195
196
|
'Origin': 'https://melcloudhome.com',
|
|
196
197
|
'Referer': 'https://melcloudhome.com/dashboard',
|
|
@@ -203,7 +204,7 @@ class MelCloud extends EventEmitter {
|
|
|
203
204
|
|
|
204
205
|
const response = await axiosInstance.get(ApiUrlsHome.GetUserContext);
|
|
205
206
|
const homeData = response.data;
|
|
206
|
-
if (this.logDebug) this.emit('debug', `
|
|
207
|
+
if (this.logDebug) this.emit('debug', `MELCloud Home data: ${JSON.stringify(homeData, null, 2)}`);
|
|
207
208
|
|
|
208
209
|
this.emit('success', `Connect to MELCloud Home Success`);
|
|
209
210
|
|
|
@@ -213,81 +214,83 @@ class MelCloud extends EventEmitter {
|
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
async
|
|
217
|
-
const
|
|
218
|
-
loginUrl.searchParams.set('client_id', '3g4d5l5kivuqi7oia68gib7uso');
|
|
219
|
-
loginUrl.searchParams.set('redirect_uri', 'https://auth.melcloudhome.com/signin-oidc-meu');
|
|
220
|
-
loginUrl.searchParams.set('response_type', 'code');
|
|
221
|
-
loginUrl.searchParams.set('scope', 'openid profile');
|
|
222
|
-
loginUrl.searchParams.set('response_mode', 'form_post');
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const browser = await puppeteer.launch({
|
|
227
|
-
headless: true,
|
|
228
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
|
229
|
-
});
|
|
230
|
-
|
|
217
|
+
async getCookies() {
|
|
218
|
+
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
|
231
219
|
const page = await browser.newPage();
|
|
232
220
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
'button[name="signIn"]',
|
|
247
|
-
'button.btn-primary',
|
|
248
|
-
'button:has-text("Sign in")',
|
|
249
|
-
'button:has-text("Zaloguj")'
|
|
250
|
-
];
|
|
251
|
-
|
|
252
|
-
// Kliknij "Sign in" (przycisk AWS Cognito)
|
|
253
|
-
let buttonFound = false;
|
|
254
|
-
for (const selector of buttonSelectors) {
|
|
255
|
-
const button = await page.$(selector);
|
|
256
|
-
if (button) {
|
|
257
|
-
console.log(`Found submit button: ${selector}`);
|
|
258
|
-
await Promise.all([
|
|
259
|
-
button.click(),
|
|
260
|
-
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 })
|
|
261
|
-
]);
|
|
262
|
-
buttonFound = true;
|
|
263
|
-
break;
|
|
221
|
+
try {
|
|
222
|
+
this.emit('warn', 'Opening login page...');
|
|
223
|
+
await page.goto(ApiUrlsHome.BaseURL, { waitUntil: 'networkidle2' });
|
|
224
|
+
|
|
225
|
+
// Kliknij przycisk logowania
|
|
226
|
+
const buttons = await page.$$('button.btn--blue');
|
|
227
|
+
let loginBtn = null;
|
|
228
|
+
for (const btn of buttons) {
|
|
229
|
+
const text = await page.evaluate(el => el.textContent, btn);
|
|
230
|
+
if (text.trim() === 'Zaloguj') {
|
|
231
|
+
loginBtn = btn;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
264
234
|
}
|
|
265
|
-
}
|
|
266
235
|
|
|
267
|
-
|
|
268
|
-
throw new Error('❌ Could not find login button on the page.');
|
|
269
|
-
}
|
|
236
|
+
if (!loginBtn) throw new Error('Login button not found on homepage');
|
|
270
237
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
238
|
+
await Promise.all([
|
|
239
|
+
loginBtn.click(),
|
|
240
|
+
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })
|
|
241
|
+
]);
|
|
274
242
|
|
|
275
|
-
|
|
276
|
-
|
|
243
|
+
// Poczekaj, aż strona przekieruje do Cognito login
|
|
244
|
+
await page.waitForSelector('input[name="username"]', { timeout: 15000 });
|
|
277
245
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
246
|
+
this.emit('warn', 'Typing credentials...');
|
|
247
|
+
await page.type('input[name="username"]', this.user, { delay: 50 });
|
|
248
|
+
await page.type('input[name="password"]', this.passwd, { delay: 50 });
|
|
281
249
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
250
|
+
// Kliknij przycisk logowania
|
|
251
|
+
const button1 = await page.$('input[type="submit"]');
|
|
252
|
+
await Promise.all([
|
|
253
|
+
button1.click(),
|
|
254
|
+
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })
|
|
255
|
+
]);
|
|
285
256
|
|
|
286
|
-
|
|
257
|
+
let c1 = null, c2 = null;
|
|
258
|
+
const start = Date.now();
|
|
287
259
|
|
|
288
|
-
|
|
260
|
+
// Loop max 20s, czekamy aż pojawią się cookies
|
|
261
|
+
while ((!c1 || !c2) && Date.now() - start < 20000) {
|
|
262
|
+
const cookies = await page.cookies();
|
|
263
|
+
c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
|
|
264
|
+
c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || c2;
|
|
265
|
+
if (!c1 || !c2) await new Promise(r => setTimeout(r, 500));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!c1 || !c2) {
|
|
269
|
+
await browser.close();
|
|
270
|
+
throw new Error('Cookies C1/C2 not found after login');
|
|
271
|
+
}
|
|
289
272
|
|
|
290
|
-
|
|
273
|
+
// Zapis do pliku
|
|
274
|
+
const data = { C1: c1, C2: c2, date: new Date().toISOString() };
|
|
275
|
+
await this.functions.saveData(this.cookiesFile, data);
|
|
276
|
+
|
|
277
|
+
this.emit('warn', 'Login successful.');
|
|
278
|
+
return data;
|
|
279
|
+
} catch (err) {
|
|
280
|
+
this.emit('error', `Login failed: ${err.message}`);
|
|
281
|
+
return null;
|
|
282
|
+
} finally {
|
|
283
|
+
await browser.close();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async connect(mode) {
|
|
288
|
+
switch (mode) {
|
|
289
|
+
case 'home':
|
|
290
|
+
return await this.connectMelCloudHome();
|
|
291
|
+
default:
|
|
292
|
+
return await this.connectMelCloud();
|
|
293
|
+
}
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
async send(accountInfo) {
|