homebridge-melcloud-control 4.0.0-beta.249 → 4.0.0-beta.250

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.249",
4
+ "version": "4.0.0-beta.250",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -43,7 +43,6 @@ 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
47
46
  const setCookie = res.headers['set-cookie'];
48
47
  if (setCookie) {
49
48
  setCookie.forEach(c => {
@@ -54,7 +53,6 @@ class MelCloudHomeToken {
54
53
  });
55
54
  }
56
55
 
57
- // Follow redirects
58
56
  if ([301, 302, 303].includes(res.statusCode) && res.headers.location) {
59
57
  const redirectUrl = res.headers.location.startsWith('http')
60
58
  ? res.headers.location
@@ -91,11 +89,11 @@ class MelCloudHomeToken {
91
89
  return { form, cookies: resp.cookies, loginPageUrl: resp.finalUrl };
92
90
  }
93
91
 
94
- // --- Submit login and handle form_post ---
92
+ // --- Submit login with debug ---
95
93
  async submitLogin(form, email, password, cookies, loginPageUrl) {
96
94
  console.log('Submitting login credentials...');
97
95
 
98
- // Step 1: Collect all hidden inputs
96
+ // 1️⃣ Zbieramy wszystkie hidden inputy
99
97
  const formData = new URLSearchParams();
100
98
  form.querySelectorAll('input').forEach(input => {
101
99
  const name = input.getAttribute('name');
@@ -106,61 +104,75 @@ class MelCloudHomeToken {
106
104
  formData.set('username', email);
107
105
  formData.set('password', password);
108
106
 
107
+ console.log('Login form data (hidden + credentials):');
108
+ for (const [key, value] of formData.entries()) {
109
+ console.log(` ${key} = ${value}`);
110
+ }
111
+
109
112
  const actionUrl = new URL(form.getAttribute('action'), loginPageUrl).toString();
110
113
  const resp = await this.httpsRequest(actionUrl, {
111
114
  method: 'POST',
112
115
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
113
116
  }, formData.toString(), cookies);
114
117
 
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
+ // 2️⃣ Obsługa form_post (Cognito auto-submit)
119
+ const domPost = new JSDOM(resp.html);
120
+ const postForm = domPost.window.document.querySelector('form');
118
121
  if (postForm) {
119
- // Collect all hidden inputs from form_post
120
122
  const postData = new URLSearchParams();
121
123
  postForm.querySelectorAll('input').forEach(input => {
122
124
  const name = input.getAttribute('name');
123
125
  if (name) postData.append(name, input.getAttribute('value') || '');
124
126
  });
125
127
 
128
+ console.log('Form_post hidden inputs:');
129
+ for (const [key, value] of postData.entries()) {
130
+ console.log(` ${key} = ${value}`);
131
+ }
132
+
126
133
  const postAction = new URL(postForm.getAttribute('action'), resp.finalUrl).toString();
127
134
  const postResp = await this.httpsRequest(postAction, {
128
135
  method: 'POST',
129
136
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
130
137
  }, postData.toString(), cookies);
131
138
 
132
- // Try to extract code from redirect URL
133
- const codeMatch = postResp.finalUrl.match(/[?&]code=([^&]+)/);
134
- if (codeMatch) return codeMatch[1];
139
+ // 3️⃣ Wyciągamy authorization code
140
+ let codeMatch = postResp.finalUrl.match(/[?&]code=([^&]+)/);
141
+ if (!codeMatch) codeMatch = postResp.html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
135
142
 
136
- // Or from body (JS redirect)
137
- const codeFromBody = postResp.html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
138
- if (codeFromBody) return codeFromBody[1];
143
+ if (codeMatch) {
144
+ const code = codeMatch[1];
145
+ console.log(`Authorization code obtained: ${code}`);
146
+ return code;
147
+ }
139
148
  }
140
149
 
141
- // Fallback: check resp finalUrl or body
142
- const codeMatch = resp.finalUrl.match(/[?&]code=([^&]+)/);
143
- if (codeMatch) return codeMatch[1];
144
- const codeFromBody = resp.html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
145
- if (codeFromBody) return codeFromBody[1];
150
+ // Fallback: sprawdzamy resp finalUrl i body
151
+ let codeMatch = resp.finalUrl.match(/[?&]code=([^&]+)/);
152
+ if (!codeMatch) codeMatch = resp.html.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
153
+ if (codeMatch) {
154
+ const code = codeMatch[1];
155
+ console.log(`Authorization code obtained: ${code}`);
156
+ return code;
157
+ }
146
158
 
147
159
  throw new Error('No authorization code found after login');
148
160
  }
149
161
 
150
- // --- Exchange code for tokens ---
162
+ // --- Exchange code for tokens (debug) ---
151
163
  async exchangeCodeForTokens(code, codeVerifier) {
152
- console.log('Exchanging authorization code for tokens...');
164
+ console.log('Exchanging authorization code for tokens...');
165
+ console.log(`Authorization code: ${code}`);
166
+ console.log(`Code verifier: ${codeVerifier}`);
153
167
 
154
- // Dane POST do token endpoint
155
168
  const tokenData = new URLSearchParams({
156
169
  grant_type: 'authorization_code',
157
170
  code,
158
- redirect_uri: REDIRECT_URI, // musi być dokładnie ten sam co w authorize URL
171
+ redirect_uri: REDIRECT_URI,
159
172
  client_id: CLIENT_ID,
160
173
  code_verifier: codeVerifier,
161
174
  });
162
175
 
163
- // Wysyłamy POST bez Basic Auth (PKCE wystarczy)
164
176
  const resp = await this.httpsRequest(TOKEN_ENDPOINT, {
165
177
  method: 'POST',
166
178
  headers: {
@@ -169,33 +181,23 @@ class MelCloudHomeToken {
169
181
  }
170
182
  }, tokenData.toString());
171
183
 
172
- // Parsujemy odpowiedź
184
+ console.log('Token endpoint response:', resp.html);
185
+
173
186
  let tokens;
174
- try {
175
- tokens = JSON.parse(resp.html);
176
- } catch (err) {
177
- console.log('Failed to parse token response:', resp.html);
178
- throw err;
179
- }
187
+ try { tokens = JSON.parse(resp.html); }
188
+ catch (err) { console.log('Failed to parse token response'); throw err; }
180
189
 
181
190
  if (tokens.error) {
182
- console.log('Token response error:', tokens);
191
+ console.log('Token response error:', tokens);
183
192
  throw new Error(`Token exchange failed: ${tokens.error_description || tokens.error}`);
184
193
  }
185
194
 
186
- return {
187
- accessToken: tokens.access_token,
188
- refreshToken: tokens.refresh_token,
189
- expiresIn: tokens.expires_in,
190
- idToken: tokens.id_token,
191
- };
195
+ return tokens;
192
196
  }
193
197
 
194
-
195
198
  // --- Main flow ---
196
199
  async getTokensWithCredentials(email, password) {
197
200
  console.log('Obtaining OAuth tokens automatically...');
198
-
199
201
  const pkce = this.generatePKCE();
200
202
  const state = this.generateState();
201
203
 
@@ -208,17 +210,14 @@ class MelCloudHomeToken {
208
210
  authUrl.searchParams.set('code_challenge_method', 'S256');
209
211
  authUrl.searchParams.set('state', state);
210
212
 
211
- // 1️⃣ Get login page
212
213
  const { form, cookies, loginPageUrl } = await this.getLoginPage(authUrl.toString());
213
-
214
- // 2️⃣ Submit login + handle form_post
215
214
  const authCode = await this.submitLogin(form, email, password, cookies, loginPageUrl);
216
-
217
- // 3️⃣ Exchange code for tokens
218
215
  const tokens = await this.exchangeCodeForTokens(authCode, pkce.verifier);
216
+
219
217
  console.log('✓ Successfully obtained OAuth tokens');
220
218
  return tokens;
221
219
  }
220
+
222
221
  }
223
222
 
224
223
  export default MelCloudHomeToken;