@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,15 @@
1
+ import { antfu } from '@antfu/eslint-config'
2
+
3
+ /** @type {typeof antfu} */
4
+ export default antfu(
5
+ {
6
+ ignores: [],
7
+ jsx: false,
8
+ rules: {
9
+ 'new-cap': 'off',
10
+ 'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
11
+ 'unused-imports/no-unused-vars': ['error', { caughtErrors: 'none' }],
12
+ },
13
+ typescript: false,
14
+ },
15
+ )
@@ -0,0 +1,508 @@
1
+ import { Buffer } from 'node:buffer'
2
+
3
+ export default class {
4
+ constructor(platform, devicesInHB) {
5
+ // Set up variables from the platform
6
+ this.config = platform.config
7
+ this.debug = platform.config.debug
8
+ this.devicesInHB = devicesInHB
9
+ this.hapChar = platform.api.hap.Characteristic
10
+ this.hapServ = platform.api.hap.Service
11
+ this.hapUUIDGen = platform.api.hap.uuid.generate
12
+ this.log = platform.log
13
+ }
14
+
15
+ showHome() {
16
+ // Return the home page for the API
17
+ return `<!doctype html>
18
+ <html lang="en">
19
+ <head>
20
+ <meta charset="utf-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1">
22
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
23
+ <title>homebridge-ewelink API Docs</title>
24
+ </head>
25
+ <body>
26
+ <div class="container-fluid my-3">
27
+ <div class="row content">
28
+ <div class="col-lg-2"></div>
29
+ <div class="col-lg-8">
30
+ <p class="text-center">
31
+ <img src="https://user-images.githubusercontent.com/43026681/101325266-63126600-3863-11eb-9382-4a2924f0e540.png" alt="homebridge-ewelink logo" style="width: 60%;">
32
+ <h4 class="text-center">homebridge-ewelink</h3>
33
+ </p>
34
+ <hr class="mb-0">
35
+ <h5 class="mt-4 mb-2">Intro</h5>
36
+ <ul class="small">
37
+ <li>The internal API allows you to query and control your homebridge-ewelink accessories using HTTP requests</li>
38
+ </ul>
39
+ <h5 class="mt-4 mb-2">Documentation</h5>
40
+ <ul class="small">
41
+ <li>All requests are of type <code>HTTP GET</code></li>
42
+ <li>All requests must include an HTTP authentication header in the form:
43
+ <ul>
44
+ <li><code>Authorization: "Basic %%%"</code></li>
45
+ <li>Where <code>%%%</code> is a base64-encoded string of your eWeLink credentials in the form <code>username:password</code></li>
46
+ </ul>
47
+ </li>
48
+ <li>All requests will return a <code>HTTP 200 OK</code> success code with a JSON response</li>
49
+ <li>Success or failure of any request can be determined by the <code>success</code> response property which will be <code>true</code> or <code>false</code></li>
50
+ <li>Error messages will be returned in the <code>error</code> parameter as a <code>string</code></li>
51
+ <li>Replace <code>{hbDeviceId}</code> with the Homebridge ID of the device, e.g. <code>10000abcdeSWX</code></li>
52
+ <li>A note about offline devices:
53
+ <ul>
54
+ <li>Querying any characteristic will return the HomeKit cache value</li>
55
+ <li>Updating a characteristic which matches the HomeKit cache value will return a <code>success:true</code> response</li>
56
+ <li>Updating a characteristic which does not match the HomeKit cache value will return a <code>success:false</code> with an <code>error:"HAP Status Error: -70402"</code> response</li>
57
+ </ul>
58
+ </li>
59
+ </ul>
60
+ <div class="alert alert-secondary small text-center" role="alert" style="display: none;" id="baseUrlBanner"></div>
61
+ <h5 class="mt-4 mb-2">Accessory Query Commands</h5>
62
+ <div class="table-responsive">
63
+ <table class="table table-sm table-hover">
64
+ <thead>
65
+ <tr>
66
+ <th scope="col" class="small" style="width: 50%;">Function</th>
67
+ <th scope="col" class="small" style="width: 50%;">Path</th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>
71
+ <tr>
72
+ <td class="small">
73
+ <strong>Obtain a device list.</strong><br>
74
+ <code>response</code> property is an <code>array</code>, for example:<br>
75
+ <code><pre>{
76
+ "success": true,
77
+ "response":[
78
+ {
79
+ "name": "Bedroom Switch",
80
+ "hbdeviceid": "1000aa1a1aSW1",
81
+ "status": {
82
+ "wan": true,
83
+ "lan": true,
84
+ "ip": "192.168.1.20"
85
+ }
86
+ }
87
+ ]
88
+ }</pre>
89
+ </code>
90
+ </td>
91
+ <td class="align-middle small"><code>/get/devicelist</code></td>
92
+ </tr>
93
+ <tr>
94
+ <td class="small">
95
+ <strong>Obtain a device's current state.</strong><br>
96
+ <code>response</code> property is an <code>object</code>, for example:<br>
97
+ <code><pre>{
98
+ "success": true,
99
+ "response": {
100
+ "status": {
101
+ "wan": true,
102
+ "lan": true,
103
+ "ip": "192.168.1.20"
104
+ },
105
+ "services": ["switch"],
106
+ "switch": {
107
+ "state": "off"
108
+ }
109
+ }
110
+ }</pre>
111
+ </code>
112
+ </td>
113
+ <td class="align-middle small"><code>/get/{hbDeviceId}</code></td>
114
+ </tr>
115
+ </tbody>
116
+ </table>
117
+ </div>
118
+ <ul class="small">
119
+ <li><code>services</code> is an array of services that the device has</li>
120
+ <li>Possible services include <code>switch</code>, <code>outlet</code>, <code>light</code>, <code>fan</code>, <code>temperature</code> and <code>humidity</code>
121
+ <li>Each service will have a corresponding property of characteristic(s)
122
+ <ul>
123
+ <li>In the above example the service is <code>switch</code> and the only characteristic is <code>state</code></li>
124
+ </ul>
125
+ </li>
126
+ <li>Use the service and characteristic when using the below commands to control an accessory</li>
127
+ </ul>
128
+ <h5 class="mt-4 mb-2">Accessory Control Commands</h5>
129
+ <div class="table-responsive">
130
+ <table class="table table-sm table-hover">
131
+ <thead>
132
+ <tr>
133
+ <th scope="col" class="small" style="width: 50%;">Function</th>
134
+ <th scope="col" class="small" style="width: 50%;">Path</th>
135
+ </tr>
136
+ </thead>
137
+ <tbody>
138
+ <tr>
139
+ <td class="small">Template</td>
140
+ <td class="align-middle small"><code>/set/{hbDeviceId}/{service}/{characteristic}/{value}</code></td>
141
+ </tr>
142
+ <tr>
143
+ <td class="small">
144
+ Service: <code>switch</code>.<br>
145
+ Characteristic: <code>state</code>.<br>
146
+ Value: must be <code>on</code>, <code>off</code> or <code>toggle</code>.
147
+ </td>
148
+ <td class="align-middle small"><code>/set/{hbDeviceId}/switch/state/on</code></td>
149
+ </tr>
150
+ <tr>
151
+ <td class="small">
152
+ Service: <code>outlet</code>.<br>
153
+ Characteristic: <code>state</code>.<br>
154
+ Value: must be <code>on</code>, <code>off</code> or <code>toggle</code>.
155
+ </td>
156
+ <td class="align-middle small"><code>/set/{hbDeviceId}/outlet/state/off</code></td>
157
+ </tr>
158
+ <tr>
159
+ <td class="small">
160
+ Service: <code>light</code>.<br>
161
+ Characteristic: <code>state</code>.<br>
162
+ Value: must be <code>on</code>, <code>off</code> or <code>toggle</code>.
163
+ </td>
164
+ <td class="align-middle small"><code>/set/{hbDeviceId}/light/state/on</code></td>
165
+ </tr>
166
+ <tr>
167
+ <td class="small">
168
+ Service: <code>light</code>.<br>
169
+ Characteristic: <code>brightness</code>.<br>
170
+ Value: must be between <code>0</code> and <code>100</code>.
171
+ </td>
172
+ <td class="align-middle small"><code>/set/{hbDeviceId}/light/brightness/54</code></td>
173
+ </tr>
174
+ <tr>
175
+ <td class="small">
176
+ Service: <code>light</code>.<br>
177
+ Characteristic: <code>hue</code>.<br>
178
+ Value: must be between <code>0</code> and <code>360</code>.
179
+ </td>
180
+ <td class="align-middle small"><code>/set/{hbDeviceId}/light/hue/157</code></td>
181
+ </tr>
182
+ <tr>
183
+ <td class="small">
184
+ Service: <code>light</code>.<br>
185
+ Characteristic: <code>colourtemperature</code>.<br>
186
+ Value: must be between <code>140</code> and <code>500</code>.
187
+ </td>
188
+ <td class="align-middle small"><code>/set/{hbDeviceId}/light/colourtemperature/300</code></td>
189
+ </tr>
190
+ <tr>
191
+ <td class="small">
192
+ Service: <code>fan</code>.<br>
193
+ Characteristic: <code>speed</code>.<br>
194
+ Value: must be <code>low</code>, <code>medium</code> or <code>high</code>.
195
+ </td>
196
+ <td class="align-middle small"><code>/set/{hbDeviceId}/fan/speed/medium</code></td>
197
+ </tr>
198
+ </tbody>
199
+ </table>
200
+ </div>
201
+ </div>
202
+ <div class="col-lg-2"></div>
203
+ </div>
204
+ </div>
205
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.min.js" integrity="sha384-lpyLfhYuitXl2zRZ5Bn2fqnhNAKOAaM/0Kr9laMspuaMiZfGmfwRNFh8HlMy49eQ" crossorigin="anonymous"></script>
206
+ <script>
207
+ const base = window.location.href.slice(0, -1)
208
+ const banner = document.getElementById('baseUrlBanner')
209
+ banner.innerHTML = 'All paths are relative to <strong><code>' + base + '</code></strong>'
210
+ banner.style.display = 'block'
211
+ </script>
212
+ </body>
213
+ </html>`
214
+ }
215
+
216
+ async action(req) {
217
+ // Log the request if appropriate
218
+ if (this.debug) {
219
+ this.log('API request [%s].', req.url)
220
+ }
221
+
222
+ // Authenticate the request
223
+ if (
224
+ !req.headers
225
+ || !req.headers.authorization
226
+ || !req.headers.authorization.includes('Basic ')
227
+ ) {
228
+ throw new Error('Invalid authentication')
229
+ }
230
+ const encodedCreds = req.headers.authorization.split(' ')[1]
231
+ const buff = Buffer.from(encodedCreds, 'base64')
232
+ const decodedCreds = buff
233
+ .toString('utf8')
234
+ .replace(/\r\n|\n|\r/g, '')
235
+ .trim()
236
+ const [user, pass] = decodedCreds.split(':')
237
+ if (user !== this.config.username || pass !== this.config.password) {
238
+ throw new Error('Invalid authentication')
239
+ }
240
+
241
+ // Obtain the parts of the request url
242
+ const pathParts = req.url.split('/')
243
+ const action = pathParts[1]
244
+ const device = pathParts[2]
245
+ const servToUpdate = pathParts[3]
246
+ const charToUpdate = pathParts[4]
247
+ const newValue = pathParts[5]
248
+
249
+ // Check an action was specified
250
+ if (!action) {
251
+ throw new Error('No action specified')
252
+ }
253
+
254
+ // Check the action is either get or set
255
+ if (!['get', 'set'].includes(action)) {
256
+ throw new Error('Action must be \'get\' or \'set\'')
257
+ }
258
+
259
+ // Check a device was specified
260
+ if (!device) {
261
+ throw new Error('No accessory specified')
262
+ }
263
+
264
+ // Special case for the device list
265
+ if (device === 'devicelist') {
266
+ const deviceList = []
267
+ this.devicesInHB.forEach((accessory) => {
268
+ if (accessory.context.hidden) {
269
+ return
270
+ }
271
+ deviceList.push({
272
+ name: accessory.displayName,
273
+ hbdeviceid: accessory.context.hbDeviceId,
274
+ status: {
275
+ wan: !!accessory.context.reachableWAN,
276
+ lan: !!accessory.context.reachableLAN,
277
+ ip: accessory.context.ip || false,
278
+ },
279
+ })
280
+ })
281
+ return deviceList
282
+ }
283
+
284
+ // Try and find the device in Homebridge
285
+ const uuid = this.hapUUIDGen(device)
286
+ if (!this.devicesInHB.has(uuid)) {
287
+ throw new Error('Accessory not found in Homebridge')
288
+ }
289
+
290
+ // Obtain the corresponding accessory
291
+ const accessory = this.devicesInHB.get(uuid)
292
+
293
+ // Check the accessory isn't hidden from Homebridge
294
+ if (accessory.context.hidden) {
295
+ throw new Error('Accessory not found in Homebridge')
296
+ }
297
+
298
+ // Check the device is controllable
299
+ if (!accessory.control) {
300
+ throw new Error('Accessory has not been initialised yet')
301
+ }
302
+
303
+ // If the action is 'get' then return the properties
304
+ if (action === 'get') {
305
+ if (!accessory.control.currentState()) {
306
+ throw new Error('Accessory does not yet support querying')
307
+ }
308
+ return {
309
+ status: {
310
+ wan: !!accessory.context.reachableWAN,
311
+ lan: !!accessory.context.reachableLAN,
312
+ ip: accessory.context.ip || false,
313
+ },
314
+ ...(await accessory.control.currentState()),
315
+ }
316
+ }
317
+
318
+ // From now on the action must be 'set'
319
+
320
+ // Check a servToUpdate to update was specified
321
+ if (!servToUpdate) {
322
+ throw new Error('No service specified')
323
+ }
324
+
325
+ let service
326
+ switch (servToUpdate) {
327
+ case 'fan':
328
+ service = this.hapServ.Fan
329
+ break
330
+ case 'light':
331
+ service = this.hapServ.Lightbulb
332
+ break
333
+ case 'outlet':
334
+ service = this.hapServ.Outlet
335
+ break
336
+ case 'switch':
337
+ service = this.hapServ.Switch
338
+ break
339
+ default:
340
+ throw new Error('Invalid service specified')
341
+ }
342
+
343
+ if (!accessory.getService(service)) {
344
+ throw new Error(`Accessory does not have service:${servToUpdate}`)
345
+ }
346
+
347
+ // Check an charToUpdate for a servToUpdate was specified
348
+ if (!charToUpdate) {
349
+ throw new Error(`No characteristic specified for service:${servToUpdate}`)
350
+ }
351
+
352
+ const accServ = accessory.getService(service)
353
+
354
+ // These variables depend on the charToUpdate that was supplied
355
+ let charName
356
+
357
+ switch (charToUpdate) {
358
+ case 'adaptivelighting':
359
+ case 'colourtemperature':
360
+ charName = accServ.testCharacteristic(this.hapChar.ColorTemperature)
361
+ ? this.hapChar.ColorTemperature
362
+ : false
363
+ break
364
+ case 'brightness':
365
+ charName = accServ.testCharacteristic(this.hapChar.Brightness)
366
+ ? this.hapChar.Brightness
367
+ : false
368
+ break
369
+ case 'hue':
370
+ charName = accServ.testCharacteristic(this.hapChar.Hue) ? this.hapChar.Hue : false
371
+ break
372
+ case 'speed':
373
+ charName = accServ.testCharacteristic(this.hapChar.RotationSpeed)
374
+ ? this.hapChar.RotationSpeed
375
+ : false
376
+ break
377
+ case 'state':
378
+ charName = accServ.testCharacteristic(this.hapChar.On) ? this.hapChar.On : false
379
+ break
380
+ default:
381
+ throw new Error(`Invalid characteristic specified for service:${servToUpdate}`)
382
+ }
383
+
384
+ // Check that the accessory has the corresponding characteristic for the charToUpdate
385
+ if (!charName) {
386
+ throw new Error(
387
+ `Accessory service:${servToUpdate} does not support characteristic:${charToUpdate}`,
388
+ )
389
+ }
390
+
391
+ // Check a new status was supplied if the action is set
392
+ if (!newValue) {
393
+ throw new Error(`No value specified for characteristic:${charToUpdate}`)
394
+ }
395
+
396
+ let newHKStatus
397
+ switch (charToUpdate) {
398
+ case 'brightness': {
399
+ // The new status for brightness must be an integer between 0 and 100
400
+ newHKStatus = Number.parseInt(newValue, 10)
401
+ if (Number.isNaN(newHKStatus) || newHKStatus < 0 || newHKStatus > 100) {
402
+ throw new Error('Value must be integer 0-100 for characteristic:brightness')
403
+ }
404
+
405
+ // Check the accessory has a correct set handler for on/off
406
+ if (!accessory.control.internalBrightnessUpdate) {
407
+ throw new Error('Function to control accessory not found')
408
+ }
409
+
410
+ // Call the set handler to send the request to eWeLink
411
+ await accessory.control.internalBrightnessUpdate(newHKStatus)
412
+ break
413
+ }
414
+ case 'colourtemperature': {
415
+ // The new status for colour temperature must be an integer between 140 and 500
416
+ newHKStatus = Number.parseInt(newValue, 10)
417
+ if (Number.isNaN(newHKStatus) || newHKStatus < 140 || newHKStatus > 500) {
418
+ throw new Error('Value must be integer 140-500 for characteristic:colourtemperature')
419
+ }
420
+
421
+ // Check the accessory has a correct set handler for on/off
422
+ if (!accessory.control.internalCTUpdate) {
423
+ throw new Error(`Accessory does not support controlling characteristic:${charToUpdate}`)
424
+ }
425
+
426
+ // Call the set handler to send the request to eWeLink
427
+ if (accessory?.alController?.isAdaptiveLightingActive()) {
428
+ accessory.alController.disableAdaptiveLighting()
429
+ }
430
+ await accessory.control.internalCTUpdate(newHKStatus)
431
+ break
432
+ }
433
+ case 'hue': {
434
+ // The new status for hue must be an integer between 0 and 360
435
+ newHKStatus = Number.parseInt(newValue, 10)
436
+ if (Number.isNaN(newHKStatus) || newHKStatus < 0 || newHKStatus > 360) {
437
+ throw new Error('Value must be integer 0-360 for characteristic:hue')
438
+ }
439
+
440
+ // Check the accessory has a correct set handler for on/off
441
+ if (!accessory.control.internalColourUpdate) {
442
+ throw new Error('Function to control accessory not found')
443
+ }
444
+
445
+ // Call the set handler to send the request to eWeLink
446
+ if (accessory?.alController?.isAdaptiveLightingActive()) {
447
+ accessory.alController.disableAdaptiveLighting()
448
+ }
449
+ await accessory.control.internalColourUpdate(newHKStatus)
450
+ break
451
+ }
452
+ case 'speed':
453
+ // The new status for speed must be low, medium or high
454
+ switch (newValue) {
455
+ case 'low':
456
+ newHKStatus = 33
457
+ break
458
+ case 'medium':
459
+ newHKStatus = 66
460
+ break
461
+ case 'high':
462
+ newHKStatus = 99
463
+ break
464
+ default:
465
+ throw new Error('Value must be \'low\', \'medium\' or \'high\' for characteristic:speed')
466
+ }
467
+
468
+ // Check the accessory has a correct set handler for speed
469
+ if (!accessory.control.internalSpeedUpdate) {
470
+ throw new Error('Function to control accessory not found')
471
+ }
472
+
473
+ // Call the set handler to send the request to eWeLink
474
+ await accessory.control.internalSpeedUpdate(newHKStatus)
475
+ break
476
+ case 'state':
477
+ // The new status for state must be on, off or toggle
478
+ switch (newValue) {
479
+ case 'on':
480
+ newHKStatus = true
481
+ break
482
+ case 'off':
483
+ newHKStatus = false
484
+ break
485
+ case 'toggle':
486
+ newHKStatus = !accServ.getCharacteristic(this.hapChar.On).value
487
+ break
488
+ default:
489
+ throw new Error('Value must be \'on\', \'off\' or \'toggle\' for characteristic:state')
490
+ }
491
+
492
+ // Check the accessory has a correct set handler for on/off
493
+ if (!accessory.control.internalStateUpdate) {
494
+ throw new Error('Function to control accessory not found')
495
+ }
496
+
497
+ // Call the set handler to send the request to eWeLink
498
+ await accessory.control.internalStateUpdate(newHKStatus)
499
+ break
500
+ default:
501
+ throw new Error(`Invalid value for characteristic:${charToUpdate}`)
502
+ }
503
+
504
+ // The eWeLink request was successful so update the characteristic in HomeKit
505
+ accServ.updateCharacteristic(charName, newHKStatus)
506
+ return true
507
+ }
508
+ }