homebridge-midea-platform 1.2.7-beta.3 → 1.2.7

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.
@@ -120,7 +120,7 @@
120
120
  },
121
121
  "humiditySensor": {
122
122
  "title": "Humidity Sensor",
123
- "description": "Toggles if a seperated humidity sensor is created with the accessory. This sensor can be used for automations as for some reason the internal humidity sensor is not usable for this.",
123
+ "description": "Toggles if a seperated humidity sensor is created with the accessory. This sensor can be used for autmations as for some reason the internal humidity sensor is not usable for this.",
124
124
  "type": "boolean"
125
125
  },
126
126
  "pumpSwitch": {
@@ -306,11 +306,6 @@
306
306
  "title": "Comfort Mode Switch",
307
307
  "description": "Toggles if the comfort mode switch is created with the accessory.",
308
308
  "type": "boolean"
309
- },
310
- "temperatureSensor": {
311
- "title": "Indoor Temperature Sensor",
312
- "description": "Toggles if a seperated indoor temperature is created with the accessory. This sensor can be used for automations as for some reason the internal sensors are not usable for this.",
313
- "type": "boolean"
314
309
  }
315
310
  }
316
311
  },
@@ -657,8 +652,7 @@
657
652
  "devices[].AC_options.fanOnlyModeSwitch",
658
653
  "devices[].AC_options.fanAccessory",
659
654
  "devices[].AC_options.sleepModeSwitch",
660
- "devices[].AC_options.comfortModeSwitch",
661
- "devices[].AC_options.temperatureSensor"
655
+ "devices[].AC_options.comfortModeSwitch"
662
656
  ]
663
657
  },
664
658
  {
@@ -34,13 +34,15 @@ export default class AirConditionerAccessory extends BaseAccessory<MideaACDevice
34
34
  private sleepModeService?;
35
35
  private swingAngleService?;
36
36
  private comfortModeService?;
37
- private temperatureSensorService?;
38
37
  private swingAngleMainControl;
38
+ private heatingThresholdTemperature;
39
+ private coolingThresholdTemperature;
39
40
  /*********************************************************************
40
41
  * Constructor registers all the service types with Homebridge, registers
41
42
  * a callback function with the MideaDevice class, and requests device status.
42
43
  */
43
44
  constructor(platform: MideaPlatform, accessory: MideaAccessory, device: MideaACDevice, configDev: DeviceConfig);
45
+ private withoutPromptTone;
44
46
  /*********************************************************************
45
47
  * Callback function called by MideaDevice whenever there is a change to
46
48
  * any attribute value.
@@ -59,11 +61,20 @@ export default class AirConditionerAccessory extends BaseAccessory<MideaACDevice
59
61
  setTargetHeaterCoolerState(value: CharacteristicValue): Promise<void>;
60
62
  getCurrentTemperature(): CharacteristicValue;
61
63
  getTargetTemperature(): CharacteristicValue;
64
+ setTargetTemperature(value: CharacteristicValue): Promise<void>;
65
+ setTargetTemperatureWithinThresholds(): Promise<void>;
66
+ getCoolingThresholdTemperature(): CharacteristicValue;
67
+ getHeatingThresholdTemperature(): CharacteristicValue;
62
68
  getFanOnlyMode(): CharacteristicValue;
63
69
  setFanOnlyMode(value: CharacteristicValue): Promise<void>;
64
70
  getFanState(): CharacteristicValue;
65
71
  setFanState(value: CharacteristicValue): Promise<void>;
66
- setTargetTemperature(value: CharacteristicValue): Promise<void>;
72
+ setHeatingCoolingTemperatureThresholds(thresholds: {
73
+ heating?: number;
74
+ cooling?: number;
75
+ }): void;
76
+ setCoolingThresholdTemperature(value: CharacteristicValue): Promise<void>;
77
+ setHeatingThresholdTemperature(value: CharacteristicValue): Promise<void>;
67
78
  getSwingMode(): CharacteristicValue;
68
79
  setSwingMode(value: CharacteristicValue): Promise<void>;
69
80
  getRotationSpeed(): CharacteristicValue;
@@ -1,4 +1,4 @@
1
- import { SwingAngle, SwingMode } from '../platformUtils.js';
1
+ import { ACMode, SwingAngle, SwingMode } from '../platformUtils.js';
2
2
  import BaseAccessory, { limitValue } from './BaseAccessory.js';
3
3
  const outDoorTemperatureSubtype = 'outdoor';
4
4
  const displaySubtype = 'display';
@@ -16,7 +16,6 @@ const rateSelectSubtype = 'rateSelect';
16
16
  const sleepModeSubtype = 'sleepMode';
17
17
  const swingAngleSubtype = 'swingAngle';
18
18
  const comfortModeSubtype = 'comfortMode';
19
- const temperatureSensorSubtype = 'temperatureSensor';
20
19
  export default class AirConditionerAccessory extends BaseAccessory {
21
20
  device;
22
21
  configDev;
@@ -37,8 +36,9 @@ export default class AirConditionerAccessory extends BaseAccessory {
37
36
  sleepModeService;
38
37
  swingAngleService;
39
38
  comfortModeService;
40
- temperatureSensorService;
41
39
  swingAngleMainControl;
40
+ heatingThresholdTemperature;
41
+ coolingThresholdTemperature;
42
42
  /*********************************************************************
43
43
  * Constructor registers all the service types with Homebridge, registers
44
44
  * a callback function with the MideaDevice class, and requests device status.
@@ -71,8 +71,8 @@ export default class AirConditionerAccessory extends BaseAccessory {
71
71
  this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature).onGet(this.getCurrentTemperature.bind(this));
72
72
  this.service
73
73
  .getCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature)
74
- .onGet(this.getTargetTemperature.bind(this))
75
- .onSet(this.setTargetTemperature.bind(this))
74
+ .onGet(this.getCoolingThresholdTemperature.bind(this))
75
+ .onSet(this.setCoolingThresholdTemperature.bind(this))
76
76
  .setProps({
77
77
  minValue: this.configDev.AC_options.minTemp,
78
78
  maxValue: this.configDev.AC_options.maxTemp,
@@ -80,8 +80,8 @@ export default class AirConditionerAccessory extends BaseAccessory {
80
80
  });
81
81
  this.service
82
82
  .getCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature)
83
- .onGet(this.getTargetTemperature.bind(this))
84
- .onSet(this.setTargetTemperature.bind(this))
83
+ .onGet(this.getHeatingThresholdTemperature.bind(this))
84
+ .onSet(this.setHeatingThresholdTemperature.bind(this))
85
85
  .setProps({
86
86
  minValue: this.configDev.AC_options.minTemp,
87
87
  maxValue: this.configDev.AC_options.maxTemp,
@@ -254,7 +254,6 @@ export default class AirConditionerAccessory extends BaseAccessory {
254
254
  else if (this.sleepModeService) {
255
255
  this.accessory.removeService(this.sleepModeService);
256
256
  }
257
- // Comfort mode accessory
258
257
  this.comfortModeService = this.accessory.getServiceById(this.platform.Service.Switch, comfortModeSubtype);
259
258
  if (this.configDev.AC_options.comfortModeSwitch) {
260
259
  this.comfortModeService ??= this.accessory.addService(this.platform.Service.Switch, undefined, comfortModeSubtype);
@@ -264,16 +263,6 @@ export default class AirConditionerAccessory extends BaseAccessory {
264
263
  else if (this.comfortModeService) {
265
264
  this.accessory.removeService(this.comfortModeService);
266
265
  }
267
- // Separate temperature sensor accessory
268
- this.temperatureSensorService = this.accessory.getServiceById(this.platform.Service.TemperatureSensor, temperatureSensorSubtype);
269
- if (this.configDev.AC_options.temperatureSensor) {
270
- this.temperatureSensorService ??= this.accessory.addService(this.platform.Service.TemperatureSensor, undefined, temperatureSensorSubtype);
271
- this.handleConfiguredName(this.temperatureSensorService, temperatureSensorSubtype, 'Indoor Temperature');
272
- this.temperatureSensorService.getCharacteristic(this.platform.Characteristic.CurrentTemperature).onGet(this.getCurrentTemperature.bind(this));
273
- }
274
- else if (this.temperatureSensorService) {
275
- this.accessory.removeService(this.temperatureSensorService);
276
- }
277
266
  const swingProps = this.configDev.AC_options.swing;
278
267
  this.swingAngleMainControl =
279
268
  swingProps.mode === SwingMode.VERTICAL || (swingProps.mode === SwingMode.BOTH && swingProps.angleMainControl === SwingAngle.VERTICAL)
@@ -308,8 +297,20 @@ export default class AirConditionerAccessory extends BaseAccessory {
308
297
  }
309
298
  }
310
299
  // Misc
311
- this.device.attributes.PROMPT_TONE = this.configDev.AC_options.audioFeedback;
312
- this.device.attributes.TEMP_FAHRENHEIT = this.configDev.AC_options.fahrenheit;
300
+ this.device.attributes.PROMPT_TONE = configDev.AC_options.audioFeedback;
301
+ this.device.attributes.TEMP_FAHRENHEIT = configDev.AC_options.fahrenheit;
302
+ this.heatingThresholdTemperature = accessory.context?.thresholds?.heatingTemperature ?? configDev.AC_options.minTemp;
303
+ this.coolingThresholdTemperature = accessory.context?.thresholds?.coolingTemperature ?? configDev.AC_options.maxTemp;
304
+ }
305
+ async withoutPromptTone(fn) {
306
+ const hadPromptTone = this.device.attributes.PROMPT_TONE;
307
+ this.device.attributes.PROMPT_TONE = false;
308
+ try {
309
+ return await fn();
310
+ }
311
+ finally {
312
+ this.device.attributes.PROMPT_TONE = hadPromptTone;
313
+ }
313
314
  }
314
315
  /*********************************************************************
315
316
  * Callback function called by MideaDevice whenever there is a change to
@@ -324,102 +325,105 @@ export default class AirConditionerAccessory extends BaseAccessory {
324
325
  updateState = true;
325
326
  break;
326
327
  case 'temp_fahrenheit':
327
- this.service.getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits).updateValue(this.getTemperatureDisplayUnits());
328
+ this.service.updateCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, this.getTemperatureDisplayUnits());
328
329
  break;
329
330
  case 'screen_display':
330
331
  case 'screen_display_new':
331
- this.displayService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getDisplayActive());
332
+ this.displayService?.updateCharacteristic(this.platform.Characteristic.On, this.getDisplayActive());
332
333
  break;
333
- case 'target_temperature':
334
- // If MODE is 4 then device is heating. Therefore target temperature value must be heating target? Right?
335
- if (this.device.attributes.MODE === 4) {
336
- this.service.getCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature).updateValue(this.getTargetTemperature());
334
+ case 'target_temperature': {
335
+ const target = Number(this.getTargetTemperature());
336
+ /**
337
+ * If the device is heating, we map the target temperature to the heating threshold, if cooling we map to the cooling
338
+ * threshold and otherwise we assume an auto mode and only adjust the thresholds if the target value is outside their
339
+ * range. This should only happen if the temperature is changed outside of HomeKit and in this case we collapse the
340
+ * range to the target temperature set by the user.
341
+ */
342
+ if (this.device.attributes.MODE === ACMode.HEATING) {
343
+ this.setHeatingCoolingTemperatureThresholds({ heating: target });
344
+ }
345
+ else if (this.device.attributes.MODE === ACMode.COOLING) {
346
+ this.setHeatingCoolingTemperatureThresholds({ cooling: target });
337
347
  }
338
- else {
339
- this.service.getCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature).updateValue(this.getTargetTemperature());
348
+ else if (target < this.heatingThresholdTemperature || target > this.coolingThresholdTemperature) {
349
+ this.setHeatingCoolingTemperatureThresholds({ heating: target, cooling: target });
340
350
  }
341
351
  updateState = true;
342
352
  break;
343
- case 'indoor_temperature': {
344
- const temperature = this.getCurrentTemperature();
345
- this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(temperature);
346
- this.temperatureSensorService?.getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(temperature);
347
- break;
348
353
  }
354
+ case 'indoor_temperature':
355
+ this.service.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.getCurrentTemperature());
356
+ if (this.device.attributes.POWER && this.device.attributes.MODE === ACMode.AUTO) {
357
+ await this.withoutPromptTone(this.setTargetTemperatureWithinThresholds.bind(this));
358
+ updateState = true;
359
+ }
360
+ break;
349
361
  case 'outdoor_temperature':
350
- this.outDoorTemperatureService?.getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(this.getOutdoorTemperature());
362
+ this.outDoorTemperatureService?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.getOutdoorTemperature());
351
363
  break;
352
364
  case 'fan_speed':
353
365
  updateState = true;
354
366
  break;
355
367
  case 'fan_auto':
356
- this.fanService?.getCharacteristic(this.platform.Characteristic.TargetFanState).updateValue(this.getFanState());
368
+ this.fanService?.updateCharacteristic(this.platform.Characteristic.TargetFanState, this.getFanState());
357
369
  break;
358
370
  case 'swing_vertical':
359
371
  case 'swing_horizontal':
360
- this.service.getCharacteristic(this.platform.Characteristic.SwingMode).updateValue(this.getSwingMode());
372
+ this.service.updateCharacteristic(this.platform.Characteristic.SwingMode, this.getSwingMode());
361
373
  break;
362
374
  case 'mode':
363
375
  updateState = true;
364
376
  break;
365
377
  case 'eco_mode':
366
- this.ecoModeService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getEcoMode());
378
+ this.ecoModeService?.updateCharacteristic(this.platform.Characteristic.On, this.getEcoMode());
367
379
  break;
368
380
  case 'indirect_wind':
369
- this.breezeAwayService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getBreezeAway());
381
+ this.breezeAwayService?.updateCharacteristic(this.platform.Characteristic.On, this.getBreezeAway());
370
382
  break;
371
383
  case 'aux_heating':
372
- this.auxHeatingService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getAuxHeating());
384
+ this.auxHeatingService?.updateCharacteristic(this.platform.Characteristic.On, this.getAuxHeating());
373
385
  break;
374
386
  case 'smart_eye':
375
- this.auxService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getAux());
387
+ this.auxService?.updateCharacteristic(this.platform.Characteristic.On, this.getAux());
376
388
  break;
377
389
  case 'wind_swing_lr_angle':
378
390
  case 'wind_swing_ud_angle':
379
- this.swingAngleService?.getCharacteristic(this.platform.Characteristic.CurrentPosition).updateValue(this.getSwingAngleCurrentPosition());
380
- this.swingAngleService?.getCharacteristic(this.platform.Characteristic.TargetPosition).updateValue(this.getSwingAngleTargetPosition());
391
+ this.swingAngleService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, this.getSwingAngleCurrentPosition());
392
+ this.swingAngleService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, this.getSwingAngleTargetPosition());
381
393
  if (this.configDev.AC_options.swing.mode === SwingMode.BOTH) {
382
- this.swingAngleService
383
- ?.getCharacteristic(this.platform.Characteristic.CurrentHorizontalTiltAngle)
384
- .updateValue(this.getSwingAngleCurrentHorizontalTiltAngle());
385
- this.swingAngleService
386
- ?.getCharacteristic(this.platform.Characteristic.CurrentVerticalTiltAngle)
387
- .updateValue(this.getSwingAngleCurrentVerticalTiltAngle());
388
- this.swingAngleService
389
- ?.getCharacteristic(this.platform.Characteristic.TargetHorizontalTiltAngle)
390
- .updateValue(this.getSwingAngleTargetHorizontalTiltAngle());
391
- this.swingAngleService
392
- ?.getCharacteristic(this.platform.Characteristic.TargetVerticalTiltAngle)
393
- .updateValue(this.getSwingAngleTargetVerticalTiltAngle());
394
+ this.swingAngleService?.updateCharacteristic(this.platform.Characteristic.CurrentHorizontalTiltAngle, this.getSwingAngleCurrentHorizontalTiltAngle());
395
+ this.swingAngleService?.updateCharacteristic(this.platform.Characteristic.CurrentVerticalTiltAngle, this.getSwingAngleCurrentVerticalTiltAngle());
396
+ this.swingAngleService?.updateCharacteristic(this.platform.Characteristic.TargetHorizontalTiltAngle, this.getSwingAngleTargetHorizontalTiltAngle());
397
+ this.swingAngleService?.updateCharacteristic(this.platform.Characteristic.TargetVerticalTiltAngle, this.getSwingAngleTargetVerticalTiltAngle());
394
398
  }
395
399
  break;
396
400
  case 'self_clean':
397
401
  updateState = true;
398
- this.selfCleanService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getSelfCleanState());
402
+ this.selfCleanService?.updateCharacteristic(this.platform.Characteristic.On, this.getSelfCleanState());
399
403
  break;
400
404
  case 'ion':
401
- this.ionService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getIonState());
405
+ this.ionService?.updateCharacteristic(this.platform.Characteristic.On, this.getIonState());
402
406
  break;
403
407
  case 'rate_select':
404
- this.rateSelectService?.getCharacteristic(this.platform.Characteristic.Brightness).updateValue(this.getRateSelect());
408
+ this.rateSelectService?.updateCharacteristic(this.platform.Characteristic.Brightness, this.getRateSelect());
405
409
  break;
406
410
  default:
407
411
  this.platform.log.debug(`[${this.device.name}] Attempt to set unsupported attribute ${k} to ${v}`);
408
412
  }
409
413
  if (updateState) {
410
- this.service.getCharacteristic(this.platform.Characteristic.Active).updateValue(this.getActive());
411
- this.service.getCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState).updateValue(this.getTargetHeaterCoolerState());
412
- this.service.getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState).updateValue(this.getCurrentHeaterCoolerState());
413
- this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed).updateValue(this.getRotationSpeed());
414
- this.fanOnlyService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getFanOnlyMode());
415
- this.fanService?.getCharacteristic(this.platform.Characteristic.Active).updateValue(this.getActive());
416
- this.fanService?.getCharacteristic(this.platform.Characteristic.RotationSpeed).updateValue(this.getRotationSpeed());
417
- this.dryModeService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getDryMode());
418
- this.displayService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getDisplayActive());
419
- this.ecoModeService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getEcoMode());
420
- this.breezeAwayService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getBreezeAway());
421
- this.auxService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getAux());
422
- this.auxHeatingService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.getAuxHeating());
414
+ this.service.updateCharacteristic(this.platform.Characteristic.Active, this.getActive());
415
+ this.service.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, this.getTargetHeaterCoolerState());
416
+ this.service.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.getCurrentHeaterCoolerState());
417
+ this.service.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.getRotationSpeed());
418
+ this.fanOnlyService?.updateCharacteristic(this.platform.Characteristic.On, this.getFanOnlyMode());
419
+ this.fanService?.updateCharacteristic(this.platform.Characteristic.Active, this.getActive());
420
+ this.fanService?.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.getRotationSpeed());
421
+ this.dryModeService?.updateCharacteristic(this.platform.Characteristic.On, this.getDryMode());
422
+ this.displayService?.updateCharacteristic(this.platform.Characteristic.On, this.getDisplayActive());
423
+ this.ecoModeService?.updateCharacteristic(this.platform.Characteristic.On, this.getEcoMode());
424
+ this.breezeAwayService?.updateCharacteristic(this.platform.Characteristic.On, this.getBreezeAway());
425
+ this.auxService?.updateCharacteristic(this.platform.Characteristic.On, this.getAux());
426
+ this.auxHeatingService?.updateCharacteristic(this.platform.Characteristic.On, this.getAuxHeating());
423
427
  }
424
428
  }
425
429
  }
@@ -434,7 +438,7 @@ export default class AirConditionerAccessory extends BaseAccessory {
434
438
  async setActive(value) {
435
439
  await this.device.set_attribute({ POWER: !!value });
436
440
  this.device.attributes.SCREEN_DISPLAY = !!value;
437
- this.displayService?.getCharacteristic(this.platform.Characteristic.On).updateValue(!!value);
441
+ this.displayService?.updateCharacteristic(this.platform.Characteristic.On, !!value);
438
442
  }
439
443
  getTemperatureDisplayUnits() {
440
444
  return this.device.attributes.TEMP_FAHRENHEIT
@@ -447,60 +451,97 @@ export default class AirConditionerAccessory extends BaseAccessory {
447
451
  });
448
452
  }
449
453
  getCurrentHeaterCoolerState() {
450
- if (this.device.attributes.POWER && this.device.attributes.MODE > 0 && this.device.attributes.MODE < 5) {
451
- if (this.device.attributes.TARGET_TEMPERATURE < (this.device.attributes.INDOOR_TEMPERATURE ?? 0)) {
452
- if ([1, 2].includes(this.device.attributes.MODE)) {
453
- return this.platform.Characteristic.CurrentHeaterCoolerState.COOLING;
454
- }
455
- return this.platform.Characteristic.CurrentHeaterCoolerState.IDLE;
456
- }
457
- if (this.device.attributes.TARGET_TEMPERATURE === this.device.attributes.INDOOR_TEMPERATURE) {
458
- return this.platform.Characteristic.CurrentHeaterCoolerState.IDLE;
459
- }
460
- if (this.device.attributes.MODE === 4) {
461
- return this.platform.Characteristic.CurrentHeaterCoolerState.HEATING;
462
- }
463
- return this.platform.Characteristic.CurrentHeaterCoolerState.IDLE;
454
+ const { CurrentHeaterCoolerState } = this.platform.Characteristic;
455
+ if (!this.device.attributes.POWER || !this.device.attributes.MODE) {
456
+ return CurrentHeaterCoolerState.INACTIVE;
464
457
  }
465
- return this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE;
458
+ const isPossiblyCooling = [ACMode.COOLING, ACMode.AUTO].includes(this.device.attributes.MODE);
459
+ const isPossiblyHeating = [ACMode.HEATING, ACMode.AUTO].includes(this.device.attributes.MODE) && this.configDev.AC_options.heatingCapable;
460
+ const currentTemperature = Number(this.getCurrentTemperature());
461
+ const heatingThresholdTemperature = Number(this.getHeatingThresholdTemperature());
462
+ const coolingThresholdTemperature = Number(this.getCoolingThresholdTemperature());
463
+ if (isPossiblyCooling && currentTemperature > coolingThresholdTemperature) {
464
+ return CurrentHeaterCoolerState.COOLING;
465
+ }
466
+ if (isPossiblyHeating && currentTemperature < heatingThresholdTemperature) {
467
+ return CurrentHeaterCoolerState.HEATING;
468
+ }
469
+ return CurrentHeaterCoolerState.IDLE;
466
470
  }
467
471
  getTargetHeaterCoolerState() {
468
- if (this.device.attributes.MODE === 2) {
469
- return this.platform.Characteristic.TargetHeaterCoolerState.COOL;
472
+ switch (this.device.attributes.MODE) {
473
+ case ACMode.COOLING:
474
+ return this.platform.Characteristic.TargetHeaterCoolerState.COOL;
475
+ case ACMode.HEATING:
476
+ return this.platform.Characteristic.TargetHeaterCoolerState.HEAT;
477
+ default:
478
+ return this.platform.Characteristic.TargetHeaterCoolerState.AUTO;
470
479
  }
471
- if (this.device.attributes.MODE === 4) {
472
- return this.platform.Characteristic.TargetHeaterCoolerState.HEAT;
473
- }
474
- return this.platform.Characteristic.TargetHeaterCoolerState.AUTO;
475
480
  }
476
481
  async setTargetHeaterCoolerState(value) {
477
482
  switch (value) {
478
483
  case this.platform.Characteristic.TargetHeaterCoolerState.AUTO:
479
- await this.device.set_attribute({ POWER: true, MODE: 1 });
484
+ await this.device.set_attribute({ POWER: true, MODE: ACMode.AUTO });
480
485
  break;
481
486
  case this.platform.Characteristic.TargetHeaterCoolerState.COOL:
482
- await this.device.set_attribute({ POWER: true, MODE: 2 });
487
+ await this.device.set_attribute({ POWER: true, MODE: ACMode.COOLING });
483
488
  break;
484
489
  case this.platform.Characteristic.TargetHeaterCoolerState.HEAT:
485
- await this.device.set_attribute({ POWER: true, MODE: 4 });
490
+ await this.device.set_attribute({ POWER: true, MODE: ACMode.HEATING });
486
491
  break;
487
492
  }
493
+ await this.setTargetTemperatureWithinThresholds();
488
494
  }
489
495
  getCurrentTemperature() {
490
496
  return this.device.attributes.INDOOR_TEMPERATURE ?? this.configDev.AC_options.minTemp;
491
497
  }
492
498
  getTargetTemperature() {
493
- return limitValue(this.device.attributes.TARGET_TEMPERATURE, this.configDev.AC_options.minTemp, this.configDev.AC_options.maxTemp);
499
+ const { minTemp, maxTemp } = this.configDev.AC_options;
500
+ return limitValue(this.device.attributes.TARGET_TEMPERATURE, minTemp, maxTemp);
501
+ }
502
+ async setTargetTemperature(value) {
503
+ const { minTemp, maxTemp, tempStep } = this.configDev.AC_options;
504
+ const target = limitValue(Math.round(+value / tempStep) * tempStep, minTemp, maxTemp);
505
+ if (this.getTargetTemperature() === target)
506
+ return;
507
+ await this.device.set_target_temperature(target);
508
+ }
509
+ async setTargetTemperatureWithinThresholds() {
510
+ if (this.device.attributes.MODE === ACMode.COOLING) {
511
+ await this.setTargetTemperature(this.getCoolingThresholdTemperature());
512
+ return;
513
+ }
514
+ if (this.device.attributes.MODE === ACMode.HEATING) {
515
+ await this.setTargetTemperature(this.getHeatingThresholdTemperature());
516
+ return;
517
+ }
518
+ if (this.getCurrentTemperature() > this.getCoolingThresholdTemperature()) {
519
+ await this.setTargetTemperature(this.getCoolingThresholdTemperature());
520
+ return;
521
+ }
522
+ if (this.getCurrentTemperature() < this.getHeatingThresholdTemperature()) {
523
+ await this.setTargetTemperature(this.getHeatingThresholdTemperature());
524
+ return;
525
+ }
526
+ await this.setTargetTemperature(this.getCurrentTemperature());
527
+ }
528
+ getCoolingThresholdTemperature() {
529
+ const { minTemp, maxTemp } = this.configDev.AC_options;
530
+ return limitValue(this.coolingThresholdTemperature, minTemp, maxTemp);
531
+ }
532
+ getHeatingThresholdTemperature() {
533
+ const { minTemp, maxTemp } = this.configDev.AC_options;
534
+ return limitValue(this.heatingThresholdTemperature, minTemp, maxTemp);
494
535
  }
495
536
  getFanOnlyMode() {
496
- return this.device.attributes.POWER === true && this.device.attributes.MODE === 5;
537
+ return this.device.attributes.POWER === true && this.device.attributes.MODE === ACMode.FAN_ONLY;
497
538
  }
498
539
  async setFanOnlyMode(value) {
499
540
  if (value) {
500
- await this.device.set_attribute({ POWER: true, MODE: 5 });
541
+ await this.device.set_attribute({ POWER: true, MODE: ACMode.FAN_ONLY });
501
542
  }
502
543
  else {
503
- await this.device.set_attribute({ POWER: false, MODE: 0 });
544
+ await this.device.set_attribute({ POWER: false, MODE: ACMode.OFF });
504
545
  }
505
546
  }
506
547
  getFanState() {
@@ -509,8 +550,30 @@ export default class AirConditionerAccessory extends BaseAccessory {
509
550
  async setFanState(value) {
510
551
  await this.device.set_fan_auto(value === this.platform.Characteristic.TargetFanState.AUTO);
511
552
  }
512
- async setTargetTemperature(value) {
513
- await this.device.set_target_temperature(limitValue(value, this.configDev.AC_options.minTemp, this.configDev.AC_options.maxTemp));
553
+ setHeatingCoolingTemperatureThresholds(thresholds) {
554
+ const { minTemp, maxTemp, tempStep } = this.configDev.AC_options;
555
+ const heating = limitValue(thresholds?.heating ?? this.heatingThresholdTemperature, minTemp, maxTemp);
556
+ const cooling = limitValue(thresholds?.cooling ?? this.coolingThresholdTemperature, minTemp, maxTemp);
557
+ if (heating === this.heatingThresholdTemperature && cooling === this.coolingThresholdTemperature)
558
+ return;
559
+ this.heatingThresholdTemperature = !thresholds?.cooling || heating < cooling ? heating : cooling - tempStep;
560
+ this.coolingThresholdTemperature = !thresholds?.heating || heating < cooling ? cooling : heating + tempStep;
561
+ this.service.updateCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, this.getHeatingThresholdTemperature());
562
+ this.service.updateCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, this.getCoolingThresholdTemperature());
563
+ const { context: ctx } = this.accessory;
564
+ ctx.thresholds ??= {};
565
+ this.platform.log.debug(`[${this.device.name}] Persisting updated heating and cooling thresholds`);
566
+ ctx.thresholds.heatingTemperature = this.heatingThresholdTemperature;
567
+ ctx.thresholds.coolingTemperature = this.coolingThresholdTemperature;
568
+ this.platform.api.updatePlatformAccessories([this.accessory]);
569
+ }
570
+ async setCoolingThresholdTemperature(value) {
571
+ this.setHeatingCoolingTemperatureThresholds({ cooling: Number(value) });
572
+ await this.setTargetTemperatureWithinThresholds();
573
+ }
574
+ async setHeatingThresholdTemperature(value) {
575
+ this.setHeatingCoolingTemperatureThresholds({ heating: Number(value) });
576
+ await this.setTargetTemperatureWithinThresholds();
514
577
  }
515
578
  getSwingMode() {
516
579
  return this.device.attributes.SWING_HORIZONTAL || this.device.attributes.SWING_VERTICAL
@@ -557,14 +620,14 @@ export default class AirConditionerAccessory extends BaseAccessory {
557
620
  await this.device.set_attribute({ INDIRECT_WIND: !!value });
558
621
  }
559
622
  getDryMode() {
560
- return this.device.attributes.POWER === true && this.device.attributes.MODE === 3;
623
+ return this.device.attributes.POWER === true && this.device.attributes.MODE === ACMode.DRY;
561
624
  }
562
625
  async setDryMode(value) {
563
626
  if (value) {
564
- await this.device.set_attribute({ POWER: true, MODE: 3 });
627
+ await this.device.set_attribute({ POWER: true, MODE: ACMode.DRY });
565
628
  }
566
629
  else {
567
- await this.device.set_attribute({ POWER: false, MODE: 0 });
630
+ await this.device.set_attribute({ POWER: false, MODE: ACMode.OFF });
568
631
  }
569
632
  }
570
633
  getBoostMode() {