homebridge-melcloud-control 4.0.0-beta.317 → 4.0.0-beta.319
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/melcloud.js +1 -0
- package/src/melcloudhometoken.js +57 -59
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.319",
|
|
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
|
@@ -297,6 +297,7 @@ class MelCloud extends EventEmitter {
|
|
|
297
297
|
|
|
298
298
|
const data = await melCloudHomeToken.buildAuthorizeUrl();
|
|
299
299
|
const code = await melCloudHomeToken.loginToMelCloudHome(data.url);
|
|
300
|
+
const token = await melCloudHomeToken.getTokens(code);
|
|
300
301
|
|
|
301
302
|
return false
|
|
302
303
|
|
package/src/melcloudhometoken.js
CHANGED
|
@@ -83,58 +83,27 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
async getTokens(code
|
|
86
|
+
async getTokens(code) {
|
|
87
87
|
try {
|
|
88
|
-
const
|
|
88
|
+
const tokenResponse = await axios.post('https://auth.melcloudhome.com/oauth2/token', new URLSearchParams({
|
|
89
89
|
grant_type: 'authorization_code',
|
|
90
|
+
client_id: this.clientId,
|
|
90
91
|
code: code,
|
|
91
|
-
redirect_uri:
|
|
92
|
-
|
|
93
|
-
code_verifier: codeVerifier,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const res = await axios({
|
|
97
|
-
method: 'POST',
|
|
98
|
-
url: TOKEN_ENDPOINT,
|
|
92
|
+
redirect_uri: this.redirectUri
|
|
93
|
+
}).toString(), {
|
|
99
94
|
headers: {
|
|
100
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
101
|
-
|
|
102
|
-
},
|
|
103
|
-
data: data
|
|
95
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
96
|
+
}
|
|
104
97
|
});
|
|
105
|
-
console.log(res);
|
|
106
|
-
if (res.status !== 200) {
|
|
107
|
-
throw new Error(`Token exchange failed: ${res.status} ${res.statusText}`);
|
|
108
|
-
}
|
|
109
98
|
|
|
110
|
-
|
|
99
|
+
this.emit('warn', `Token ${tokenResponse.data}`);
|
|
100
|
+
return tokenResponse.data;
|
|
111
101
|
} catch (error) {
|
|
112
102
|
console.error('Failed to obtain OAuth tokens:', error);
|
|
113
103
|
throw error;
|
|
114
104
|
}
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
async buildAuthorizeUrl() {
|
|
118
|
-
try {
|
|
119
|
-
const pkce = this.generatePKCE();
|
|
120
|
-
const state = this.generateState();
|
|
121
|
-
const authUrl = new URL(AUTHORIZE_ENDPOINT);
|
|
122
|
-
authUrl.searchParams.set('client_id', CLIENT_ID);
|
|
123
|
-
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
|
|
124
|
-
authUrl.searchParams.set('response_type', 'code');
|
|
125
|
-
authUrl.searchParams.set('scope', SCOPE);
|
|
126
|
-
authUrl.searchParams.set('code_challenge', pkce.challenge);
|
|
127
|
-
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
128
|
-
authUrl.searchParams.set('state', state);
|
|
129
|
-
|
|
130
|
-
const loginPage = await this.getLoginPage(authUrl.toString());
|
|
131
|
-
const data = { codeVerifier: pkce.verifier, url: loginPage.url }
|
|
132
|
-
return data;
|
|
133
|
-
} catch (error) {
|
|
134
|
-
console.error('Failed to obtain OAuth link:', error);
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
107
|
|
|
139
108
|
async loginToMelCloudHome(url) {
|
|
140
109
|
try {
|
|
@@ -149,10 +118,29 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
149
118
|
|
|
150
119
|
const cookies = getResp.headers['set-cookie'] || [];
|
|
151
120
|
const dom = new JSDOM(getResp.data);
|
|
152
|
-
const
|
|
121
|
+
const formAction = dom.window.document.querySelector('form[action]')?.getAttribute('action');
|
|
122
|
+
if (!formAction) {
|
|
123
|
+
if (this.logWarn) this.emit('warn', `Login form not found in HTML`);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const formUrl = new URL(formAction, 'https://auth.melcloudhome.com'); // absolutny URL
|
|
128
|
+
const clientId = formUrl.searchParams.get('client_id');
|
|
129
|
+
const redirectUri = formUrl.searchParams.get('redirect_uri');
|
|
130
|
+
|
|
131
|
+
if (!clientId || !redirectUri) {
|
|
132
|
+
if (this.logWarn) this.emit('warn', `client_id or redirect_uri missing in login form`);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Zapisz do this.* żeby użyć później w exchangeCodeForToken()
|
|
137
|
+
this.clientId = clientId;
|
|
138
|
+
this.redirectUri = redirectUri;
|
|
153
139
|
|
|
140
|
+
const csrf = dom.window.document.querySelector('input[name="_csrf"]')?.value;
|
|
154
141
|
if (!csrf) {
|
|
155
|
-
|
|
142
|
+
if (this.logWarn) this.emit('warn', `CSRF token not found`);
|
|
143
|
+
return null;
|
|
156
144
|
}
|
|
157
145
|
|
|
158
146
|
// Step 2: Przygotuj dane logowania
|
|
@@ -171,43 +159,53 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
171
159
|
'Referer': url,
|
|
172
160
|
'User-Agent': 'Mozilla/5.0',
|
|
173
161
|
},
|
|
174
|
-
maxRedirects: 0,
|
|
162
|
+
maxRedirects: 0,
|
|
175
163
|
validateStatus: status => [200, 302, 400].includes(status),
|
|
176
164
|
});
|
|
177
165
|
|
|
178
|
-
|
|
179
|
-
if (response.status === 302 && postResp.headers.location) {
|
|
166
|
+
if (response.status === 302 && response.headers.location) {
|
|
180
167
|
// Login success — mamy redirect z "code="
|
|
181
168
|
const redirectUrl = response.headers.location;
|
|
182
|
-
console.log('Login success, redirect URL:', redirectUrl);
|
|
183
|
-
|
|
184
169
|
const match = redirectUrl.match(/[?&]code=([^&]+)/);
|
|
185
170
|
if (match) {
|
|
186
171
|
const code = match[1];
|
|
187
|
-
console.log('Authorization code:', code);
|
|
188
172
|
return code;
|
|
189
173
|
} else {
|
|
190
174
|
console.warn('Redirect URL found, but no "code=" param.');
|
|
175
|
+
if (this.logWarn) this.emit('warn', `Redirect URL found, but no "code=" param: ${redirectUrl}`);
|
|
191
176
|
return null;
|
|
192
177
|
}
|
|
193
178
|
}
|
|
194
179
|
|
|
195
|
-
if (response.status
|
|
196
|
-
if (this.logError) this.emit('error', `Login failed with status code: ${response.status}`);
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
180
|
+
if (response.status === 400) if (this.logWarn) this.emit('warn', `Login failed (bad request or invalid credentials: ${response.data}`);
|
|
199
181
|
|
|
200
|
-
|
|
201
|
-
if (!cookie) {
|
|
202
|
-
if (this.logWarn) this.emit('warn', `No cookie returned from login. Response headers: ${JSON.stringify(response.headers)}`);
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return cookie;
|
|
182
|
+
return null;
|
|
207
183
|
} catch (error) {
|
|
208
184
|
throw new Error(`Login to MELCloud Home error: ${error}`);
|
|
209
185
|
}
|
|
210
186
|
}
|
|
187
|
+
|
|
188
|
+
async buildAuthorizeUrl() {
|
|
189
|
+
try {
|
|
190
|
+
const pkce = this.generatePKCE();
|
|
191
|
+
const state = this.generateState();
|
|
192
|
+
const authUrl = new URL(AUTHORIZE_ENDPOINT);
|
|
193
|
+
authUrl.searchParams.set('client_id', CLIENT_ID);
|
|
194
|
+
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
|
|
195
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
196
|
+
authUrl.searchParams.set('scope', SCOPE);
|
|
197
|
+
authUrl.searchParams.set('code_challenge', pkce.challenge);
|
|
198
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
199
|
+
authUrl.searchParams.set('state', state);
|
|
200
|
+
|
|
201
|
+
const loginPage = await this.getLoginPage(authUrl.toString());
|
|
202
|
+
const data = { codeVerifier: pkce.verifier, url: loginPage.url }
|
|
203
|
+
return data;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error('Failed to obtain OAuth link:', error);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
211
209
|
}
|
|
212
210
|
|
|
213
211
|
export default MelCloudHomeToken;
|