@homebridge-plugins/homebridge-smarthq 0.4.0-beta.7 → 0.4.0-beta.8

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/devices/airConditioner.d.ts +54 -0
  3. package/dist/devices/airConditioner.d.ts.map +1 -0
  4. package/dist/devices/airConditioner.js +575 -0
  5. package/dist/devices/airConditioner.js.map +1 -0
  6. package/dist/platform.d.ts +2 -1
  7. package/dist/platform.d.ts.map +1 -1
  8. package/dist/platform.js +56 -4
  9. package/dist/platform.js.map +1 -1
  10. package/dist/settings.d.ts +15 -0
  11. package/dist/settings.d.ts.map +1 -1
  12. package/dist/settings.js +8 -0
  13. package/dist/settings.js.map +1 -1
  14. package/docs/assets/dmt/dmt-component-data.js +1 -1
  15. package/docs/assets/dmt/dmt-search.cmp +0 -0
  16. package/docs/classes/SmartHQPlatform.html +8 -8
  17. package/docs/functions/default.html +1 -1
  18. package/docs/interfaces/DeviceOptions.html +2 -2
  19. package/docs/interfaces/SmartHQPlatformConfig.html +2 -2
  20. package/docs/interfaces/SmartHqContext.html +2 -2
  21. package/docs/interfaces/SmartHqERDResponse.html +8 -0
  22. package/docs/interfaces/credentials.html +2 -2
  23. package/docs/interfaces/devicesConfig.html +2 -2
  24. package/docs/interfaces/options.html +2 -2
  25. package/docs/modules.html +1 -1
  26. package/docs/variables/API_URL.html +1 -1
  27. package/docs/variables/ERD_CODES.html +1 -1
  28. package/docs/variables/ERD_TYPES.html +1 -1
  29. package/docs/variables/KEEPALIVE_TIMEOUT.html +1 -1
  30. package/docs/variables/LOGIN_URL.html +1 -1
  31. package/docs/variables/OAUTH2_CLIENT_ID.html +1 -1
  32. package/docs/variables/OAUTH2_CLIENT_SECRET.html +1 -1
  33. package/docs/variables/OAUTH2_REDIRECT_URI.html +1 -1
  34. package/docs/variables/PLATFORM_NAME.html +1 -1
  35. package/docs/variables/PLUGIN_NAME.html +1 -1
  36. package/docs/variables/SECURE_URL.html +1 -1
  37. package/eslint.config.js +6 -2
  38. package/package.json +25 -24
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/)
4
4
 
5
+ ## [0.4.0](https://github.com/homebridge-plugins/homebridge-smarthq/releases/tag/v0.4.0) (2025-06-26)
6
+
7
+ ### What's Changes
8
+
9
+ - Add air conditioner support [#34](https://github.com/homebridge-plugins/homebridge-smarthq/pull/34), Thanks [@nicholasodonnell](https://github.com/nicholasodonnell)
10
+ - Opal Monitoring Services Improvements [#26](https://github.com/homebridge-plugins/homebridge-smarthq/pull/26), Thanks [@jamesh48](https://github.com/jamesh48)
11
+ - Opal Scheduling Manager [#25](https://github.com/homebridge-plugins/homebridge-smarthq/pull/25), Thanks [@jamesh48](https://github.com/jamesh48)
12
+ - Fast Follower- Remove Unnecessary info logs [#24](https://github.com/homebridge-plugins/homebridge-smarthq/pull/24), Thanks [@jamesh48](https://github.com/jamesh48)
13
+ - Opal Ice Maker- Labels, Sorting, Auto Shutoff Feature, Descale Notification [#23](https://github.com/homebridge-plugins/homebridge-smarthq/pull/23), Thanks [@jamesh48](https://github.com/jamesh48)
14
+ - Favor deviceOptions over options for options that are specific to devices. [#21](https://github.com/homebridge-plugins/homebridge-smarthq/pull/21), Thanks [@jamesh48](https://github.com/jamesh48)
15
+ - Opal Ice Maker Production/Progress Feature [#20](https://github.com/homebridge-plugins/homebridge-smarthq/pull/20), Thanks [@jamesh48](https://github.com/jamesh48)
16
+ - Housekeeping and updated dependencies.
17
+
18
+ **Full Changelog**: https://github.com/homebridge-plugins/homebridge-smarthq/compare/v0.3.0...v0.4.0
19
+
5
20
  ## [0.3.0](https://github.com/homebridge-plugins/homebridge-smarthq/releases/tag/v0.3.0) (2025-03-04)
6
21
 
7
22
  # *No New Releases During Lent*
@@ -0,0 +1,54 @@
1
+ import type { CharacteristicValue, PlatformAccessory } from 'homebridge';
2
+ import type { SmartHQPlatform } from '../platform.js';
3
+ import type { devicesConfig, SmartHqContext } from '../settings.js';
4
+ import { deviceBase } from './device.js';
5
+ declare enum OperationMode {
6
+ COOL = "00",
7
+ FAN_ONLY = "01",
8
+ ENERGY_SAVER = "02",
9
+ DRY = "04"
10
+ }
11
+ export declare class SmartHQAirConditioner extends deviceBase {
12
+ protected readonly platform: SmartHQPlatform;
13
+ protected readonly accessory: PlatformAccessory<SmartHqContext>;
14
+ protected readonly device: SmartHqContext['device'] & devicesConfig;
15
+ private readonly HEATER_COOLER_SVC_NAME;
16
+ private readonly heaterCoolerSvc;
17
+ private readonly MODE_SWITCH_SVC_PREFIX;
18
+ private readonly modeSwitchSvc;
19
+ constructor(platform: SmartHQPlatform, accessory: PlatformAccessory<SmartHqContext>, device: SmartHqContext['device'] & devicesConfig);
20
+ private getErdValue;
21
+ private setErdValue;
22
+ private getPowerState;
23
+ private setPowerState;
24
+ private getAmbientTemperature;
25
+ private getTemperature;
26
+ private setTemperature;
27
+ private getTemperatureDisplayUnits;
28
+ private setTemperatureDisplayUnits;
29
+ private getOperationMode;
30
+ private setOperationMode;
31
+ private getFanSetting;
32
+ private setFanSetting;
33
+ private getFilterStatus;
34
+ handleGetActive(): Promise<CharacteristicValue>;
35
+ handleSetActive(value: CharacteristicValue): Promise<void>;
36
+ handleGetCurrentHeaterCoolerState(): Promise<number>;
37
+ handleGetTargetHeaterCoolerState(): Promise<CharacteristicValue>;
38
+ handleSetTargetHeaterCoolerState(_value: CharacteristicValue): Promise<void>;
39
+ handleGetCurrentTemperature(): Promise<number>;
40
+ handleGetCoolingThresholdTemperature(): Promise<number>;
41
+ handleSetCoolingThresholdTemperature(value: CharacteristicValue): Promise<void>;
42
+ handleGetRotationSpeed(): Promise<CharacteristicValue>;
43
+ handleSetRotationSpeed(value: CharacteristicValue): Promise<void>;
44
+ handleGetTemperatureDisplayUnits(): Promise<CharacteristicValue>;
45
+ handleSetTemperatureDisplayUnits(value: CharacteristicValue): Promise<void>;
46
+ handleGetFilterChangeIndication(): Promise<CharacteristicValue>;
47
+ handleGetOperationMode(mode: OperationMode): Promise<CharacteristicValue>;
48
+ handleSetOperationMode(mode: OperationMode, value: CharacteristicValue): Promise<void>;
49
+ refreshState(): Promise<void>;
50
+ private fahrenheitToCelsius;
51
+ private celsiusToFahrenheit;
52
+ }
53
+ export {};
54
+ //# sourceMappingURL=airConditioner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"airConditioner.d.ts","sourceRoot":"","sources":["../../src/devices/airConditioner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAW,MAAM,YAAY,CAAA;AAEjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAsB,MAAM,gBAAgB,CAAA;AAUvF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAwBxC,aAAK,aAAa;IAChB,IAAI,OAAO;IACX,QAAQ,OAAO;IACf,YAAY,OAAO;IACnB,GAAG,OAAO;CACX;AAED,qBAAa,qBAAsB,SAAQ,UAAU;IAUjD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe;IAC5C,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC,cAAc,CAAC;IAC/D,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,aAAa;IAVrE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAG1C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAChE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgC;gBAGzC,QAAQ,EAAE,eAAe,EACzB,SAAS,EAAE,iBAAiB,CAAC,cAAc,CAAC,EAC5C,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,aAAa;YAqGvD,WAAW;YAeX,WAAW;YAqBX,aAAa;YAMb,aAAa;YAMb,qBAAqB;YAWrB,cAAc;YAWd,cAAc;YAad,0BAA0B;YAU1B,0BAA0B;YAU1B,gBAAgB;YAUhB,gBAAgB;YAUhB,aAAa;YAUb,aAAa;YAUb,eAAe;IAchB,eAAe,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAe/C,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+D1D,iCAAiC,IAAI,OAAO,CAAC,MAAM,CAAC;IA+BpD,gCAAgC,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAYhE,gCAAgC,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB5E,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC;IAa9C,oCAAoC,IAAI,OAAO,CAAC,MAAM,CAAC;IAavD,oCAAoC,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/E,sBAAsB,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAwBtD,sBAAsB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCjE,gCAAgC,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAehE,gCAAgC,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3E,+BAA+B,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAe/D,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAgBzE,sBAAsB,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqEtF,YAAY;IAqEzB,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,mBAAmB;CAG5B"}
@@ -0,0 +1,575 @@
1
+ import axios from 'axios';
2
+ import { interval, startWith } from 'rxjs';
3
+ import { ERD_TYPES, } from '../settings.js';
4
+ import { deviceBase } from './device.js';
5
+ var PowerState;
6
+ (function (PowerState) {
7
+ PowerState["ON"] = "01";
8
+ PowerState["OFF"] = "00";
9
+ })(PowerState || (PowerState = {}));
10
+ var TemperatureUnit;
11
+ (function (TemperatureUnit) {
12
+ TemperatureUnit["FAHRENHEIT"] = "00";
13
+ TemperatureUnit["CELSIUS"] = "01";
14
+ })(TemperatureUnit || (TemperatureUnit = {}));
15
+ var FanSetting;
16
+ (function (FanSetting) {
17
+ FanSetting["AUTO"] = "01";
18
+ FanSetting["LOW"] = "02";
19
+ FanSetting["MED"] = "04";
20
+ FanSetting["HIGH"] = "08";
21
+ })(FanSetting || (FanSetting = {}));
22
+ var FilterStatus;
23
+ (function (FilterStatus) {
24
+ FilterStatus["OK"] = "00";
25
+ FilterStatus["CLEAN"] = "01";
26
+ })(FilterStatus || (FilterStatus = {}));
27
+ var OperationMode;
28
+ (function (OperationMode) {
29
+ OperationMode["COOL"] = "00";
30
+ OperationMode["FAN_ONLY"] = "01";
31
+ OperationMode["ENERGY_SAVER"] = "02";
32
+ OperationMode["DRY"] = "04";
33
+ })(OperationMode || (OperationMode = {}));
34
+ export class SmartHQAirConditioner extends deviceBase {
35
+ platform;
36
+ accessory;
37
+ device;
38
+ // HeaterCooler service
39
+ HEATER_COOLER_SVC_NAME = 'AIR_CONDITIONER';
40
+ heaterCoolerSvc;
41
+ // Mode SwitchServices
42
+ MODE_SWITCH_SVC_PREFIX = 'AIR_CONDITIONER_MODE';
43
+ modeSwitchSvc;
44
+ constructor(platform, accessory, device) {
45
+ super(platform, accessory, device);
46
+ this.platform = platform;
47
+ this.accessory = accessory;
48
+ this.device = device;
49
+ // HeaterCooler service
50
+ this.heaterCoolerSvc = this.accessory.getService(this.HEATER_COOLER_SVC_NAME)
51
+ ?? this.accessory.addService(this.platform.Service.HeaterCooler, accessory.displayName, this.HEATER_COOLER_SVC_NAME);
52
+ // Mode SwitchServices
53
+ this.modeSwitchSvc = {
54
+ [OperationMode.COOL]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_COOL`)
55
+ ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Cool Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_COOL`),
56
+ [OperationMode.FAN_ONLY]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_FAN_ONLY`)
57
+ ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Fan Only Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_FAN_ONLY`),
58
+ [OperationMode.ENERGY_SAVER]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_ENERGY_SAVER`)
59
+ ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Energy Saver Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_ENERGY_SAVER`),
60
+ [OperationMode.DRY]: this.accessory.getService(`${this.MODE_SWITCH_SVC_PREFIX}_DRY`)
61
+ ?? this.accessory.addService(this.platform.Service.Switch, `${accessory.displayName} Dry Mode`, `${this.MODE_SWITCH_SVC_PREFIX}_DRY`),
62
+ };
63
+ // Active
64
+ this.heaterCoolerSvc
65
+ .getCharacteristic(this.platform.Characteristic.Active)
66
+ .onGet(this.handleGetActive.bind(this))
67
+ .onSet(this.handleSetActive.bind(this));
68
+ // Current mode
69
+ this.heaterCoolerSvc
70
+ .getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState)
71
+ .setProps({
72
+ validValues: [
73
+ this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE,
74
+ this.platform.Characteristic.CurrentHeaterCoolerState.IDLE,
75
+ this.platform.Characteristic.CurrentHeaterCoolerState.COOLING,
76
+ ],
77
+ })
78
+ .onGet(this.handleGetCurrentHeaterCoolerState.bind(this));
79
+ // Target mode (COOL only)
80
+ this.heaterCoolerSvc
81
+ .getCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState)
82
+ .setProps({
83
+ validValues: [
84
+ this.platform.Characteristic.TargetHeaterCoolerState.COOL,
85
+ ],
86
+ })
87
+ .onGet(this.handleGetTargetHeaterCoolerState.bind(this))
88
+ .onSet(this.handleSetTargetHeaterCoolerState.bind(this));
89
+ // Ambient temp
90
+ this.heaterCoolerSvc
91
+ .getCharacteristic(this.platform.Characteristic.CurrentTemperature)
92
+ .onGet(this.handleGetCurrentTemperature.bind(this));
93
+ // Target temperature
94
+ this.heaterCoolerSvc
95
+ .getCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature)
96
+ .setProps({
97
+ minValue: 17.7778, // 64F
98
+ maxValue: 30, // 86F
99
+ })
100
+ .onGet(this.handleGetCoolingThresholdTemperature.bind(this))
101
+ .onSet(this.handleSetCoolingThresholdTemperature.bind(this));
102
+ // Rotation speed
103
+ this.heaterCoolerSvc
104
+ .getCharacteristic(this.platform.Characteristic.RotationSpeed)
105
+ .onGet(this.handleGetRotationSpeed.bind(this))
106
+ .onSet(this.handleSetRotationSpeed.bind(this));
107
+ // Display units
108
+ this.heaterCoolerSvc
109
+ .getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits)
110
+ .onGet(this.handleGetTemperatureDisplayUnits.bind(this))
111
+ .onSet(this.handleSetTemperatureDisplayUnits.bind(this));
112
+ // Filter
113
+ this.heaterCoolerSvc
114
+ .getCharacteristic(this.platform.Characteristic.FilterChangeIndication)
115
+ .onGet(this.handleGetFilterChangeIndication.bind(this));
116
+ // Modes
117
+ for (const mode of Object.values(OperationMode)) {
118
+ this.modeSwitchSvc[mode]
119
+ .getCharacteristic(this.platform.Characteristic.On)
120
+ .onGet(this.handleGetOperationMode.bind(this, mode))
121
+ .onSet(this.handleSetOperationMode.bind(this, mode));
122
+ }
123
+ // Start an update interval to refresh state
124
+ interval(this.deviceRefreshRate * 1000)
125
+ .pipe(startWith(0))
126
+ .subscribe(this.refreshState.bind(this));
127
+ }
128
+ // API
129
+ async getErdValue(erd) {
130
+ try {
131
+ const response = await axios.get(`/appliance/${this.accessory.context.device.applianceId}/erd/${erd}`);
132
+ return response.data.value;
133
+ }
134
+ catch (cause) {
135
+ throw new Error(axios.isAxiosError(cause) && cause.response
136
+ ? `Failed to fetch ERD: ${cause.response.data.message}`
137
+ : `Failed to fetch ERD: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
138
+ }
139
+ }
140
+ async setErdValue(erd, value) {
141
+ try {
142
+ await axios.post(`/appliance/${this.accessory.context.device.applianceId}/erd/${erd}`, {
143
+ kind: 'appliance#erdListEntry',
144
+ userId: this.accessory.context.userId,
145
+ applianceId: this.accessory.context.device.applianceId,
146
+ erd,
147
+ value,
148
+ });
149
+ this.platform.log.debug(`[${this.accessory.displayName}] Set ERD ${erd}=${value}`);
150
+ }
151
+ catch (cause) {
152
+ throw new Error(axios.isAxiosError(cause) && cause.response
153
+ ? `Failed to set ERD value for ${erd}: ${cause.response.data.message}`
154
+ : `Failed to set ERD value for ${erd}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
155
+ }
156
+ }
157
+ async getPowerState() {
158
+ const erdValue = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_POWER_STATUS);
159
+ return erdValue;
160
+ }
161
+ async setPowerState(value) {
162
+ await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_POWER_STATUS, value);
163
+ this.platform.log.debug(`[${this.accessory.displayName}] Set power state to ${value}`);
164
+ }
165
+ async getAmbientTemperature() {
166
+ try {
167
+ const erdValue = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_AMBIENT_TEMPERATURE);
168
+ const temperatureInFahrenheit = Number.parseInt(erdValue, 16); // erdValue is a hex string representing the temperature in Fahrenheit
169
+ return this.fahrenheitToCelsius(temperatureInFahrenheit); // homekit expects Celsius
170
+ }
171
+ catch (cause) {
172
+ throw new Error(`Failed to get current temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
173
+ }
174
+ }
175
+ async getTemperature() {
176
+ try {
177
+ const erdValue = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_TARGET_TEMPERATURE);
178
+ const temperatureInFahrenheit = Number.parseInt(erdValue, 16); // erdValue is a hex string representing the temperature in Fahrenheit
179
+ return this.fahrenheitToCelsius(temperatureInFahrenheit); // homekit expects Celsius
180
+ }
181
+ catch (cause) {
182
+ throw new Error(`Failed to get target temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
183
+ }
184
+ }
185
+ async setTemperature(value) {
186
+ try {
187
+ const temperatureInFahrenheit = this.celsiusToFahrenheit(value);
188
+ const hexTemperature = temperatureInFahrenheit.toString(16).padStart(4, '0').toUpperCase(); // Convert to hex and ensure it's 4 characters long
189
+ await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_TARGET_TEMPERATURE, hexTemperature);
190
+ this.platform.log.debug(`[${this.accessory.displayName}] Set temperature to ${value}°C (${temperatureInFahrenheit}°F)`);
191
+ }
192
+ catch (cause) {
193
+ throw new Error(`Failed to set target temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
194
+ }
195
+ }
196
+ async getTemperatureDisplayUnits() {
197
+ try {
198
+ const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_TEMPERATURE_UNIT);
199
+ return value;
200
+ }
201
+ catch (cause) {
202
+ throw new Error(`Failed to get temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
203
+ }
204
+ }
205
+ async setTemperatureDisplayUnits(value) {
206
+ try {
207
+ await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_TEMPERATURE_UNIT, value);
208
+ this.platform.log.debug(`[${this.accessory.displayName}] Set temperature display units to ${value}`);
209
+ }
210
+ catch (cause) {
211
+ throw new Error(`Failed to set temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
212
+ }
213
+ }
214
+ async getOperationMode() {
215
+ try {
216
+ const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_OPERATION_MODE);
217
+ return value;
218
+ }
219
+ catch (cause) {
220
+ throw new Error(`Failed to get operation mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
221
+ }
222
+ }
223
+ async setOperationMode(value) {
224
+ try {
225
+ await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_OPERATION_MODE, value);
226
+ this.platform.log.debug(`[${this.accessory.displayName}] Set operation mode to ${value}`);
227
+ }
228
+ catch (cause) {
229
+ throw new Error(`Failed to set operation mode: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
230
+ }
231
+ }
232
+ async getFanSetting() {
233
+ try {
234
+ const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_FAN_SETTING);
235
+ return value;
236
+ }
237
+ catch (cause) {
238
+ throw new Error(`Failed to get fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
239
+ }
240
+ }
241
+ async setFanSetting(value) {
242
+ try {
243
+ await this.setErdValue(ERD_TYPES.AIR_CONDITIONER_FAN_SETTING, value);
244
+ this.platform.log.debug(`[${this.accessory.displayName}] Set fan setting to ${value}`);
245
+ }
246
+ catch (cause) {
247
+ throw new Error(`Failed to set fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
248
+ }
249
+ }
250
+ async getFilterStatus() {
251
+ try {
252
+ const value = await this.getErdValue(ERD_TYPES.AIR_CONDITIONER_FILTER_STATUS);
253
+ return value;
254
+ }
255
+ catch (cause) {
256
+ throw new Error(`Failed to get filter status: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
257
+ }
258
+ }
259
+ // Characteristic handlers
260
+ // active
261
+ async handleGetActive() {
262
+ try {
263
+ const powerState = await this.getPowerState();
264
+ return powerState === PowerState.ON
265
+ ? this.platform.Characteristic.Active.ACTIVE
266
+ : this.platform.Characteristic.Active.INACTIVE;
267
+ }
268
+ catch (cause) {
269
+ const error = new Error(`Failed to handle get active: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
270
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
271
+ throw error;
272
+ }
273
+ }
274
+ async handleSetActive(value) {
275
+ try {
276
+ const [powerState, ambientTemperature, targetTemperature, operationMode] = await Promise.all([
277
+ this.getPowerState(),
278
+ this.getAmbientTemperature(),
279
+ this.getTemperature(),
280
+ this.getOperationMode(),
281
+ ]);
282
+ if (value === this.platform.Characteristic.Active.ACTIVE) {
283
+ // If the air conditioner is currently off, turn it on
284
+ if (powerState === PowerState.OFF) {
285
+ await this.setPowerState(PowerState.ON);
286
+ // There's a bug (feature?) in SmartHQ where it resets operation mode when turning on the air conditioner
287
+ // so we need to set it back to the previous mode
288
+ await this.setOperationMode(operationMode);
289
+ }
290
+ // Keep mode switches in sync
291
+ for (const mode of Object.values(OperationMode)) {
292
+ this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, mode === operationMode);
293
+ }
294
+ // Update CurrentHeaterCoolerState
295
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, ambientTemperature <= targetTemperature
296
+ ? this.platform.Characteristic.CurrentHeaterCoolerState.IDLE
297
+ : this.platform.Characteristic.CurrentHeaterCoolerState.COOLING);
298
+ return;
299
+ }
300
+ if (powerState === PowerState.ON) {
301
+ await this.setPowerState(PowerState.OFF);
302
+ }
303
+ // Keep mode switches in sync
304
+ for (const mode of Object.values(OperationMode)) {
305
+ this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, false);
306
+ }
307
+ // Keep TargetHeaterCoolerState in sync
308
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE);
309
+ }
310
+ catch (cause) {
311
+ const error = new Error(`Failed to handle set active: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
312
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
313
+ throw error;
314
+ }
315
+ }
316
+ async handleGetCurrentHeaterCoolerState() {
317
+ try {
318
+ const [powerState, ambientTemperature, targetTemperature] = await Promise.all([
319
+ this.getPowerState(),
320
+ this.getAmbientTemperature(),
321
+ this.getTemperature(),
322
+ ]);
323
+ if (powerState === PowerState.OFF) {
324
+ return this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE;
325
+ }
326
+ if (ambientTemperature <= targetTemperature) {
327
+ return this.platform.Characteristic.CurrentHeaterCoolerState.IDLE;
328
+ }
329
+ // Keep TargetHeaterCoolerState in sync
330
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.COOL);
331
+ return this.platform.Characteristic.CurrentHeaterCoolerState.COOLING;
332
+ }
333
+ catch (cause) {
334
+ const error = new Error(`Failed to handle get current heater cooler state: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
335
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
336
+ throw error;
337
+ }
338
+ }
339
+ async handleGetTargetHeaterCoolerState() {
340
+ try {
341
+ // Cool is the only supported state for an air conditioner
342
+ return this.platform.Characteristic.TargetHeaterCoolerState.COOL;
343
+ }
344
+ catch (cause) {
345
+ const error = new Error(`Failed to handle get target heater cooler state: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
346
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
347
+ throw error;
348
+ }
349
+ }
350
+ async handleSetTargetHeaterCoolerState(_value) {
351
+ try {
352
+ const powerState = await this.getPowerState();
353
+ // Turn on the air conditioner if it's currently off
354
+ if (powerState === PowerState.OFF) {
355
+ await this.setPowerState(PowerState.ON);
356
+ }
357
+ // Keep CurrentHeaterCoolerState in sync with TargetHeaterCoolerState
358
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.COOLING);
359
+ }
360
+ catch (cause) {
361
+ const error = new Error(`Failed to handle set target heater cooler state: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
362
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
363
+ throw error;
364
+ }
365
+ }
366
+ async handleGetCurrentTemperature() {
367
+ try {
368
+ const value = await this.getAmbientTemperature();
369
+ return value;
370
+ }
371
+ catch (cause) {
372
+ const error = new Error(`Failed to handle get current temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
373
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
374
+ throw error;
375
+ }
376
+ }
377
+ async handleGetCoolingThresholdTemperature() {
378
+ try {
379
+ const value = await this.getTemperature();
380
+ return value;
381
+ }
382
+ catch (cause) {
383
+ const error = new Error(`Failed to handle get cooling threshold temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
384
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
385
+ throw error;
386
+ }
387
+ }
388
+ async handleSetCoolingThresholdTemperature(value) {
389
+ try {
390
+ const targetTemperature = Number.parseInt(value, 10);
391
+ await this.setTemperature(targetTemperature);
392
+ }
393
+ catch (cause) {
394
+ const error = new Error(`Failed to handle set cooling threshold temperature: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
395
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
396
+ throw error;
397
+ }
398
+ }
399
+ async handleGetRotationSpeed() {
400
+ try {
401
+ const value = await this.getFanSetting();
402
+ switch (value) {
403
+ case FanSetting.AUTO:
404
+ return 0;
405
+ case FanSetting.LOW:
406
+ return 33;
407
+ case FanSetting.MED:
408
+ return 66;
409
+ case FanSetting.HIGH:
410
+ return 100;
411
+ default:
412
+ throw new Error(`Unknown fan setting: ${value}`);
413
+ }
414
+ }
415
+ catch (cause) {
416
+ const error = new Error(`Failed to handle get fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
417
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
418
+ throw error;
419
+ }
420
+ }
421
+ async handleSetRotationSpeed(value) {
422
+ try {
423
+ const speed = value;
424
+ // AUTO
425
+ if (speed === 0) {
426
+ await this.setFanSetting(FanSetting.AUTO);
427
+ return;
428
+ }
429
+ // LOW
430
+ if (speed <= 33) {
431
+ await this.setFanSetting(FanSetting.LOW);
432
+ return;
433
+ }
434
+ // MED
435
+ if (speed <= 66) {
436
+ await this.setFanSetting(FanSetting.MED);
437
+ return;
438
+ }
439
+ // HIGH
440
+ await this.setFanSetting(FanSetting.HIGH);
441
+ }
442
+ catch (cause) {
443
+ const error = new Error(`Failed to handle set fan setting: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
444
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
445
+ throw error;
446
+ }
447
+ }
448
+ async handleGetTemperatureDisplayUnits() {
449
+ try {
450
+ const value = await this.getTemperatureDisplayUnits();
451
+ return value === TemperatureUnit.FAHRENHEIT
452
+ ? this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT
453
+ : this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS;
454
+ }
455
+ catch (cause) {
456
+ const error = new Error(`Failed to handle get temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
457
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
458
+ throw error;
459
+ }
460
+ }
461
+ async handleSetTemperatureDisplayUnits(value) {
462
+ try {
463
+ const temperatureUnit = value === this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT
464
+ ? TemperatureUnit.FAHRENHEIT
465
+ : TemperatureUnit.CELSIUS;
466
+ await this.setTemperatureDisplayUnits(temperatureUnit);
467
+ }
468
+ catch (cause) {
469
+ const error = new Error(`Failed to handle set temperature display units: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
470
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
471
+ throw error;
472
+ }
473
+ }
474
+ async handleGetFilterChangeIndication() {
475
+ try {
476
+ const value = await this.getFilterStatus();
477
+ return value === FilterStatus.OK
478
+ ? this.platform.Characteristic.FilterChangeIndication.FILTER_OK
479
+ : this.platform.Characteristic.FilterChangeIndication.CHANGE_FILTER;
480
+ }
481
+ catch (cause) {
482
+ const error = new Error(`Failed to handle get filter change indication: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
483
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
484
+ throw error;
485
+ }
486
+ }
487
+ async handleGetOperationMode(mode) {
488
+ try {
489
+ const value = await this.getOperationMode();
490
+ return value === mode;
491
+ }
492
+ catch (cause) {
493
+ const error = new Error(`Failed to handle get operation mode ${mode}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
494
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
495
+ throw error;
496
+ }
497
+ }
498
+ async handleSetOperationMode(mode, value) {
499
+ try {
500
+ const [powerState, currentOperationMode] = await Promise.all([
501
+ this.getPowerState(),
502
+ this.getOperationMode(),
503
+ ]);
504
+ // turn on the Air Conditioner if it's currently off
505
+ if (value && powerState === PowerState.OFF) {
506
+ await this.setPowerState(PowerState.ON);
507
+ // Keep Active in sync
508
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.Active, this.platform.Characteristic.Active.ACTIVE);
509
+ // Keep CurrentHeaterCoolerState in sync
510
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.COOLING);
511
+ // Keep TargetHeaterCoolerState in sync
512
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.COOL);
513
+ // turn off the Air Conditioner if the user turned off the current mode
514
+ }
515
+ else if (!value && currentOperationMode === mode && powerState === PowerState.ON) {
516
+ await this.setPowerState(PowerState.OFF);
517
+ // Keep Active in sync
518
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.Active, this.platform.Characteristic.Active.INACTIVE);
519
+ // Keep CurrentHeaterCoolerState in sync
520
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE);
521
+ }
522
+ if (value) {
523
+ await this.setOperationMode(mode);
524
+ }
525
+ // switch the rest off
526
+ for (const m of Object.values(OperationMode)) {
527
+ this.modeSwitchSvc[m].updateCharacteristic(this.platform.Characteristic.On, m === mode);
528
+ }
529
+ }
530
+ catch (cause) {
531
+ const error = new Error(`Failed to handle set operation mode ${mode}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
532
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
533
+ throw error;
534
+ }
535
+ }
536
+ // Refresh state
537
+ async refreshState() {
538
+ try {
539
+ // active
540
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.Active, await this.handleGetActive());
541
+ // Current mode
542
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, await this.handleGetCurrentHeaterCoolerState());
543
+ // Target mode
544
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, await this.handleGetTargetHeaterCoolerState());
545
+ // Ambient temp
546
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, await this.handleGetCurrentTemperature());
547
+ // Target temperature
548
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, await this.handleGetCoolingThresholdTemperature());
549
+ // Rotation speed
550
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.RotationSpeed, await this.handleGetRotationSpeed());
551
+ // Display units
552
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, await this.handleGetTemperatureDisplayUnits());
553
+ // Filter
554
+ this.heaterCoolerSvc.updateCharacteristic(this.platform.Characteristic.FilterChangeIndication, await this.handleGetFilterChangeIndication());
555
+ // Modes
556
+ for (const mode of Object.values(OperationMode)) {
557
+ this.modeSwitchSvc[mode].updateCharacteristic(this.platform.Characteristic.On, await this.handleGetOperationMode(mode));
558
+ }
559
+ this.platform.log.debug(`[${this.accessory.displayName}] Refreshed state`);
560
+ }
561
+ catch (cause) {
562
+ const error = new Error(`Failed to refresh state for ${this.accessory.displayName}: ${cause instanceof Error ? cause.message : 'An unknown error occurred'}`, { cause });
563
+ this.platform.log.error(`[${this.accessory.displayName}] ${error.message}`);
564
+ throw error;
565
+ }
566
+ }
567
+ // Helpers
568
+ fahrenheitToCelsius(fahrenheit) {
569
+ return (fahrenheit - 32) * 5 / 9;
570
+ }
571
+ celsiusToFahrenheit(celsius) {
572
+ return (celsius * 9 / 5) + 32;
573
+ }
574
+ }
575
+ //# sourceMappingURL=airConditioner.js.map