homebridge-melcloud-control 4.2.3-beta.4 → 4.2.3-beta.40

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/src/melcloud.js CHANGED
@@ -1,13 +1,8 @@
1
- import fs from 'fs';
2
1
  import axios from 'axios';
3
- import { exec } from 'child_process';
4
- import { promisify } from 'util';
5
2
  import EventEmitter from 'events';
6
- import puppeteer from 'puppeteer';
7
3
  import ImpulseGenerator from './impulsegenerator.js';
8
4
  import Functions from './functions.js';
9
- import { ApiUrls, ApiUrlsHome } from './constants.js';
10
- const execPromise = promisify(exec);
5
+ import { ApiUrls } from './constants.js';
11
6
 
12
7
  class MelCloud extends EventEmitter {
13
8
  constructor(account, accountFile, buildingsFile, devicesFile, pluginStart = false) {
@@ -22,7 +17,8 @@ class MelCloud extends EventEmitter {
22
17
  this.accountFile = accountFile;
23
18
  this.buildingsFile = buildingsFile;
24
19
  this.devicesFile = devicesFile;
25
- this.contextKey = null;
20
+ this.headers = {};
21
+
26
22
  this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
27
23
  .on('warn', warn => this.emit('warn', warn))
28
24
  .on('error', error => this.emit('error', error))
@@ -31,13 +27,9 @@ class MelCloud extends EventEmitter {
31
27
  if (pluginStart) {
32
28
  //lock flags
33
29
  this.locks = {
34
- connect: false,
35
30
  checkDevicesList: false
36
31
  };
37
32
  this.impulseGenerator = new ImpulseGenerator()
38
- .on('connect', () => this.handleWithLock('connect', async () => {
39
- await this.connect(true);
40
- }))
41
33
  .on('checkDevicesList', () => this.handleWithLock('checkDevicesList', async () => {
42
34
  await this.checkDevicesList();
43
35
  }))
@@ -61,20 +53,15 @@ class MelCloud extends EventEmitter {
61
53
  }
62
54
 
63
55
  // MELCloud
64
- async checkMelcloudDevicesList() {
56
+ async checkDevicesList() {
65
57
  try {
66
- const devicesList = { State: false, Info: null, Devices: [] }
67
- const headers = {
68
- 'X-MitsContextKey': this.contextKey,
69
- 'Content-Type': 'application/json'
70
- }
71
-
58
+ const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
72
59
  if (this.logDebug) this.emit('debug', `Scanning for devices...`);
73
60
  const listDevicesData = await axios(ApiUrls.ListDevices, {
74
61
  method: 'GET',
75
62
  baseURL: ApiUrls.BaseURL,
76
63
  timeout: 15000,
77
- headers: headers
64
+ headers: this.headers
78
65
  });
79
66
 
80
67
  if (!listDevicesData || !listDevicesData.data) {
@@ -93,6 +80,7 @@ class MelCloud extends EventEmitter {
93
80
  await this.functions.saveData(this.buildingsFile, buildingsList);
94
81
  if (this.logDebug) this.emit('debug', `Buildings list saved`);
95
82
 
83
+ const devices = [];
96
84
  for (const building of buildingsList) {
97
85
  if (!building.Structure) {
98
86
  this.emit('warn', `Building missing structure: ${building.BuildingName || 'Unnamed'}`);
@@ -113,48 +101,52 @@ class MelCloud extends EventEmitter {
113
101
  // Zamiana ID na string
114
102
  allDevices.forEach(device => {
115
103
  device.DeviceID = String(device.DeviceID);
116
- device.Headers = headers;
104
+ device.Headers = this.headers;
117
105
  });
118
106
 
119
107
  if (this.logDebug) this.emit('debug', `Found ${allDevices.length} devices in building: ${building.Name || 'Unnamed'}`);
120
- devicesList.Devices.push(...allDevices);
108
+ devices.push(...allDevices);
121
109
  }
122
110
 
123
- const devicesCount = devicesList.Devices.length;
111
+ const devicesCount = devices.length;
124
112
  if (devicesCount === 0) {
125
113
  devicesList.Info = 'No devices found'
126
114
  return devicesList;
127
115
  }
128
116
 
129
- await this.functions.saveData(this.devicesFile, devicesList.Devices);
130
- if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
131
-
132
117
  devicesList.State = true;
133
118
  devicesList.Info = `Found ${devicesCount} devices`;
119
+ devicesList.Devices = devices;
120
+
121
+ await this.functions.saveData(this.devicesFile, devicesList);
122
+ if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
123
+
134
124
  return devicesList;
135
125
  } catch (error) {
136
126
  throw new Error(`Check devices list error: ${error.message}`);
137
127
  }
138
128
  }
139
129
 
140
- async connectToMelCloud() {
130
+ async connect() {
141
131
  if (this.logDebug) this.emit('debug', `Connecting to MELCloud`);
142
132
 
143
133
  try {
144
- const accountInfo = { State: false, Info: '', LoginData: null, ContextKey: null, UseFahrenheit: false }
134
+ const accountInfo = { State: false, Info: '', LoginData: null, Headers: {}, UseFahrenheit: false }
135
+
136
+ const payload = {
137
+ Email: this.user,
138
+ Password: this.passwd,
139
+ Language: this.language,
140
+ AppVersion: '1.34.12',
141
+ CaptchaChallenge: '',
142
+ CaptchaResponse: '',
143
+ Persist: true
144
+ };
145
145
  const accountData = await axios(ApiUrls.ClientLogin, {
146
146
  method: 'POST',
147
147
  baseURL: ApiUrls.BaseURL,
148
148
  timeout: 15000,
149
- data: {
150
- Email: this.user,
151
- Password: this.passwd,
152
- Language: this.language,
153
- AppVersion: '1.34.12',
154
- CaptchaChallenge: '',
155
- CaptchaResponse: '',
156
- Persist: true
157
- }
149
+ data: payload
158
150
  });
159
151
  const account = accountData.data;
160
152
  const loginData = account.LoginData ?? [];
@@ -175,12 +167,16 @@ class MelCloud extends EventEmitter {
175
167
  accountInfo.Info = 'Context key missing'
176
168
  return accountInfo;
177
169
  }
178
- this.contextKey = contextKey;
170
+
171
+ this.headers = {
172
+ 'X-MitsContextKey': contextKey,
173
+ 'Content-Type': 'application/json'
174
+ };
179
175
 
180
176
  accountInfo.State = true;
181
177
  accountInfo.Info = 'Connect to MELCloud Success';
182
178
  accountInfo.LoginData = loginData;
183
- accountInfo.ContextKey = contextKey;
179
+ accountInfo.Headers = this.headers;
184
180
  await this.functions.saveData(this.accountFile, accountInfo);
185
181
 
186
182
  return accountInfo
@@ -189,345 +185,16 @@ class MelCloud extends EventEmitter {
189
185
  }
190
186
  }
191
187
 
192
- // MELCloud Home
193
- async checkMelcloudHomeDevicesList() {
194
- try {
195
- const devicesList = { State: false, Info: null, Devices: [] }
196
- const headers = {
197
- 'Accept': '*/*',
198
- 'Accept-Language': 'en-US,en;q=0.9',
199
- 'Cookie': this.contextKey,
200
- 'User-Agent': 'homebridge-melcloud-control/4.0.0',
201
- 'DNT': '1',
202
- 'Origin': 'https://melcloudhome.com',
203
- 'Referer': 'https://melcloudhome.com/dashboard',
204
- 'Sec-Fetch-Dest': 'empty',
205
- 'Sec-Fetch-Mode': 'cors',
206
- 'Sec-Fetch-Site': 'same-origin',
207
- 'X-CSRF': '1'
208
- };
209
-
210
- if (this.logDebug) this.emit('debug', `Scanning for devices`);
211
- const listDevicesData = await axios(ApiUrlsHome.GetUserContext, {
212
- method: 'GET',
213
- baseURL: ApiUrlsHome.BaseURL,
214
- timeout: 25000,
215
- headers: headers
216
- });
217
- const buildingsList = listDevicesData.data.buildings;
218
- if (this.logDebug) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
219
-
220
- if (!buildingsList) {
221
- devicesList.Info = 'No building found'
222
- return devicesList;
223
- }
224
-
225
- await this.functions.saveData(this.buildingsFile, buildingsList);
226
- if (this.logDebug) this.emit('debug', `Buildings list saved`);
227
-
228
- const devices = buildingsList.flatMap(building => {
229
- // Funkcja kapitalizująca klucze obiektu
230
- const capitalizeKeys = obj =>
231
- Object.fromEntries(
232
- Object.entries(obj).map(([key, value]) => [
233
- key.charAt(0).toUpperCase() + key.slice(1),
234
- value
235
- ])
236
- );
237
-
238
- // Rekurencyjna kapitalizacja kluczy w obiekcie lub tablicy
239
- const capitalizeKeysDeep = obj => {
240
- if (Array.isArray(obj)) return obj.map(capitalizeKeysDeep);
241
- if (obj && typeof obj === 'object') {
242
- return Object.fromEntries(
243
- Object.entries(obj).map(([key, value]) => [
244
- key.charAt(0).toUpperCase() + key.slice(1),
245
- capitalizeKeysDeep(value)
246
- ])
247
- );
248
- }
249
- return obj;
250
- };
251
-
252
- // Funkcja tworząca finalny obiekt Device
253
- const createDevice = (device, type) => {
254
- // Settings już kapitalizowane w nazwach
255
- const settingsArray = device.Settings || [];
256
-
257
- const settingsObject = Object.fromEntries(
258
- settingsArray.map(({ name, value }) => {
259
- let parsedValue = value;
260
- if (value === "True") parsedValue = true;
261
- else if (value === "False") parsedValue = false;
262
- else if (!isNaN(value) && value !== "") parsedValue = Number(value);
263
-
264
- const key = name.charAt(0).toUpperCase() + name.slice(1);
265
- return [key, parsedValue];
266
- })
267
- );
268
-
269
- // Scal Capabilities + Settings + DeviceType w Device
270
- const deviceObject = {
271
- ...capitalizeKeys(device.Capabilities || {}),
272
- ...settingsObject,
273
- DeviceType: type,
274
- IsConnected: device.IsConnected
275
- };
276
-
277
- // Kapitalizacja brakujących obiektów/tablic
278
- if (device.FrostProtection) device.FrostProtection = { ...capitalizeKeys(device.FrostProtection || {}) };
279
- if (device.OverheatProtection) device.OverheatProtection = { ...capitalizeKeys(device.OverheatProtection || {}) };
280
- if (device.HolidayMode) device.HolidayMode = { ...capitalizeKeys(device.HolidayMode || {}) };
281
- if (Array.isArray(device.Schedule)) device.Schedule = device.Schedule.map(capitalizeKeysDeep);
282
-
283
- // Usuń stare pola Settings i Capabilities
284
- const { Settings, Capabilities, Id, GivenDisplayName, ...rest } = device;
285
-
286
- return {
287
- ...rest,
288
- Type: type,
289
- DeviceID: Id,
290
- DeviceName: GivenDisplayName,
291
- Device: deviceObject,
292
- Headers: headers
293
- };
294
- };
295
-
296
- return [
297
- ...(building.airToAirUnits || []).map(d => createDevice(capitalizeKeys(d), 0)),
298
- ...(building.airToWaterUnits || []).map(d => createDevice(capitalizeKeys(d), 1)),
299
- ...(building.airToVentilationUnits || []).map(d => createDevice(capitalizeKeys(d), 3))
300
- ];
301
- });
302
-
303
- const devicesCount = devices.length;
304
- if (devicesCount === 0) {
305
- devicesList.Info = 'No devices found'
306
- return devicesList;
307
- }
308
-
309
- await this.functions.saveData(this.devicesFile, devices);
310
- if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
311
-
312
- devicesList.State = true;
313
- devicesList.Info = `Found ${devicesCount} devices`;
314
- devicesList.Devices = devices;
315
- return devicesList;
316
- } catch (error) {
317
- if (error.response?.status === 401) {
318
- await connectToMelCloudHome();
319
- if (this.logWarn) this.emit('warn', 'Check devices list not possible, cookies expired, trying to get new.');
320
- return;
321
- }
322
-
323
- throw new Error(`Check devices list error: ${error.message}`);
324
- }
325
- }
326
-
327
- async connectToMelCloudHome() {
328
- if (this.logDebug) this.emit('debug', 'Connecting to MELCloud Home');
329
- const GLOBAL_TIMEOUT = 90000;
330
-
331
- let browser;
332
- try {
333
- const accountInfo = { State: false, Info: '', ContextKey: null, UseFahrenheit: false };
334
- let chromiumPath = await this.functions.ensureChromiumInstalled();
335
-
336
- // === Fallback to Puppeteer's built-in Chromium ===
337
- if (!chromiumPath) {
338
- try {
339
- const puppeteerPath = puppeteer.executablePath();
340
- if (puppeteerPath && fs.existsSync(puppeteerPath)) {
341
- chromiumPath = puppeteerPath;
342
- if (this.logDebug) this.emit('debug', `Using puppeteer Chromium at ${chromiumPath}`);
343
- }
344
- } catch { }
345
- } else {
346
- if (this.logDebug) this.emit('debug', `Using system Chromium at ${chromiumPath}`);
347
- }
348
-
349
- if (!chromiumPath) {
350
- accountInfo.Info = 'Chromium not found on Your device, please install it manually and try again';
351
- return accountInfo;
352
- }
353
-
354
- // Verify executable works
355
- try {
356
- const { stdout } = await execPromise(`"${chromiumPath}" --version`);
357
- if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
358
- } catch (error) {
359
- accountInfo.Info = `Chromium found at ${chromiumPath}, but cannot be executed: ${error.message}`;
360
- return accountInfo;
361
- }
362
-
363
- if (this.logDebug) this.emit('debug', `Launching Chromium...`);
364
- browser = await puppeteer.launch({
365
- headless: true,
366
- executablePath: chromiumPath,
367
- timeout: GLOBAL_TIMEOUT,
368
- args: [
369
- '--no-sandbox',
370
- '--disable-setuid-sandbox',
371
- '--disable-dev-shm-usage',
372
- '--single-process',
373
- '--disable-gpu',
374
- '--no-zygote'
375
- ]
376
- });
377
- browser.on('disconnected', () => this.emit('debug', 'Browser disconnected'));
378
-
379
- const page = await browser.newPage();
380
- page.on('error', error => this.emit('error', `Page crashed: ${error.message}`));
381
- page.on('pageerror', error => this.emit('error', `Browser error: ${error.message}`));
382
- page.setDefaultTimeout(GLOBAL_TIMEOUT);
383
- page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
384
-
385
- // Clear cookies before navigation
386
- try {
387
- const client = await page.createCDPSession();
388
- await client.send('Network.clearBrowserCookies');
389
- } catch (error) {
390
- if (this.logError) this.emit('error', `Clear cookies error: ${error.message}`);
391
- }
392
-
393
- try {
394
- await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
395
- } catch (error) {
396
- accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
397
- return accountInfo;
398
- }
399
-
400
- // Wait extra to ensure UI is rendered
401
- await new Promise(r => setTimeout(r, 3000));
402
- const loginBtn = await page.waitForSelector('button.btn--blue', { timeout: GLOBAL_TIMEOUT / 4 });
403
- const loginText = await page.evaluate(el => el.textContent.trim(), loginBtn);
404
-
405
- if (!['Zaloguj', 'Sign In', 'Login'].includes(loginText)) {
406
- accountInfo.Info = `Login button ${loginText} not found`;
407
- return accountInfo;
408
- }
409
-
410
- await loginBtn.click();
411
- await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: GLOBAL_TIMEOUT / 3 });
412
-
413
- const usernameInput = await page.$('input[name="username"]');
414
- const passwordInput = await page.$('input[name="password"]');
415
- if (!usernameInput || !passwordInput) {
416
- accountInfo.Info = 'Username or password input not found';
417
- return accountInfo;
418
- }
419
-
420
- await page.type('input[name="username"]', this.user, { delay: 50 });
421
- await page.type('input[name="password"]', this.passwd, { delay: 50 });
422
-
423
- const submitButton = await page.$('input[type="submit"], button[type="submit"]');
424
- if (!submitButton) {
425
- accountInfo.Info = 'Submit button not found';
426
- return accountInfo;
427
- }
428
- await Promise.race([Promise.all([submitButton.click(), page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT / 4 })]), new Promise(r => setTimeout(r, GLOBAL_TIMEOUT / 3))]);
429
-
430
- // Extract cookies
431
- let c1 = null, c2 = null;
432
- const start = Date.now();
433
- while ((!c1 || !c2) && Date.now() - start < GLOBAL_TIMEOUT / 2) {
434
- const cookies = await page.browserContext().cookies();
435
- c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
436
- c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || c2;
437
- if (!c1 || !c2) await new Promise(r => setTimeout(r, 500));
438
- }
439
-
440
- if (!c1 || !c2) {
441
- accountInfo.Info = 'Cookies C1/C2 missing';
442
- return accountInfo;
443
- }
444
-
445
- const contextKey = [
446
- '__Secure-monitorandcontrol=chunks-2',
447
- `__Secure-monitorandcontrolC1=${c1}`,
448
- `__Secure-monitorandcontrolC2=${c2}`
449
- ].join('; ');
450
- this.contextKey = contextKey;
451
-
452
- accountInfo.State = true;
453
- accountInfo.Info = 'Connect to MELCloud Home Success';
454
- accountInfo.ContextKey = contextKey;
455
- await this.functions.saveData(this.accountFile, accountInfo);
456
-
457
- return accountInfo;
458
- } catch (error) {
459
- throw new Error(`Connect error: ${error.message}`);
460
- } finally {
461
- if (browser) {
462
- try { await browser.close(); }
463
- catch (closeErr) {
464
- if (this.logError) this.emit('error', `Failed to close Puppeteer: ${closeErr.message}`);
465
- }
466
- }
467
- }
468
- }
469
-
470
- async checkDevicesList() {
471
- const TIMEOUT_MS = 30000; // 30 seconds timeout
472
- try {
473
- const devicesList = await Promise.race([
474
- (async () => {
475
- switch (this.accountType) {
476
- case "melcloud":
477
- return await this.checkMelcloudDevicesList();
478
- case "melcloudhome":
479
- return await this.checkMelcloudHomeDevicesList();
480
- default:
481
- return [];
482
- }
483
- })(),
484
- new Promise((_, reject) => setTimeout(() => reject(new Error('Device list timeout (30s)')), TIMEOUT_MS))
485
- ]);
486
-
487
- return devicesList;
488
- } catch (error) {
489
- throw new Error(error);
490
- }
491
- }
492
-
493
- async connect() {
494
- const TIMEOUT_MS = 120000;
495
-
496
- try {
497
- const response = await Promise.race([
498
- (async () => {
499
- switch (this.accountType) {
500
- case "melcloud":
501
- return await this.connectToMelCloud();
502
- case "melcloudhome":
503
- return await this.connectToMelCloudHome();
504
- default:
505
- return {};
506
- }
507
- })(),
508
- new Promise((_, reject) => setTimeout(() => reject(new Error('Connection timeout (90s)')), TIMEOUT_MS))
509
- ]);
510
-
511
- return response;
512
- } catch (error) {
513
- throw new Error(error);
514
- }
515
- }
516
-
517
188
  async send(accountInfo) {
518
189
  try {
519
- const axiosInstance = axios.create({
190
+ const payload = { data: accountInfo.LoginData };
191
+ await axios(ApiUrls.UpdateApplicationOptions, {
520
192
  method: 'POST',
521
193
  baseURL: ApiUrls.BaseURL,
522
194
  timeout: 15000,
523
- headers: {
524
- 'X-MitsContextKey': accountInfo.ContextKey,
525
- 'content-type': 'application/json'
526
- }
195
+ headers: accountInfo.Headers,
196
+ data: payload
527
197
  });
528
-
529
- const payload = { data: accountInfo.LoginData };
530
- await axiosInstance(ApiUrls.UpdateApplicationOptions, payload);
531
198
  await this.functions.saveData(this.accountFile, accountInfo);
532
199
  return true;
533
200
  } catch (error) {
@@ -52,15 +52,9 @@ class MelCloudAta extends EventEmitter {
52
52
  try {
53
53
  //read device info from file
54
54
  const devicesData = await this.functions.readData(this.devicesFile, true);
55
- if (!Array.isArray(devicesData)) {
56
- if (this.logWarn) this.emit('warn', `Device data not found`);
57
- return null;
58
- }
59
- const deviceData = devicesData.find(device => device.DeviceID === this.deviceId);
55
+ const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
60
56
 
61
57
  if (this.accountType === 'melcloudhome') {
62
- deviceData.SerialNumber = deviceData.DeviceID || '4.0.0';
63
- deviceData.Device.FirmwareAppVersion = deviceData.ConnectedInterfaceIdentifier || '4.0.0';
64
58
  deviceData.Device.OperationMode = AirConditioner.OperationModeMapStringToEnum[deviceData.Device.OperationMode] ?? deviceData.Device.OperationMode;
65
59
  deviceData.Device.ActualFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.ActualFanSpeed] ?? deviceData.Device.ActualFanSpeed;
66
60
  deviceData.Device.SetFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.SetFanSpeed] ?? deviceData.Device.SetFanSpeed;
@@ -80,13 +74,12 @@ class MelCloudAta extends EventEmitter {
80
74
  if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(safeConfig, null, 2)}`);
81
75
 
82
76
  //device
83
- const serialNumber = deviceData.SerialNumber;
84
- const firmwareAppVersion = deviceData.Device?.FirmwareAppVersion;
77
+ const serialNumber = deviceData.SerialNumber || '4.0.0';
78
+ const firmwareAppVersion = deviceData.Device?.FirmwareAppVersion || '4.0.0';
85
79
 
86
80
  //units
87
81
  const units = Array.isArray(deviceData.Device?.Units) ? deviceData.Device?.Units : [];
88
82
  const unitsCount = units.length;
89
- const manufacturer = 'Mitsubishi';
90
83
 
91
84
  const { indoor, outdoor } = units.reduce((acc, unit) => {
92
85
  const target = unit.IsIndoor ? 'indoor' : 'outdoor';
@@ -118,14 +111,11 @@ class MelCloudAta extends EventEmitter {
118
111
 
119
112
  //check state changes
120
113
  const deviceDataHasNotChanged = JSON.stringify(deviceData) === JSON.stringify(this.deviceData);
121
- if (deviceDataHasNotChanged) {
122
- if (this.logDebug) this.emit('debug', `Device state not changed`);
123
- return;
124
- }
114
+ if (deviceDataHasNotChanged) return;
125
115
  this.deviceData = deviceData;
126
116
 
127
117
  //emit info
128
- this.emit('deviceInfo', manufacturer, indoor.model, outdoor.model, serialNumber, firmwareAppVersion);
118
+ this.emit('deviceInfo', indoor.model, outdoor.model, serialNumber, firmwareAppVersion);
129
119
 
130
120
  //emit state
131
121
  this.emit('deviceState', deviceData);
@@ -201,6 +191,7 @@ class MelCloudAta extends EventEmitter {
201
191
  };
202
192
  method = 'POST';
203
193
  path = ApiUrlsHome.PostProtectionFrost;
194
+ deviceData.Headers.Referer = ApiUrlsHome.Referers.PostProtectionFrost.replace('deviceid', deviceData.DeviceID);
204
195
  break;
205
196
  case 'overheatprotection':
206
197
  payload = {
@@ -211,6 +202,7 @@ class MelCloudAta extends EventEmitter {
211
202
  };
212
203
  method = 'POST';
213
204
  path = ApiUrlsHome.PostProtectionOverheat;
205
+ deviceData.Headers.Referer = ApiUrlsHome.Referers.PostProtectionOverheat.replace('deviceid', deviceData.DeviceID);
214
206
  break;
215
207
  case 'holidaymode':
216
208
  payload = {
@@ -221,11 +213,13 @@ class MelCloudAta extends EventEmitter {
221
213
  };
222
214
  method = 'POST';
223
215
  path = ApiUrlsHome.PostHolidayMode;
216
+ deviceData.Headers.Referer = ApiUrlsHome.Referers.PostHolidayMode.replace('deviceid', deviceData.DeviceID);
224
217
  break;
225
218
  case 'schedule':
226
219
  payload = { enabled: deviceData.ScheduleEnabled };
227
220
  method = 'PUT';
228
- path = ApiUrlsHome.PutScheduleEnable.replace('deviceid', deviceData.DeviceID);
221
+ path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
222
+ deviceData.Headers.Referer = ApiUrlsHome.Referers.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
229
223
  break;
230
224
  default:
231
225
  payload = {
@@ -237,11 +231,14 @@ class MelCloudAta extends EventEmitter {
237
231
  VaneVerticalDirection: AirConditioner.VaneVerticalDirectionMapEnumToString[deviceData.Device.VaneVerticalDirection],
238
232
  };
239
233
  method = 'PUT';
240
- path = ApiUrlsHome.SetAta.replace('deviceid', deviceData.DeviceID);
234
+ path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
235
+ deviceData.Headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings
241
236
  break
242
237
  }
243
238
 
244
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
239
+ deviceData.Headers['Content-Type'] = 'application/json; charset=utf-8';
240
+ deviceData.Headers.Origin = ApiUrlsHome.Origin;
241
+ if (!this.logDebug) this.emit('warn', `Send Data: ${JSON.stringify(payload, null, 2)}, Headers: ${JSON.stringify(deviceData.Headers, null, 2)}`);
245
242
  await axios(path, {
246
243
  method: method,
247
244
  baseURL: ApiUrlsHome.BaseURL,
@@ -266,8 +263,7 @@ class MelCloudAta extends EventEmitter {
266
263
 
267
264
  setTimeout(() => {
268
265
  this.lock = false
269
- }, 3000);
266
+ }, 2500);
270
267
  }
271
-
272
268
  };
273
269
  export default MelCloudAta;