homebridge-melcloud-control 4.2.3-beta.6 → 4.2.3-beta.61

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