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 +1 -1
- package/src/melcloudhometoken.js +44 -45
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.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",
|
package/src/melcloudhometoken.js
CHANGED
|
@@ -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
|
|
92
|
+
// --- Submit login with debug ---
|
|
95
93
|
async submitLogin(form, email, password, cookies, loginPageUrl) {
|
|
96
94
|
console.log('Submitting login credentials...');
|
|
97
95
|
|
|
98
|
-
//
|
|
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
|
-
//
|
|
116
|
-
const
|
|
117
|
-
const postForm =
|
|
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
|
-
//
|
|
133
|
-
|
|
134
|
-
if (codeMatch)
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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:
|
|
142
|
-
|
|
143
|
-
if (codeMatch)
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
184
|
+
console.log('Token endpoint response:', resp.html);
|
|
185
|
+
|
|
173
186
|
let tokens;
|
|
174
|
-
try {
|
|
175
|
-
|
|
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
|
-
|
|
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;
|