@homebridge-plugins/homebridge-ewelink 12.3.3

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 (113) hide show
  1. package/CHANGELOG.md +1363 -0
  2. package/LICENSE +21 -0
  3. package/README.md +68 -0
  4. package/config.schema.json +1757 -0
  5. package/eslint.config.js +15 -0
  6. package/lib/connection/api.js +508 -0
  7. package/lib/connection/http.js +304 -0
  8. package/lib/connection/lan.js +318 -0
  9. package/lib/connection/ws.js +414 -0
  10. package/lib/device/curtain.js +324 -0
  11. package/lib/device/diffuser.js +473 -0
  12. package/lib/device/fan.js +349 -0
  13. package/lib/device/group.js +106 -0
  14. package/lib/device/humidifier.js +199 -0
  15. package/lib/device/index.js +61 -0
  16. package/lib/device/light-cct.js +333 -0
  17. package/lib/device/light-dimmer.js +257 -0
  18. package/lib/device/light-rgb-cct.js +809 -0
  19. package/lib/device/light-rgb.js +272 -0
  20. package/lib/device/motor.js +363 -0
  21. package/lib/device/outlet-multi.js +417 -0
  22. package/lib/device/outlet-single-inched.js +409 -0
  23. package/lib/device/outlet-single.js +384 -0
  24. package/lib/device/panel.js +382 -0
  25. package/lib/device/rf-bridge.js +321 -0
  26. package/lib/device/rf-button.js +125 -0
  27. package/lib/device/rf-sensor.js +173 -0
  28. package/lib/device/sensor-ambient.js +311 -0
  29. package/lib/device/sensor-contact.js +140 -0
  30. package/lib/device/sensor-temp-humi.js +182 -0
  31. package/lib/device/simulation/blind.js +253 -0
  32. package/lib/device/simulation/cooler.js +312 -0
  33. package/lib/device/simulation/door.js +253 -0
  34. package/lib/device/simulation/doorbell.js +190 -0
  35. package/lib/device/simulation/garage-eachen.js +235 -0
  36. package/lib/device/simulation/garage-four.js +176 -0
  37. package/lib/device/simulation/garage-od-switch.js +36 -0
  38. package/lib/device/simulation/garage-one.js +395 -0
  39. package/lib/device/simulation/garage-two.js +394 -0
  40. package/lib/device/simulation/gate-one.js +327 -0
  41. package/lib/device/simulation/heater.js +389 -0
  42. package/lib/device/simulation/index.js +75 -0
  43. package/lib/device/simulation/light-fan.js +258 -0
  44. package/lib/device/simulation/lock-eachen.js +163 -0
  45. package/lib/device/simulation/lock-one.js +366 -0
  46. package/lib/device/simulation/p-button.js +223 -0
  47. package/lib/device/simulation/purifier.js +289 -0
  48. package/lib/device/simulation/rf-blind.js +192 -0
  49. package/lib/device/simulation/rf-door.js +180 -0
  50. package/lib/device/simulation/rf-window.js +180 -0
  51. package/lib/device/simulation/sensor-hidden.js +105 -0
  52. package/lib/device/simulation/sensor-leak.js +123 -0
  53. package/lib/device/simulation/sensor-visible.js +231 -0
  54. package/lib/device/simulation/sensor.js +300 -0
  55. package/lib/device/simulation/switch-valve.js +351 -0
  56. package/lib/device/simulation/tap-one.js +283 -0
  57. package/lib/device/simulation/tap-two.js +266 -0
  58. package/lib/device/simulation/th-cooler.js +443 -0
  59. package/lib/device/simulation/th-dehumidifier.js +424 -0
  60. package/lib/device/simulation/th-heater.js +449 -0
  61. package/lib/device/simulation/th-humidifier.js +424 -0
  62. package/lib/device/simulation/th-thermostat.js +611 -0
  63. package/lib/device/simulation/tv.js +252 -0
  64. package/lib/device/simulation/valve-four.js +232 -0
  65. package/lib/device/simulation/valve-one.js +345 -0
  66. package/lib/device/simulation/valve-two.js +340 -0
  67. package/lib/device/simulation/window.js +253 -0
  68. package/lib/device/switch-man.js +106 -0
  69. package/lib/device/switch-mate.js +82 -0
  70. package/lib/device/switch-multi.js +411 -0
  71. package/lib/device/switch-single-inched.js +332 -0
  72. package/lib/device/switch-single.js +307 -0
  73. package/lib/device/template.js +43 -0
  74. package/lib/device/thermostat.js +286 -0
  75. package/lib/device/zigbee/index.js +27 -0
  76. package/lib/device/zigbee/light-cct.js +263 -0
  77. package/lib/device/zigbee/light-dimmer.js +148 -0
  78. package/lib/device/zigbee/light-rgb-cct.js +400 -0
  79. package/lib/device/zigbee/motor.js +301 -0
  80. package/lib/device/zigbee/sensor-ambient.js +177 -0
  81. package/lib/device/zigbee/sensor-contact.js +137 -0
  82. package/lib/device/zigbee/sensor-leak.js +119 -0
  83. package/lib/device/zigbee/sensor-motion.js +119 -0
  84. package/lib/device/zigbee/sensor-occupancy.js +119 -0
  85. package/lib/device/zigbee/sensor-smoke.js +96 -0
  86. package/lib/device/zigbee/switch-stateless.js +104 -0
  87. package/lib/device/zigbee/thermostat.js +323 -0
  88. package/lib/fakegato/LICENSE +21 -0
  89. package/lib/fakegato/fakegato-history.js +813 -0
  90. package/lib/fakegato/fakegato-storage.js +108 -0
  91. package/lib/fakegato/fakegato-timer.js +125 -0
  92. package/lib/fakegato/uuid.js +27 -0
  93. package/lib/homebridge-ui/public/index.html +365 -0
  94. package/lib/homebridge-ui/server.js +46 -0
  95. package/lib/index.js +7 -0
  96. package/lib/node-dns-sd/LICENSE +21 -0
  97. package/lib/node-dns-sd/README.md +613 -0
  98. package/lib/node-dns-sd/lib/dns-sd-classes.json +6 -0
  99. package/lib/node-dns-sd/lib/dns-sd-composer.js +83 -0
  100. package/lib/node-dns-sd/lib/dns-sd-parser.js +344 -0
  101. package/lib/node-dns-sd/lib/dns-sd-types.json +52 -0
  102. package/lib/node-dns-sd/lib/dns-sd.js +580 -0
  103. package/lib/platform.js +2156 -0
  104. package/lib/utils/colour.js +552 -0
  105. package/lib/utils/constants.js +589 -0
  106. package/lib/utils/custom-chars.js +33 -0
  107. package/lib/utils/eve-chars.js +130 -0
  108. package/lib/utils/functions.js +42 -0
  109. package/lib/utils/lang-en.js +160 -0
  110. package/lib/utils/lang-fr.js +163 -0
  111. package/lib/utils/lang-th.js +160 -0
  112. package/lib/utils/lang.js +5 -0
  113. package/package.json +75 -0
@@ -0,0 +1,611 @@
1
+ import platformConsts from '../../utils/constants.js'
2
+ import { hasProperty, parseError } from '../../utils/functions.js'
3
+
4
+ export default class {
5
+ constructor(platform, accessory) {
6
+ // Set up variables from the platform
7
+ this.hapChar = platform.api.hap.Characteristic
8
+ this.hapErr = platform.api.hap.HapStatusError
9
+ this.hapServ = platform.api.hap.Service
10
+ this.lang = platform.lang
11
+ this.log = platform.log
12
+ this.platform = platform
13
+
14
+ // Set up variables from the accessory
15
+ this.name = accessory.displayName
16
+ this.accessory = accessory
17
+
18
+ // Initially set the online flag as true (to be then updated as false if necessary)
19
+ this.isOnline = true
20
+
21
+ // Set up custom variables for this device type
22
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
23
+ this.tempOffset = deviceConf.offset || platformConsts.defaultValues.offset
24
+ this.tempOffsetFactor = deviceConf.offsetFactor
25
+ this.humiOffset = deviceConf.humidityOffset
26
+ ? Number.parseInt(deviceConf.humidityOffset, 10)
27
+ : platformConsts.defaultValues.humidityOffset
28
+ this.humiOffsetFactor = deviceConf.humidityOffsetFactor
29
+ this.minTarget = deviceConf.minTarget || platformConsts.defaultValues.minTarget
30
+ this.maxTarget = deviceConf.maxTarget
31
+ ? Math.max(deviceConf.maxTarget, this.minTarget + 1)
32
+ : platformConsts.defaultValues.maxTarget
33
+ this.showHeatCool = deviceConf.showHeatCool
34
+
35
+ // Set the correct logging variables for this accessory
36
+ switch (deviceConf.overrideLogging) {
37
+ case 'standard':
38
+ this.enableLogging = true
39
+ this.enableDebugLogging = false
40
+ break
41
+ case 'debug':
42
+ this.enableLogging = true
43
+ this.enableDebugLogging = true
44
+ break
45
+ case 'disable':
46
+ this.enableLogging = false
47
+ this.enableDebugLogging = false
48
+ break
49
+ default:
50
+ this.enableLogging = !platform.config.disableDeviceLogging
51
+ this.enableDebugLogging = platform.config.debug
52
+ break
53
+ }
54
+
55
+ // If the accessory has a switch service then remove it
56
+ if (this.accessory.getService(this.hapServ.Switch)) {
57
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
58
+ }
59
+
60
+ // If the accessory has a temperature sensor service then remove it
61
+ if (this.accessory.getService(this.hapServ.TemperatureSensor)) {
62
+ this.accessory.removeService(this.accessory.getService(this.hapServ.TemperatureSensor))
63
+ }
64
+
65
+ // Set up the accessory with default target temp when added the first time
66
+ if (!hasProperty(this.accessory.context, 'cacheTarget')) {
67
+ this.accessory.context.cacheTarget = 20
68
+ }
69
+
70
+ // If the accessory has a heater service then remove it
71
+ if (this.accessory.getService(this.hapServ.HeaterCooler)) {
72
+ this.accessory.removeService(this.accessory.getService(this.hapServ.HeaterCooler))
73
+ }
74
+
75
+ // If the accessory has a humidifier service then remove it
76
+ if (this.accessory.getService(this.hapServ.HumidifierDehumidifier)) {
77
+ this.accessory.removeService(this.accessory.getService(this.hapServ.HumidifierDehumidifier))
78
+ }
79
+
80
+ // If the accessory has a humidity sensor service then remove it
81
+ if (this.accessory.getService(this.hapServ.HumiditySensor)) {
82
+ this.accessory.removeService(this.accessory.getService(this.hapServ.HumiditySensor))
83
+ }
84
+
85
+ // Add a context property to save the desired state of the showHeatCool option
86
+ if (!hasProperty(accessory.context, 'showHeatCool')) {
87
+ accessory.context.showHeatCool = false
88
+ }
89
+
90
+ // Reset the thermostat service if showHeatCool has been switched on from off
91
+ if (this.showHeatCool && !accessory.context.showHeatCool) {
92
+ accessory.context.showHeatCool = true
93
+ if (this.accessory.getService(this.hapServ.Thermostat)) {
94
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Thermostat))
95
+ }
96
+ }
97
+
98
+ // Reset the thermostat service if showHeatCool has been switched off from on
99
+ if (!this.showHeatCool && accessory.context.showHeatCool) {
100
+ accessory.context.showHeatCool = false
101
+ if (this.accessory.getService(this.hapServ.Thermostat)) {
102
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Thermostat))
103
+ }
104
+ }
105
+
106
+ // Add the thermostat service if it doesn't already exist
107
+ this.service = this.accessory.getService(this.hapServ.Thermostat)
108
+ || this.accessory.addService(this.hapServ.Thermostat)
109
+
110
+ // The DS18B20 sensor does not provide humidity readings
111
+ if (this.accessory.context.sensorType === 'DS18B20') {
112
+ if (this.service.testCharacteristic(this.hapChar.CurrentRelativeHumidity)) {
113
+ this.service.removeCharacteristic(
114
+ this.service.getCharacteristic(this.hapChar.CurrentRelativeHumidity),
115
+ )
116
+ }
117
+ } else {
118
+ if (!this.service.testCharacteristic(this.hapChar.CurrentRelativeHumidity)) {
119
+ this.service.addCharacteristic(this.hapChar.CurrentRelativeHumidity)
120
+ }
121
+ this.humiService = true
122
+ }
123
+
124
+ // Set custom properties of the current temperature characteristic
125
+ this.service.getCharacteristic(this.hapChar.CurrentTemperature).setProps({
126
+ minStep: 0.1,
127
+ })
128
+ this.cacheTemp = this.service.getCharacteristic(this.hapChar.CurrentTemperature).value
129
+ this.updateCache()
130
+
131
+ // Add options to the target state characteristic
132
+ this.service
133
+ .getCharacteristic(this.hapChar.TargetHeatingCoolingState)
134
+ .setProps({
135
+ validValues: this.showHeatCool ? [0, 1, 2] : [0, 3],
136
+ })
137
+ .onSet(async value => this.internalStateUpdate(value))
138
+
139
+ // Add the set handler to the target temperature characteristic
140
+ this.service
141
+ .getCharacteristic(this.hapChar.TargetTemperature)
142
+ .updateValue(this.accessory.context.cacheTarget)
143
+ .setProps({
144
+ minValue: this.minTarget,
145
+ maxValue: this.maxTarget,
146
+ minStep: 0.5,
147
+ })
148
+ .onSet(async value => this.internalTargetTempUpdate(value))
149
+
150
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
151
+ if (!platform.config.disableNoResponse) {
152
+ this.service.getCharacteristic(this.hapChar.CurrentTemperature).onGet(() => {
153
+ if (!this.isOnline) {
154
+ throw new this.hapErr(-70402)
155
+ }
156
+ return this.service.getCharacteristic(this.hapChar.CurrentTemperature).value
157
+ })
158
+ this.service.getCharacteristic(this.hapChar.TargetHeatingCoolingState).onGet(() => {
159
+ if (!this.isOnline) {
160
+ throw new this.hapErr(-70402)
161
+ }
162
+ return this.service.getCharacteristic(this.hapChar.TargetHeatingCoolingState).value
163
+ })
164
+ this.service.getCharacteristic(this.hapChar.CurrentHeatingCoolingState).onGet(() => {
165
+ if (!this.isOnline) {
166
+ throw new this.hapErr(-70402)
167
+ }
168
+ return this.service.getCharacteristic(this.hapChar.CurrentHeatingCoolingState).value
169
+ })
170
+ this.service.getCharacteristic(this.hapChar.TargetTemperature).onGet(() => {
171
+ if (!this.isOnline) {
172
+ throw new this.hapErr(-70402)
173
+ }
174
+ return this.service.getCharacteristic(this.hapChar.TargetTemperature).value
175
+ })
176
+ }
177
+
178
+ // Initialise these caches now since they aren't determined by the initial externalUpdate()
179
+ const curState = this.service.getCharacteristic(this.hapChar.TargetHeatingCoolingState).value
180
+ this.cacheState = curState > 0 ? 'on' : 'off'
181
+ const heatTest = this.service.getCharacteristic(this.hapChar.TargetTemperature).value
182
+ > this.service.getCharacteristic(this.hapChar.CurrentTemperature).value
183
+ ? 'on'
184
+ : 'off'
185
+ this.cacheHeat = this.cacheState === 'on' && [1, 3].includes(curState)
186
+ ? heatTest
187
+ : undefined
188
+ const coolTest = this.service.getCharacteristic(this.hapChar.TargetTemperature).value
189
+ < this.service.getCharacteristic(this.hapChar.CurrentTemperature).value
190
+ ? 'on'
191
+ : 'off'
192
+ this.cacheCool = this.cacheState === 'on' && curState === 2
193
+ ? coolTest
194
+ : undefined
195
+
196
+ // Pass the accessory to Fakegato to set up with Eve
197
+ this.accessory.eveService = new platform.eveService('custom', this.accessory, {
198
+ log: () => {},
199
+ })
200
+
201
+ // Set up an interval to get eWeLink to send regular temperature/humidity updates
202
+ if (platform.config.mode !== 'lan') {
203
+ setTimeout(() => {
204
+ this.internalUIUpdate()
205
+ this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
206
+ }, 5000)
207
+
208
+ // Stop the intervals on Homebridge shutdown
209
+ platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
210
+ }
211
+
212
+ // Output the customised options to the log
213
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
214
+ const opts = JSON.stringify({
215
+ humidityOffset: this.humiOffset,
216
+ humidityOffsetFactor: this.humiOffsetFactor,
217
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
218
+ maxTarget: this.maxTarget,
219
+ minTarget: this.minTarget,
220
+ offset: this.tempOffset,
221
+ offsetFactor: this.tempOffsetFactor,
222
+ showAs: 'heater',
223
+ })
224
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
225
+ }
226
+
227
+ async internalStateUpdate(value) {
228
+ try {
229
+ const params = { deviceType: 'normal' }
230
+
231
+ // this.showHeatCool true, values can be 0, 1, 2 otherwise 0, 3
232
+ let newState
233
+ let newHeat
234
+ let newCool
235
+ switch (value) {
236
+ case 0:
237
+ // Turning off
238
+ params.mainSwitch = 'off'
239
+ params.switch = 'off'
240
+ newState = 'off'
241
+ newHeat = 'off'
242
+ newCool = 'off'
243
+ break
244
+ case 1:
245
+ // Turning to heat
246
+ if (!this.showHeatCool) {
247
+ return
248
+ }
249
+ if (this.cacheTemp < this.accessory.context.cacheTarget) {
250
+ params.mainSwitch = 'on'
251
+ params.switch = 'on'
252
+ newState = 'on'
253
+ newHeat = 'on'
254
+ newCool = 'off'
255
+ } else {
256
+ params.mainSwitch = 'off'
257
+ params.switch = 'off'
258
+ newState = 'on'
259
+ newHeat = 'off'
260
+ newCool = 'off'
261
+ }
262
+ break
263
+ case 2:
264
+ // Turning to cool
265
+ if (!this.showHeatCool) {
266
+ return
267
+ }
268
+ if (this.cacheTemp > this.accessory.context.cacheTarget) {
269
+ params.mainSwitch = 'on'
270
+ params.switch = 'on'
271
+ newState = 'on'
272
+ newHeat = 'off'
273
+ newCool = 'on'
274
+ } else {
275
+ params.mainSwitch = 'off'
276
+ params.switch = 'off'
277
+ newState = 'on'
278
+ newHeat = 'off'
279
+ newCool = 'off'
280
+ }
281
+ break
282
+ case 3:
283
+ // Turning to auto
284
+ if (this.showHeatCool) {
285
+ return
286
+ }
287
+ if (this.cacheTemp < this.accessory.context.cacheTarget) {
288
+ params.mainSwitch = 'on'
289
+ params.switch = 'on'
290
+ newState = 'on'
291
+ newHeat = 'on'
292
+ newCool = 'off'
293
+ } else {
294
+ params.mainSwitch = 'off'
295
+ params.switch = 'off'
296
+ newState = 'on'
297
+ newHeat = 'off'
298
+ newCool = 'off'
299
+ }
300
+ break
301
+ default:
302
+ return
303
+ }
304
+
305
+ // Only send the update if either:
306
+ // * The new value (state) is OFF and (the cacheHeat was ON or cacheCool was ON)
307
+ // * The new value (state) is ON and (newHeat is 'on' or newCool is 'on')
308
+ // * (cacheHeat was ON and newCool is ON) or (cacheCool was ON and newHeat is ON)
309
+ if (
310
+ (value === 0 && (this.cacheHeat === 'on' || this.cacheCool === 'on'))
311
+ || ([1, 3].includes(value) && newHeat !== this.cacheHeat)
312
+ || (value === 2 && newCool !== this.cacheCool)
313
+ ) {
314
+ await this.platform.sendDeviceUpdate(this.accessory, params)
315
+ }
316
+ if (newState !== this.cacheState) {
317
+ this.cacheState = newState
318
+ this.cacheHeat = undefined
319
+ this.cacheCool = undefined
320
+ if (this.enableLogging) {
321
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
322
+ }
323
+ }
324
+ if ([1, 3].includes(value) && newHeat !== this.cacheHeat) {
325
+ this.cacheHeat = newHeat
326
+ this.cacheCool = undefined
327
+ if (this.enableLogging) {
328
+ this.log('[%s] %s [%s].', this.name, this.lang.curHeat, this.cacheHeat)
329
+ }
330
+ }
331
+ if (value === 2 && newCool !== this.cacheCool) {
332
+ this.cacheCool = newCool
333
+ this.cacheHeat = undefined
334
+ if (this.enableLogging) {
335
+ this.log('[%s] %s [%s].', this.name, this.lang.curCool, this.cacheCool)
336
+ }
337
+ }
338
+ const hapState = this.cacheHeat === 'on' ? 1 : 0
339
+ this.service.updateCharacteristic(
340
+ this.hapChar.CurrentHeatingCoolingState,
341
+ this.cacheCool === 'on' ? 2 : hapState,
342
+ )
343
+ } catch (err) {
344
+ this.platform.deviceUpdateError(this.accessory, err, true)
345
+ setTimeout(() => {
346
+ const hapState = this.cacheHeat === 'on' ? 1 : 0
347
+ this.service.updateCharacteristic(
348
+ this.hapChar.TargetHeatingCoolingState,
349
+ this.cacheCool === 'on' ? 2 : hapState,
350
+ )
351
+ }, 2000)
352
+ throw new this.hapErr(-70402)
353
+ }
354
+ }
355
+
356
+ async internalTargetTempUpdate(value) {
357
+ try {
358
+ if (value === this.accessory.context.cacheTarget) {
359
+ return
360
+ }
361
+ this.accessory.context.cacheTarget = value
362
+ if (this.enableLogging) {
363
+ this.log('[%s] %s [%s°C].', this.name, this.lang.curTarg, value)
364
+ }
365
+ if (this.cacheState === 'off') {
366
+ return
367
+ }
368
+ const params = { deviceType: 'normal' }
369
+ let newHeat
370
+ let newCool
371
+
372
+ const curMode = this.service.getCharacteristic(this.hapChar.TargetHeatingCoolingState).value
373
+ switch (curMode) {
374
+ case 1:
375
+ case 3:
376
+ // Currently in heating or auto mode
377
+ if (this.cacheTemp < this.accessory.context.cacheTarget) {
378
+ params.mainSwitch = 'on'
379
+ params.switch = 'on'
380
+ newHeat = 'on'
381
+ newCool = 'off'
382
+ } else {
383
+ params.mainSwitch = 'off'
384
+ params.switch = 'off'
385
+ newHeat = 'off'
386
+ newCool = 'off'
387
+ }
388
+ break
389
+ case 2:
390
+ // Currently in cooling mode
391
+ if (this.cacheTemp > this.accessory.context.cacheTarget) {
392
+ params.mainSwitch = 'on'
393
+ params.switch = 'on'
394
+ newHeat = 'off'
395
+ newCool = 'on'
396
+ } else {
397
+ params.mainSwitch = 'off'
398
+ params.switch = 'off'
399
+ newHeat = 'off'
400
+ newCool = 'off'
401
+ }
402
+ break
403
+ default:
404
+ return
405
+ }
406
+
407
+ // Only send the update if either:
408
+ // * The new value (state) is ON and (newHeat is 'on' or newCool is 'on')
409
+ // * (cacheHeat was ON and newCool is ON) or (cacheCool was ON and newHeat is ON)
410
+ if (
411
+ ([1, 3].includes(curMode) && newHeat !== this.cacheHeat)
412
+ || (curMode === 2 && newCool !== this.cacheCool)
413
+ ) {
414
+ await this.platform.sendDeviceUpdate(this.accessory, params)
415
+ }
416
+ if ([1, 3].includes(curMode) && newHeat !== this.cacheHeat) {
417
+ this.cacheHeat = newHeat
418
+ this.cacheCool = undefined
419
+ if (this.enableLogging) {
420
+ this.log('[%s] %s [%s].', this.name, this.lang.curHeat, this.cacheHeat)
421
+ }
422
+ }
423
+ if (curMode === 2 && newCool !== this.cacheCool) {
424
+ this.cacheCool = newCool
425
+ this.cacheHeat = undefined
426
+ if (this.enableLogging) {
427
+ this.log('[%s] %s [%s].', this.name, this.lang.curCool, this.cacheCool)
428
+ }
429
+ }
430
+ const hapState = this.cacheHeat === 'on' ? 1 : 0
431
+ this.service.updateCharacteristic(
432
+ this.hapChar.CurrentHeatingCoolingState,
433
+ this.cacheCool === 'on' ? 2 : hapState,
434
+ )
435
+ } catch (err) {
436
+ this.platform.deviceUpdateError(this.accessory, err, true)
437
+ setTimeout(() => {
438
+ this.service.updateCharacteristic(
439
+ this.hapChar.TargetTemperature,
440
+ this.accessory.context.cacheTarget,
441
+ )
442
+ }, 2000)
443
+ throw new this.hapErr(-70402)
444
+ }
445
+ }
446
+
447
+ async internalCurrentTempUpdate() {
448
+ try {
449
+ if (this.cacheState === 'off') {
450
+ return
451
+ }
452
+ const params = { deviceType: 'normal' }
453
+ let newHeat
454
+ let newCool
455
+
456
+ const curMode = this.service.getCharacteristic(this.hapChar.TargetHeatingCoolingState).value
457
+ switch (curMode) {
458
+ case 1:
459
+ case 3:
460
+ // Currently in heating or auto mode
461
+ if (this.cacheTemp < this.accessory.context.cacheTarget) {
462
+ params.mainSwitch = 'on'
463
+ params.switch = 'on'
464
+ newHeat = 'on'
465
+ newCool = 'off'
466
+ } else {
467
+ params.mainSwitch = 'off'
468
+ params.switch = 'off'
469
+ newHeat = 'off'
470
+ newCool = 'off'
471
+ }
472
+ break
473
+ case 2:
474
+ // Currently in cooling mode
475
+ if (this.cacheTemp > this.accessory.context.cacheTarget) {
476
+ params.mainSwitch = 'on'
477
+ params.switch = 'on'
478
+ newHeat = 'off'
479
+ newCool = 'on'
480
+ } else {
481
+ params.mainSwitch = 'off'
482
+ params.switch = 'off'
483
+ newHeat = 'off'
484
+ newCool = 'off'
485
+ }
486
+ break
487
+ default:
488
+ return
489
+ }
490
+
491
+ // Only send the update if either:
492
+ // * The new value (state) is ON and (newHeat is 'on' or newCool is 'on')
493
+ // * (cacheHeat was ON and newCool is ON) or (cacheCool was ON and newHeat is ON)
494
+ if (
495
+ ([1, 3].includes(curMode) && newHeat !== this.cacheHeat)
496
+ || (curMode === 2 && newCool !== this.cacheCool)
497
+ ) {
498
+ await this.platform.sendDeviceUpdate(this.accessory, params)
499
+ }
500
+ if ([1, 3].includes(curMode) && newHeat !== this.cacheHeat) {
501
+ this.cacheHeat = newHeat
502
+ this.cacheCool = undefined
503
+ if (this.enableLogging) {
504
+ this.log('[%s] %s [%s].', this.name, this.lang.curHeat, this.cacheHeat)
505
+ }
506
+ }
507
+ if (curMode === 2 && newCool !== this.cacheCool) {
508
+ this.cacheCool = newCool
509
+ this.cacheHeat = undefined
510
+ if (this.enableLogging) {
511
+ this.log('[%s] %s [%s].', this.name, this.lang.curCool, this.cacheCool)
512
+ }
513
+ }
514
+ const hapState = this.cacheHeat === 'on' ? 1 : 0
515
+ this.service.updateCharacteristic(
516
+ this.hapChar.CurrentHeatingCoolingState,
517
+ this.cacheCool === 'on' ? 2 : hapState,
518
+ )
519
+ } catch (err) {
520
+ // Suppress errors here
521
+ }
522
+ }
523
+
524
+ async internalUIUpdate() {
525
+ try {
526
+ // Skip polling if device isn't online
527
+ if (!this.isOnline) {
528
+ return
529
+ }
530
+
531
+ // Send the params to request the updates
532
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
533
+ } catch (err) {
534
+ // Suppress errors here
535
+ }
536
+ }
537
+
538
+ async externalUpdate(params) {
539
+ try {
540
+ if (
541
+ hasProperty(params, 'currentTemperature')
542
+ && params.currentTemperature !== 'unavailable'
543
+ ) {
544
+ let newTemp = Number(params.currentTemperature)
545
+ if (this.tempOffsetFactor) {
546
+ newTemp *= this.tempOffset
547
+ } else {
548
+ newTemp += this.tempOffset
549
+ }
550
+ if (newTemp !== this.cacheTemp) {
551
+ this.cacheTemp = newTemp
552
+ this.service.updateCharacteristic(this.hapChar.CurrentTemperature, this.cacheTemp)
553
+ this.accessory.eveService.addEntry({ temp: this.cacheTemp })
554
+ if (params.updateSource && this.enableLogging) {
555
+ this.log('[%s] %s [%s°C].', this.name, this.lang.curTemp, this.cacheTemp)
556
+ }
557
+
558
+ // Update the cache file with the new temperature
559
+ this.updateCache()
560
+ this.internalCurrentTempUpdate()
561
+ }
562
+ }
563
+ if (
564
+ hasProperty(params, 'currentHumidity')
565
+ && params.currentHumidity !== 'unavailable'
566
+ && this.humiService
567
+ ) {
568
+ let newHumi = Number.parseInt(params.currentHumidity, 10)
569
+ if (this.humiOffsetFactor) {
570
+ newHumi *= this.humiOffset
571
+ } else {
572
+ newHumi += this.humiOffset
573
+ }
574
+ newHumi = Math.max(Math.min(Number.parseInt(newHumi, 10), 100), 0)
575
+ if (newHumi !== this.cacheHumi) {
576
+ this.cacheHumi = newHumi
577
+ this.service.updateCharacteristic(this.hapChar.CurrentRelativeHumidity, this.cacheHumi)
578
+ this.accessory.eveService.addEntry({ humidity: this.cacheHumi })
579
+ if (params.updateSource && this.enableLogging) {
580
+ this.log('[%s] %s [%s%].', this.name, this.lang.curHumi, this.cacheHumi)
581
+ }
582
+ }
583
+ }
584
+ } catch (err) {
585
+ this.platform.deviceUpdateError(this.accessory, err, false)
586
+ }
587
+ }
588
+
589
+ async updateCache() {
590
+ // Don't continue if the storage client hasn't initialised properly
591
+ if (!this.platform.storageClientData) {
592
+ return
593
+ }
594
+
595
+ // Attempt to save the new temperature to the cache
596
+ try {
597
+ await this.platform.storageData.setItem(
598
+ `${this.accessory.context.eweDeviceId}_temp`,
599
+ this.cacheTemp,
600
+ )
601
+ } catch (err) {
602
+ if (this.enableLogging) {
603
+ this.log.warn('[%s] %s %s.', this.name, this.lang.storageWriteErr, parseError(err))
604
+ }
605
+ }
606
+ }
607
+
608
+ markStatus(isOnline) {
609
+ this.isOnline = isOnline
610
+ }
611
+ }