@homebridge-plugins/homebridge-resideo 2.1.7-beta.0

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 (87) hide show
  1. package/.github/FUNDING.yml +4 -0
  2. package/.github/ISSUE_TEMPLATE/bug-report.yml +97 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. package/.github/ISSUE_TEMPLATE/feature-request.yml +38 -0
  5. package/.github/ISSUE_TEMPLATE/support-request.yml +85 -0
  6. package/.github/ISSUE_TEMPLATE.md +52 -0
  7. package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +27 -0
  8. package/.github/auto-merge.yml +3 -0
  9. package/.github/dependabot.yml +17 -0
  10. package/.github/labeler.yml +38 -0
  11. package/.github/npm-version-script-esm.js +85 -0
  12. package/.github/release-drafter.yml +33 -0
  13. package/.github/workflows/beta-release.yml +55 -0
  14. package/.github/workflows/build.yml +18 -0
  15. package/.github/workflows/changerelease.yml +11 -0
  16. package/.github/workflows/labeler.yml +9 -0
  17. package/.github/workflows/release-drafter.yml +14 -0
  18. package/.github/workflows/release.yml +35 -0
  19. package/.github/workflows/stale.yml +12 -0
  20. package/CHANGELOG.md +161 -0
  21. package/LICENSE +14 -0
  22. package/README.md +67 -0
  23. package/SECURITY.md +19 -0
  24. package/branding/Homebridge_x_Resideo.svg +28 -0
  25. package/branding/honeywell/Homebridge_x_Honeywell.svg +1 -0
  26. package/branding/honeywell/homebridge-honeywell-home-changelog.md +851 -0
  27. package/branding/icon.png +0 -0
  28. package/config.schema.json +611 -0
  29. package/dist/devices/device.d.ts +41 -0
  30. package/dist/devices/device.d.ts.map +1 -0
  31. package/dist/devices/device.js +279 -0
  32. package/dist/devices/device.js.map +1 -0
  33. package/dist/devices/leaksensors.d.ts +34 -0
  34. package/dist/devices/leaksensors.d.ts.map +1 -0
  35. package/dist/devices/leaksensors.js +300 -0
  36. package/dist/devices/leaksensors.js.map +1 -0
  37. package/dist/devices/roomsensors.d.ts +38 -0
  38. package/dist/devices/roomsensors.d.ts.map +1 -0
  39. package/dist/devices/roomsensors.js +274 -0
  40. package/dist/devices/roomsensors.js.map +1 -0
  41. package/dist/devices/roomsensorthermostats.d.ts +57 -0
  42. package/dist/devices/roomsensorthermostats.d.ts.map +1 -0
  43. package/dist/devices/roomsensorthermostats.js +700 -0
  44. package/dist/devices/roomsensorthermostats.js.map +1 -0
  45. package/dist/devices/thermostats.d.ts +73 -0
  46. package/dist/devices/thermostats.d.ts.map +1 -0
  47. package/dist/devices/thermostats.js +930 -0
  48. package/dist/devices/thermostats.js.map +1 -0
  49. package/dist/devices/valve.d.ts +21 -0
  50. package/dist/devices/valve.d.ts.map +1 -0
  51. package/dist/devices/valve.js +151 -0
  52. package/dist/devices/valve.js.map +1 -0
  53. package/dist/homebridge-ui/public/index.html +423 -0
  54. package/dist/homebridge-ui/server.d.ts +2 -0
  55. package/dist/homebridge-ui/server.d.ts.map +1 -0
  56. package/dist/homebridge-ui/server.js +131 -0
  57. package/dist/homebridge-ui/server.js.map +1 -0
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +7 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/platform.d.ts +94 -0
  63. package/dist/platform.d.ts.map +1 -0
  64. package/dist/platform.js +890 -0
  65. package/dist/platform.js.map +1 -0
  66. package/dist/settings.d.ts +409 -0
  67. package/dist/settings.d.ts.map +1 -0
  68. package/dist/settings.js +25 -0
  69. package/dist/settings.js.map +1 -0
  70. package/dist/utils.d.ts +21 -0
  71. package/dist/utils.d.ts.map +1 -0
  72. package/dist/utils.js +58 -0
  73. package/dist/utils.js.map +1 -0
  74. package/docs/.nojekyll +1 -0
  75. package/docs/assets/highlight.css +22 -0
  76. package/docs/assets/icons.js +18 -0
  77. package/docs/assets/icons.svg +1 -0
  78. package/docs/assets/main.js +60 -0
  79. package/docs/assets/navigation.js +1 -0
  80. package/docs/assets/search.js +1 -0
  81. package/docs/assets/style.css +1493 -0
  82. package/docs/functions/default.html +1 -0
  83. package/docs/index.html +73 -0
  84. package/docs/modules.html +2 -0
  85. package/eslint.config.js +50 -0
  86. package/package.json +102 -0
  87. package/typedoc.json +22 -0
@@ -0,0 +1,930 @@
1
+ // import { request } from 'undici';
2
+ import { interval, Subject } from 'rxjs';
3
+ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
4
+ import { DeviceURL } from '../settings.js';
5
+ import { HomeKitModes, ResideoModes, toCelsius, toFahrenheit } from '../utils.js';
6
+ import { deviceBase } from './device.js';
7
+ /**
8
+ * Platform Accessory
9
+ * An instance of this class is created for each accessory your platform registers
10
+ * Each accessory may expose multiple services of different service types.
11
+ */
12
+ export class Thermostats extends deviceBase {
13
+ platform;
14
+ // Services
15
+ Thermostat;
16
+ Fan;
17
+ HumiditySensor;
18
+ StatefulProgrammableSwitch;
19
+ // Config
20
+ thermostatSetpointStatus;
21
+ // Thermostat Updates
22
+ thermostatUpdateInProgress;
23
+ doThermostatUpdate;
24
+ // Fan Updates
25
+ fanStatus;
26
+ fanUpdateInProgress;
27
+ doFanUpdate;
28
+ // Room Updates - T9 Only
29
+ roomPriorityStatus;
30
+ roomUpdateInProgress;
31
+ doRoomUpdate;
32
+ constructor(platform, accessory, location, device) {
33
+ super(platform, accessory, location, device);
34
+ this.platform = platform;
35
+ this.getThermostatConfigSettings(accessory, device);
36
+ // this is subject we use to track when we need to POST Room Priority changes to the Resideo API for Room Changes - T9 Only
37
+ this.doRoomUpdate = new Subject();
38
+ this.roomUpdateInProgress = false;
39
+ // this is subject we use to track when we need to POST Thermostat changes to the Resideo API
40
+ this.doThermostatUpdate = new Subject();
41
+ this.thermostatUpdateInProgress = false;
42
+ // this is subject we use to track when we need to POST Fan changes to the Resideo API
43
+ this.doFanUpdate = new Subject();
44
+ this.fanUpdateInProgress = false;
45
+ // Initialize Thermostat Service
46
+ accessory.context.Thermostat = accessory.context.Thermostat ?? {};
47
+ this.Thermostat = {
48
+ Name: accessory.context.Thermostat.Name ?? accessory.displayName,
49
+ Service: accessory.getService(this.hap.Service.Thermostat) ?? this.accessory.addService(this.hap.Service.Thermostat),
50
+ TargetTemperature: accessory.context.TargetTemperature ?? 20,
51
+ CurrentTemperature: accessory.context.CurrentTemperature ?? 20,
52
+ TemperatureDisplayUnits: accessory.context.TemperatureDisplayUnits,
53
+ TargetHeatingCoolingState: accessory.context.TargetHeatingCoolingState ?? this.hap.Characteristic.TargetHeatingCoolingState.AUTO,
54
+ CurrentHeatingCoolingState: accessory.context.CurrentHeatingCoolingState ?? this.hap.Characteristic.CurrentHeatingCoolingState.OFF,
55
+ CoolingThresholdTemperature: accessory.context.CoolingThresholdTemperature ?? 20,
56
+ HeatingThresholdTemperature: accessory.context.HeatingThresholdTemperature ?? 22,
57
+ };
58
+ accessory.context.Thermostat = this.Thermostat;
59
+ // Service Name
60
+ this.Thermostat.Service
61
+ .setCharacteristic(this.hap.Characteristic.Name, this.Thermostat.Name)
62
+ .setCharacteristic(this.hap.Characteristic.CurrentHeatingCoolingState, this.Thermostat.CurrentHeatingCoolingState)
63
+ .getCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits)
64
+ .onGet(() => {
65
+ this.Thermostat.TemperatureDisplayUnits = device.units === 'Celsius'
66
+ ? this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS
67
+ : this.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT;
68
+ accessory.context.TemperatureDisplayUnits = this.Thermostat.TemperatureDisplayUnits;
69
+ this.debugLog(`${this.device.deviceClass} ${accessory.displayName} TemperatureDisplayUnits: ${this.Thermostat.TemperatureDisplayUnits}`);
70
+ return this.Thermostat.TemperatureDisplayUnits;
71
+ })
72
+ .onSet(this.setTemperatureDisplayUnits.bind(this));
73
+ // Set Min and Max
74
+ if (device.minHeatSetpoint && device.maxHeatSetpoint) {
75
+ this.debugLog(`${this.device.deviceClass} ${accessory.displayName} minHeatSetpoint: ${device.minHeatSetpoint}, maxHeatSetpoint: ${device.maxHeatSetpoint}, TemperatureDisplayUnits: ${this.Thermostat.TemperatureDisplayUnits}`);
76
+ const minValue = toCelsius(device.minHeatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
77
+ const maxValue = toCelsius(device.maxHeatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
78
+ this.debugLog(`${this.device.deviceClass} ${accessory.displayName} minValue: ${minValue}, maxValue: ${maxValue}`);
79
+ if (device.changeableValues.heatCoolMode === 'Heat') {
80
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} is in "${device.changeableValues?.heatCoolMode}" mode`);
81
+ this.Thermostat.Service
82
+ .getCharacteristic(this.hap.Characteristic.TargetTemperature)
83
+ .setProps({
84
+ minValue,
85
+ maxValue,
86
+ minStep: 0.1,
87
+ })
88
+ .onGet(() => {
89
+ return this.Thermostat.TargetTemperature;
90
+ });
91
+ this.debugWarnLog(`${device.deviceClass} ${accessory.displayName} (HEAT) Min Heat Setpoint: ${device.minHeatSetpoint}. Max Heat Setpoint: ${device.maxHeatSetpoint} TemperatureDisplayUnits: ${this.Thermostat.TemperatureDisplayUnits}`);
92
+ }
93
+ else {
94
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} is in "${device.changeableValues?.heatCoolMode}" mode`);
95
+ this.Thermostat.Service
96
+ .getCharacteristic(this.hap.Characteristic.TargetTemperature)
97
+ .setProps({
98
+ minValue,
99
+ maxValue,
100
+ minStep: 0.1,
101
+ })
102
+ .onGet(() => {
103
+ return this.Thermostat.TargetTemperature;
104
+ });
105
+ this.debugWarnLog(`${device.deviceClass} ${accessory.displayName} (NOT HEAT) Min Heat Setpoint: ${device.minHeatSetpoint}. Max Heat Setpoint: ${device.maxHeatSetpoint} TemperatureDisplayUnits: ${this.Thermostat.TemperatureDisplayUnits}`);
106
+ }
107
+ }
108
+ // The value property of TargetHeaterCoolerState must be one of the following:
109
+ // AUTO = 3; HEAT = 1; COOL = 2; OFF = 0;
110
+ // Set control bindings
111
+ const TargetState = [4];
112
+ TargetState.pop();
113
+ if (this.device.allowedModes?.includes('Cool')) {
114
+ TargetState.push(this.hap.Characteristic.TargetHeatingCoolingState.COOL);
115
+ }
116
+ if (this.device.allowedModes?.includes('Heat')) {
117
+ TargetState.push(this.hap.Characteristic.TargetHeatingCoolingState.HEAT);
118
+ }
119
+ if (this.device.allowedModes?.includes('Off')) {
120
+ TargetState.push(this.hap.Characteristic.TargetHeatingCoolingState.OFF);
121
+ }
122
+ if (this.device.allowedModes?.includes('Auto') || this.device.thermostat?.show_auto) {
123
+ TargetState.push(this.hap.Characteristic.TargetHeatingCoolingState.AUTO);
124
+ }
125
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} allowedModes: ${this.device.allowedModes}`);
126
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Only Show These Modes: ${JSON.stringify(TargetState)}`);
127
+ this.Thermostat.Service
128
+ .getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState)
129
+ .setProps({
130
+ validValues: TargetState,
131
+ })
132
+ .onGet(() => {
133
+ return this.Thermostat.TargetHeatingCoolingState;
134
+ })
135
+ .onSet(this.setTargetHeatingCoolingState.bind(this));
136
+ this.Thermostat.Service
137
+ .getCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature)
138
+ .onGet(() => {
139
+ return this.Thermostat.HeatingThresholdTemperature;
140
+ })
141
+ .onSet(this.setHeatingThresholdTemperature.bind(this));
142
+ this.Thermostat.Service
143
+ .getCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature)
144
+ .onGet(() => {
145
+ return this.Thermostat.CoolingThresholdTemperature;
146
+ })
147
+ .onSet(this.setCoolingThresholdTemperature.bind(this));
148
+ this.Thermostat.Service
149
+ .getCharacteristic(this.hap.Characteristic.TargetTemperature)
150
+ .onGet(() => {
151
+ return this.Thermostat.TargetTemperature;
152
+ })
153
+ .onSet(this.setTargetTemperature.bind(this));
154
+ // Initialize Fan Service
155
+ if (device.thermostat?.hide_fan) {
156
+ if (this.Fan) {
157
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Fan Service`);
158
+ this.Fan.Service = accessory.getService(this.hap.Service.Fanv2);
159
+ accessory.removeService(this.Fan.Service);
160
+ }
161
+ else {
162
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Fan Service Not Found`);
163
+ }
164
+ }
165
+ else if (device.settings?.fan) {
166
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} Available Fan Settings ${JSON.stringify(device.settings.fan)}`);
167
+ accessory.context.Fan = accessory.context.Fan ?? {};
168
+ this.Fan = {
169
+ Name: accessory.context.Fan.Name ?? `${accessory.displayName} Fan`,
170
+ Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2),
171
+ Active: accessory.context.Active ?? this.hap.Characteristic.Active.ACTIVE,
172
+ TargetFanState: accessory.context.TargetFanState ?? this.hap.Characteristic.TargetFanState.MANUAL,
173
+ };
174
+ accessory.context.Fan = this.Fan;
175
+ // Initialize Fan Characteristic
176
+ this.Fan.Service
177
+ .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name)
178
+ .getCharacteristic(this.hap.Characteristic.Active)
179
+ .onGet(() => {
180
+ return this.Fan.Active;
181
+ })
182
+ .onSet(this.setActive.bind(this));
183
+ this.Fan.Service
184
+ .getCharacteristic(this.hap.Characteristic.TargetFanState)
185
+ .onGet(() => {
186
+ return this.Fan.TargetFanState;
187
+ })
188
+ .onSet(this.setTargetFanState.bind(this));
189
+ }
190
+ else {
191
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} Fanv2 Service Not Added`);
192
+ }
193
+ // Initialize Humidity Sensor Service
194
+ if (device.thermostat?.hide_humidity) {
195
+ if (this.HumiditySensor) {
196
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
197
+ this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor);
198
+ accessory.removeService(this.HumiditySensor.Service);
199
+ }
200
+ else {
201
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Found`);
202
+ }
203
+ }
204
+ else if (device.indoorHumidity) {
205
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} Add Humidity Sensor Service`);
206
+ accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {};
207
+ this.HumiditySensor = {
208
+ Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`,
209
+ Service: accessory.getService(this.hap.Service.HumiditySensor) ?? accessory.addService(this.hap.Service.HumiditySensor),
210
+ CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50,
211
+ };
212
+ accessory.context.HumiditySensor = this.HumiditySensor;
213
+ // Initialize Humidity Sensor Characteristic
214
+ this.HumiditySensor.Service
215
+ .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name);
216
+ this.HumiditySensor.Service
217
+ .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity)
218
+ .setProps({
219
+ minStep: 0.1,
220
+ })
221
+ .onGet(() => {
222
+ return this.HumiditySensor.CurrentRelativeHumidity;
223
+ });
224
+ }
225
+ else {
226
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} Humidity Sensor Service Not Added`);
227
+ }
228
+ // Initialize StatefulProgrammableSwitch property
229
+ accessory.context.StatefulProgrammableSwitch = accessory.context.StatefulProgrammableSwitch ?? {};
230
+ this.StatefulProgrammableSwitch = {
231
+ Name: accessory.context.StatefulProgrammableSwitch.Name ?? accessory.displayName,
232
+ Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch),
233
+ ProgrammableSwitchEvent: accessory.context.ProgrammableSwitchEvent ?? this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
234
+ ProgrammableSwitchOutputState: accessory.context.ProgrammableSwitchOutputState ?? 0,
235
+ };
236
+ accessory.context.StatefulProgrammableSwitch = this.StatefulProgrammableSwitch;
237
+ this.StatefulProgrammableSwitch.Service
238
+ .setCharacteristic(this.hap.Characteristic.Name, this.StatefulProgrammableSwitch.Name)
239
+ .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent)
240
+ .setProps({
241
+ validValues: [this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS],
242
+ })
243
+ .onGet(() => {
244
+ return this.StatefulProgrammableSwitch.ProgrammableSwitchEvent;
245
+ });
246
+ this.StatefulProgrammableSwitch.Service
247
+ .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState)
248
+ .onGet(() => {
249
+ return this.StatefulProgrammableSwitch.ProgrammableSwitchOutputState;
250
+ })
251
+ .onSet(this.handleProgrammableSwitchOutputStateSet.bind(this));
252
+ // Intial Refresh
253
+ this.refreshStatus();
254
+ // Start an update interval
255
+ interval(this.config.options.refreshRate * 1000)
256
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
257
+ .subscribe(async () => {
258
+ await this.refreshStatus();
259
+ });
260
+ // Watch for thermostat change events
261
+ // We put in a debounce of 100ms so we don't make duplicate calls
262
+ if (device.thermostat?.roompriority?.deviceType === 'Thermostat' && device.deviceModel === 'T9-T10') {
263
+ this.doRoomUpdate
264
+ .pipe(tap(() => {
265
+ this.roomUpdateInProgress = true;
266
+ }), debounceTime(this.devicePushRate * 500))
267
+ .subscribe(async () => {
268
+ try {
269
+ await this.pushRoomChanges();
270
+ }
271
+ catch (e) {
272
+ const action = 'pushRoomChanges';
273
+ if (this.device.retry) {
274
+ // Refresh the status from the API
275
+ interval(5000)
276
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
277
+ .pipe(take(1))
278
+ .subscribe(async () => {
279
+ await this.pushRoomChanges();
280
+ });
281
+ }
282
+ this.resideoAPIError(e, action);
283
+ this.platform.refreshAccessToken();
284
+ this.apiError(e);
285
+ }
286
+ this.roomUpdateInProgress = false;
287
+ // Refresh the status from the API
288
+ interval(5000)
289
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
290
+ .pipe(take(1))
291
+ .subscribe(async () => {
292
+ await this.refreshStatus();
293
+ });
294
+ });
295
+ }
296
+ this.doThermostatUpdate
297
+ .pipe(tap(() => {
298
+ this.thermostatUpdateInProgress = true;
299
+ }), debounceTime(this.devicePushRate * 1000))
300
+ .subscribe(async () => {
301
+ try {
302
+ await this.pushChanges();
303
+ }
304
+ catch (e) {
305
+ const action = 'pushChanges';
306
+ if (this.device.retry) {
307
+ // Refresh the status from the API
308
+ interval(5000)
309
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
310
+ .pipe(take(1))
311
+ .subscribe(async () => {
312
+ await this.pushChanges();
313
+ });
314
+ }
315
+ this.resideoAPIError(e, action);
316
+ this.platform.refreshAccessToken();
317
+ this.apiError(e);
318
+ }
319
+ this.thermostatUpdateInProgress = false;
320
+ // Refresh the status from the API
321
+ interval(15000)
322
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
323
+ .pipe(take(1))
324
+ .subscribe(async () => {
325
+ await this.refreshStatus();
326
+ });
327
+ });
328
+ if (device.settings?.fan && !device.thermostat?.hide_fan) {
329
+ this.doFanUpdate
330
+ .pipe(tap(() => {
331
+ this.fanUpdateInProgress = true;
332
+ }), debounceTime(this.devicePushRate * 1000))
333
+ .subscribe(async () => {
334
+ try {
335
+ await this.pushFanChanges();
336
+ }
337
+ catch (e) {
338
+ const action = 'pushFanChanges';
339
+ if (this.device.retry) {
340
+ // Refresh the status from the API
341
+ interval(5000)
342
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
343
+ .pipe(take(1))
344
+ .subscribe(async () => {
345
+ await this.pushFanChanges();
346
+ });
347
+ }
348
+ this.resideoAPIError(e, action);
349
+ this.platform.refreshAccessToken();
350
+ this.apiError(e);
351
+ }
352
+ this.fanUpdateInProgress = false;
353
+ // Refresh the status from the API
354
+ interval(5000)
355
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
356
+ .pipe(take(1))
357
+ .subscribe(async () => {
358
+ await this.refreshStatus();
359
+ });
360
+ });
361
+ }
362
+ }
363
+ /**
364
+ * Parse the device status from the Resideo api
365
+ */
366
+ async parseStatus() {
367
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus`);
368
+ if (this.device.units === 'Fahrenheit') {
369
+ this.Thermostat.TemperatureDisplayUnits = this.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT;
370
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus TemperatureDisplayUnits: ${this.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT}`);
371
+ }
372
+ if (this.device.units === 'Celsius') {
373
+ this.Thermostat.TemperatureDisplayUnits = this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS;
374
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus TemperatureDisplayUnits: ${this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS}`);
375
+ }
376
+ this.Thermostat.CurrentTemperature = toCelsius(this.device.indoorTemperature, Number(this.Thermostat.TemperatureDisplayUnits));
377
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus CurrentTemperature: ${toCelsius(this.device.indoorTemperature, Number(this.Thermostat.TemperatureDisplayUnits))}`);
378
+ if (this.device.indoorHumidity) {
379
+ if (this.HumiditySensor) {
380
+ this.HumiditySensor.CurrentRelativeHumidity = this.device.indoorHumidity;
381
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}`);
382
+ }
383
+ }
384
+ if (this.device.changeableValues.heatSetpoint > 0) {
385
+ this.Thermostat.HeatingThresholdTemperature = toCelsius(this.device.changeableValues.heatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
386
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus HeatingThresholdTemperature: ${toCelsius(this.device.changeableValues.heatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits))}`);
387
+ }
388
+ if (this.device.changeableValues.coolSetpoint > 0) {
389
+ this.Thermostat.CoolingThresholdTemperature = toCelsius(this.device.changeableValues.coolSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
390
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus CoolingThresholdTemperature: ${toCelsius(this.device.changeableValues.coolSetpoint, Number(this.Thermostat.TemperatureDisplayUnits))}`);
391
+ }
392
+ this.Thermostat.TargetHeatingCoolingState = HomeKitModes[this.device.changeableValues.mode];
393
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus TargetHeatingCoolingState: ${HomeKitModes[this.device.changeableValues.mode]}`);
394
+ /**
395
+ * The CurrentHeatingCoolingState is either 'Heat', 'Cool', or 'Off'
396
+ * CurrentHeatingCoolingState = OFF = 0, HEAT = 1, COOL = 2
397
+ */
398
+ switch (this.device.operationStatus.mode) {
399
+ case 'Heat':
400
+ this.Thermostat.CurrentHeatingCoolingState = this.hap.Characteristic.CurrentHeatingCoolingState.HEAT;
401
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus Currently Mode (HEAT): ${this.device.operationStatus.mode}(${this.Thermostat.CurrentHeatingCoolingState})`);
402
+ break;
403
+ case 'Cool':
404
+ this.Thermostat.CurrentHeatingCoolingState = this.hap.Characteristic.CurrentHeatingCoolingState.COOL;
405
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus Currently Mode (COOL): ${this.device.operationStatus.mode}(${this.Thermostat.CurrentHeatingCoolingState})`);
406
+ break;
407
+ default:
408
+ this.Thermostat.CurrentHeatingCoolingState = this.hap.Characteristic.CurrentHeatingCoolingState.OFF;
409
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus Currently Mode (OFF): ${this.device.operationStatus.mode}(${this.Thermostat.CurrentHeatingCoolingState})`);
410
+ }
411
+ // Set the TargetTemperature value based on the current mode
412
+ if (this.Thermostat.TargetHeatingCoolingState === this.hap.Characteristic.TargetHeatingCoolingState.HEAT) {
413
+ if (this.device.changeableValues.heatSetpoint > 0) {
414
+ this.Thermostat.TargetTemperature = toCelsius(this.device.changeableValues.heatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
415
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus TargetTemperature (HEAT): ${toCelsius(this.device.changeableValues.heatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits))})`);
416
+ }
417
+ }
418
+ else {
419
+ if (this.device.changeableValues) {
420
+ if (this.device.changeableValues.coolSetpoint > 0) {
421
+ this.Thermostat.TargetTemperature = toCelsius(this.device.changeableValues.coolSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
422
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} parseStatus TargetTemperature (OFF/COOL): ${toCelsius(this.device.changeableValues.coolSetpoint, Number(this.Thermostat.TemperatureDisplayUnits))})`);
423
+ }
424
+ }
425
+ }
426
+ // Set the Target Fan State
427
+ if (this.device.settings?.fan && !this.device.thermostat?.hide_fan) {
428
+ if (this.Fan) {
429
+ if (this.fanStatus) {
430
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} fanStatus: ${JSON.stringify(this.fanStatus)}`);
431
+ if (this.fanStatus.changeableValues.mode === 'Auto') {
432
+ this.Fan.TargetFanState = this.hap.Characteristic.TargetFanState.AUTO;
433
+ this.Fan.Active = this.hap.Characteristic.Active.INACTIVE;
434
+ }
435
+ else if (this.fanStatus.changeableValues.mode === 'On') {
436
+ this.Fan.TargetFanState = this.hap.Characteristic.TargetFanState.MANUAL;
437
+ this.Fan.Active = this.hap.Characteristic.Active.ACTIVE;
438
+ }
439
+ else if (this.fanStatus.changeableValues.mode === 'Circulate') {
440
+ this.Fan.TargetFanState = this.hap.Characteristic.TargetFanState.MANUAL;
441
+ this.Fan.Active = this.hap.Characteristic.Active.INACTIVE;
442
+ }
443
+ }
444
+ }
445
+ }
446
+ // Set the Room Priority Status - T9 Only
447
+ if (this.device.thermostat?.roompriority?.deviceType === 'Thermostat' && this.device.deviceModel === 'T9-T10') {
448
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} roomPriorityStatus: ${JSON.stringify(this.roomPriorityStatus)}`);
449
+ }
450
+ }
451
+ /**
452
+ * Asks the Resideo Home API for the latest device information
453
+ */
454
+ async refreshStatus() {
455
+ try {
456
+ const device = await this.getDeviceStatus();
457
+ const fanStatus = await this.getFanStatus();
458
+ const roomPriorityStatus = await this.getRoomPriorityStatus();
459
+ this.device = device;
460
+ this.fanStatus = fanStatus;
461
+ this.roomPriorityStatus = roomPriorityStatus;
462
+ this.parseStatus();
463
+ this.updateHomeKitCharacteristics();
464
+ }
465
+ catch (e) {
466
+ const action = 'refreshStatus';
467
+ if (this.device.retry) {
468
+ // Refresh the status from the API
469
+ interval(5000)
470
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
471
+ .pipe(take(1))
472
+ .subscribe(async () => {
473
+ await this.refreshStatus();
474
+ });
475
+ }
476
+ this.resideoAPIError(e, action);
477
+ this.apiError(e);
478
+ }
479
+ }
480
+ async getDeviceStatus() {
481
+ const device = (await this.platform.axios.get(`${DeviceURL}/thermostats/${this.device.deviceID}`, {
482
+ params: {
483
+ locationId: this.location.locationID,
484
+ },
485
+ })).data;
486
+ this.debugLog(`(refreshStatus) ${device.deviceClass} device: ${JSON.stringify(device)}`);
487
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} refreshStatus for ${this.device.name} from Resideo API: ${JSON.stringify(this.device.changeableValues)}`);
488
+ return device;
489
+ }
490
+ async getRoomPriorityStatus() {
491
+ if (this.device.thermostat?.roompriority?.deviceType === 'Thermostat' && this.device.deviceModel === 'T9-T10') {
492
+ const roompriority = (await this.platform.axios.get(`${DeviceURL}/thermostats/${this.device.deviceID}/priority`, {
493
+ params: {
494
+ locationId: this.location.locationID,
495
+ },
496
+ })).data;
497
+ this.debugLog(`Thermostat: ${this.accessory.displayName} Priority: ${JSON.stringify(roompriority.data)}`);
498
+ const action = 'refreshRoomPriority';
499
+ await this.statusCode(200, action);
500
+ return roompriority;
501
+ }
502
+ }
503
+ async getFanStatus() {
504
+ if (this.device.settings?.fan && !this.device.thermostat?.hide_fan) {
505
+ const fan = (await this.platform.axios.get(`${DeviceURL}/thermostats/${this.device.deviceID}/fan`, {
506
+ params: {
507
+ locationId: this.location.locationID,
508
+ },
509
+ })).data;
510
+ const action = 'refreshFanStatus';
511
+ await this.statusCode(200, action);
512
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} (refreshFanStatus) fanMode: ${JSON.stringify(fan)}`);
513
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} fanMode: ${JSON.stringify(fan)}`);
514
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} refreshStatus for ${this.device.name} Fan from Resideo Fan API: ${JSON.stringify(fan)}`);
515
+ return fan;
516
+ }
517
+ return null;
518
+ }
519
+ /**
520
+ * Pushes the requested changes to the Resideo API
521
+ */
522
+ async pushChanges() {
523
+ try {
524
+ const payload = {};
525
+ // Only include mode on certain models
526
+ switch (this.device.deviceModel) {
527
+ case 'Unknown':
528
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} didn't send TargetHeatingCoolingState, Model: ${this.device.deviceModel}`);
529
+ break;
530
+ default:
531
+ payload.mode = await this.ResideoMode();
532
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} send TargetHeatingCoolingState: ${payload.mode}`);
533
+ }
534
+ // Only include thermostatSetpointStatus on certain models
535
+ switch (this.device.deviceModel) {
536
+ case 'Round':
537
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} didn't send thermostatSetpointStatus, Model: ${this.device.deviceModel}`);
538
+ break;
539
+ default:
540
+ this.pushChangesthermostatSetpointStatus();
541
+ payload.thermostatSetpointStatus = this.thermostatSetpointStatus;
542
+ if (this.thermostatSetpointStatus === 'TemporaryHold') {
543
+ this.warnLog(`${this.device.deviceClass} ${this.accessory.displayName} send thermostatSetpointStatus: `
544
+ + `${payload.thermostatSetpointStatus}, Model: ${this.device.deviceModel}`);
545
+ }
546
+ else {
547
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} send thermostatSetpointStatus: `
548
+ + `${payload.thermostatSetpointStatus}, Model: ${this.device.deviceModel}`);
549
+ }
550
+ }
551
+ switch (this.device.deviceModel) {
552
+ case 'Round':
553
+ case 'D6':
554
+ if (this.deviceLogging.includes('debug')) {
555
+ this.warnLog(`${this.device.deviceClass} ${this.accessory.displayName} set autoChangeoverActive, Model: ${this.device.deviceModel}`);
556
+ }
557
+ // for Round the 'Auto' feature is enabled via the special mode so only flip this bit when
558
+ // the heating/cooling state is set to `Auto
559
+ if (this.Thermostat.TargetHeatingCoolingState === this.hap.Characteristic.TargetHeatingCoolingState.AUTO) {
560
+ payload.autoChangeoverActive = true;
561
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Heating/Cooling state set to Auto for`
562
+ + ` Model: ${this.device.deviceModel}, Force autoChangeoverActive: ${payload.autoChangeoverActive}`);
563
+ }
564
+ else {
565
+ payload.autoChangeoverActive = this.device.changeableValues?.autoChangeoverActive;
566
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Heating/cooling state not set to Auto for`
567
+ + ` Model: ${this.device.deviceModel}, Using device setting`
568
+ + ` autoChangeoverActive: ${this.device.changeableValues.autoChangeoverActive}`);
569
+ }
570
+ break;
571
+ case 'Unknown':
572
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} do not send autoChangeoverActive, Model: ${this.device.deviceModel}`);
573
+ break;
574
+ default:
575
+ payload.autoChangeoverActive = this.device.changeableValues.autoChangeoverActive;
576
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} set autoChangeoverActive to ${this.device.changeableValues.autoChangeoverActive} for Model: ${this.device.deviceModel}`);
577
+ }
578
+ switch (this.device.deviceModel) {
579
+ case 'Unknown':
580
+ this.errorLog(JSON.stringify(this.device));
581
+ payload.thermostatSetpoint = toFahrenheit(Number(this.Thermostat.TargetTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
582
+ switch (this.device.units) {
583
+ case 'Fahrenheit':
584
+ payload.unit = 'Fahrenheit';
585
+ break;
586
+ case 'Celsius':
587
+ payload.unit = 'Celsius';
588
+ break;
589
+ }
590
+ this.successLog(`${this.device.deviceClass} ${this.accessory.displayName} sent request to Resideo API thermostatSetpoint: ${payload.thermostatSetpoint}, unit: ${payload.unit}`);
591
+ break;
592
+ default:
593
+ // Set the heat and cool set point value based on the selected mode
594
+ switch (this.Thermostat.TargetHeatingCoolingState) {
595
+ case this.hap.Characteristic.TargetHeatingCoolingState.HEAT:
596
+ payload.heatSetpoint = toFahrenheit(Number(this.Thermostat.TargetTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
597
+ payload.coolSetpoint = toFahrenheit(Number(this.Thermostat.CoolingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
598
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetHeatingCoolingState (HEAT): ${this.Thermostat.TargetHeatingCoolingState}, TargetTemperature: ${toFahrenheit(Number(this.Thermostat.TargetTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} heatSetpoint, CoolingThresholdTemperature: ${toFahrenheit(Number(this.Thermostat.CoolingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} coolSetpoint`);
599
+ break;
600
+ case this.hap.Characteristic.TargetHeatingCoolingState.COOL:
601
+ payload.coolSetpoint = toFahrenheit(Number(this.Thermostat.TargetTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
602
+ payload.heatSetpoint = toFahrenheit(Number(this.Thermostat.HeatingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
603
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetHeatingCoolingState (COOL): ${this.Thermostat.TargetHeatingCoolingState}, TargetTemperature: ${toFahrenheit(Number(this.Thermostat.TargetTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} coolSetpoint, CoolingThresholdTemperature: ${toFahrenheit(Number(this.Thermostat.HeatingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} heatSetpoint`);
604
+ break;
605
+ case this.hap.Characteristic.TargetHeatingCoolingState.AUTO:
606
+ payload.coolSetpoint = toFahrenheit(Number(this.Thermostat.CoolingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
607
+ payload.heatSetpoint = toFahrenheit(Number(this.Thermostat.HeatingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
608
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetHeatingCoolingState (AUTO): ${this.Thermostat.TargetHeatingCoolingState}, CoolingThresholdTemperature: ${toFahrenheit(Number(this.Thermostat.CoolingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} coolSetpoint, HeatingThresholdTemperature: ${toFahrenheit(Number(this.Thermostat.HeatingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} heatSetpoint`);
609
+ break;
610
+ default:
611
+ payload.coolSetpoint = toFahrenheit(Number(this.Thermostat.CoolingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
612
+ payload.heatSetpoint = toFahrenheit(Number(this.Thermostat.HeatingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits));
613
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetHeatingCoolingState (OFF): ${this.Thermostat.TargetHeatingCoolingState}, CoolingThresholdTemperature: ${toFahrenheit(Number(this.Thermostat.CoolingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} coolSetpoint, HeatingThresholdTemperature: ${toFahrenheit(Number(this.Thermostat.HeatingThresholdTemperature), Number(this.Thermostat.TemperatureDisplayUnits))} heatSetpoint`);
614
+ }
615
+ this.successLog(`${this.device.deviceClass} ${this.accessory.displayName} set request (${JSON.stringify(payload)}) to Resideo API.`);
616
+ }
617
+ // Attempt to make the API request
618
+ await this.platform.axios.post(`${DeviceURL}/thermostats/${this.device.deviceID}`, payload, {
619
+ params: {
620
+ locationId: this.location.locationID,
621
+ },
622
+ });
623
+ const action = 'pushChanges';
624
+ await this.statusCode(200, action);
625
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} pushChanges: ${JSON.stringify(payload)}`);
626
+ await this.updateHomeKitCharacteristics();
627
+ }
628
+ catch (e) {
629
+ const action = 'pushChanges';
630
+ if (this.device.retry) {
631
+ // Refresh the status from the API
632
+ interval(5000)
633
+ .pipe(skipWhile(() => this.thermostatUpdateInProgress))
634
+ .pipe(take(1))
635
+ .subscribe(async () => {
636
+ await this.pushChanges();
637
+ });
638
+ }
639
+ this.resideoAPIError(e, action);
640
+ this.apiError(e);
641
+ }
642
+ }
643
+ async ResideoMode() {
644
+ let resideoMode;
645
+ switch (this.Thermostat.TargetHeatingCoolingState) {
646
+ case this.hap.Characteristic.TargetHeatingCoolingState.HEAT:
647
+ resideoMode = ResideoModes.Heat;
648
+ break;
649
+ case this.hap.Characteristic.TargetHeatingCoolingState.COOL:
650
+ resideoMode = ResideoModes.Cool;
651
+ break;
652
+ case this.hap.Characteristic.TargetHeatingCoolingState.AUTO:
653
+ resideoMode = ResideoModes.Auto;
654
+ break;
655
+ case this.hap.Characteristic.TargetHeatingCoolingState.OFF:
656
+ resideoMode = ResideoModes.Off;
657
+ break;
658
+ default:
659
+ resideoMode = 'Unknown';
660
+ this.debugErrorLog(`${this.device.deviceClass} ${this.accessory.displayName} Unknown TargetHeatingCoolingState: ${this.Thermostat.TargetHeatingCoolingState}`);
661
+ }
662
+ return resideoMode;
663
+ }
664
+ async pushChangesthermostatSetpointStatus() {
665
+ if (this.thermostatSetpointStatus) {
666
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} thermostatSetpointStatus config set to ${this.thermostatSetpointStatus}`);
667
+ }
668
+ else {
669
+ this.thermostatSetpointStatus = 'PermanentHold';
670
+ this.accessory.context.thermostatSetpointStatus = this.thermostatSetpointStatus;
671
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} thermostatSetpointStatus config not set`);
672
+ }
673
+ }
674
+ /**
675
+ * Pushes the requested changes for Room Priority to the Resideo API
676
+ */
677
+ async pushRoomChanges() {
678
+ this.debugLog(`Thermostat Room Priority for ${this.accessory.displayName}
679
+ Current Room: ${JSON.stringify(this.roomPriorityStatus.currentPriority.selectedRooms)},
680
+ Changing Room: [${this.device.inBuiltSensorState.roomId}]`);
681
+ if (`[${this.device.inBuiltSensorState.roomId}]` !== `[${this.roomPriorityStatus.currentPriority.selectedRooms}]`) {
682
+ const payload = {
683
+ currentPriority: {
684
+ priorityType: this.device.thermostat?.roompriority?.priorityType,
685
+ },
686
+ };
687
+ if (this.device.thermostat?.roompriority?.priorityType === 'PickARoom') {
688
+ payload.currentPriority.selectedRooms = [this.device.inBuiltSensorState.roomId];
689
+ }
690
+ /**
691
+ * For "LCC-" devices only.
692
+ * "NoHold" will return to schedule.
693
+ * "TemporaryHold" will hold the set temperature until next schedule.
694
+ * "PermanentHold" will hold the setpoint until user requests another change.
695
+ */
696
+ if (this.device.thermostat?.roompriority?.deviceType === 'Thermostat') {
697
+ if (this.device.priorityType === 'FollowMe') {
698
+ this.successLog(`Sending request for ${this.accessory.displayName} to Resideo API Priority Type: ${this.device.priorityType}, Built-in Occupancy Sensor(s) Will be used to set Priority Automatically`);
699
+ }
700
+ else if (this.device.priorityType === 'WholeHouse') {
701
+ this.successLog(`Sending request for ${this.accessory.displayName} to Resideo API Priority Type:` + ` ${this.device.priorityType}`);
702
+ }
703
+ else if (this.device.priorityType === 'PickARoom') {
704
+ this.successLog(`Sending request for ${this.accessory.displayName} to Resideo API Room Priority: ${this.device.inBuiltSensorState.roomName}, Priority Type: ${this.device.thermostat?.roompriority.priorityType}`);
705
+ }
706
+ // Make the API request
707
+ await this.platform.axios.put(`${DeviceURL}/thermostats/${this.device.deviceID}/priority`, payload, {
708
+ params: {
709
+ locationId: this.location.locationID,
710
+ },
711
+ });
712
+ const action = 'pushRoomChanges';
713
+ await this.statusCode(200, action);
714
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} pushRoomChanges: ${JSON.stringify(payload)}`);
715
+ }
716
+ }
717
+ }
718
+ /**
719
+ * Updates the status for each of the HomeKit Characteristics
720
+ */
721
+ async updateHomeKitCharacteristics() {
722
+ if (this.Thermostat.TemperatureDisplayUnits === undefined) {
723
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TemperatureDisplayUnits: ${this.Thermostat.TemperatureDisplayUnits}`);
724
+ }
725
+ else {
726
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits, Number(this.Thermostat.TemperatureDisplayUnits));
727
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic TemperatureDisplayUnits: ${this.Thermostat.TemperatureDisplayUnits}`);
728
+ }
729
+ if (this.Thermostat.CurrentTemperature === undefined) {
730
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} CurrentTemperature: ${this.Thermostat.CurrentTemperature}`);
731
+ }
732
+ else {
733
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.Thermostat.CurrentTemperature);
734
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.Thermostat.CurrentTemperature}`);
735
+ }
736
+ if (this.Thermostat.TargetTemperature === undefined) {
737
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetTemperature: ${this.Thermostat.TargetTemperature}`);
738
+ }
739
+ else {
740
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TargetTemperature, this.Thermostat.TargetTemperature);
741
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic TargetTemperature: ${this.Thermostat.TargetTemperature}`);
742
+ }
743
+ if (this.Thermostat.HeatingThresholdTemperature === undefined) {
744
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} HeatingThresholdTemperature: ${this.Thermostat.HeatingThresholdTemperature}`);
745
+ }
746
+ else {
747
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, this.Thermostat.HeatingThresholdTemperature);
748
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic HeatingThresholdTemperature: ${this.Thermostat.HeatingThresholdTemperature}`);
749
+ }
750
+ if (this.Thermostat.CoolingThresholdTemperature === undefined) {
751
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} CoolingThresholdTemperature: ${this.Thermostat.CoolingThresholdTemperature}`);
752
+ }
753
+ else {
754
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, this.Thermostat.CoolingThresholdTemperature);
755
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic CoolingThresholdTemperature: ${this.Thermostat.CoolingThresholdTemperature}`);
756
+ }
757
+ if (this.Thermostat.TargetHeatingCoolingState === undefined) {
758
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetHeatingCoolingState: ${this.Thermostat.TargetHeatingCoolingState}`);
759
+ }
760
+ else {
761
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState, this.Thermostat.TargetHeatingCoolingState);
762
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic TargetHeatingCoolingState: ${this.Thermostat.TargetHeatingCoolingState}`);
763
+ }
764
+ if (this.Thermostat.CurrentHeatingCoolingState === undefined) {
765
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} CurrentHeatingCoolingState: ${this.Thermostat.CurrentHeatingCoolingState}`);
766
+ }
767
+ else {
768
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeatingCoolingState, this.Thermostat.CurrentHeatingCoolingState);
769
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic CurrentHeatingCoolingState: ${this.Thermostat.TargetHeatingCoolingState}`);
770
+ }
771
+ if (!this.device.thermostat?.hide_humidity) {
772
+ if (this.device.indoorHumidity) {
773
+ if (this.HumiditySensor?.CurrentRelativeHumidity === undefined) {
774
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} CurrentRelativeHumidity: ${this.HumiditySensor?.CurrentRelativeHumidity}`);
775
+ }
776
+ else {
777
+ this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor.CurrentRelativeHumidity);
778
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}`);
779
+ }
780
+ }
781
+ }
782
+ if (!this.device.thermostat?.hide_fan) {
783
+ if (this.device.settings?.fan) {
784
+ if (this.Fan?.TargetFanState === undefined) {
785
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Fan TargetFanState: ${this.Fan?.TargetFanState}`);
786
+ }
787
+ else {
788
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.TargetFanState, this.Fan.TargetFanState);
789
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Fan updateCharacteristic TargetFanState: ${this.Fan.TargetFanState}`);
790
+ }
791
+ if (this.Fan?.Active === undefined) {
792
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Fan Active: ${this.Fan?.Active}`);
793
+ }
794
+ else {
795
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active);
796
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Fan updateCharacteristic Active: ${this.Fan.Active}`);
797
+ }
798
+ }
799
+ }
800
+ }
801
+ async apiError(e) {
802
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits, e);
803
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
804
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TargetTemperature, e);
805
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, e);
806
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, e);
807
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState, e);
808
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeatingCoolingState, e);
809
+ if (!this.device.thermostat?.hide_humidity) {
810
+ if (this.device.indoorHumidity) {
811
+ this.HumiditySensor?.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
812
+ }
813
+ }
814
+ if (!this.device.thermostat?.hide_fan) {
815
+ if (this.device.settings?.fan) {
816
+ this.Fan?.Service.updateCharacteristic(this.hap.Characteristic.TargetFanState, e);
817
+ this.Fan?.Service.updateCharacteristic(this.hap.Characteristic.Active, e);
818
+ }
819
+ }
820
+ }
821
+ async getThermostatConfigSettings(accessory, device) {
822
+ if (this.thermostatSetpointStatus === undefined) {
823
+ accessory.context.thermostatSetpointStatus = device.thermostat?.thermostatSetpointStatus;
824
+ this.thermostatSetpointStatus = accessory.context.thermostatSetpointStatus;
825
+ this.debugLog(`${device.deviceClass} ${accessory.displayName} thermostatSetpointStatus: ${this.thermostatSetpointStatus}`);
826
+ }
827
+ }
828
+ async setTargetHeatingCoolingState(value) {
829
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set TargetHeatingCoolingState: ${value}`);
830
+ this.Thermostat.TargetHeatingCoolingState = value;
831
+ // Set the TargetTemperature value based on the selected mode
832
+ if (this.Thermostat.TargetHeatingCoolingState === this.hap.Characteristic.TargetHeatingCoolingState.HEAT) {
833
+ this.Thermostat.TargetTemperature = toCelsius(this.device.changeableValues.heatSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
834
+ }
835
+ else {
836
+ this.Thermostat.TargetTemperature = toCelsius(this.device.changeableValues.coolSetpoint, Number(this.Thermostat.TemperatureDisplayUnits));
837
+ }
838
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TargetTemperature, this.Thermostat.TargetTemperature);
839
+ if (this.device.thermostat?.roompriority?.deviceType === 'Thermostat' && this.device.deviceModel === 'T9-T10') {
840
+ this.doRoomUpdate.next();
841
+ }
842
+ if (this.Thermostat.TargetHeatingCoolingState !== HomeKitModes[this.device.changeableValues.mode]) {
843
+ this.doThermostatUpdate.next();
844
+ }
845
+ }
846
+ async setHeatingThresholdTemperature(value) {
847
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set HeatingThresholdTemperature: ${value}`);
848
+ this.Thermostat.HeatingThresholdTemperature = value;
849
+ this.doThermostatUpdate.next();
850
+ }
851
+ async setCoolingThresholdTemperature(value) {
852
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set CoolingThresholdTemperature: ${value}`);
853
+ this.Thermostat.CoolingThresholdTemperature = value;
854
+ this.doThermostatUpdate.next();
855
+ }
856
+ async setTargetTemperature(value) {
857
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set TargetTemperature: ${value}`);
858
+ this.Thermostat.TargetTemperature = value;
859
+ this.doThermostatUpdate.next();
860
+ }
861
+ async setTemperatureDisplayUnits(value) {
862
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set TemperatureDisplayUnits: ${value}`);
863
+ this.warnLog('Changing the Hardware Display Units from HomeKit is not supported.');
864
+ this.Thermostat.TemperatureDisplayUnits = this.device.units === 'Celsius'
865
+ ? this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS
866
+ : this.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT;
867
+ // change the temp units back to the one the Resideo API said the thermostat was set to
868
+ setTimeout(() => {
869
+ this.Thermostat.Service.updateCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits, Number(this.Thermostat.TemperatureDisplayUnits));
870
+ }, 100);
871
+ }
872
+ /**
873
+ * Handle requests to set the "Programmable Switch Output State" characteristic
874
+ */
875
+ handleProgrammableSwitchOutputStateSet(value) {
876
+ this.debugLog('Triggered SET ProgrammableSwitchOutputState:', value);
877
+ }
878
+ /**
879
+ * Pushes the requested changes for Fan to the Resideo API
880
+ */
881
+ async pushFanChanges() {
882
+ let payload = {
883
+ mode: 'Auto', // default to Auto
884
+ };
885
+ if (this.device.settings?.fan && !this.device.thermostat?.hide_fan) {
886
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} TargetFanState: ${this.Fan?.TargetFanState}, Active: ${this.Fan?.Active}`);
887
+ if (this.Fan?.TargetFanState === this.hap.Characteristic.TargetFanState.AUTO) {
888
+ payload = {
889
+ mode: 'Auto',
890
+ };
891
+ }
892
+ else if (this.Fan?.TargetFanState === this.hap.Characteristic.TargetFanState.MANUAL
893
+ && this.Fan?.Active === this.hap.Characteristic.Active.ACTIVE) {
894
+ payload = {
895
+ mode: 'On',
896
+ };
897
+ }
898
+ else if (this.Fan?.TargetFanState === this.hap.Characteristic.TargetFanState.MANUAL
899
+ && this.Fan?.Active === this.hap.Characteristic.Active.INACTIVE) {
900
+ payload = {
901
+ mode: 'Circulate',
902
+ };
903
+ }
904
+ this.successLog(`Sending request for ${this.accessory.displayName} to Resideo API Fan Mode: ${payload.mode}`);
905
+ // Make the API request
906
+ await this.platform.axios.post(`${DeviceURL}/thermostats/${this.device.deviceID}/fan`, payload, {
907
+ params: {
908
+ locationId: this.location.locationID,
909
+ },
910
+ });
911
+ const action = 'pushFanChanges';
912
+ await this.statusCode(200, action);
913
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} pushChanges: ${JSON.stringify(payload)}`);
914
+ }
915
+ }
916
+ /**
917
+ * Updates the status for each of the HomeKit Characteristics
918
+ */
919
+ async setActive(value) {
920
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set Active: ${value}`);
921
+ this.Fan.Active = value;
922
+ this.doFanUpdate.next();
923
+ }
924
+ async setTargetFanState(value) {
925
+ this.debugLog(`${this.device.deviceClass} ${this.accessory.displayName} Set TargetFanState: ${value}`);
926
+ this.Fan.TargetFanState = value;
927
+ this.doFanUpdate.next();
928
+ }
929
+ }
930
+ //# sourceMappingURL=thermostats.js.map