UNPKG

@homebridge-plugins/homebridge-smarthq

Version:

The SmartHQ plugin allows you to interact with SmartHQ Devices in HomeKit and with Siri.

743 lines 37.4 kB
import axios from 'axios'; import { interval, startWith } from 'rxjs'; import { ERD_TYPES } from '../settings.js'; import { deviceBase } from './device.js'; var PowerState; (function (PowerState) { PowerState["ON"] = "01"; PowerState["OFF"] = "00"; })(PowerState || (PowerState = {})); var TemperatureUnit; (function (TemperatureUnit) { TemperatureUnit["FAHRENHEIT"] = "00"; TemperatureUnit["CELSIUS"] = "01"; })(TemperatureUnit || (TemperatureUnit = {})); var FanSetting; (function (FanSetting) { FanSetting["AUTO"] = "01"; FanSetting["LOW"] = "02"; FanSetting["MED"] = "04"; FanSetting["HIGH"] = "08"; })(FanSetting || (FanSetting = {})); var FilterStatus; (function (FilterStatus) { FilterStatus["OK"] = "00"; FilterStatus["CLEAN"] = "01"; })(FilterStatus || (FilterStatus = {})); var AcSwingMode; (function (AcSwingMode) { AcSwingMode["DISABLED"] = "00"; AcSwingMode["ENABLED"] = "01"; })(AcSwingMode || (AcSwingMode = {})); var OperationMode; (function (OperationMode) { OperationMode["COOL"] = "00"; OperationMode["FAN_ONLY"] = "01"; OperationMode["ENERGY_SAVER"] = "02"; OperationMode["HEAT"] = "03"; OperationMode["DRY"] = "04"; })(OperationMode || (OperationMode = {})); export class SmartHQAirConditioner extends deviceBase { platform; accessory; device; // HeaterCooler service HEATER_COOLER_SVC_NAME = 'AIR_CONDITIONER'; heaterCoolerSvc; // Mode SwitchServices MODE_SWITCH_SVC_PREFIX = 'AIR_CONDITIONER_MODE'; modeSwitchSvc; constructor(platform, accessory, device) { super(platform, accessory, device); this.platform = platform; this.accessory = accessory; this.device = device; // HeaterCooler service this.heaterCoolerSvc = this.accessory.getService(this.HEATER_COOLER_SVC_NAME) ?? this.accessory.addService(this.platform.Service.HeaterCooler, accessory.displayName, this.HEATER_COOLER_SVC_NAME); // Mode SwitchServices this.modeSwitchSvc = { [OperationMode.COOL]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_COOL`) ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Cool Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_COOL`), [OperationMode.FAN_ONLY]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_FAN_ONLY`) ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Fan Only Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_FAN_ONLY`), [OperationMode.ENERGY_SAVER]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_ENERGY_SAVER`) ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Energy Saver Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_ENERGY_SAVER`), [OperationMode.HEAT]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_HEAT`) ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Heat Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_HEAT`), [OperationMode.DRY]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_DRY`) ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Dry Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_DRY`), }; // Active this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.Active) .onGet(this.handleGetActive.bind(this)) .onSet(this.handleSetActive.bind(this)); // Current mode this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .setProps({ validValues: [ this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE, this.platform.Characteristic.CurrentHeaterCoolerState.IDLE, this.platform.Characteristic.CurrentHeaterCoolerState.HEATING, this.platform.Characteristic.CurrentHeaterCoolerState.COOLING, ], }) .onGet(this.handleGetCurrentHeaterCoolerState.bind(this)); // Target mode (HEAT and COOL) this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState) .setProps({ validValues: [ this.platform.Characteristic.TargetHeaterCoolerState.HEAT, this.platform.Characteristic.TargetHeaterCoolerState.COOL, ], }) .onGet(this.handleGetTargetHeaterCoolerState.bind(this)) .onSet(this.handleSetTargetHeaterCoolerState.bind(this)); // Ambient temp this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.CurrentTemperature) .onGet(this.handleGetCurrentTemperature.bind(this)); // Target temperature this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature) .setProps({ minValue: 17.7778, // 64F maxValue: 30, // 86F }) .onGet(this.handleGetCoolingThresholdTemperature.bind(this)) .onSet(this.handleSetCoolingThresholdTemperature.bind(this)); // Heating threshold temperature this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature) .setProps({ minValue: 17.7778, // 64F maxValue: 30, // 86F }) .onGet(this.handleGetHeatingThresholdTemperature.bind(this)) .onSet(this.handleSetHeatingThresholdTemperature.bind(this)); // Rotation speed this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.RotationSpeed) .onGet(this.handleGetRotationSpeed.bind(this)) .onSet(this.handleSetRotationSpeed.bind(this)); // Display units this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) .onGet(this.handleGetTemperatureDisplayUnits.bind(this)) .onSet(this.handleSetTemperatureDisplayUnits.bind(this)); // Filter this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.FilterChangeIndication) .onGet(this.handleGetFilterChangeIndication.bind(this)); // Swing mode this.heaterCoolerSvc .getCharacteristic(this.platform.Characteristic.SwingMode) .onGet(this.handleGetSwingMode.bind(this)) .onSet(this.handleSetSwingMode.bind(this)); // Modes for (const mode of Object.values(OperationMode)) { this.modeSwitchSvc[mode] .getCharacteristic(this.platform.Characteristic.On) .onGet(this.handleGetOperationMode.bind(this, mode)) .onSet(this.handleSetOperationMode.bind(this, mode)); this.modeSwitchSvc[mode] .getCharacteristic(this.platform.Characteristic.Name) .onGet(this.handleGetOperationModeName.bind(this, mode)); } // Start an update interval to refresh state interval(this.deviceRefreshRate * 1000) .pipe(startWith(0)) .subscribe(this.refreshState.bind(this)); } // API async getErdValue(erd) { try { const response = await axios.get(`/appliance/${this.accessory.context.device.applianceId}/erd/${erd}`); return response.data.value; } catch (cause) { throw new Error(axios.isAxiosError(cause) && cause.response ? `Failed to fetch ERD: ${cause.response.data.message}` : `Failed to fetch ERD: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async setErdValue(erd, value) { try { await axios.post(`/appliance/${this.accessory.context.device.applianceId}/erd/${erd}`, { kind: 'appliance#erdListEntry', userId: this.accessory.context.userId, applianceId: this.accessory.context.device.applianceId, erd, value, }); this.platform.log.debug(`[${this.accessory.displayName}] Set ERD ${erd}=${value}`); } catch (cause) { throw new Error(axios.isAxiosError(cause) && cause.response ? `Failed to set ERD ${erd}=${value}: ${cause.response.data.message}` : `Failed to set ERD ${erd}=${value}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getPowerState() { const erdValue = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_POWER_STATUS); return erdValue; } async setPowerState(value) { await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_POWER_STATUS, value); this.platform.log.debug(`[${this.accessory.displayName}] Set power state to ${value}`); } async getAmbientTemperature() { try { const erdValue = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_AMBIENT_TEMPERATURE); const temperatureInFahrenheit = Number.parseInt(erdValue, 16); // erdValue is a hex string representing the temperature in Fahrenheit return this.fahrenheitToCelsius(temperatureInFahrenheit); // homekit expects Celsius } catch (cause) { throw new Error(`Failed to get current temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getTemperature() { try { const erdValue = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_TARGET_TEMPERATURE); const temperatureInFahrenheit = Number.parseInt(erdValue, 16); // erdValue is a hex string representing the temperature in Fahrenheit return this.fahrenheitToCelsius(temperatureInFahrenheit); // homekit expects Celsius } catch (cause) { throw new Error(`Failed to get target temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async setTemperature(value) { try { const temperatureInFahrenheit = this.celsiusToFahrenheit(value); const hexTemperature = Math.round(temperatureInFahrenheit).toString(16).padStart(4, '0').toUpperCase(); // Convert to hex and ensure it's 4 characters long await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_TARGET_TEMPERATURE, hexTemperature); this.platform.log.debug(`[${this.accessory.displayName}] Set temperature to ${value}°C (${temperatureInFahrenheit}°F)`); } catch (cause) { throw new Error(`Failed to set target temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getTemperatureDisplayUnits() { try { const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_TEMPERATURE_UNIT); return value; } catch (cause) { throw new Error(`Failed to get temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async setTemperatureDisplayUnits(value) { try { await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_TEMPERATURE_UNIT, value); this.platform.log.debug(`[${this.accessory.displayName}] Set temperature display units to ${value}`); } catch (cause) { throw new Error(`Failed to set temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getOperationMode() { try { const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_OPERATION_MODE); return value; } catch (cause) { throw new Error(`Failed to get operation mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async setOperationMode(value) { try { await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_OPERATION_MODE, value); this.platform.log.debug(`[${this.accessory.displayName}] Set operation mode to ${value}`); } catch (cause) { throw new Error(`Failed to set operation mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getFanSetting() { try { const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_FAN_SETTING); return value; } catch (cause) { throw new Error(`Failed to get fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async setFanSetting(value) { try { await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_FAN_SETTING, value); this.platform.log.debug(`[${this.accessory.displayName}] Set fan setting to ${value}`); } catch (cause) { throw new Error(`Failed to set fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getFilterStatus() { try { const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_FILTER_STATUS); return value; } catch (cause) { throw new Error(`Failed to get filter status: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async getSwingMode() { try { const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_SWING_MODE); return value; } catch (cause) { // Some devices (e.g. PHNT10CC) do not support the swing mode ERD and return a 400 error const axiosCause = cause instanceof Error ? cause.cause : null; if (axios.isAxiosError(axiosCause) && axiosCause.response?.status === 400) { this.platform.log.debug(`[${this.accessory.displayName}] Swing mode not supported by this device, defaulting to disabled`); return AcSwingMode.DISABLED; } throw new Error(`Failed to get swing mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } async setSwingMode(value) { try { await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_SWING_MODE, value); this.platform.log.debug(`[${this.accessory.displayName}] Set swing mode to ${value}`); } catch (cause) { throw new Error(`Failed to set swing mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); } } // Characteristic handlers // active async handleGetActive() { try { const powerState = await this.getPowerState(); return powerState === PowerState.ON ? this.platform.Characteristic.Active.ACTIVE : this.platform.Characteristic.Active.INACTIVE; } catch (cause) { const error = new Error(`Failed to handle get active: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetActive(value) { try { const [powerState, ambientTemperature, targetTemperature, operationMode] = await Promise.all([ this.getPowerState(), this.getAmbientTemperature(), this.getTemperature(), this.getOperationMode(), ]); if (value === this.platform.Characteristic.Active.ACTIVE) { // If the air conditioner is currently off, turn it on if (powerState === PowerState.OFF) { await this.setPowerState(PowerState.ON); // There's a bug (feature?) in SmartHQ where it resets operation mode when turning on the air conditioner // so we need to set it back to the previous mode await this.setOperationMode(operationMode); } // Keep mode switches in sync for (const mode of Object.values(OperationMode)) { this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, mode === operationMode); } // Update CurrentHeaterCoolerState this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, operationMode === OperationMode.HEAT ? (ambientTemperature >= targetTemperature ? this.platform.Characteristic.CurrentHeaterCoolerState.IDLE : this.platform.Characteristic.CurrentHeaterCoolerState.HEATING) : (ambientTemperature <= targetTemperature ? this.platform.Characteristic.CurrentHeaterCoolerState.IDLE : this.platform.Characteristic.CurrentHeaterCoolerState.COOLING)); return; } if (powerState === PowerState.ON) { await this.setPowerState(PowerState.OFF); } // Keep mode switches in sync for (const mode of Object.values(OperationMode)) { this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, false); } // Keep TargetHeaterCoolerState in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE); } catch (cause) { const error = new Error(`Failed to handle set active: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetCurrentHeaterCoolerState() { try { const [powerState, ambientTemperature, targetTemperature, operationMode] = await Promise.all([ this.getPowerState(), this.getAmbientTemperature(), this.getTemperature(), this.getOperationMode(), ]); if (powerState === PowerState.OFF) { return this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE; } if (operationMode === OperationMode.HEAT) { if (ambientTemperature >= targetTemperature) { return this.platform.Characteristic.CurrentHeaterCoolerState.IDLE; } // Keep TargetHeaterCoolerState in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.HEAT); return this.platform.Characteristic.CurrentHeaterCoolerState.HEATING; } if (ambientTemperature <= targetTemperature) { return this.platform.Characteristic.CurrentHeaterCoolerState.IDLE; } // Keep TargetHeaterCoolerState in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.COOL); return this.platform.Characteristic.CurrentHeaterCoolerState.COOLING; } catch (cause) { const error = new Error(`Failed to handle get current heater cooler state: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetTargetHeaterCoolerState() { try { const operationMode = await this.getOperationMode(); return operationMode === OperationMode.HEAT ? this.platform.Characteristic.TargetHeaterCoolerState.HEAT : this.platform.Characteristic.TargetHeaterCoolerState.COOL; } catch (cause) { const error = new Error(`Failed to handle get target heater cooler state: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetTargetHeaterCoolerState(value) { try { const powerState = await this.getPowerState(); // Turn on the air conditioner if it's currently off if (powerState === PowerState.OFF) { await this.setPowerState(PowerState.ON); } if (value === this.platform.Characteristic.TargetHeaterCoolerState.HEAT) { await this.setOperationMode(OperationMode.HEAT); // Keep CurrentHeaterCoolerState in sync with TargetHeaterCoolerState this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.HEATING); // Keep mode switches in sync for (const mode of Object.values(OperationMode)) { this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, mode === OperationMode.HEAT); } } else { await this.setOperationMode(OperationMode.COOL); // Keep CurrentHeaterCoolerState in sync with TargetHeaterCoolerState this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.COOLING); // Keep mode switches in sync for (const mode of Object.values(OperationMode)) { this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, mode === OperationMode.COOL); } } } catch (cause) { const error = new Error(`Failed to handle set target heater cooler state: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetCurrentTemperature() { try { const value = await this.getAmbientTemperature(); return value; } catch (cause) { const error = new Error(`Failed to handle get current temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetCoolingThresholdTemperature() { try { const value = await this.getTemperature(); return value; } catch (cause) { const error = new Error(`Failed to handle get cooling threshold temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetCoolingThresholdTemperature(value) { try { const targetTemperature = Number.parseFloat(value); await this.setTemperature(targetTemperature); } catch (cause) { const error = new Error(`Failed to handle set cooling threshold temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetHeatingThresholdTemperature() { try { const value = await this.getTemperature(); return value; } catch (cause) { const error = new Error(`Failed to handle get heating threshold temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetHeatingThresholdTemperature(value) { try { const targetTemperature = Number.parseFloat(value); await this.setTemperature(targetTemperature); } catch (cause) { const error = new Error(`Failed to handle set heating threshold temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetRotationSpeed() { try { const value = await this.getFanSetting(); switch (value) { case FanSetting.AUTO: return 0; case FanSetting.LOW: return 33; case FanSetting.MED: return 66; case FanSetting.HIGH: return 100; default: throw new Error(`Unknown fan setting: ${value}`); } } catch (cause) { const error = new Error(`Failed to handle get fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetRotationSpeed(value) { try { const speed = value; // AUTO if (speed === 0) { await this.setFanSetting(FanSetting.AUTO); return; } // LOW if (speed <= 33) { await this.setFanSetting(FanSetting.LOW); return; } // MED if (speed <= 66) { await this.setFanSetting(FanSetting.MED); return; } // HIGH await this.setFanSetting(FanSetting.HIGH); } catch (cause) { const error = new Error(`Failed to handle set fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetTemperatureDisplayUnits() { try { const value = await this.getTemperatureDisplayUnits(); return value === TemperatureUnit.FAHRENHEIT ? this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT : this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS; } catch (cause) { const error = new Error(`Failed to handle get temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetTemperatureDisplayUnits(value) { try { const temperatureUnit = value === this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT ? TemperatureUnit.FAHRENHEIT : TemperatureUnit.CELSIUS; await this.setTemperatureDisplayUnits(temperatureUnit); } catch (cause) { const error = new Error(`Failed to handle set temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetFilterChangeIndication() { try { const value = await this.getFilterStatus(); return value === FilterStatus.OK ? this.platform.Characteristic.FilterChangeIndication.FILTER_OK : this.platform.Characteristic.FilterChangeIndication.CHANGE_FILTER; } catch (cause) { const error = new Error(`Failed to handle get filter change indication: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetSwingMode() { try { const value = await this.getSwingMode(); return value === AcSwingMode.ENABLED ? this.platform.Characteristic.SwingMode.SWING_ENABLED : this.platform.Characteristic.SwingMode.SWING_DISABLED; } catch (cause) { const error = new Error(`Failed to handle get swing mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetSwingMode(value) { try { const swingMode = value === this.platform.Characteristic.SwingMode.SWING_ENABLED ? AcSwingMode.ENABLED : AcSwingMode.DISABLED; await this.setSwingMode(swingMode); } catch (cause) { const error = new Error(`Failed to handle set swing mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleGetOperationMode(mode) { try { const [powerState, currentOperationMode] = await Promise.all([ this.getPowerState(), this.getOperationMode(), ]); // If the air conditioner is off, all modes are off if (powerState === PowerState.OFF) { return false; } return currentOperationMode === mode; } catch (cause) { const error = new Error(`Failed to handle get operation mode ${mode}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } async handleSetOperationMode(mode, value) { try { const [powerState, currentOperationMode] = await Promise.all([ this.getPowerState(), this.getOperationMode(), ]); // turn on the Air Conditioner if it's currently off if (value && powerState === PowerState.OFF) { await this.setPowerState(PowerState.ON); // Keep Active in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.Active, this.platform.Characteristic.Active.ACTIVE); // Keep CurrentHeaterCoolerState in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, mode === OperationMode.HEAT ? this.platform.Characteristic.CurrentHeaterCoolerState.HEATING : this.platform.Characteristic.CurrentHeaterCoolerState.COOLING); // Keep TargetHeaterCoolerState in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, mode === OperationMode.HEAT ? this.platform.Characteristic.TargetHeaterCoolerState.HEAT : this.platform.Characteristic.TargetHeaterCoolerState.COOL); // turn off the Air Conditioner if the user turned off the current mode } else if (!value && currentOperationMode === mode && powerState === PowerState.ON) { await this.setPowerState(PowerState.OFF); // Keep Active in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.Active, this.platform.Characteristic.Active.INACTIVE); // Keep CurrentHeaterCoolerState in sync this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE); } if (value) { await this.setOperationMode(mode); } // switch the rest off for (const m of Object.values(OperationMode)) { this.modeSwitchSvc[m].updateCharacteristic(this.platform.Characteristic.On, m === mode); } } catch (cause) { const error = new Error(`Failed to handle set operation mode ${mode}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } handleGetOperationModeName(mode) { try { switch (mode) { case OperationMode.COOL: return 'Cool Mode'; case OperationMode.FAN_ONLY: return 'Fan Only Mode'; case OperationMode.ENERGY_SAVER: return 'Energy Saver Mode'; case OperationMode.HEAT: return 'Heat Mode'; case OperationMode.DRY: return 'Dry Mode'; default: throw new Error(`Unknown operation mode: ${mode}`); } } catch (cause) { const error = new Error(`Failed to handle get operation mode name for ${mode}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } // Refresh state async refreshState() { try { // active this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.Active, await this.handleGetActive()); // Current mode this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, await this.handleGetCurrentHeaterCoolerState()); // Target mode this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, await this.handleGetTargetHeaterCoolerState()); // Ambient temp this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, await this.handleGetCurrentTemperature()); // Target temperature this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, await this.handleGetCoolingThresholdTemperature()); // Heating threshold temperature this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, await this.handleGetHeatingThresholdTemperature()); // Rotation speed this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.RotationSpeed, await this.handleGetRotationSpeed()); // Display units this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, await this.handleGetTemperatureDisplayUnits()); // Filter this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.FilterChangeIndication, await this.handleGetFilterChangeIndication()); // Swing mode this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.SwingMode, await this.handleGetSwingMode()); // Modes for (const mode of Object.values(OperationMode)) { this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, await this.handleGetOperationMode(mode)); } this.platform.log.debug(`[${this.accessory.displayName}] Refreshed state`); } catch (cause) { const error = new Error(`Failed to refresh state for ${this.accessory.displayName}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause }); this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`); throw error; } } // Helpers fahrenheitToCelsius(fahrenheit) { return (fahrenheit - 32) * 5 / 9; } celsiusToFahrenheit(celsius) { return (celsius * 9 / 5) + 32; } } //# sourceMappingURL=airConditioner.js.map