@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,384 @@
1
+ import platformConsts from '../utils/constants.js'
2
+ import { hasProperty } from '../utils/functions.js'
3
+
4
+ export default class {
5
+ constructor(platform, accessory) {
6
+ // Set up variables from the platform
7
+ this.eveChar = platform.eveChar
8
+ this.hapChar = platform.api.hap.Characteristic
9
+ this.hapErr = platform.api.hap.HapStatusError
10
+ this.hapServ = platform.api.hap.Service
11
+ this.lang = platform.lang
12
+ this.log = platform.log
13
+ this.platform = platform
14
+
15
+ // Set up variables from the accessory
16
+ this.name = accessory.displayName
17
+ this.accessory = accessory
18
+
19
+ // Initially set the online flag as true (to be then updated as false if necessary)
20
+ this.isOnline = true
21
+
22
+ /*
23
+ UIID 1/6/14/24/27/112/1009/1256/7004: single switch, no power readings
24
+ UIID 5: single switch, with wattage readings
25
+ UIID 32: single switch, with wattage, voltage and amp readings
26
+ UIID 77/78/81/107/112/138/160: single switch but firmware uses multiple channels (only need CH0)
27
+ UIID 182/190: single switch but multiple channels with wattage, voltage and amp readings
28
+ */
29
+
30
+ // Set up custom variables for this device type
31
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
32
+ this.inUsePowerThreshold = deviceConf.inUsePowerThreshold || platformConsts.defaultValues.inUsePowerThreshold
33
+
34
+ // Set the correct logging variables for this accessory
35
+ switch (deviceConf.overrideLogging) {
36
+ case 'standard':
37
+ this.enableLogging = true
38
+ this.enableDebugLogging = false
39
+ break
40
+ case 'debug':
41
+ this.enableLogging = true
42
+ this.enableDebugLogging = true
43
+ break
44
+ case 'disable':
45
+ this.enableLogging = false
46
+ this.enableDebugLogging = false
47
+ break
48
+ default:
49
+ this.enableLogging = !platform.config.disableDeviceLogging
50
+ this.enableDebugLogging = platform.config.debug
51
+ break
52
+ }
53
+
54
+ // If the accessory has a switch service then remove it
55
+ if (this.accessory.getService(this.hapServ.Switch)) {
56
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
57
+ }
58
+
59
+ // Add the outlet service if it doesn't already exist
60
+ this.service = this.accessory.getService(this.hapServ.Outlet)
61
+ || this.accessory.addService(this.hapServ.Outlet)
62
+
63
+ // Some models provide a power reading in different decimals
64
+ this.divisor = 1
65
+
66
+ switch (this.accessory.context.eweUIID) {
67
+ case 5:
68
+ // Add Eve power characteristics
69
+ this.powerReadings = true
70
+ if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
71
+ this.service.addCharacteristic(this.hapChar.OutletInUse)
72
+ }
73
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
74
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
75
+ }
76
+ if (!this.service.testCharacteristic(this.eveChar.TotalConsumption)) {
77
+ this.service.addCharacteristic(this.eveChar.TotalConsumption)
78
+ }
79
+ if (!this.service.testCharacteristic(this.eveChar.ResetTotal)) {
80
+ this.service.addCharacteristic(this.eveChar.ResetTotal)
81
+ }
82
+
83
+ // Remove unused Eve characteristics
84
+ if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
85
+ this.service.removeCharacteristic(
86
+ this.service.getCharacteristic(this.eveChar.ElectricCurrent),
87
+ )
88
+ }
89
+ if (this.service.testCharacteristic(this.eveChar.Voltage)) {
90
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
91
+ }
92
+ break
93
+ case 32:
94
+ case 182:
95
+ case 190:
96
+ // Add Eve power characteristics
97
+ this.powerReadings = true
98
+ if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
99
+ this.service.addCharacteristic(this.hapChar.OutletInUse)
100
+ }
101
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
102
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
103
+ }
104
+ if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
105
+ this.service.addCharacteristic(this.eveChar.ElectricCurrent)
106
+ }
107
+ if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
108
+ this.service.addCharacteristic(this.eveChar.Voltage)
109
+ }
110
+ if (!this.service.testCharacteristic(this.eveChar.TotalConsumption)) {
111
+ this.service.addCharacteristic(this.eveChar.TotalConsumption)
112
+ }
113
+ if (!this.service.testCharacteristic(this.eveChar.ResetTotal)) {
114
+ this.service.addCharacteristic(this.eveChar.ResetTotal)
115
+ }
116
+ if (this.accessory.context.eweUIID === 190) {
117
+ this.divisor = 100
118
+ }
119
+ break
120
+ default:
121
+ // Remove unused Eve characteristics
122
+ if (this.service.testCharacteristic(this.hapChar.OutletInUse)) {
123
+ this.service.removeCharacteristic(
124
+ this.service.getCharacteristic(this.eveChar.OutletInUse),
125
+ )
126
+ }
127
+ if (this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
128
+ this.service.removeCharacteristic(
129
+ this.service.getCharacteristic(this.eveChar.CurrentConsumption),
130
+ )
131
+ }
132
+ if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
133
+ this.service.removeCharacteristic(
134
+ this.service.getCharacteristic(this.eveChar.ElectricCurrent),
135
+ )
136
+ }
137
+ if (this.service.testCharacteristic(this.eveChar.Voltage)) {
138
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
139
+ }
140
+ if (this.service.testCharacteristic(this.eveChar.TotalConsumption)) {
141
+ this.service.removeCharacteristic(
142
+ this.service.getCharacteristic(this.eveChar.TotalConsumption),
143
+ )
144
+ }
145
+ if (this.service.testCharacteristic(this.eveChar.ResetTotal)) {
146
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.ResetTotal))
147
+ }
148
+ break
149
+ }
150
+
151
+ // Set a flag for devices with hardware that use multi-channel format
152
+ if (
153
+ platformConsts.devices.switchSCM.includes(this.accessory.context.eweUIID)
154
+ || platformConsts.devices.switchSCMPower.includes(this.accessory.context.eweUIID)
155
+ ) {
156
+ this.isSCM = true
157
+ }
158
+
159
+ // Add the set handler to the outlet on/off characteristic
160
+ this.service
161
+ .getCharacteristic(this.hapChar.On)
162
+ .onSet(async value => this.internalStateUpdate(value))
163
+
164
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
165
+ if (!platform.config.disableNoResponse) {
166
+ this.service.getCharacteristic(this.hapChar.On).onGet(() => {
167
+ if (!this.isOnline) {
168
+ throw new this.hapErr(-70402)
169
+ }
170
+ return this.service.getCharacteristic(this.hapChar.On).value
171
+ })
172
+ }
173
+
174
+ // Pass the accessory to Fakegato to set up with Eve
175
+ this.accessory.eveService = new platform.eveService('energy', this.accessory, {
176
+ log: () => {},
177
+ })
178
+
179
+ // Set up extra features for outlets that provide power readings
180
+ if (this.powerReadings) {
181
+ // TotalConsumption is calculated by the plugin with these context readings
182
+ if (!hasProperty(this.accessory.context, 'energyReadings')) {
183
+ this.accessory.context.energyReadings = []
184
+ }
185
+ if (!hasProperty(this.accessory.context, 'energyReadingTotal')) {
186
+ this.accessory.context.energyReadingTotal = 0
187
+ }
188
+
189
+ // Add the set handler to the outlet eve reset total energy characteristic
190
+ this.service.getCharacteristic(this.eveChar.ResetTotal).onSet(() => {
191
+ this.accessory.context.energyReadings = []
192
+ this.accessory.context.energyReadingTotal = 0
193
+ this.service.updateCharacteristic(this.eveChar.TotalConsumption, 0)
194
+ })
195
+
196
+ // Set up an interval for the plugin to calculate an approx total consumption
197
+ this.intervalPower = setInterval(() => {
198
+ // Every 30 seconds start with a zero reading
199
+ let total = 0
200
+
201
+ // Check we have had readings within the previous 30 seconds
202
+ if (this.accessory.context.energyReadings.length > 0) {
203
+ // Accumulate the total from the energy readings
204
+ this.accessory.context.energyReadings.forEach((x) => {
205
+ total += x
206
+ })
207
+
208
+ // Divide this by the number of entries to get an average W5m
209
+ total /= this.accessory.context.energyReadings.length
210
+
211
+ // Convert this to Wh then kWh
212
+ total /= 12
213
+ total /= 1000
214
+
215
+ // Accumulate the grand total that Eve reads as the total consumption
216
+ this.accessory.context.energyReadingTotal += total
217
+ }
218
+
219
+ // Reset the array for each 30 second readings
220
+ this.accessory.context.energyReadings = []
221
+
222
+ // Update Eve with the new grand total
223
+ this.service.updateCharacteristic(
224
+ this.eveChar.TotalConsumption,
225
+ this.accessory.context.energyReadingTotal,
226
+ )
227
+ }, 30000)
228
+
229
+ // Set up an interval to get eWeLink to send power updates
230
+ setTimeout(() => {
231
+ this.internalUIUpdate()
232
+ this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
233
+ }, 5000)
234
+
235
+ // Stop the intervals on Homebridge shutdown
236
+ platform.api.on('shutdown', () => {
237
+ clearInterval(this.intervalPoll)
238
+ clearInterval(this.intervalPower)
239
+ })
240
+ }
241
+
242
+ // Output the customised options to the log
243
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
244
+ const opts = JSON.stringify({
245
+ inUsePowerThreshold: this.inUsePowerThreshold,
246
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
247
+ showAs: 'outlet',
248
+ })
249
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
250
+ }
251
+
252
+ async internalStateUpdate(value) {
253
+ try {
254
+ const newValue = value ? 'on' : 'off'
255
+ if (newValue === this.cacheState) {
256
+ return
257
+ }
258
+ const params = {}
259
+ if (this.isSCM) {
260
+ params.switches = [{ switch: newValue, outlet: 0 }]
261
+ } else {
262
+ params.switch = newValue
263
+ }
264
+ await this.platform.sendDeviceUpdate(this.accessory, params)
265
+ this.cacheState = newValue
266
+ this.accessory.eveService.addEntry({ status: value ? 1 : 0 })
267
+ if (this.enableLogging) {
268
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
269
+ }
270
+ } catch (err) {
271
+ this.platform.deviceUpdateError(this.accessory, err, true)
272
+ setTimeout(() => {
273
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
274
+ }, 2000)
275
+ throw new this.hapErr(-70402)
276
+ }
277
+ }
278
+
279
+ async internalUIUpdate() {
280
+ try {
281
+ // Skip polling if device isn't online
282
+ if (!this.isOnline) {
283
+ return
284
+ }
285
+
286
+ // Send the params to request the updates
287
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
288
+ } catch (err) {
289
+ // Suppress errors here
290
+ }
291
+ }
292
+
293
+ async externalUpdate(params) {
294
+ try {
295
+ if (!this.isSCM && params.switch && params.switch !== this.cacheState) {
296
+ this.cacheState = params.switch
297
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
298
+ this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
299
+ if (params.updateSource && this.enableLogging) {
300
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
301
+ }
302
+ }
303
+ if (this.isSCM && params.switches && params.switches[0].switch !== this.cacheState) {
304
+ this.cacheState = params.switches[0].switch
305
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
306
+ this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
307
+ if (params.updateSource && this.enableLogging) {
308
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
309
+ }
310
+ }
311
+ if (!this.powerReadings) {
312
+ return
313
+ }
314
+ let logger = false
315
+ let power
316
+ let voltage
317
+ let current
318
+ if (hasProperty(params, 'power')) {
319
+ power = Math.round((Number.parseFloat(params.power) * 100) / this.divisor) / 100
320
+ this.service.updateCharacteristic(
321
+ this.hapChar.OutletInUse,
322
+ this.cacheState === 'on' && power > this.inUsePowerThreshold,
323
+ )
324
+ this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
325
+ this.accessory.eveService.addEntry({ power })
326
+ this.accessory.context.energyReadings.push(power)
327
+ logger = true
328
+ }
329
+ if (hasProperty(params, 'voltage')) {
330
+ voltage = Math.round((Number.parseFloat(params.voltage) * 100) / this.divisor) / 100
331
+ this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
332
+ logger = true
333
+ }
334
+ if (hasProperty(params, 'current')) {
335
+ current = Math.round((Number.parseFloat(params.current) * 100) / this.divisor) / 100
336
+ this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
337
+ logger = true
338
+ }
339
+ if (params.updateSource && logger && this.enableLogging) {
340
+ this.log(
341
+ '[%s] %s%s%s.',
342
+ this.name,
343
+ power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
344
+ voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
345
+ current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
346
+ )
347
+ }
348
+ } catch (err) {
349
+ this.platform.deviceUpdateError(this.accessory, err, false)
350
+ }
351
+ }
352
+
353
+ markStatus(isOnline) {
354
+ this.isOnline = isOnline
355
+ }
356
+
357
+ async currentState() {
358
+ const toReturn = {}
359
+ toReturn.services = ['outlet']
360
+ toReturn.outlet = {
361
+ state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
362
+ }
363
+ if (this.powerReadings) {
364
+ try {
365
+ toReturn.services.push('power')
366
+ toReturn.power = {
367
+ state: await this.platform.sendDeviceUpdate(this.accessory, { hundredDaysKwh: 'get' }),
368
+ voltage: this.service.testCharacteristic(this.eveChar.Voltage)
369
+ ? this.service.getCharacteristic(this.eveChar.Voltage).value
370
+ : undefined,
371
+ power: this.service.testCharacteristic(this.eveChar.CurrentConsumption)
372
+ ? this.service.getCharacteristic(this.eveChar.CurrentConsumption).value
373
+ : undefined,
374
+ current: this.service.testCharacteristic(this.eveChar.ElectricCurrent)
375
+ ? this.service.getCharacteristic(this.eveChar.ElectricCurrent).value
376
+ : undefined,
377
+ }
378
+ } catch (err) {
379
+ // Suppress errors here
380
+ }
381
+ }
382
+ return toReturn
383
+ }
384
+ }