@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,349 @@
1
+ import { generateRandomString, sleep } from '../utils/functions.js'
2
+
3
+ export default class {
4
+ constructor(platform, accessory) {
5
+ // Set up variables from the platform
6
+ this.hapChar = platform.api.hap.Characteristic
7
+ this.hapErr = platform.api.hap.HapStatusError
8
+ this.hapServ = platform.api.hap.Service
9
+ this.lang = platform.lang
10
+ this.log = platform.log
11
+ this.platform = platform
12
+
13
+ // Set up variables from the accessory
14
+ this.name = accessory.displayName
15
+ this.accessory = accessory
16
+
17
+ // Initially set the online flag as true (to be then updated as false if necessary)
18
+ this.isOnline = true
19
+
20
+ // Set up custom variables for this device type
21
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
22
+ this.hideLight = deviceConf.hideLight
23
+
24
+ // Set the correct logging variables for this accessory
25
+ switch (deviceConf.overrideLogging) {
26
+ case 'standard':
27
+ this.enableLogging = true
28
+ this.enableDebugLogging = false
29
+ break
30
+ case 'debug':
31
+ this.enableLogging = true
32
+ this.enableDebugLogging = true
33
+ break
34
+ case 'disable':
35
+ this.enableLogging = false
36
+ this.enableDebugLogging = false
37
+ break
38
+ default:
39
+ this.enableLogging = !platform.config.disableDeviceLogging
40
+ this.enableDebugLogging = platform.config.debug
41
+ break
42
+ }
43
+
44
+ // Add the fan service if it doesn't already exist
45
+ this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
46
+
47
+ // Add the set handler to the fan on/off characteristic
48
+ this.service
49
+ .getCharacteristic(this.hapChar.On)
50
+ .onSet(async value => this.internalStateUpdate(value))
51
+
52
+ // Add the set handler to the fan rotation speed characteristic
53
+ this.service
54
+ .getCharacteristic(this.hapChar.RotationSpeed)
55
+ .setProps({
56
+ maxValue: 3,
57
+ minStep: 1,
58
+ minValue: 0,
59
+ unit: 'unitless', // This is actually from HAP for Bluetooth LE Specification, but fits
60
+ })
61
+ .onSet(async value => this.internalSpeedUpdate(value))
62
+
63
+ // Check to see if the user has hidden the light channel
64
+ if (this.hideLight) {
65
+ // The user has hidden the light channel, so remove it if it exists
66
+ if (this.accessory.getService(this.hapServ.Lightbulb)) {
67
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Lightbulb))
68
+ }
69
+ } else {
70
+ // The user has not hidden the light channel, so add it if it doesn't exist
71
+ this.lightService = this.accessory.getService(this.hapServ.Lightbulb)
72
+ || this.accessory.addService(this.hapServ.Lightbulb)
73
+
74
+ // Add the set handler to the lightbulb on/off characteristic
75
+ this.lightService
76
+ .getCharacteristic(this.hapChar.On)
77
+ .onSet(async value => this.internalLightUpdate(value))
78
+ }
79
+
80
+ // Set the fan service to the primary service
81
+ this.service.setPrimaryService()
82
+
83
+ // Conversion object eWeLink mode to text label
84
+ this.speed2label = {
85
+ 1: 'low',
86
+ 2: 'medium',
87
+ 3: 'high',
88
+ }
89
+
90
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
91
+ if (!platform.config.disableNoResponse) {
92
+ this.service.getCharacteristic(this.hapChar.On).onGet(() => {
93
+ if (!this.isOnline) {
94
+ throw new this.hapErr(-70402)
95
+ }
96
+ return this.service.getCharacteristic(this.hapChar.On).value
97
+ })
98
+ this.service.getCharacteristic(this.hapChar.RotationSpeed).onGet(() => {
99
+ if (!this.isOnline) {
100
+ throw new this.hapErr(-70402)
101
+ }
102
+ return this.service.getCharacteristic(this.hapChar.RotationSpeed).value
103
+ })
104
+ if (this.lightService) {
105
+ this.lightService.getCharacteristic(this.hapChar.On).onGet(() => {
106
+ if (!this.isOnline) {
107
+ throw new this.hapErr(-70402)
108
+ }
109
+ return this.lightService.getCharacteristic(this.hapChar.On).value
110
+ })
111
+ }
112
+ }
113
+
114
+ // Output the customised options to the log
115
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
116
+ const opts = JSON.stringify({
117
+ hideLight: this.hideLight,
118
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
119
+ })
120
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
121
+ }
122
+
123
+ async internalStateUpdate(value) {
124
+ try {
125
+ const newState = value ? 'on' : 'off'
126
+ if (newState === this.cacheState) {
127
+ return
128
+ }
129
+ const params = {
130
+ switches: [],
131
+ }
132
+ params.switches.push({
133
+ switch: newState,
134
+ outlet: 1,
135
+ })
136
+ if (newState === 'on') {
137
+ params.switches.push({
138
+ switch: this.cacheSpeed === 2 ? 'on' : 'off',
139
+ outlet: 2,
140
+ })
141
+ params.switches.push({
142
+ switch: this.cacheSpeed === 3 ? 'on' : 'off',
143
+ outlet: 3,
144
+ })
145
+ }
146
+ await this.platform.sendDeviceUpdate(this.accessory, params)
147
+ this.cacheState = newState
148
+ if (this.enableLogging) {
149
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, newState)
150
+ }
151
+ } catch (err) {
152
+ this.platform.deviceUpdateError(this.accessory, err, true)
153
+ setTimeout(() => {
154
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
155
+ }, 2000)
156
+ throw new this.hapErr(-70402)
157
+ }
158
+ }
159
+
160
+ async internalSpeedUpdate(value) {
161
+ try {
162
+ // This acts like a debounce function when endlessly sliding the slider
163
+ const updateKey = generateRandomString(5)
164
+ this.updateKey = updateKey
165
+ await sleep(500)
166
+ if (updateKey !== this.updateKey) {
167
+ return
168
+ }
169
+ const params = {
170
+ switches: [],
171
+ }
172
+ const newState = value >= 1 ? 'on' : 'off'
173
+ let newSpeed
174
+
175
+ if (value < 0 || value > 3) {
176
+ newSpeed = 0
177
+ } else {
178
+ newSpeed = value
179
+ }
180
+ if (newSpeed === this.cacheSpeed) {
181
+ return
182
+ }
183
+ params.switches.push({
184
+ switch: newState === 'on' && newSpeed >= 1 ? 'on' : 'off',
185
+ outlet: 1,
186
+ })
187
+ params.switches.push({
188
+ switch: newState === 'on' && newSpeed === 2 ? 'on' : 'off',
189
+ outlet: 2,
190
+ })
191
+ params.switches.push({
192
+ switch: newState === 'on' && newSpeed === 3 ? 'on' : 'off',
193
+ outlet: 3,
194
+ })
195
+ await this.platform.sendDeviceUpdate(this.accessory, params)
196
+ if (newState !== this.cacheState) {
197
+ this.cacheState = newState
198
+ if (this.enableLogging) {
199
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
200
+ }
201
+ }
202
+ if (newSpeed === 0) {
203
+ // Update the rotation speed back to the previous value (with the fan still off)
204
+ setTimeout(() => {
205
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
206
+ }, 2000)
207
+ return
208
+ }
209
+ this.cacheSpeed = newSpeed
210
+ if (this.enableLogging) {
211
+ this.log('[%s] %s [%s].', this.name, this.lang.curSpeed, this.speed2label[this.cacheSpeed])
212
+ }
213
+ } catch (err) {
214
+ this.platform.deviceUpdateError(this.accessory, err, true)
215
+ setTimeout(() => {
216
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
217
+ }, 2000)
218
+ throw new this.hapErr(-70402)
219
+ }
220
+ }
221
+
222
+ async internalLightUpdate(value) {
223
+ try {
224
+ const params = {
225
+ switches: [],
226
+ }
227
+ const newLight = value ? 'on' : 'off'
228
+ if (newLight === this.cacheLight) {
229
+ return
230
+ }
231
+ params.switches.push({
232
+ switch: newLight,
233
+ outlet: 0,
234
+ })
235
+ await this.platform.sendDeviceUpdate(this.accessory, params)
236
+ this.cacheLight = newLight
237
+ if (this.enableLogging) {
238
+ this.log('[%s] %s [%s].', this.name, this.lang.curLight, this.cacheLight)
239
+ }
240
+ } catch (err) {
241
+ this.platform.deviceUpdateError(this.accessory, err, true)
242
+ setTimeout(() => {
243
+ this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLight === 'on')
244
+ }, 2000)
245
+ throw new this.hapErr(-70402)
246
+ }
247
+ }
248
+
249
+ async externalUpdate(params) {
250
+ try {
251
+ if (params.light && params.fan && params.speed) {
252
+ // LAN update from iFan03
253
+ params.switches = {}
254
+ params.switches[0] = { switch: params.light }
255
+ params.switches[1] = { switch: params.fan }
256
+ switch (params.speed) {
257
+ case 1:
258
+ params.switches[2] = { switch: 'off' }
259
+ params.switches[3] = { switch: 'off' }
260
+ break
261
+ case 2:
262
+ params.switches[2] = { switch: 'on' }
263
+ params.switches[3] = { switch: 'off' }
264
+ break
265
+ case 3:
266
+ params.switches[2] = { switch: 'off' }
267
+ params.switches[3] = { switch: 'on' }
268
+ break
269
+ default:
270
+ return
271
+ }
272
+ }
273
+ if (!params.switches) {
274
+ return
275
+ }
276
+ if (params.switches[1].switch !== this.cacheState) {
277
+ this.cacheState = params.switches[1].switch
278
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
279
+ if (params.updateSource && this.enableLogging) {
280
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
281
+ }
282
+ }
283
+ let speed
284
+ switch (params.switches[2].switch + params.switches[3].switch) {
285
+ case 'onoff':
286
+ speed = 2
287
+ break
288
+ case 'offon':
289
+ speed = 3
290
+ break
291
+ default:
292
+ speed = 1
293
+ break
294
+ }
295
+ if (speed !== this.cacheSpeed && this.cacheState === 'on') {
296
+ this.cacheSpeed = speed
297
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
298
+ if (params.updateSource && this.enableLogging) {
299
+ this.log(
300
+ '[%s] %s [%s].',
301
+ this.name,
302
+ this.lang.curSpeed,
303
+ this.speed2label[this.cacheSpeed],
304
+ )
305
+ }
306
+ }
307
+ if (this.lightService && params.switches[0].switch !== this.cacheLight) {
308
+ this.cacheLight = params.switches[0].switch
309
+ this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLight === 'on')
310
+ if (params.updateSource && this.enableLogging) {
311
+ this.log('[%s] %s [%s].', this.name, this.lang.curLight, this.cacheLight)
312
+ }
313
+ }
314
+ } catch (err) {
315
+ this.platform.deviceUpdateError(this.accessory, err, false)
316
+ }
317
+ }
318
+
319
+ currentState() {
320
+ const toReturn = {}
321
+ let speedLabel
322
+ const speed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
323
+ if (speed === 0) {
324
+ speedLabel = 'off'
325
+ } else if (speed <= 1) {
326
+ speedLabel = 'low'
327
+ } else if (speed <= 2) {
328
+ speedLabel = 'medium'
329
+ } else {
330
+ speedLabel = 'high'
331
+ }
332
+ toReturn.services = ['fan']
333
+ toReturn.fan = {
334
+ state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
335
+ speed: speedLabel,
336
+ }
337
+ if (!this.hideLight) {
338
+ toReturn.services.push('light')
339
+ toReturn.light = {
340
+ state: this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
341
+ }
342
+ }
343
+ return toReturn
344
+ }
345
+
346
+ markStatus(isOnline) {
347
+ this.isOnline = isOnline
348
+ }
349
+ }
@@ -0,0 +1,106 @@
1
+ import platformConsts from '../utils/constants.js'
2
+
3
+ export default class {
4
+ constructor(platform, accessory) {
5
+ // Set up variables from the platform
6
+ this.eveChar = platform.eveChar
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
+ // Set the correct logging variables for this accessory
19
+ this.enableLogging = !platform.config.disableDeviceLogging
20
+ this.enableDebugLogging = platform.config.debug
21
+
22
+ if (platformConsts.devices.switchSCM.includes(this.accessory.context.groupUIID)) {
23
+ this.isSCM = true
24
+ }
25
+
26
+ // Add the switch service if it doesn't already exist
27
+ this.service = this.accessory.getService(this.hapServ.Switch)
28
+ || this.accessory.addService(this.hapServ.Switch)
29
+
30
+ // Add the set handler to the switch on/off characteristic
31
+ this.service
32
+ .getCharacteristic(this.hapChar.On)
33
+ .onSet(async value => this.internalStateUpdate(value))
34
+
35
+ // Pass the accessory to Fakegato to set up with Eve
36
+ this.accessory.eveService = new platform.eveService('switch', this.accessory, {
37
+ log: () => {},
38
+ })
39
+
40
+ // Output the customised options to the log
41
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
42
+ const opts = JSON.stringify({
43
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
44
+ })
45
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
46
+ }
47
+
48
+ async internalStateUpdate(value) {
49
+ try {
50
+ const newValue = value ? 'on' : 'off'
51
+ if (newValue === this.cacheState) {
52
+ return
53
+ }
54
+ const params = {}
55
+ if (this.isSCM) {
56
+ params.switches = [{ switch: newValue, outlet: 0 }]
57
+ } else {
58
+ params.switch = newValue
59
+ }
60
+ await this.platform.sendGroupUpdate(this.accessory, params)
61
+ this.cacheState = newValue
62
+ this.accessory.eveService.addEntry({ status: value ? 1 : 0 })
63
+ if (this.enableLogging) {
64
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
65
+ }
66
+ } catch (err) {
67
+ this.platform.deviceUpdateError(this.accessory, err, true)
68
+ setTimeout(() => {
69
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
70
+ }, 2000)
71
+ throw new this.hapErr(-70402)
72
+ }
73
+ }
74
+
75
+ async externalUpdate(params) {
76
+ try {
77
+ if (!this.isSCM && params.switch && params.switch !== this.cacheState) {
78
+ this.cacheState = params.switch
79
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
80
+ this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
81
+ if (params.updateSource && this.enableLogging) {
82
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
83
+ }
84
+ }
85
+ if (this.isSCM && params.switches && params.switches[0].switch !== this.cacheState) {
86
+ this.cacheState = params.switches[0].switch
87
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
88
+ this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
89
+ if (params.updateSource && this.enableLogging) {
90
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
91
+ }
92
+ }
93
+ } catch (err) {
94
+ this.platform.deviceUpdateError(this.accessory, err, false)
95
+ }
96
+ }
97
+
98
+ async currentState() {
99
+ const toReturn = {}
100
+ toReturn.services = ['switch']
101
+ toReturn.switch = {
102
+ state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
103
+ }
104
+ return toReturn
105
+ }
106
+ }
@@ -0,0 +1,199 @@
1
+ import { generateRandomString, sleep } from '../utils/functions.js'
2
+
3
+ export default class {
4
+ constructor(platform, accessory) {
5
+ // Set up variables from the platform
6
+ this.hapChar = platform.api.hap.Characteristic
7
+ this.hapErr = platform.api.hap.HapStatusError
8
+ this.hapServ = platform.api.hap.Service
9
+ this.lang = platform.lang
10
+ this.log = platform.log
11
+ this.platform = platform
12
+
13
+ // Set up variables from the accessory
14
+ this.name = accessory.displayName
15
+ this.accessory = accessory
16
+
17
+ // Initially set the online flag as true (to be then updated as false if necessary)
18
+ this.isOnline = true
19
+
20
+ // Set up custom variables for this device type
21
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
22
+
23
+ // Set the correct logging variables for this accessory
24
+ switch (deviceConf.overrideLogging) {
25
+ case 'standard':
26
+ this.enableLogging = true
27
+ this.enableDebugLogging = false
28
+ break
29
+ case 'debug':
30
+ this.enableLogging = true
31
+ this.enableDebugLogging = true
32
+ break
33
+ case 'disable':
34
+ this.enableLogging = false
35
+ this.enableDebugLogging = false
36
+ break
37
+ default:
38
+ this.enableLogging = !platform.config.disableDeviceLogging
39
+ this.enableDebugLogging = platform.config.debug
40
+ break
41
+ }
42
+
43
+ /*
44
+ The device does not provide a current humidity reading so
45
+ we use a fan accessory to be able to control the on/off state
46
+ and the modes (1, 2, 3) using a rotation speed of (33%, 66%, 99%)
47
+ */
48
+
49
+ // Add the fan service if it doesn't already exist
50
+ this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
51
+
52
+ // Add the set handler to the fan on/off characteristic
53
+ this.service
54
+ .getCharacteristic(this.hapChar.On)
55
+ .onSet(async value => this.internalStateUpdate(value))
56
+
57
+ // Add the set handler to the fan rotation speed characteristic
58
+ this.service
59
+ .getCharacteristic(this.hapChar.RotationSpeed)
60
+ .setProps({ minStep: 33 })
61
+ .onSet(async value => this.internalModeUpdate(value))
62
+
63
+ // Conversion object eWeLink mode to text label
64
+ this.mode2label = {
65
+ 1: 'low',
66
+ 2: 'medium',
67
+ 3: 'high',
68
+ }
69
+
70
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
71
+ if (!platform.config.disableNoResponse) {
72
+ this.service.getCharacteristic(this.hapChar.On).onGet(() => {
73
+ if (!this.isOnline) {
74
+ throw new this.hapErr(-70402)
75
+ }
76
+ return this.service.getCharacteristic(this.hapChar.On).value
77
+ })
78
+ this.service.getCharacteristic(this.hapChar.RotationSpeed).onGet(() => {
79
+ if (!this.isOnline) {
80
+ throw new this.hapErr(-70402)
81
+ }
82
+ return this.service.getCharacteristic(this.hapChar.RotationSpeed).value
83
+ })
84
+ }
85
+
86
+ // Output the customised options to the log
87
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
88
+ const opts = JSON.stringify({
89
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
90
+ })
91
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
92
+ }
93
+
94
+ async internalStateUpdate(value) {
95
+ try {
96
+ const newState = value ? 'on' : 'off'
97
+ if (newState === this.cacheState) {
98
+ return
99
+ }
100
+ const params = { switch: newState }
101
+ if (newState === 'on') {
102
+ params.state = this.cacheMode
103
+ }
104
+ await this.platform.sendDeviceUpdate(this.accessory, params)
105
+ this.cacheState = newState
106
+ if (this.enableLogging) {
107
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, newState)
108
+ }
109
+ } catch (err) {
110
+ this.platform.deviceUpdateError(this.accessory, err, true)
111
+ setTimeout(() => {
112
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
113
+ }, 2000)
114
+ throw new this.hapErr(-70402)
115
+ }
116
+ }
117
+
118
+ async internalModeUpdate(value) {
119
+ try {
120
+ const updateKey = generateRandomString(5)
121
+ this.updateKey = updateKey
122
+ await sleep(500)
123
+ if (updateKey !== this.updateKey) {
124
+ return
125
+ }
126
+ const params = {}
127
+ const newState = value >= 1 ? 'on' : 'off'
128
+ params.switch = newState
129
+ let newMode
130
+ if (value > 0) {
131
+ if (value <= 33) {
132
+ newMode = 1
133
+ } else if (value <= 66) {
134
+ newMode = 2
135
+ } else {
136
+ newMode = 3
137
+ }
138
+ if (this.cacheMode === newMode) {
139
+ return
140
+ }
141
+ params.state = newMode
142
+ }
143
+ await this.platform.sendDeviceUpdate(this.accessory, params)
144
+ if (newState !== this.cacheState) {
145
+ this.cacheState = newState
146
+ if (this.enableLogging) {
147
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
148
+ }
149
+ }
150
+ if (value === 0) {
151
+ // Update the rotation speed back to the previous value (with the fan still off)
152
+ setTimeout(() => {
153
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheMode * 33)
154
+ }, 2000)
155
+ return
156
+ }
157
+ this.cacheMode = newMode
158
+ if (this.enableLogging) {
159
+ this.log('[%s] %s [%s].', this.name, this.lang.curMode, this.mode2label[this.cacheMode])
160
+ }
161
+ } catch (err) {
162
+ this.platform.deviceUpdateError(this.accessory, err, true)
163
+ setTimeout(() => {
164
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheMode * 33)
165
+ }, 2000)
166
+ throw new this.hapErr(-70402)
167
+ }
168
+ }
169
+
170
+ async externalUpdate(params) {
171
+ try {
172
+ if (params.switch && params.switch !== this.cacheState) {
173
+ this.cacheState = params.switch
174
+ if (params.updateSource && this.enableLogging) {
175
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
176
+ }
177
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
178
+ if (this.cacheState !== 'on') {
179
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, 0)
180
+ }
181
+ }
182
+ if (params.state && params.state !== this.cacheMode) {
183
+ this.cacheMode = params.state
184
+ if (this.cacheState === 'on') {
185
+ if (params.updateSource && this.enableLogging) {
186
+ this.log('[%s] %s [%s].', this.name, this.lang.curMode, this.mode2label[this.cacheMode])
187
+ }
188
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheMode * 33)
189
+ }
190
+ }
191
+ } catch (err) {
192
+ this.platform.deviceUpdateError(this.accessory, err, false)
193
+ }
194
+ }
195
+
196
+ markStatus(isOnline) {
197
+ this.isOnline = isOnline
198
+ }
199
+ }