homebridge-melcloud-control 4.0.0-beta.246 → 4.0.0-beta.248

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.246",
4
+ "version": "4.0.0-beta.248",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -40,8 +40,6 @@
40
40
  "axios": "^1.12.2",
41
41
  "express": "^5.1.0",
42
42
  "puppeteer": "^24.26.1",
43
- "tough-cookie": "^6.0.0",
44
- "axios-cookiejar-support": "^6.0.4",
45
43
  "jsdom": "^27.0.1"
46
44
  },
47
45
  "keywords": [
@@ -12,8 +12,8 @@ const TOKEN_ENDPOINT = 'https://auth.melcloudhome.com/connect/token';
12
12
  const AUTHORIZE_ENDPOINT = 'https://auth.melcloudhome.com/connect/authorize';
13
13
 
14
14
  class MelCloudHomeToken {
15
- constructor() {
16
-
15
+ constructor(log) {
16
+ this.log = log;
17
17
  }
18
18
 
19
19
  // --- PKCE ---
@@ -43,7 +43,7 @@ class MelCloudHomeToken {
43
43
  if (cookies.length > 0) reqOptions.headers['Cookie'] = cookies.join('; ');
44
44
 
45
45
  const req = https.request(reqOptions, (res) => {
46
- // collect Set-Cookie
46
+ // Collect Set-Cookie
47
47
  const setCookie = res.headers['set-cookie'];
48
48
  if (setCookie) {
49
49
  setCookie.forEach(c => {
@@ -54,7 +54,7 @@ class MelCloudHomeToken {
54
54
  });
55
55
  }
56
56
 
57
- // follow redirect
57
+ // Follow redirects
58
58
  if ([301, 302, 303].includes(res.statusCode) && res.headers.location) {
59
59
  const redirectUrl = res.headers.location.startsWith('http')
60
60
  ? res.headers.location
@@ -88,14 +88,14 @@ class MelCloudHomeToken {
88
88
  const form = dom.window.document.querySelector('form');
89
89
  if (!form) throw new Error('Login form not found');
90
90
 
91
- return { formHtml: resp.html, cookies: resp.cookies, form };
91
+ return { form, cookies: resp.cookies, loginPageUrl: resp.finalUrl };
92
92
  }
93
93
 
94
- // --- Submit login ---
94
+ // --- Submit login and handle form_post ---
95
95
  async submitLogin(form, email, password, cookies, loginPageUrl) {
96
- console.log('Submitting login credentials...');
96
+ console.log('Submitting login credentials...');
97
97
 
98
- // Collect all hidden inputs
98
+ // Step 1: Collect all hidden inputs
99
99
  const formData = new URLSearchParams();
100
100
  form.querySelectorAll('input').forEach(input => {
101
101
  const name = input.getAttribute('name');
@@ -112,11 +112,35 @@ class MelCloudHomeToken {
112
112
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
113
113
  }, formData.toString(), cookies);
114
114
 
115
- // Try to extract code from redirect
116
- const codeFromLocation = resp.finalUrl.match(/[?&]code=([^&]+)/);
117
- if (codeFromLocation) return codeFromLocation[1];
115
+ // Step 2: Check if body contains hidden form_post
116
+ const dom = new JSDOM(resp.html);
117
+ const postForm = dom.window.document.querySelector('form');
118
+ if (postForm) {
119
+ // Collect all hidden inputs from form_post
120
+ const postData = new URLSearchParams();
121
+ postForm.querySelectorAll('input').forEach(input => {
122
+ const name = input.getAttribute('name');
123
+ if (name) postData.append(name, input.getAttribute('value') || '');
124
+ });
125
+
126
+ const postAction = new URL(postForm.getAttribute('action'), resp.finalUrl).toString();
127
+ const postResp = await this.httpsRequest(postAction, {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
130
+ }, postData.toString(), cookies);
131
+
132
+ // Try to extract code from redirect URL
133
+ const codeMatch = postResp.finalUrl.match(/[?&]code=([^&]+)/);
134
+ if (codeMatch) return codeMatch[1];
135
+
136
+ // Or from body (JS redirect)
137
+ const codeFromBody = postResp.html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
138
+ if (codeFromBody) return codeFromBody[1];
139
+ }
118
140
 
119
- // Or from body (JS redirect or hidden form_post)
141
+ // Fallback: check resp finalUrl or body
142
+ const codeMatch = resp.finalUrl.match(/[?&]code=([^&]+)/);
143
+ if (codeMatch) return codeMatch[1];
120
144
  const codeFromBody = resp.html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
121
145
  if (codeFromBody) return codeFromBody[1];
122
146
 
@@ -165,15 +189,15 @@ class MelCloudHomeToken {
165
189
  authUrl.searchParams.set('code_challenge_method', 'S256');
166
190
  authUrl.searchParams.set('state', state);
167
191
 
168
- // 1️⃣ get login page
169
- const { form, cookies } = await this.getLoginPage(authUrl.toString());
192
+ // 1️⃣ Get login page
193
+ const { form, cookies, loginPageUrl } = await this.getLoginPage(authUrl.toString());
170
194
 
171
- // 2️⃣ submit login
172
- const authCode = await this.submitLogin(form, email, password, cookies, authUrl.toString());
195
+ // 2️⃣ Submit login + handle form_post
196
+ const authCode = await this.submitLogin(form, email, password, cookies, loginPageUrl);
173
197
 
174
- // 3️⃣ exchange code for tokens
198
+ // 3️⃣ Exchange code for tokens
175
199
  const tokens = await this.exchangeCodeForTokens(authCode, pkce.verifier);
176
- console.log('✓ Successfully obtained OAuth tokens');
200
+ console.log('✓ Successfully obtained OAuth tokens');
177
201
  return tokens;
178
202
  }
179
203
  }