homebridge-melcloud-control 4.0.0-beta.360 → 4.0.0-beta.362

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.360",
4
+ "version": "4.0.0-beta.362",
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
@@ -298,7 +298,7 @@ class MelCloud extends EventEmitter {
298
298
  const url = data.url;
299
299
  const codeVerifier = data.codeVerifier;
300
300
  const code = await melCloudHomeToken.loginToMelCloudHome(url);
301
- //const token = await melCloudHomeToken.getTokens(code, codeVerifier);
301
+ const token = await melCloudHomeToken.getTokens(code, codeVerifier);
302
302
 
303
303
  const accountInfo = { ContextKey: code, UseFahrenheit: false };
304
304
  this.contextKey = code;
@@ -94,7 +94,7 @@ class MelCloudHomeToken extends EventEmitter {
94
94
  code_verifier: codeVerifier,
95
95
  });
96
96
 
97
- const tokenResponse = await axios.post(TOKEN_ENDPOINT, tokenData.toString(), {
97
+ const tokenResponse = await this.client.post(TOKEN_ENDPOINT, tokenData.toString(), {
98
98
  headers: {
99
99
  'Content-Type': 'application/x-www-form-urlencoded',
100
100
  'Authorization': 'Basic aG9tZW1vYmlsZTo=',
@@ -105,64 +105,74 @@ class MelCloudHomeToken extends EventEmitter {
105
105
  this.emit('warn', `Token ${JSON.stringify(tokens)}`);
106
106
  return tokens;
107
107
  } catch (error) {
108
- throw new Error(`Failed to obtain OAuth token: ${error.response?.data || error}`);
108
+ throw new Error(`Failed to obtain OAuth token: ${error}`);
109
109
  }
110
110
  }
111
111
 
112
112
  async loginToMelCloudHome(url) {
113
113
  try {
114
-
115
- const jar = new CookieJar();
116
- const client = wrapper(axios.create({ jar, withCredentials: true }));
117
- client.defaults.headers['User-Agent'] = MOBILE_USER_AGENT;
118
-
119
- // --- Krok 1: GET strony logowania ---
120
- const getResp = await client.get(url, {
121
- headers: { 'User-Agent': MOBILE_USER_AGENT }
114
+ // Step 1: Pobierz stronę logowania, żeby uzyskać sesję + token _csrf
115
+ const getResp = await this.client.get(url, {
116
+ headers: {
117
+ 'Accept': 'text/html',
118
+ },
119
+ withCredentials: true,
122
120
  });
123
121
 
122
+ const cookies = getResp.headers['set-cookie'] || [];
124
123
  const dom = new JSDOM(getResp.data);
125
- const csrf = dom.window.document.querySelector('input[name="_csrf"]')?.getAttribute('value');
126
- if (!csrf) throw new Error('CSRF token not found');
124
+ const csrf = dom.window.document.querySelector('input[name="_csrf"]')?.value;
125
+ if (!csrf) {
126
+ if (this.logWarn) this.emit('warn', `CSRF token not found`);
127
+ return null;
128
+ }
127
129
 
128
- // --- Krok 2: POST login ---
130
+ // Step 2: Przygotuj dane logowania
129
131
  const formData = new URLSearchParams({
130
132
  _csrf: csrf,
131
133
  username: this.user,
132
- password: this.passwd
133
- }).toString();
134
+ password: this.passwd,
135
+ });
134
136
 
135
- const postResp = await client.post(url, formData, {
137
+ if (this.logWarn) this.emit('warn', `Redirect URL: ${url}`);
138
+
139
+ // Step 3: Wyślij POST z danymi logowania
140
+ const response = await this.client.post(url, formData.toString(), {
136
141
  headers: {
137
- 'User-Agent': MOBILE_USER_AGENT,
142
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
143
+ 'Accept-Language': 'en-US,en;q=0.9',
138
144
  'Content-Type': 'application/x-www-form-urlencoded',
145
+ 'Content-Length': formData.toString().length,
146
+ 'Cookie': cookies,
139
147
  'Origin': 'https://live-melcloudhome.auth.eu-west-1.amazoncognito.com',
140
148
  'Referer': url,
141
149
  },
142
150
  maxRedirects: 0,
143
- validateStatus: status => [200, 302, 400].includes(status)
151
+ validateStatus: status => [200, 302, 400].includes(status),
144
152
  });
145
153
 
146
- if (postResp.status === 400) throw new Error('Login failed');
147
-
148
- // --- Krok 3: GET dashboard po zalogowaniu ---
149
- const dashboardResp = await client.get('https://melcloudhome.com/dashboard', {
150
- headers: { 'User-Agent': MOBILE_USER_AGENT }
151
- });
152
154
 
153
- this.emit('warn', `1 ${dashboardResp.data}, ${dashboardResp.headers}`);
155
+ if (response.status === 302 && response.headers.location) {
156
+ // Login success — mamy redirect z "code="
157
+ const redirectUrl = response.headers.location;
158
+ if (this.logWarn) this.emit('warn', `Redirect URL: ${redirectUrl}`);
154
159
 
155
- // --- Krok 4: Pobierz cookies z dashboard ---
156
- const dashboardCookies = await jar.getCookies('https://melcloudhome.com/dashboard');
157
- this.emit('warn', `2 ${dashboardCookies}`);
158
160
 
159
- // Możemy zwrócić jako string do użycia w nagłówku:
160
- const cookieHeader = dashboardCookies.map(c => `${c.key}=${c.value}`).join('; ');
161
161
 
162
- this.emit('warn', `Cookies ${cookieHeader}`);
162
+ const match = redirectUrl.match(/[?&]code=([^&]+)/);
163
+ if (match) {
164
+ const code = match[1];
165
+ if (this.logWarn) this.emit('warn', `Redirect URL found code: ${code}`);
166
+ return code;
167
+ } else {
168
+ if (this.logWarn) this.emit('warn', `Redirect URL found, but no "code=" param: ${redirectUrl}`);
169
+ return null;
170
+ }
171
+ }
163
172
 
164
- return dashboardCookies.map(c => `${c.key}=${c.value}`);
173
+ if (response.status === 400) if (this.logWarn) this.emit('warn', `Login failed (bad request or invalid credentials: ${response.data}`);
165
174
 
175
+ return null;
166
176
  } catch (error) {
167
177
  throw new Error(`Login to MELCloud Home error: ${error}`);
168
178
  }