homebridge-melcloud-control 4.0.0-beta.373 → 4.0.0-beta.374
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 +74 -62
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.374",
|
|
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
|
@@ -31,11 +31,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
async getLoginPage(authUrl) {
|
|
34
|
-
const res = await this.axiosInstance({
|
|
35
|
-
method: 'GET',
|
|
36
|
-
baseURL: authUrl,
|
|
37
|
-
maxRedirects: 10
|
|
38
|
-
});
|
|
34
|
+
const res = await this.axiosInstance.get(authUrl, { maxRedirects: 10 });
|
|
39
35
|
return { url: res.request.res.responseUrl };
|
|
40
36
|
}
|
|
41
37
|
|
|
@@ -86,47 +82,46 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
86
82
|
try {
|
|
87
83
|
const tokenData = new URLSearchParams({
|
|
88
84
|
grant_type: 'authorization_code',
|
|
89
|
-
code
|
|
85
|
+
code,
|
|
90
86
|
redirect_uri: REDIRECT_URI,
|
|
91
87
|
client_id: CLIENT_ID,
|
|
92
88
|
code_verifier: codeVerifier,
|
|
93
89
|
});
|
|
94
90
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
91
|
+
const response = await this.axiosInstance.post(
|
|
92
|
+
TOKEN_ENDPOINT,
|
|
93
|
+
tokenData.toString(),
|
|
94
|
+
{
|
|
95
|
+
headers: {
|
|
96
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
97
|
+
'Authorization': 'Basic aG9tZW1vYmlsZTo=', // optional, Cognito may ignore
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
this.emit('warn', `Token response: ${JSON.stringify(response.data)}`);
|
|
103
|
+
return response.data;
|
|
104
104
|
|
|
105
|
-
const tokens = tokenResponse.data;
|
|
106
|
-
this.emit('warn', `Token ${JSON.stringify(tokens)}`);
|
|
107
|
-
return tokens;
|
|
108
105
|
} catch (error) {
|
|
109
|
-
throw new Error(`Failed to obtain OAuth token: ${error}`);
|
|
106
|
+
throw new Error(`Failed to obtain OAuth token: ${error.response?.data || error.message}`);
|
|
110
107
|
}
|
|
111
108
|
}
|
|
112
109
|
|
|
110
|
+
|
|
113
111
|
async loginToMelCloudHome(url) {
|
|
114
112
|
try {
|
|
115
|
-
// Step 1: Pobierz stronę logowania
|
|
116
|
-
const getResp = await this.axiosInstance({
|
|
117
|
-
|
|
118
|
-
baseURL: url,
|
|
119
|
-
headers: {
|
|
120
|
-
'Accept': 'text/html',
|
|
121
|
-
},
|
|
113
|
+
// Step 1: Pobierz stronę logowania
|
|
114
|
+
const getResp = await this.axiosInstance.get(url, {
|
|
115
|
+
headers: { 'Accept': 'text/html' },
|
|
122
116
|
withCredentials: true,
|
|
123
117
|
});
|
|
124
118
|
|
|
125
|
-
const cookies = getResp.headers['set-cookie'] || [];
|
|
119
|
+
const cookies = (getResp.headers['set-cookie'] || []).map(c => c.split(';')[0]).join('; ');
|
|
126
120
|
const dom = new JSDOM(getResp.data);
|
|
127
121
|
const csrf = dom.window.document.querySelector('input[name="_csrf"]')?.value;
|
|
122
|
+
|
|
128
123
|
if (!csrf) {
|
|
129
|
-
|
|
124
|
+
this.emit('warn', `CSRF token not found`);
|
|
130
125
|
return null;
|
|
131
126
|
}
|
|
132
127
|
|
|
@@ -137,49 +132,66 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
137
132
|
password: this.passwd,
|
|
138
133
|
});
|
|
139
134
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
validateStatus: status => [200, 302, 400].includes(status),
|
|
157
|
-
});
|
|
135
|
+
// Step 3: Wyślij login POST
|
|
136
|
+
const postResp = await this.axiosInstance.post(
|
|
137
|
+
url,
|
|
138
|
+
formData.toString(),
|
|
139
|
+
{
|
|
140
|
+
headers: {
|
|
141
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
142
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
143
|
+
'Cookie': cookies,
|
|
144
|
+
'Origin': 'https://live-melcloudhome.auth.eu-west-1.amazoncognito.com',
|
|
145
|
+
'Referer': url,
|
|
146
|
+
},
|
|
147
|
+
maxRedirects: 0, // kontrolujmy redirecty ręcznie
|
|
148
|
+
validateStatus: s => [200, 302, 400].includes(s),
|
|
149
|
+
}
|
|
150
|
+
);
|
|
158
151
|
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
152
|
+
if (postResp.status === 400) {
|
|
153
|
+
this.emit('warn', `Login failed: ${postResp.data}`);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let redirectUrl = postResp.headers.location;
|
|
158
|
+
let cookieChain = cookies;
|
|
159
|
+
|
|
160
|
+
// Step 4: Podążaj za przekierowaniami
|
|
161
|
+
for (let i = 0; i < 5 && redirectUrl; i++) {
|
|
162
|
+
const redirectResp = await this.axiosInstance.get(redirectUrl, {
|
|
163
|
+
headers: { 'Cookie': cookieChain },
|
|
164
|
+
maxRedirects: 0,
|
|
165
|
+
validateStatus: s => [200, 302].includes(s),
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// aktualizuj cookies
|
|
169
|
+
const newCookies = redirectResp.headers['set-cookie'] || [];
|
|
170
|
+
if (newCookies.length) {
|
|
171
|
+
cookieChain += '; ' + newCookies.map(c => c.split(';')[0]).join('; ');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
redirectUrl = redirectResp.headers.location;
|
|
175
|
+
|
|
176
|
+
if (redirectUrl && redirectUrl.includes('code=')) {
|
|
177
|
+
const match = redirectUrl.match(/[?&]code=([^&]+)/);
|
|
178
|
+
if (match) {
|
|
179
|
+
const code = match[1];
|
|
180
|
+
this.emit('warn', `Authorization code obtained: ${code}`);
|
|
181
|
+
return code;
|
|
182
|
+
}
|
|
174
183
|
}
|
|
175
184
|
}
|
|
176
185
|
|
|
186
|
+
this.emit('warn', 'Login flow finished, but no code found');
|
|
177
187
|
return null;
|
|
188
|
+
|
|
178
189
|
} catch (error) {
|
|
179
|
-
throw new Error(`Login to MELCloud Home error: ${error}`);
|
|
190
|
+
throw new Error(`Login to MELCloud Home error: ${error.message}`);
|
|
180
191
|
}
|
|
181
192
|
}
|
|
182
193
|
|
|
194
|
+
|
|
183
195
|
async buildAuthorizeUrl() {
|
|
184
196
|
try {
|
|
185
197
|
const pkce = this.generatePKCE();
|