homebridge-melcloud-control 4.0.0-beta.294 → 4.0.0-beta.296

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.0.0-beta.294",
4
+ "version": "4.0.0-beta.296",
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
@@ -305,8 +305,7 @@ class MelCloud extends EventEmitter {
305
305
  const code = await melCloudHomeToken.extractCodeFromHtml(html);
306
306
 
307
307
  console.log(code);
308
- return
309
-
308
+ return
310
309
  // Check for form_post response (HTML with hidden form)
311
310
  const formCodeMatch = html.match(/name="code"\s+value="([^"]+)"/);
312
311
  const formStateMatch = html.match(/name="state"\s+value="([^"]+)"/);
@@ -1,5 +1,7 @@
1
+ import axios from 'axios';
2
+ import { wrapper } from 'axios-cookiejar-support';
3
+ import { CookieJar } from 'tough-cookie';
1
4
  import crypto from 'crypto';
2
- import * as cheerio from 'cheerio';
3
5
 
4
6
  const MOBILE_USER_AGENT = 'MonitorAndControl.App.Mobile/35 CFNetwork/3860.100.1 Darwin/25.0.0';
5
7
  const CLIENT_ID = 'homemobile';
@@ -7,11 +9,13 @@ const REDIRECT_URI = 'melcloudhome://';
7
9
  const SCOPE = 'openid profile email offline_access IdentityServerApi';
8
10
  const TOKEN_ENDPOINT = 'https://auth.melcloudhome.com/connect/token';
9
11
  const AUTHORIZE_ENDPOINT = 'https://auth.melcloudhome.com/connect/authorize';
10
- const LOGIN_ENDPOINT = 'https://auth.melcloudhome.com/Account/Login';
11
12
 
12
13
  class MelCloudHomeToken {
13
14
  constructor(log = console) {
14
15
  this.log = log;
16
+ const jar = new CookieJar();
17
+ this.client = wrapper(axios.create({ jar, withCredentials: true }));
18
+ this.client.defaults.headers['User-Agent'] = MOBILE_USER_AGENT;
15
19
  }
16
20
 
17
21
  generatePKCE() {
@@ -24,54 +28,75 @@ class MelCloudHomeToken {
24
28
  return crypto.randomBytes(32).toString('hex');
25
29
  }
26
30
 
27
- async buildAuthorizeUrl() {
28
- const pkce = this.generatePKCE();
29
- this.pkce = pkce;
30
- const state = this.generateState();
31
- this.state = state;
32
-
33
- const authUrl = new URL(AUTHORIZE_ENDPOINT);
34
- authUrl.searchParams.set('client_id', CLIENT_ID);
35
- authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
36
- authUrl.searchParams.set('response_type', 'code');
37
- authUrl.searchParams.set('scope', SCOPE);
38
- authUrl.searchParams.set('code_challenge', pkce.challenge);
39
- authUrl.searchParams.set('code_challenge_method', 'S256');
40
- authUrl.searchParams.set('state', state);
41
-
42
- return authUrl.toString();
31
+ async getLoginPage(authUrl) {
32
+ const res = await this.client.get(authUrl, { maxRedirects: 10 });
33
+ return { url: res.request.res.responseUrl };
43
34
  }
44
35
 
45
36
  async extractCodeFromHtml(html) {
46
- const $ = cheerio.load(html);
47
37
  let code = null;
48
38
 
49
- const formCode = html.match(/name="code"\s+value="([^"]+)"/);
50
- if (formCode) return formCode[1];
39
+ // 1️⃣ Sprawdzenie, czy code jest w URL
40
+ const url1 = page.url();
41
+ const urlMatch = url1.match(/[?&]code=([^&]+)/);
42
+ if (urlMatch) {
43
+ console.log('Authorization code found in URL');
44
+ return urlMatch[1];
45
+ }
51
46
 
52
- const deepLink = html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
53
- if (deepLink) return deepLink[1];
47
+ // Check for form_post response (HTML with hidden form)
48
+ const formCodeMatch = html.match(/name="code"\s+value="([^"]+)"/);
49
+ const formStateMatch = html.match(/name="state"\s+value="([^"]+)"/);
50
+ const formActionMatch = html.match(/action="([^"]+)"/);
51
+
52
+ if (formCodeMatch && formStateMatch && formActionMatch) {
53
+ console.log('Found form_post response, submitting to callback endpoint...');
54
+ this.submitFormPost(formActionMatch[1], formCodeMatch[1], formStateMatch[1], cookies)
55
+ .then(code => resolve(code))
56
+ .catch(err => reject(err));
57
+ return;
58
+ }
54
59
 
55
- const dataUrl = $('[data-url]').attr('data-url');
56
- if (dataUrl && dataUrl.includes('code=')) {
57
- const parsedUrl = new URL(dataUrl);
58
- return parsedUrl.searchParams.get('code');
60
+ // Check for melcloudhome:// redirect in JS
61
+ const bodyCodeMatch = html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
62
+ if (bodyCodeMatch) {
63
+ console.log('Found authorization code in response body (JS)');
64
+ resolve(bodyCodeMatch[1]);
65
+ return;
59
66
  }
60
67
 
61
- $('a[href]').each((_, el) => {
62
- const href = $(el).attr('href');
63
- if (href && href.includes('code=')) {
64
- const parsedUrl = new URL(href);
65
- code = parsedUrl.searchParams.get('code');
66
- }
67
- });
68
+ // 🆕 NEW: Check for data-url with code parameter (like in 500 error pages)
69
+ const dataUrlMatch = html.match(/data-url="[^"]*code=([^&"']+)/);
70
+ if (dataUrlMatch) {
71
+ console.log('Found authorization code in data-url attribute');
72
+ resolve(dataUrlMatch[1]);
73
+ return;
74
+ }
68
75
 
69
76
  return code;
70
77
  }
71
78
 
72
-
73
-
74
-
79
+ async buildAuthorizeUrl() {
80
+ try {
81
+ const pkce = this.generatePKCE();
82
+ const state = this.generateState();
83
+ const authUrl = new URL(AUTHORIZE_ENDPOINT);
84
+ authUrl.searchParams.set('client_id', CLIENT_ID);
85
+ authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
86
+ authUrl.searchParams.set('response_type', 'code');
87
+ authUrl.searchParams.set('scope', SCOPE);
88
+ authUrl.searchParams.set('code_challenge', pkce.challenge);
89
+ authUrl.searchParams.set('code_challenge_method', 'S256');
90
+ authUrl.searchParams.set('state', state);
91
+
92
+ const loginPage = await this.getLoginPage(authUrl.toString());
93
+
94
+ return loginPage.url;
95
+ } catch (error) {
96
+ console.error('Failed to obtain OAuth tokens:', error);
97
+ throw error;
98
+ }
99
+ }
75
100
  }
76
101
 
77
102
  export default MelCloudHomeToken;