homebridge-nest-accfactory 0.0.4-a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +176 -0
- package/README.md +121 -0
- package/config.schema.json +107 -0
- package/dist/HomeKitDevice.js +441 -0
- package/dist/HomeKitHistory.js +2835 -0
- package/dist/camera.js +1276 -0
- package/dist/doorbell.js +122 -0
- package/dist/index.js +35 -0
- package/dist/nexustalk.js +741 -0
- package/dist/protect.js +240 -0
- package/dist/protobuf/google/rpc/status.proto +91 -0
- package/dist/protobuf/google/rpc/stream_body.proto +26 -0
- package/dist/protobuf/google/trait/product/camera.proto +53 -0
- package/dist/protobuf/googlehome/foyer.proto +208 -0
- package/dist/protobuf/nest/messages.proto +8 -0
- package/dist/protobuf/nest/services/apigateway.proto +107 -0
- package/dist/protobuf/nest/trait/audio.proto +7 -0
- package/dist/protobuf/nest/trait/cellular.proto +313 -0
- package/dist/protobuf/nest/trait/debug.proto +37 -0
- package/dist/protobuf/nest/trait/detector.proto +41 -0
- package/dist/protobuf/nest/trait/diagnostics.proto +87 -0
- package/dist/protobuf/nest/trait/firmware.proto +221 -0
- package/dist/protobuf/nest/trait/guest.proto +105 -0
- package/dist/protobuf/nest/trait/history.proto +345 -0
- package/dist/protobuf/nest/trait/humanlibrary.proto +19 -0
- package/dist/protobuf/nest/trait/hvac.proto +1353 -0
- package/dist/protobuf/nest/trait/input.proto +29 -0
- package/dist/protobuf/nest/trait/lighting.proto +61 -0
- package/dist/protobuf/nest/trait/located.proto +193 -0
- package/dist/protobuf/nest/trait/media.proto +68 -0
- package/dist/protobuf/nest/trait/network.proto +352 -0
- package/dist/protobuf/nest/trait/occupancy.proto +373 -0
- package/dist/protobuf/nest/trait/olive.proto +15 -0
- package/dist/protobuf/nest/trait/pairing.proto +85 -0
- package/dist/protobuf/nest/trait/product/camera.proto +283 -0
- package/dist/protobuf/nest/trait/product/detect.proto +67 -0
- package/dist/protobuf/nest/trait/product/doorbell.proto +18 -0
- package/dist/protobuf/nest/trait/product/guard.proto +59 -0
- package/dist/protobuf/nest/trait/product/protect.proto +344 -0
- package/dist/protobuf/nest/trait/promonitoring.proto +14 -0
- package/dist/protobuf/nest/trait/resourcedirectory.proto +32 -0
- package/dist/protobuf/nest/trait/safety.proto +119 -0
- package/dist/protobuf/nest/trait/security.proto +516 -0
- package/dist/protobuf/nest/trait/selftest.proto +78 -0
- package/dist/protobuf/nest/trait/sensor.proto +291 -0
- package/dist/protobuf/nest/trait/service.proto +46 -0
- package/dist/protobuf/nest/trait/structure.proto +85 -0
- package/dist/protobuf/nest/trait/system.proto +51 -0
- package/dist/protobuf/nest/trait/test.proto +15 -0
- package/dist/protobuf/nest/trait/ui.proto +65 -0
- package/dist/protobuf/nest/trait/user.proto +98 -0
- package/dist/protobuf/nest/trait/voiceassistant.proto +30 -0
- package/dist/protobuf/nestlabs/eventingapi/v1.proto +83 -0
- package/dist/protobuf/nestlabs/gateway/v1.proto +273 -0
- package/dist/protobuf/nestlabs/gateway/v2.proto +96 -0
- package/dist/protobuf/nestlabs/history/v1.proto +73 -0
- package/dist/protobuf/root.proto +64 -0
- package/dist/protobuf/wdl-event-importance.proto +11 -0
- package/dist/protobuf/wdl.proto +450 -0
- package/dist/protobuf/weave/common.proto +144 -0
- package/dist/protobuf/weave/trait/audio.proto +12 -0
- package/dist/protobuf/weave/trait/auth.proto +22 -0
- package/dist/protobuf/weave/trait/description.proto +32 -0
- package/dist/protobuf/weave/trait/heartbeat.proto +38 -0
- package/dist/protobuf/weave/trait/locale.proto +20 -0
- package/dist/protobuf/weave/trait/network.proto +24 -0
- package/dist/protobuf/weave/trait/pairing.proto +8 -0
- package/dist/protobuf/weave/trait/peerdevices.proto +18 -0
- package/dist/protobuf/weave/trait/power.proto +86 -0
- package/dist/protobuf/weave/trait/schedule.proto +76 -0
- package/dist/protobuf/weave/trait/security.proto +343 -0
- package/dist/protobuf/weave/trait/telemetry/tunnel.proto +37 -0
- package/dist/protobuf/weave/trait/time.proto +16 -0
- package/dist/res/Nest_camera_connecting.h264 +0 -0
- package/dist/res/Nest_camera_connecting.jpg +0 -0
- package/dist/res/Nest_camera_off.h264 +0 -0
- package/dist/res/Nest_camera_off.jpg +0 -0
- package/dist/res/Nest_camera_offline.h264 +0 -0
- package/dist/res/Nest_camera_offline.jpg +0 -0
- package/dist/res/Nest_camera_transfer.jpg +0 -0
- package/dist/streamer.js +344 -0
- package/dist/system.js +3112 -0
- package/dist/tempsensor.js +99 -0
- package/dist/thermostat.js +1026 -0
- package/dist/weather.js +205 -0
- package/dist/webrtc.js +55 -0
- package/package.json +66 -0
|
@@ -0,0 +1,1026 @@
|
|
|
1
|
+
// Nest Thermostat
|
|
2
|
+
// Part of homebridge-nest-accfactory
|
|
3
|
+
//
|
|
4
|
+
// Code version 3/9/2024
|
|
5
|
+
// Mark Hulskamp
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
// Define our modules
|
|
9
|
+
import HomeKitDevice from './HomeKitDevice.js';
|
|
10
|
+
|
|
11
|
+
// Define nodejs module requirements
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
|
|
14
|
+
const LOWBATTERYLEVEL = 10; // Low battery level percentage
|
|
15
|
+
const MIN_TEMPERATURE = 9; // Minimum temperature for Nest Thermostat
|
|
16
|
+
const MAX_TEMPERATURE = 32; // Maximum temperature for Nest Thermostat
|
|
17
|
+
|
|
18
|
+
export default class NestThermostat extends HomeKitDevice {
|
|
19
|
+
batteryService = undefined;
|
|
20
|
+
occupancyService = undefined;
|
|
21
|
+
humidityService = undefined;
|
|
22
|
+
fanService = undefined;
|
|
23
|
+
dehumidifierService = undefined;
|
|
24
|
+
externalCool = undefined; // External module function
|
|
25
|
+
externalHeat = undefined; // External module function
|
|
26
|
+
externalFan = undefined; // External module function
|
|
27
|
+
externalDehumidifier = undefined; // External module function
|
|
28
|
+
|
|
29
|
+
constructor(accessory, api, log, eventEmitter, deviceData) {
|
|
30
|
+
super(accessory, api, log, eventEmitter, deviceData);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Class functions
|
|
34
|
+
async addServices() {
|
|
35
|
+
// Setup the thermostat service if not already present on the accessory
|
|
36
|
+
this.thermostatService = this.accessory.getService(this.hap.Service.Thermostat);
|
|
37
|
+
if (this.thermostatService === undefined) {
|
|
38
|
+
this.thermostatService = this.accessory.addService(this.hap.Service.Thermostat, '', 1);
|
|
39
|
+
}
|
|
40
|
+
this.thermostatService.setPrimaryService();
|
|
41
|
+
|
|
42
|
+
if (this.thermostatService.testCharacteristic(this.hap.Characteristic.StatusActive) === false) {
|
|
43
|
+
// Used to indicate active temperature if the thermostat is using its temperature sensor data
|
|
44
|
+
// or an external temperature sensor ie: Nest Temperature Sensor
|
|
45
|
+
this.thermostatService.addCharacteristic(this.hap.Characteristic.StatusActive);
|
|
46
|
+
}
|
|
47
|
+
if (this.thermostatService.testCharacteristic(this.hap.Characteristic.StatusFault) === false) {
|
|
48
|
+
this.thermostatService.addCharacteristic(this.hap.Characteristic.StatusFault);
|
|
49
|
+
}
|
|
50
|
+
if (this.thermostatService.testCharacteristic(this.hap.Characteristic.LockPhysicalControls) === false) {
|
|
51
|
+
// Setting can only be accessed via Eve App (or other 3rd party).
|
|
52
|
+
this.thermostatService.addCharacteristic(this.hap.Characteristic.LockPhysicalControls);
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
this.deviceData?.has_air_filter === true &&
|
|
56
|
+
this.thermostatService.testCharacteristic(this.hap.Characteristic.FilterChangeIndication) === false
|
|
57
|
+
) {
|
|
58
|
+
// Setup air filter change characteristic
|
|
59
|
+
this.thermostatService.addCharacteristic(this.hap.Characteristic.FilterChangeIndication);
|
|
60
|
+
}
|
|
61
|
+
if (
|
|
62
|
+
this.deviceData?.has_air_filter === false &&
|
|
63
|
+
this.thermostatService.testCharacteristic(this.hap.Characteristic.FilterChangeIndication) === true
|
|
64
|
+
) {
|
|
65
|
+
// No longer configured to have an air filter, so remove characteristic from the accessory
|
|
66
|
+
this.thermostatService.removeCharacteristic(this.hap.Characteristic.FilterChangeIndication);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
this.deviceData?.has_humidifier === true &&
|
|
71
|
+
this.thermostatService.testCharacteristic(this.hap.Characteristic.TargetRelativeHumidity) === false
|
|
72
|
+
) {
|
|
73
|
+
// We have the capability for a humidifier, so setup target humidity characterisitc
|
|
74
|
+
this.thermostatService.addCharacteristic(this.hap.Characteristic.TargetRelativeHumidity);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
this.deviceData?.has_humidifier === false &&
|
|
79
|
+
this.thermostatService.testCharacteristic(this.hap.Characteristic.TargetRelativeHumidity) === true
|
|
80
|
+
) {
|
|
81
|
+
// No longer configured to use a humdifier, so remove characteristic from the accessory
|
|
82
|
+
this.thermostatService.removeCharacteristic(this.hap.Characteristic.TargetRelativeHumidity);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (this.thermostatService.testCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity) === false) {
|
|
86
|
+
this.thermostatService.addCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Limit prop ranges
|
|
90
|
+
if (this.deviceData?.can_cool === false && this.deviceData?.can_heat === true) {
|
|
91
|
+
// Can heat only, so set values allowed for mode off/heat
|
|
92
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
93
|
+
validValues: [this.hap.Characteristic.TargetHeatingCoolingState.OFF, this.hap.Characteristic.TargetHeatingCoolingState.HEAT],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (this.deviceData?.can_cool === true && this.deviceData?.can_heat === false) {
|
|
97
|
+
// Can cool only
|
|
98
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
99
|
+
validValues: [this.hap.Characteristic.TargetHeatingCoolingState.OFF, this.hap.Characteristic.TargetHeatingCoolingState.COOL],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (this.deviceData?.can_cool === true && this.deviceData?.can_heat === true) {
|
|
103
|
+
// heat and cool
|
|
104
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
105
|
+
validValues: [
|
|
106
|
+
this.hap.Characteristic.TargetHeatingCoolingState.OFF,
|
|
107
|
+
this.hap.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
108
|
+
this.hap.Characteristic.TargetHeatingCoolingState.COOL,
|
|
109
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO,
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (this.deviceData?.can_cool === false && this.deviceData?.can_heat === false) {
|
|
114
|
+
// only off mode
|
|
115
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
116
|
+
validValues: [this.hap.Characteristic.TargetHeatingCoolingState.OFF],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Set default ranges - based on celsuis ranges to which the Nest Thermostat operates
|
|
121
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.CurrentTemperature).setProps({
|
|
122
|
+
minStep: 0.5,
|
|
123
|
+
});
|
|
124
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetTemperature).setProps({
|
|
125
|
+
minStep: 0.5,
|
|
126
|
+
minValue: MIN_TEMPERATURE,
|
|
127
|
+
maxValue: MAX_TEMPERATURE,
|
|
128
|
+
});
|
|
129
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature).setProps({
|
|
130
|
+
minStep: 0.5,
|
|
131
|
+
minValue: MIN_TEMPERATURE,
|
|
132
|
+
maxValue: MAX_TEMPERATURE,
|
|
133
|
+
});
|
|
134
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature).setProps({
|
|
135
|
+
minStep: 0.5,
|
|
136
|
+
minValue: MIN_TEMPERATURE,
|
|
137
|
+
maxValue: MAX_TEMPERATURE,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Setup callbacks for characteristics
|
|
141
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits).onSet((value) => {
|
|
142
|
+
this.setDisplayUnit(value);
|
|
143
|
+
});
|
|
144
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).onSet((value) => {
|
|
145
|
+
this.setMode(value);
|
|
146
|
+
});
|
|
147
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetTemperature).onSet((value) => {
|
|
148
|
+
this.setTemperature(this.hap.Characteristic.TargetTemperature, value);
|
|
149
|
+
});
|
|
150
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature).onSet((value) => {
|
|
151
|
+
this.setTemperature(this.hap.Characteristic.CoolingThresholdTemperature, value);
|
|
152
|
+
});
|
|
153
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature).onSet((value) => {
|
|
154
|
+
this.setTemperature(this.hap.Characteristic.HeatingThresholdTemperature, value);
|
|
155
|
+
});
|
|
156
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.LockPhysicalControls).onSet((value) => {
|
|
157
|
+
this.setChildlock('', value);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits).onGet(() => {
|
|
161
|
+
return this.deviceData.temperature_scale === 'C'
|
|
162
|
+
? this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS
|
|
163
|
+
: this.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT;
|
|
164
|
+
});
|
|
165
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetTemperature).onGet(() => {
|
|
166
|
+
return this.getTemperature(this.hap.Characteristic.TargetTemperature);
|
|
167
|
+
});
|
|
168
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature).onGet(() => {
|
|
169
|
+
return this.getTemperature(this.hap.Characteristic.CoolingThresholdTemperature);
|
|
170
|
+
});
|
|
171
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature).onGet(() => {
|
|
172
|
+
return this.getTemperature(this.hap.Characteristic.HeatingThresholdTemperature);
|
|
173
|
+
});
|
|
174
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).onGet(() => {
|
|
175
|
+
return this.getMode();
|
|
176
|
+
});
|
|
177
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.LockPhysicalControls).onGet(() => {
|
|
178
|
+
return this.deviceData.temperature_lock === true
|
|
179
|
+
? this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED
|
|
180
|
+
: this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Setup occupancy service if not already present on the accessory
|
|
184
|
+
this.occupancyService = this.accessory.getService(this.hap.Service.OccupancySensor);
|
|
185
|
+
if (this.occupancyService === undefined) {
|
|
186
|
+
this.occupancyService = this.accessory.addService(this.hap.Service.OccupancySensor, '', 1);
|
|
187
|
+
}
|
|
188
|
+
if (this.occupancyService.testCharacteristic(this.hap.Characteristic.StatusFault) === false) {
|
|
189
|
+
this.occupancyService.addCharacteristic(this.hap.Characteristic.StatusFault);
|
|
190
|
+
}
|
|
191
|
+
this.thermostatService.addLinkedService(this.occupancyService);
|
|
192
|
+
|
|
193
|
+
// Setup battery service if not already present on the accessory
|
|
194
|
+
this.batteryService = this.accessory.getService(this.hap.Service.Battery);
|
|
195
|
+
if (this.batteryService === undefined) {
|
|
196
|
+
this.batteryService = this.accessory.addService(this.hap.Service.Battery, '', 1);
|
|
197
|
+
}
|
|
198
|
+
this.batteryService.setHiddenService(true);
|
|
199
|
+
this.thermostatService.addLinkedService(this.batteryService);
|
|
200
|
+
|
|
201
|
+
// Setup fan service if supported by the thermostat and not already present on the accessory
|
|
202
|
+
this.fanService = this.accessory.getService(this.hap.Service.Fanv2);
|
|
203
|
+
if (this.deviceData?.has_fan === true) {
|
|
204
|
+
if (this.fanService === undefined) {
|
|
205
|
+
this.fanService = this.accessory.addService(this.hap.Service.Fanv2, '', 1);
|
|
206
|
+
}
|
|
207
|
+
this.thermostatService.addLinkedService(this.fanService);
|
|
208
|
+
|
|
209
|
+
this.fanService.getCharacteristic(this.hap.Characteristic.Active).onSet((value) => {
|
|
210
|
+
this.setFan(value);
|
|
211
|
+
});
|
|
212
|
+
this.fanService.getCharacteristic(this.hap.Characteristic.Active).onGet(() => {
|
|
213
|
+
return this.deviceData.fan_state === true ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (this.deviceData?.has_fan === false && this.fanService !== undefined) {
|
|
217
|
+
// No longer have a Fan configured and service present, so removed it
|
|
218
|
+
this.accessory.removeService(this.fanService);
|
|
219
|
+
this.fanService === undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Setup dehumifider service if supported by the thermostat and not already present on the accessory
|
|
223
|
+
this.dehumidifierService = this.accessory.getService(this.hap.Service.HumidifierDehumidifier);
|
|
224
|
+
if (this.deviceData?.has_dehumidifier === true) {
|
|
225
|
+
if (this.dehumidifierService === undefined) {
|
|
226
|
+
this.dehumidifierService = this.accessory.addService(this.hap.Service.HumidifierDehumidifier, '', 1);
|
|
227
|
+
}
|
|
228
|
+
this.thermostatService.addLinkedService(this.dehumidifierService);
|
|
229
|
+
|
|
230
|
+
this.dehumidifierService.getCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState).setProps({
|
|
231
|
+
validValues: [this.hap.Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER],
|
|
232
|
+
});
|
|
233
|
+
this.dehumidifierService.getCharacteristic(this.hap.Characteristic.Active).onSet((value) => {
|
|
234
|
+
this.setDehumidifier(value);
|
|
235
|
+
});
|
|
236
|
+
this.dehumidifierService.getCharacteristic(this.hap.Characteristic.Active).onGet(() => {
|
|
237
|
+
return this.deviceData.dehumidifier_state === true
|
|
238
|
+
? this.hap.Characteristic.Active.ACTIVE
|
|
239
|
+
: this.hap.Characteristic.Active.INACTIVE;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (this.deviceData?.has_dehumidifier === false && this.dehumidifierService !== undefined) {
|
|
243
|
+
// No longer have a dehumidifier configured and service present, so removed it
|
|
244
|
+
this.accessory.removeService(this.dehumidifierService);
|
|
245
|
+
this.dehumidifierService = undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Setup humdity service if configured to be seperate and not already present on the accessory
|
|
249
|
+
this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor);
|
|
250
|
+
if (this.deviceData?.humiditySensor === true) {
|
|
251
|
+
if (this.humidityService === undefined) {
|
|
252
|
+
this.humidityService = this.accessory.addService(this.hap.Service.HumiditySensor, '', 1);
|
|
253
|
+
}
|
|
254
|
+
this.thermostatService.addLinkedService(this.humidityService);
|
|
255
|
+
}
|
|
256
|
+
if (this.deviceData?.humiditySensor === false && this.humidityService !== undefined) {
|
|
257
|
+
// No longer have a seperate humidity sensor configure and service present, so removed it
|
|
258
|
+
this.accessory.removeService(this.humidityService);
|
|
259
|
+
this.humidityService = undefined;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Setup linkage to EveHome app if configured todo so
|
|
263
|
+
if (
|
|
264
|
+
this.deviceData?.eveHistory === true &&
|
|
265
|
+
this.thermostatService !== undefined &&
|
|
266
|
+
typeof this.historyService?.linkToEveHome === 'function'
|
|
267
|
+
) {
|
|
268
|
+
this.historyService.linkToEveHome(this.thermostatService, {
|
|
269
|
+
description: this.deviceData.description,
|
|
270
|
+
getcommand: this.#EveHomeGetcommand.bind(this),
|
|
271
|
+
setcommand: this.#EveHomeSetcommand.bind(this),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Attempt to load any external modules for this thermostat
|
|
276
|
+
// We support external cool/heat/fan/dehumidifier module functions
|
|
277
|
+
// This is all undocumented on how to use, as its for my specific use case :-)
|
|
278
|
+
const loadExternalModule = async (module) => {
|
|
279
|
+
if (typeof module !== 'string' || module === '') {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let loadedModule = undefined;
|
|
284
|
+
try {
|
|
285
|
+
let values = module.match(/('.*?'|[^' ]+)(?=\s* |\s*$)/g);
|
|
286
|
+
let script = path.resolve(values[0]); // external library name
|
|
287
|
+
let options = values.slice(1); // options to be passed into the external library
|
|
288
|
+
let externalModule = await import(script);
|
|
289
|
+
if (typeof externalModule?.default === 'function') {
|
|
290
|
+
loadedModule = externalModule.default(this.log, options);
|
|
291
|
+
}
|
|
292
|
+
// eslint-disable-next-line no-unused-vars
|
|
293
|
+
} catch (error) {
|
|
294
|
+
this?.log?.warn && this.log.warn('Failed to load specified external module for thermostat "%s"', module);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return loadedModule;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
this.externalCool = await loadExternalModule(this.deviceData?.externalCool);
|
|
301
|
+
this.externalHeat = await loadExternalModule(this.deviceData?.externalHeat);
|
|
302
|
+
this.externalFan = await loadExternalModule(this.deviceData?.externalFan);
|
|
303
|
+
this.externalDehumidifier = await loadExternalModule(this.deviceData?.externalDehumidifier);
|
|
304
|
+
|
|
305
|
+
// Create extra details for output
|
|
306
|
+
let postSetupDetails = [];
|
|
307
|
+
this.humidityService !== undefined && postSetupDetails.push('Seperate humidity sensor');
|
|
308
|
+
this.externalCool !== undefined && postSetupDetails.push('Using external cooling module');
|
|
309
|
+
this.externalHeat !== undefined && postSetupDetails.push('Using external heating module');
|
|
310
|
+
this.externalFan !== undefined && postSetupDetails.push('Using external fan module');
|
|
311
|
+
this.externalDehumidifier !== undefined && postSetupDetails.push('Using external dehumidification module');
|
|
312
|
+
|
|
313
|
+
return postSetupDetails;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
setFan(fanState) {
|
|
317
|
+
this.set({ fan_state: fanState === this.hap.Characteristic.Active.ACTIVE ? true : false });
|
|
318
|
+
this.fanService.updateCharacteristic(this.hap.Characteristic.Active, fanState);
|
|
319
|
+
|
|
320
|
+
this?.log?.info &&
|
|
321
|
+
this.log.info(
|
|
322
|
+
'Set fan on thermostat "%s" to "%s"',
|
|
323
|
+
this.deviceData.description,
|
|
324
|
+
fanState === this.hap.Characteristic.Active.ACTIVE ? 'On with initial fan speed of ' + this.deviceData.fan_current_speed : 'Off',
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
setDehumidifier(dehumidiferState) {
|
|
329
|
+
this.set({ dehumidifer_State: dehumidiferState === this.hap.Characteristic.Active.ACTIVE ? true : false });
|
|
330
|
+
this.dehumidifierService.updateCharacteristic(this.hap.Characteristic.Active, dehumidiferState);
|
|
331
|
+
|
|
332
|
+
this?.log?.info &&
|
|
333
|
+
this.log.info(
|
|
334
|
+
'Set dehumidifer on thermostat "%s" to "%s"',
|
|
335
|
+
this.deviceData.description,
|
|
336
|
+
dehumidiferState === this.hap.Characteristic.Active.ACTIVE
|
|
337
|
+
? 'On with target humidity level of ' + this.deviceData.target_humidity + '%'
|
|
338
|
+
: 'Off',
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
setDisplayUnit(temperatureUnit) {
|
|
343
|
+
this.set({ temperature_scale: temperatureUnit === this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS ? 'C' : 'F' });
|
|
344
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TemperatureDisplayUnits, temperatureUnit);
|
|
345
|
+
|
|
346
|
+
this?.log?.info &&
|
|
347
|
+
this.log.info(
|
|
348
|
+
'Set temperature units on thermostat "%s" to "%s"',
|
|
349
|
+
this.deviceData.description,
|
|
350
|
+
temperatureUnit === this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS ? '°C' : '°F',
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
setMode(thermostatMode) {
|
|
355
|
+
if (thermostatMode !== this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value) {
|
|
356
|
+
// Work out based on the HomeKit requested mode, what can the thermostat really switch too
|
|
357
|
+
// We may over-ride the requested HomeKit mode
|
|
358
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.HEAT && this.deviceData.can_heat === false) {
|
|
359
|
+
thermostatMode = this.hap.Characteristic.TargetHeatingCoolingState.OFF;
|
|
360
|
+
}
|
|
361
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.COOL && this.deviceData.can_cool === false) {
|
|
362
|
+
thermostatMode = this.hap.Characteristic.TargetHeatingCoolingState.OFF;
|
|
363
|
+
}
|
|
364
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.AUTO) {
|
|
365
|
+
// Workaround for 'Hey Siri, turn on my thermostat'
|
|
366
|
+
// Appears to automatically request mode as 'auto', but we need to see what Nest device supports
|
|
367
|
+
if (this.deviceData.can_cool === true && this.deviceData.can_heat === false) {
|
|
368
|
+
thermostatMode = this.hap.Characteristic.TargetHeatingCoolingState.COOL;
|
|
369
|
+
}
|
|
370
|
+
if (this.deviceData.can_cool === false && this.deviceData.can_heat === true) {
|
|
371
|
+
thermostatMode = this.hap.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
372
|
+
}
|
|
373
|
+
if (this.deviceData.can_cool === false && this.deviceData.can_heat === false) {
|
|
374
|
+
thermostatMode = this.hap.Characteristic.TargetHeatingCoolingState.OFF;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
let mode = '';
|
|
379
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.OFF) {
|
|
380
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, this.deviceData.target_temperature);
|
|
381
|
+
this.thermostatService.updateCharacteristic(
|
|
382
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
383
|
+
this.hap.Characteristic.TargetHeatingCoolingState.OFF,
|
|
384
|
+
);
|
|
385
|
+
mode = 'off';
|
|
386
|
+
}
|
|
387
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.COOL) {
|
|
388
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, this.deviceData.target_temperature_high);
|
|
389
|
+
this.thermostatService.updateCharacteristic(
|
|
390
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
391
|
+
this.hap.Characteristic.TargetHeatingCoolingState.COOL,
|
|
392
|
+
);
|
|
393
|
+
mode = 'cool';
|
|
394
|
+
}
|
|
395
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.HEAT) {
|
|
396
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, this.deviceData.target_temperature_low);
|
|
397
|
+
this.thermostatService.updateCharacteristic(
|
|
398
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
399
|
+
this.hap.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
400
|
+
);
|
|
401
|
+
mode = 'heat';
|
|
402
|
+
}
|
|
403
|
+
if (thermostatMode === this.hap.Characteristic.TargetHeatingCoolingState.AUTO) {
|
|
404
|
+
this.thermostatService.updateCharacteristic(
|
|
405
|
+
this.hap.Characteristic.TargetTemperature,
|
|
406
|
+
(this.deviceData.target_temperature_low + this.deviceData.target_temperature_high) * 0.5,
|
|
407
|
+
);
|
|
408
|
+
this.thermostatService.updateCharacteristic(
|
|
409
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
410
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO,
|
|
411
|
+
);
|
|
412
|
+
mode = 'range';
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
this.set({ hvac_mode: mode });
|
|
416
|
+
|
|
417
|
+
this?.log?.info && this.log.info('Set mode on "%s" to "%s"', this.deviceData.description, mode);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
getMode() {
|
|
422
|
+
let currentMode = null;
|
|
423
|
+
|
|
424
|
+
if (this.deviceData.hvac_mode.toUpperCase() === 'HEAT' || this.deviceData.hvac_mode.toUpperCase() === 'ECOHEAT') {
|
|
425
|
+
// heating mode, either eco or normal;
|
|
426
|
+
currentMode = this.hap.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
427
|
+
}
|
|
428
|
+
if (this.deviceData.hvac_mode.toUpperCase() === 'COOL' || this.deviceData.hvac_mode.toUpperCase() === 'ECOCOOL') {
|
|
429
|
+
// cooling mode, either eco or normal
|
|
430
|
+
currentMode = this.hap.Characteristic.TargetHeatingCoolingState.COOL;
|
|
431
|
+
}
|
|
432
|
+
if (this.deviceData.hvac_mode.toUpperCase() === 'RANGE' || this.deviceData.hvac_mode.toUpperCase() === 'ECORANGE') {
|
|
433
|
+
// range mode, either eco or normal
|
|
434
|
+
currentMode = this.hap.Characteristic.TargetHeatingCoolingState.AUTO;
|
|
435
|
+
}
|
|
436
|
+
if (this.deviceData.hvac_mode.toUpperCase() === 'OFF' || (this.deviceData.can_cool === false && this.deviceData.can_heat === false)) {
|
|
437
|
+
// off mode or no heating or cooling capability
|
|
438
|
+
currentMode = this.hap.Characteristic.TargetHeatingCoolingState.OFF;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return currentMode;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
setTemperature(characteristic, temperature) {
|
|
445
|
+
if (typeof characteristic === 'function' && typeof characteristic?.UUID === 'string') {
|
|
446
|
+
if (
|
|
447
|
+
characteristic.UUID === this.hap.Characteristic.TargetTemperature.UUID &&
|
|
448
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value !==
|
|
449
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO
|
|
450
|
+
) {
|
|
451
|
+
this.set({ target_temperature: temperature });
|
|
452
|
+
|
|
453
|
+
this?.log?.info &&
|
|
454
|
+
this.log.info(
|
|
455
|
+
'Set %s%s temperature on "%s" to "%s °C"',
|
|
456
|
+
this.deviceData.hvac_mode.toUpperCase().includes('ECO') ? 'eco mode ' : '',
|
|
457
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value ===
|
|
458
|
+
this.hap.Characteristic.TargetHeatingCoolingState.HEAT
|
|
459
|
+
? 'heating'
|
|
460
|
+
: 'cooling',
|
|
461
|
+
this.deviceData.description,
|
|
462
|
+
temperature,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
if (
|
|
466
|
+
characteristic.UUID === this.hap.Characteristic.HeatingThresholdTemperature.UUID &&
|
|
467
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value ===
|
|
468
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO
|
|
469
|
+
) {
|
|
470
|
+
this.set({ target_temperature_low: temperature });
|
|
471
|
+
|
|
472
|
+
this?.log?.info &&
|
|
473
|
+
this.log.info(
|
|
474
|
+
'Set %sheating temperature on "%s" to "%s °C"',
|
|
475
|
+
this.deviceData.hvac_mode.toUpperCase().includes('ECO') ? 'eco mode ' : '',
|
|
476
|
+
this.deviceData.description,
|
|
477
|
+
temperature,
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
if (
|
|
481
|
+
characteristic.UUID === this.hap.Characteristic.CoolingThresholdTemperature.UUID &&
|
|
482
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value ===
|
|
483
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO
|
|
484
|
+
) {
|
|
485
|
+
this.set({ target_temperature_high: temperature });
|
|
486
|
+
|
|
487
|
+
this?.log?.info &&
|
|
488
|
+
this.log.info(
|
|
489
|
+
'Set %scooling temperature on "%s" to "%s °C"',
|
|
490
|
+
this.deviceData.hvac_mode.toUpperCase().includes('ECO') ? 'eco mode ' : '',
|
|
491
|
+
this.deviceData.description,
|
|
492
|
+
temperature,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
this.thermostatService.updateCharacteristic(characteristic, temperature); // Update HomeKit with value
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
getTemperature(characteristic) {
|
|
501
|
+
let currentTemperature = null;
|
|
502
|
+
|
|
503
|
+
if (typeof characteristic === 'function' && typeof characteristic?.UUID === 'string') {
|
|
504
|
+
if (characteristic.UUID === this.hap.Characteristic.TargetTemperature.UUID) {
|
|
505
|
+
currentTemperature = this.deviceData.target_temperature;
|
|
506
|
+
}
|
|
507
|
+
if (characteristic.UUID === this.hap.Characteristic.HeatingThresholdTemperature.UUID) {
|
|
508
|
+
currentTemperature = this.deviceData.target_temperature_low;
|
|
509
|
+
}
|
|
510
|
+
if (characteristic.UUID === this.hap.Characteristic.CoolingThresholdTemperature.UUID) {
|
|
511
|
+
currentTemperature = this.deviceData.target_temperature_high;
|
|
512
|
+
}
|
|
513
|
+
if (currentTemperature < MIN_TEMPERATURE) {
|
|
514
|
+
currentTemperature = MIN_TEMPERATURE;
|
|
515
|
+
}
|
|
516
|
+
if (currentTemperature > MAX_TEMPERATURE) {
|
|
517
|
+
currentTemperature = MAX_TEMPERATURE;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return currentTemperature;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
setChildlock(pin, value) {
|
|
525
|
+
// TODO - pincode setting when turning on.
|
|
526
|
+
// On REST API, writes to device.xxxxxxxx.temperature_lock_pin_hash. How is the hash calculated???
|
|
527
|
+
// Do we set temperature range limits when child lock on??
|
|
528
|
+
|
|
529
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.LockPhysicalControls, value); // Update HomeKit with value
|
|
530
|
+
if (value === this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED) {
|
|
531
|
+
// Set pin hash????
|
|
532
|
+
}
|
|
533
|
+
if (value === this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED) {
|
|
534
|
+
// Clear pin hash????
|
|
535
|
+
}
|
|
536
|
+
this.set({ temperature_lock: value === this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED ? true : false });
|
|
537
|
+
|
|
538
|
+
this?.log?.info &&
|
|
539
|
+
this.log.info(
|
|
540
|
+
'Setting Childlock on "%s" to "%s"',
|
|
541
|
+
this.deviceData.description,
|
|
542
|
+
value === this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED ? 'Enabled' : 'Disabled',
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
updateServices(deviceData) {
|
|
547
|
+
if (
|
|
548
|
+
typeof deviceData !== 'object' ||
|
|
549
|
+
this.thermostatService === undefined ||
|
|
550
|
+
this.batteryService === undefined ||
|
|
551
|
+
this.occupancyService === undefined
|
|
552
|
+
) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
let historyEntry = {};
|
|
557
|
+
|
|
558
|
+
this.thermostatService.updateCharacteristic(
|
|
559
|
+
this.hap.Characteristic.TemperatureDisplayUnits,
|
|
560
|
+
deviceData.temperature_scale.toUpperCase() === 'C'
|
|
561
|
+
? this.hap.Characteristic.TemperatureDisplayUnits.CELSIUS
|
|
562
|
+
: this.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT,
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, deviceData.current_temperature);
|
|
566
|
+
|
|
567
|
+
this.thermostatService.updateCharacteristic(
|
|
568
|
+
this.hap.Characteristic.StatusFault,
|
|
569
|
+
deviceData.online === true && deviceData.removed_from_base === false
|
|
570
|
+
? this.hap.Characteristic.StatusFault.NO_FAULT
|
|
571
|
+
: this.hap.Characteristic.StatusFault.GENERAL_FAULT,
|
|
572
|
+
); // If Nest isn't online or removed from base, report in HomeKit
|
|
573
|
+
|
|
574
|
+
this.thermostatService.updateCharacteristic(
|
|
575
|
+
this.hap.Characteristic.LockPhysicalControls,
|
|
576
|
+
deviceData.temperature_lock === true
|
|
577
|
+
? this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED
|
|
578
|
+
: this.hap.Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED,
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
// Update air filter sttaus if has been added
|
|
582
|
+
if (this.thermostatService.testCharacteristic(this.hap.Characteristic.FilterChangeIndication) === true) {
|
|
583
|
+
this.thermostatService.updateCharacteristic(
|
|
584
|
+
this.hap.Characteristic.FilterChangeIndication,
|
|
585
|
+
deviceData.has_air_filter && deviceData.filter_replacement_needed === true
|
|
586
|
+
? this.hap.Characteristic.FilterChangeIndication.CHANGE_FILTER
|
|
587
|
+
: this.hap.Characteristic.FilterChangeIndication.FILTER_OK,
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Using a temperature sensor as active temperature?
|
|
592
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.StatusActive, deviceData.active_rcs_sensor === '');
|
|
593
|
+
|
|
594
|
+
// Update battery status
|
|
595
|
+
this.batteryService.updateCharacteristic(this.hap.Characteristic.BatteryLevel, deviceData.battery_level);
|
|
596
|
+
this.batteryService.updateCharacteristic(
|
|
597
|
+
this.hap.Characteristic.StatusLowBattery,
|
|
598
|
+
deviceData.battery_level > LOWBATTERYLEVEL
|
|
599
|
+
? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL
|
|
600
|
+
: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW,
|
|
601
|
+
);
|
|
602
|
+
this.batteryService.updateCharacteristic(
|
|
603
|
+
this.hap.Characteristic.ChargingState,
|
|
604
|
+
(deviceData.battery_level > this.deviceData.battery_level && this.deviceData.battery_level !== 0 ? true : false)
|
|
605
|
+
? this.hap.Characteristic.ChargingState.CHARGING
|
|
606
|
+
: this.hap.Characteristic.ChargingState.NOT_CHARGING,
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
// Update for away/home status. Away = no occupancy detected, Home = Occupancy Detected
|
|
610
|
+
this.occupancyService.updateCharacteristic(
|
|
611
|
+
this.hap.Characteristic.OccupancyDetected,
|
|
612
|
+
deviceData.occupancy === true
|
|
613
|
+
? this.hap.Characteristic.OccupancyDetected.OCCUPANCY_DETECTED
|
|
614
|
+
: this.hap.Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED,
|
|
615
|
+
);
|
|
616
|
+
this.occupancyService.updateCharacteristic(
|
|
617
|
+
this.hap.Characteristic.StatusFault,
|
|
618
|
+
deviceData.online === true && deviceData.removed_from_base === false
|
|
619
|
+
? this.hap.Characteristic.StatusFault.NO_FAULT
|
|
620
|
+
: this.hap.Characteristic.StatusFault.GENERAL_FAULT,
|
|
621
|
+
); // If Nest isn't online or removed from base, report in HomeKit
|
|
622
|
+
|
|
623
|
+
// Update seperate humidity sensor if configured todo so
|
|
624
|
+
if (this.humidityService !== undefined) {
|
|
625
|
+
this.humidityService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, deviceData.current_humidity);
|
|
626
|
+
this.humidityService.updateCharacteristic(
|
|
627
|
+
this.hap.Characteristic.StatusFault,
|
|
628
|
+
deviceData.online === true && deviceData.removed_from_base === false
|
|
629
|
+
? this.hap.Characteristic.StatusFault.NO_FAULT
|
|
630
|
+
: this.hap.Characteristic.StatusFault.GENERAL_FAULT,
|
|
631
|
+
); // If Nest isn't online or removed from base, report in HomeKit
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Update humity on thermostat
|
|
635
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, deviceData.current_humidity);
|
|
636
|
+
|
|
637
|
+
// Check for fan setup change on thermostat
|
|
638
|
+
if (deviceData.has_fan !== this.deviceData.has_fan) {
|
|
639
|
+
if (deviceData.has_fan === true && this.deviceData.has_fan === false && this.fanService === undefined) {
|
|
640
|
+
// Fan has been added
|
|
641
|
+
this.fanService = this.accessory.addService(this.hap.Service.Fanv2, '', 1);
|
|
642
|
+
this.thermostatService.addLinkedService(this.fanService);
|
|
643
|
+
|
|
644
|
+
this.fanService.getCharacteristic(this.hap.Characteristic.Active).onSet((value) => {
|
|
645
|
+
this.setFan(value);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
this.fanService.getCharacteristic(this.hap.Characteristic.Active).onGet(() => {
|
|
649
|
+
return this.deviceData.fan_state === true ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
if (deviceData.has_fan === false && this.deviceData.has_fan === true && this.fanService !== undefined) {
|
|
653
|
+
// Fan has been removed
|
|
654
|
+
this.accessory.removeService(this.fanService);
|
|
655
|
+
this.fanService = undefined;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
this?.log?.info &&
|
|
659
|
+
this.log.info(
|
|
660
|
+
'Fan setup on thermostat "%s" has changed. Fan was',
|
|
661
|
+
this.deviceData.description,
|
|
662
|
+
this.fanService === undefined ? 'removed' : 'added',
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Check for dehumidifer setup change on thermostat
|
|
667
|
+
if (deviceData.has_dehumidifier !== this.deviceData.has_dehumidifier) {
|
|
668
|
+
if (deviceData.has_dehumidifier === true && this.deviceData.has_dehumidifier === false && this.dehumidifierService === undefined) {
|
|
669
|
+
// Dehumidifier has been added
|
|
670
|
+
this.dehumidifierService = this.accessory.addService(this.hap.Service.HumidifierDehumidifier, '', 1);
|
|
671
|
+
this.thermostatService.addLinkedService(this.dehumidifierService);
|
|
672
|
+
|
|
673
|
+
this.dehumidifierService.getCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState).setProps({
|
|
674
|
+
validValues: [this.hap.Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER],
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
this.dehumidifierService.getCharacteristic(this.hap.Characteristic.Active).onSet((value) => {
|
|
678
|
+
this.setDehumidifier(value);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
this.dehumidifierService.getCharacteristic(this.hap.Characteristic.Active).onGet(() => {
|
|
682
|
+
return this.deviceData.dehumidifier_state === true
|
|
683
|
+
? this.hap.Characteristic.Active.ACTIVE
|
|
684
|
+
: this.hap.Characteristic.Active.INACTIVE;
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
if (deviceData.has_dehumidifier === false && this.deviceData.has_dehumidifier === true && this.dehumidifierService !== undefined) {
|
|
688
|
+
// Dehumidifer has been removed
|
|
689
|
+
this.accessory.removeService(this.dehumidifierService);
|
|
690
|
+
this.dehumidifierService = undefined;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
this?.log?.info &&
|
|
694
|
+
this.log.info(
|
|
695
|
+
'Dehumidifier setup on thermostat "%s" has changed. Dehumidifier was',
|
|
696
|
+
this.deviceData.description,
|
|
697
|
+
this.dehumidifierService === undefined ? 'removed' : 'added',
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (deviceData.can_cool !== this.deviceData.can_cool || deviceData.can_heat !== this.deviceData.can_heat) {
|
|
702
|
+
// Heating and/cooling setup has changed on thermostat
|
|
703
|
+
|
|
704
|
+
// Limit prop ranges
|
|
705
|
+
if (deviceData.can_cool === false && deviceData.can_heat === true) {
|
|
706
|
+
// Can heat only, so set values allowed for mode off/heat
|
|
707
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
708
|
+
validValues: [this.hap.Characteristic.TargetHeatingCoolingState.OFF, this.hap.Characteristic.TargetHeatingCoolingState.HEAT],
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
if (deviceData.can_cool === true && deviceData.can_heat === false) {
|
|
712
|
+
// Can cool only
|
|
713
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
714
|
+
validValues: [this.hap.Characteristic.TargetHeatingCoolingState.OFF, this.hap.Characteristic.TargetHeatingCoolingState.COOL],
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
if (deviceData.can_cool === true && deviceData.can_heat === true) {
|
|
718
|
+
// heat and cool
|
|
719
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
720
|
+
validValues: [
|
|
721
|
+
this.hap.Characteristic.TargetHeatingCoolingState.OFF,
|
|
722
|
+
this.hap.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
723
|
+
this.hap.Characteristic.TargetHeatingCoolingState.COOL,
|
|
724
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO,
|
|
725
|
+
],
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
if (deviceData.can_cool === false && deviceData.can_heat === false) {
|
|
729
|
+
// only off mode
|
|
730
|
+
this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).setProps({
|
|
731
|
+
validValues: [this.hap.Characteristic.TargetHeatingCoolingState.OFF],
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
this?.log?.info && this.log.info('Heating/cooling setup on thermostat on "%s" has changed', this.deviceData.description);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Update current mode temperatures
|
|
739
|
+
if (
|
|
740
|
+
deviceData.can_heat === true &&
|
|
741
|
+
(deviceData.hvac_mode.toUpperCase() === 'HEAT' || deviceData.hvac_mode.toUpperCase() === 'ECOHEAT')
|
|
742
|
+
) {
|
|
743
|
+
// heating mode, either eco or normal
|
|
744
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, deviceData.target_temperature_low);
|
|
745
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, deviceData.target_temperature_low);
|
|
746
|
+
this.thermostatService.updateCharacteristic(
|
|
747
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
748
|
+
this.hap.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
749
|
+
);
|
|
750
|
+
historyEntry.target = { low: 0, high: deviceData.target_temperature_low }; // single target temperature for heating limit
|
|
751
|
+
}
|
|
752
|
+
if (
|
|
753
|
+
deviceData.can_cool === true &&
|
|
754
|
+
(deviceData.hvac_mode.toUpperCase() === 'COOL' || deviceData.hvac_mode.toUpperCase() === 'ECOCOOL')
|
|
755
|
+
) {
|
|
756
|
+
// cooling mode, either eco or normal
|
|
757
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, deviceData.target_temperature_high);
|
|
758
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, deviceData.target_temperature_high);
|
|
759
|
+
this.thermostatService.updateCharacteristic(
|
|
760
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
761
|
+
this.hap.Characteristic.TargetHeatingCoolingState.COOL,
|
|
762
|
+
);
|
|
763
|
+
historyEntry.target = { low: deviceData.target_temperature_high, high: 0 }; // single target temperature for cooling limit
|
|
764
|
+
}
|
|
765
|
+
if (
|
|
766
|
+
deviceData.can_cool === true &&
|
|
767
|
+
deviceData.can_heat === true &&
|
|
768
|
+
(deviceData.hvac_mode.toUpperCase() === 'RANGE' || deviceData.hvac_mode.toUpperCase() === 'ECORANGE')
|
|
769
|
+
) {
|
|
770
|
+
// range mode, either eco or normal
|
|
771
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.HeatingThresholdTemperature, deviceData.target_temperature_low);
|
|
772
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.CoolingThresholdTemperature, deviceData.target_temperature_high);
|
|
773
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, deviceData.target_temperature);
|
|
774
|
+
this.thermostatService.updateCharacteristic(
|
|
775
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
776
|
+
this.hap.Characteristic.TargetHeatingCoolingState.AUTO,
|
|
777
|
+
);
|
|
778
|
+
historyEntry.target = { low: deviceData.target_temperature_low, high: deviceData.target_temperature_high };
|
|
779
|
+
}
|
|
780
|
+
if (deviceData.can_cool === false && deviceData.can_heat === false && deviceData.hvac_mode.toUpperCase() === 'OFF') {
|
|
781
|
+
// off mode.
|
|
782
|
+
this.thermostatService.updateCharacteristic(this.hap.Characteristic.TargetTemperature, deviceData.target_temperature);
|
|
783
|
+
this.thermostatService.updateCharacteristic(
|
|
784
|
+
this.hap.Characteristic.TargetHeatingCoolingState,
|
|
785
|
+
this.hap.Characteristic.TargetHeatingCoolingState.OFF,
|
|
786
|
+
);
|
|
787
|
+
historyEntry.target = { low: 0, high: 0 }; // thermostat off, so no target temperatures
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Update current state
|
|
791
|
+
if (deviceData.hvac_state.toUpperCase() === 'HEATING') {
|
|
792
|
+
if (this.deviceData.hvac_state.toUpperCase() === 'COOLING' && this.externalCool !== undefined) {
|
|
793
|
+
// Switched to heating mode and external cooling external code was being used, so stop cooling via cooling external code
|
|
794
|
+
if (typeof this.externalCool.off === 'function') {
|
|
795
|
+
this.externalCool.off();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (
|
|
799
|
+
(this.deviceData.hvac_state.toUpperCase() !== 'HEATING' ||
|
|
800
|
+
deviceData.target_temperature_low !== this.deviceData.target_temperature_low) &&
|
|
801
|
+
this.externalHeat !== undefined
|
|
802
|
+
) {
|
|
803
|
+
// Switched to heating mode and external heating external code is being used
|
|
804
|
+
// Start heating via heating external code OR adjust heating target temperature due to change
|
|
805
|
+
if (typeof this.externalHeat.heat === 'function') {
|
|
806
|
+
this.externalHeat.heat(deviceData.deviceData.target_temperature_low);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
this.thermostatService.updateCharacteristic(
|
|
810
|
+
this.hap.Characteristic.CurrentHeatingCoolingState,
|
|
811
|
+
this.hap.Characteristic.CurrentHeatingCoolingState.HEAT,
|
|
812
|
+
);
|
|
813
|
+
historyEntry.status = 2; // heating
|
|
814
|
+
}
|
|
815
|
+
if (deviceData.hvac_state.toUpperCase() === 'COOLING') {
|
|
816
|
+
if (this.deviceData.hvac_state.toUpperCase() === 'HEATING' && this.externalHeat !== undefined) {
|
|
817
|
+
// Switched to cooling mode and external heating external code was being used, so stop heating via heating external code
|
|
818
|
+
if (typeof this.externalHeat.off === 'function') {
|
|
819
|
+
this.externalHeat.off();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (
|
|
823
|
+
(this.deviceData.hvac_state.toUpperCase() !== 'COOLING' ||
|
|
824
|
+
deviceData.target_temperature_high !== this.deviceData.target_temperature_high) &&
|
|
825
|
+
this.externalCool !== undefined
|
|
826
|
+
) {
|
|
827
|
+
// Switched to cooling mode and external cooling external code is being used
|
|
828
|
+
// Start cooling via cooling external code OR adjust cooling target temperature due to change
|
|
829
|
+
if (typeof this.externalCool.cool === 'function') {
|
|
830
|
+
this.externalCool.cool(deviceData.target_temperature_high);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
this.thermostatService.updateCharacteristic(
|
|
834
|
+
this.hap.Characteristic.CurrentHeatingCoolingState,
|
|
835
|
+
this.hap.Characteristic.CurrentHeatingCoolingState.COOL,
|
|
836
|
+
);
|
|
837
|
+
historyEntry.status = 3; // cooling
|
|
838
|
+
}
|
|
839
|
+
if (deviceData.hvac_state.toUpperCase() === 'OFF') {
|
|
840
|
+
if (this.deviceData.hvac_state.toUpperCase() === 'COOLING' && this.externalCool !== undefined) {
|
|
841
|
+
// Switched to off mode and external cooling external code was being used, so stop cooling via cooling external code
|
|
842
|
+
if (typeof this.externalCool.off === 'function') {
|
|
843
|
+
this.externalCool.off();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (this.deviceData.hvac_state.toUpperCase() === 'HEATING' && this.externalHeat !== undefined) {
|
|
847
|
+
// Switched to off mode and external heating external code was being used, so stop heating via heating external code
|
|
848
|
+
if (typeof this.externalHeat.heat === 'function') {
|
|
849
|
+
this.externalHeat.off();
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
this.thermostatService.updateCharacteristic(
|
|
853
|
+
this.hap.Characteristic.CurrentHeatingCoolingState,
|
|
854
|
+
this.hap.Characteristic.CurrentHeatingCoolingState.OFF,
|
|
855
|
+
);
|
|
856
|
+
historyEntry.status = 0; // off
|
|
857
|
+
}
|
|
858
|
+
if (this.fanService !== undefined) {
|
|
859
|
+
if (this.deviceData.fan_state === false && deviceData.fan_state === true && this.externalFan !== undefined) {
|
|
860
|
+
// Fan mode was switched on and external fan external code is being used, so start fan via fan external code
|
|
861
|
+
if (typeof this.externalFan.fan === 'function') {
|
|
862
|
+
this.externalFan.fan(0); // Fan speed will be auto
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (this.deviceData.fan_state === true && deviceData.fan_state === false && this.externalFan !== undefined) {
|
|
866
|
+
// Fan mode was switched off and external fan external code was being used, so stop fan via fan external code
|
|
867
|
+
if (typeof this.externalFan.off === 'function') {
|
|
868
|
+
this.externalFan.off();
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
//this.fanService.updateCharacteristic(this.hap.Characteristic.RotationSpeed,
|
|
872
|
+
// (deviceData.fan_state === true ? (deviceData.fan_current_speed / deviceData.fan_max_speed) * 100 : 0));
|
|
873
|
+
this.fanService.updateCharacteristic(
|
|
874
|
+
this.hap.Characteristic.Active,
|
|
875
|
+
deviceData.fan_state === true ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE,
|
|
876
|
+
); // fan status on or off
|
|
877
|
+
historyEntry.status = 1; // fan
|
|
878
|
+
}
|
|
879
|
+
if (this.dehumidifierService !== undefined) {
|
|
880
|
+
if (
|
|
881
|
+
this.deviceData.dehumidifier_state === false &&
|
|
882
|
+
deviceData.dehumidifier_state === true &&
|
|
883
|
+
this.externalDehumidifier !== undefined
|
|
884
|
+
) {
|
|
885
|
+
// Dehumidifier mode was switched on and external dehumidifier external code is being used
|
|
886
|
+
// Start dehumidifier via dehumidifiern external code
|
|
887
|
+
if (typeof this.externalDehumidifier.dehumififier === 'function') {
|
|
888
|
+
this.externalDehumidifier.dehumififier(0);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (
|
|
892
|
+
this.deviceData.dehumidifier_state === true &&
|
|
893
|
+
deviceData.dehumidifier_state === false &&
|
|
894
|
+
this.externalDehumidifier !== undefined
|
|
895
|
+
) {
|
|
896
|
+
// Dehumidifier mode was switched off and external dehumidifier external code was being used
|
|
897
|
+
// Stop dehumidifier via dehumidifier external code
|
|
898
|
+
if (typeof this.externalDehumidifier.off === 'function') {
|
|
899
|
+
this.externalDehumidifier.off();
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
this.dehumidifierService.updateCharacteristic(
|
|
904
|
+
this.hap.Characteristic.Active,
|
|
905
|
+
deviceData.dehumidifier_state === true ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE,
|
|
906
|
+
); // dehumidifier status on or off
|
|
907
|
+
historyEntry.status = 4; // dehumidifier
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Log thermostat metrics to history only if changed to previous recording
|
|
911
|
+
if (this.thermostatService !== undefined && typeof this.historyService?.addHistory === 'function') {
|
|
912
|
+
let tempEntry = this.historyService.lastHistory(this.thermostatService);
|
|
913
|
+
if (
|
|
914
|
+
tempEntry === null ||
|
|
915
|
+
(typeof tempEntry === 'object' && tempEntry.status !== historyEntry.status) ||
|
|
916
|
+
tempEntry.temperature !== deviceData.current_temperature ||
|
|
917
|
+
JSON.stringify(tempEntry.target) !== JSON.stringify(historyEntry.target) ||
|
|
918
|
+
tempEntry.humidity !== deviceData.current_humidity
|
|
919
|
+
) {
|
|
920
|
+
this.historyService.addHistory(this.thermostatService, {
|
|
921
|
+
time: Math.floor(Date.now() / 1000),
|
|
922
|
+
status: historyEntry.status,
|
|
923
|
+
temperature: deviceData.current_temperature,
|
|
924
|
+
target: historyEntry.target,
|
|
925
|
+
humidity: deviceData.current_humidity,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// Notify Eve App of device status changes if linked
|
|
931
|
+
if (
|
|
932
|
+
this.deviceData.eveHistory === true &&
|
|
933
|
+
this.thermostatService !== undefined &&
|
|
934
|
+
typeof this.historyService?.updateEveHome === 'function'
|
|
935
|
+
) {
|
|
936
|
+
// Update our internal data with properties Eve will need to process
|
|
937
|
+
this.deviceData.online = deviceData.online;
|
|
938
|
+
this.deviceData.removed_from_base = deviceData.removed_from_base;
|
|
939
|
+
this.deviceData.vacation_mode = deviceData.vacation_mode;
|
|
940
|
+
this.deviceData.hvac_mode = deviceData.hvac_mode;
|
|
941
|
+
this.deviceData.schedules = deviceData.schedules;
|
|
942
|
+
this.deviceData.schedule_mode = deviceData.schedule_mode;
|
|
943
|
+
this.historyService.updateEveHome(this.thermostatService, this.#EveHomeGetcommand.bind(this));
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
#EveHomeGetcommand(EveHomeGetData) {
|
|
948
|
+
// Pass back extra data for Eve Thermo onGet() to process command
|
|
949
|
+
// Data will already be an object, our only job is to add/modify it
|
|
950
|
+
if (typeof EveHomeGetData === 'object') {
|
|
951
|
+
EveHomeGetData.enableschedule = this.deviceData.schedule_mode === 'heat'; // Schedules on/off
|
|
952
|
+
EveHomeGetData.attached = this.deviceData.online === true && this.deviceData.removed_from_base === false;
|
|
953
|
+
EveHomeGetData.vacation = this.deviceData.vacation_mode === true; // Vaction mode on/off
|
|
954
|
+
EveHomeGetData.vacationtemp = this.deviceData.vacation_mode === true ? EveHomeGetData.vacationtemp : null;
|
|
955
|
+
EveHomeGetData.programs = []; // No programs yet, we'll process this below
|
|
956
|
+
if (this.deviceData.schedule_mode.toUpperCase() === 'HEAT' || this.deviceData.schedule_mode.toUpperCase() === 'RANGE') {
|
|
957
|
+
const DAYSOFWEEK = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
|
|
958
|
+
|
|
959
|
+
Object.entries(this.deviceData.schedules).forEach(([day, schedules]) => {
|
|
960
|
+
let tempSchedule = [];
|
|
961
|
+
let tempTemperatures = [];
|
|
962
|
+
Object.values(schedules)
|
|
963
|
+
.reverse()
|
|
964
|
+
.forEach((schedule) => {
|
|
965
|
+
if (schedule.entry_type === 'setpoint' && (schedule.type === 'HEAT' || schedule.type === 'RANGE')) {
|
|
966
|
+
tempSchedule.push({
|
|
967
|
+
start: schedule.time,
|
|
968
|
+
duration: 0,
|
|
969
|
+
temperature: typeof schedule['temp-min'] === 'number' ? schedule['temp-min'] : schedule.temp,
|
|
970
|
+
});
|
|
971
|
+
tempTemperatures.push(typeof schedule['temp-min'] === 'number' ? schedule['temp-min'] : schedule.temp);
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
// Sort the schedule array by start time
|
|
976
|
+
tempSchedule = tempSchedule.sort((a, b) => {
|
|
977
|
+
if (a.start < b.start) {
|
|
978
|
+
return -1;
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
let ecoTemp = tempTemperatures.length === 0 ? 0 : Math.min(...tempTemperatures);
|
|
983
|
+
let comfortTemp = tempTemperatures.length === 0 ? 0 : Math.max(...tempTemperatures);
|
|
984
|
+
let program = {};
|
|
985
|
+
program.id = parseInt(day) + 1;
|
|
986
|
+
program.days = DAYSOFWEEK[day];
|
|
987
|
+
program.schedule = [];
|
|
988
|
+
let lastTime = 86400; // seconds in a day
|
|
989
|
+
Object.values(tempSchedule)
|
|
990
|
+
.reverse()
|
|
991
|
+
.forEach((schedule) => {
|
|
992
|
+
if (schedule.temperature === comfortTemp) {
|
|
993
|
+
// We only want to add the schedule time if its using the 'max' temperature
|
|
994
|
+
program.schedule.push({
|
|
995
|
+
start: schedule.start,
|
|
996
|
+
duration: lastTime - schedule.start,
|
|
997
|
+
ecotemp: ecoTemp,
|
|
998
|
+
comforttemp: comfortTemp,
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
lastTime = schedule.start;
|
|
1002
|
+
});
|
|
1003
|
+
EveHomeGetData.programs.push(program);
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return EveHomeGetData;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
#EveHomeSetcommand(EveHomeSetData) {
|
|
1011
|
+
if (typeof EveHomeSetData !== 'object') {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (typeof EveHomeSetData?.vacation === 'boolean') {
|
|
1016
|
+
this.deviceData.vacation_mode = EveHomeSetData.vacation.status;
|
|
1017
|
+
this.set({ vacation_mode: this.deviceData.vacation_mode });
|
|
1018
|
+
}
|
|
1019
|
+
if (typeof EveHomeSetData?.programs === 'object') {
|
|
1020
|
+
/* EveHomeSetData.programs.forEach((day) => {
|
|
1021
|
+
// Convert into Nest thermostat schedule format and set. Need to work this out
|
|
1022
|
+
//this.set({'days' : {6 : { 'temp' : 17 , 'time' : 13400, touched_at: Date.now()}} });
|
|
1023
|
+
}); */
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|