homebridge-melcloud-control 4.0.0-beta.257 → 4.0.0-beta.258

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.257",
4
+ "version": "4.0.0-beta.258",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -41,7 +41,8 @@
41
41
  "express": "^5.1.0",
42
42
  "puppeteer": "^24.26.1",
43
43
  "axios-cookiejar-support": "^6.0.4",
44
- "tough-cookie": "^6.0.0"
44
+ "tough-cookie": "^6.0.0",
45
+ "cheerio": "^1.1.2"
45
46
  },
46
47
  "keywords": [
47
48
  "homebridge",
@@ -2,6 +2,7 @@ import axios from 'axios';
2
2
  import { wrapper } from 'axios-cookiejar-support';
3
3
  import { CookieJar } from 'tough-cookie';
4
4
  import crypto from 'crypto';
5
+ import cheerio from 'cheerio';
5
6
 
6
7
  const MOBILE_USER_AGENT = 'MonitorAndControl.App.Mobile/35 CFNetwork/3860.100.1 Darwin/25.0.0';
7
8
  const CLIENT_ID = 'homemobile';
@@ -29,40 +30,84 @@ class MelCloudHomeToken {
29
30
  }
30
31
 
31
32
  async getLoginPage(authUrl) {
32
- this.log.debug('Getting login page...');
33
+ console.log('Getting login page...');
33
34
  const res = await this.client.get(authUrl, { maxRedirects: 10 });
34
- const csrfMatch = res.data.match(/name="_csrf"\s+value="([^"]+)"/);
35
- if (!csrfMatch) throw new Error('Could not extract CSRF token from login page');
36
- return { html: res.data, csrf: csrfMatch[1], url: res.request.res.responseUrl };
35
+ const $ = cheerio.load(res.data);
36
+ const csrf = $('input[name="_csrf"]').attr('value');
37
+ if (!csrf) throw new Error('Could not extract CSRF token from login page');
38
+ console.log('CSRF token found:', csrf);
39
+ return { csrf, url: res.request.res.responseUrl };
40
+ }
41
+
42
+ async submitFormPost(actionUrl, code, state) {
43
+ console.log('Submitting form_post to callback URL:', actionUrl);
44
+ const form = new URLSearchParams({ code, state });
45
+ const res = await this.client.post(actionUrl, form.toString(), {
46
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
47
+ maxRedirects: 0,
48
+ validateStatus: (status) => status >= 200 && status < 400,
49
+ });
50
+
51
+ const location = res.headers.location;
52
+ if (location?.startsWith('melcloudhome://')) {
53
+ const codeMatch = location.match(/[?&]code=([^&]+)/);
54
+ if (codeMatch) {
55
+ console.log('Authorization code obtained from form_post redirect:', codeMatch[1]);
56
+ return codeMatch[1];
57
+ }
58
+ }
59
+ throw new Error('No authorization code found in form_post response');
37
60
  }
38
61
 
39
62
  async submitLogin(loginUrl, csrf, email, password) {
63
+ console.log('Submitting login credentials to:', loginUrl);
40
64
  const form = new URLSearchParams({ _csrf: csrf, username: email, password });
41
65
  const res = await this.client.post(loginUrl, form.toString(), {
42
66
  headers: {
43
67
  'Content-Type': 'application/x-www-form-urlencoded',
44
68
  Referer: loginUrl,
45
69
  },
46
- maxRedirects: 0, // manual redirect handling
70
+ maxRedirects: 0,
47
71
  validateStatus: (status) => status >= 200 && status < 400,
48
72
  });
49
73
 
50
- // Try to get code from Location header
74
+ console.log('Login POST status:', res.status);
75
+
76
+ // Spróbuj pobrać kod z nagłówka Location
51
77
  const location = res.headers.location;
52
78
  if (location?.startsWith('melcloudhome://')) {
53
79
  const codeMatch = location.match(/[?&]code=([^&]+)/);
54
- if (codeMatch) return codeMatch[1];
80
+ if (codeMatch) {
81
+ console.log('Authorization code obtained from Location header:', codeMatch[1]);
82
+ return codeMatch[1];
83
+ }
55
84
  }
56
85
 
57
- // Try to get code from HTML (form_post or JS)
58
- const codeMatch = res.data.match(/name="code"\s+value="([^"]+)"/) ||
59
- res.data.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
60
- if (codeMatch) return codeMatch[1];
86
+ // Spróbuj parsować HTML w poszukiwaniu form_post
87
+ const $ = cheerio.load(res.data);
88
+ const formElement = $('form');
89
+ if (formElement.length) {
90
+ const action = formElement.attr('action');
91
+ const code = $('input[name="code"]').attr('value');
92
+ const state = $('input[name="state"]').attr('value');
93
+ if (action && code && state) {
94
+ console.log('Found form_post. Action:', action, 'Code:', code, 'State:', state);
95
+ return this.submitFormPost(action, code, state);
96
+ }
97
+ }
98
+
99
+ // Spróbuj wyciągnąć kod z JS w ciele strony
100
+ const bodyCodeMatch = res.data.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
101
+ if (bodyCodeMatch) {
102
+ console.log('Authorization code obtained from HTML/JS body:', bodyCodeMatch[1]);
103
+ return bodyCodeMatch[1];
104
+ }
61
105
 
62
106
  throw new Error('Authorization code not found in login response');
63
107
  }
64
108
 
65
109
  async exchangeCodeForTokens(code, codeVerifier) {
110
+ console.log('Exchanging authorization code for tokens...');
66
111
  const form = new URLSearchParams({
67
112
  grant_type: 'authorization_code',
68
113
  code,
@@ -79,6 +124,8 @@ class MelCloudHomeToken {
79
124
  });
80
125
 
81
126
  if (res.data.error) throw new Error(`Token exchange failed: ${res.data.error_description || res.data.error}`);
127
+
128
+ console.log('Tokens obtained:', res.data);
82
129
  return {
83
130
  accessToken: res.data.access_token,
84
131
  refreshToken: res.data.refresh_token,
@@ -89,7 +136,7 @@ class MelCloudHomeToken {
89
136
 
90
137
  async getTokensWithCredentials(email, password) {
91
138
  try {
92
- this.log.info('Obtaining OAuth tokens...');
139
+ console.log('Starting OAuth flow...');
93
140
  const pkce = this.generatePKCE();
94
141
  const state = this.generateState();
95
142
 
@@ -106,16 +153,17 @@ class MelCloudHomeToken {
106
153
  const authCode = await this.submitLogin(loginPage.url, loginPage.csrf, email, password);
107
154
  const tokens = await this.exchangeCodeForTokens(authCode, pkce.verifier);
108
155
 
109
- this.log.info('✓ Successfully obtained OAuth tokens');
156
+ console.log('✓ Successfully obtained OAuth tokens');
110
157
  return tokens;
111
158
  } catch (error) {
112
- this.log.error('Failed to obtain OAuth tokens:', error);
159
+ console.error('Failed to obtain OAuth tokens:', error);
113
160
  throw error;
114
161
  }
115
162
  }
116
163
  }
117
164
 
118
165
 
166
+
119
167
  export default MelCloudHomeToken;
120
168
 
121
169