@switchbot/homebridge-switchbot 3.7.1-beta.0 → 3.7.1-beta.10

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 (110) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/config.schema.json +7 -1
  3. package/dist/device/blindtilt.d.ts +3 -1
  4. package/dist/device/blindtilt.d.ts.map +1 -1
  5. package/dist/device/blindtilt.js +61 -28
  6. package/dist/device/blindtilt.js.map +1 -1
  7. package/dist/device/bot.d.ts +1 -0
  8. package/dist/device/bot.d.ts.map +1 -1
  9. package/dist/device/bot.js +102 -80
  10. package/dist/device/bot.js.map +1 -1
  11. package/dist/device/ceilinglight.d.ts +1 -0
  12. package/dist/device/ceilinglight.d.ts.map +1 -1
  13. package/dist/device/ceilinglight.js +53 -28
  14. package/dist/device/ceilinglight.js.map +1 -1
  15. package/dist/device/colorbulb.d.ts +1 -0
  16. package/dist/device/colorbulb.d.ts.map +1 -1
  17. package/dist/device/colorbulb.js +53 -28
  18. package/dist/device/colorbulb.js.map +1 -1
  19. package/dist/device/contact.d.ts +1 -0
  20. package/dist/device/contact.d.ts.map +1 -1
  21. package/dist/device/contact.js +53 -28
  22. package/dist/device/contact.js.map +1 -1
  23. package/dist/device/curtain.d.ts +2 -0
  24. package/dist/device/curtain.d.ts.map +1 -1
  25. package/dist/device/curtain.js +74 -43
  26. package/dist/device/curtain.js.map +1 -1
  27. package/dist/device/device.d.ts.map +1 -1
  28. package/dist/device/device.js +247 -394
  29. package/dist/device/device.js.map +1 -1
  30. package/dist/device/fan.d.ts +1 -0
  31. package/dist/device/fan.d.ts.map +1 -1
  32. package/dist/device/fan.js +53 -28
  33. package/dist/device/fan.js.map +1 -1
  34. package/dist/device/hub.d.ts +1 -0
  35. package/dist/device/hub.d.ts.map +1 -1
  36. package/dist/device/hub.js +53 -28
  37. package/dist/device/hub.js.map +1 -1
  38. package/dist/device/humidifier.d.ts +1 -0
  39. package/dist/device/humidifier.d.ts.map +1 -1
  40. package/dist/device/humidifier.js +53 -28
  41. package/dist/device/humidifier.js.map +1 -1
  42. package/dist/device/iosensor.d.ts +1 -0
  43. package/dist/device/iosensor.d.ts.map +1 -1
  44. package/dist/device/iosensor.js +53 -28
  45. package/dist/device/iosensor.js.map +1 -1
  46. package/dist/device/lightstrip.d.ts +1 -0
  47. package/dist/device/lightstrip.d.ts.map +1 -1
  48. package/dist/device/lightstrip.js +53 -28
  49. package/dist/device/lightstrip.js.map +1 -1
  50. package/dist/device/lock.d.ts +1 -0
  51. package/dist/device/lock.d.ts.map +1 -1
  52. package/dist/device/lock.js +54 -29
  53. package/dist/device/lock.js.map +1 -1
  54. package/dist/device/meter.d.ts +1 -0
  55. package/dist/device/meter.d.ts.map +1 -1
  56. package/dist/device/meter.js +53 -28
  57. package/dist/device/meter.js.map +1 -1
  58. package/dist/device/meterplus.d.ts +1 -0
  59. package/dist/device/meterplus.d.ts.map +1 -1
  60. package/dist/device/meterplus.js +53 -28
  61. package/dist/device/meterplus.js.map +1 -1
  62. package/dist/device/motion.d.ts +1 -0
  63. package/dist/device/motion.d.ts.map +1 -1
  64. package/dist/device/motion.js +53 -28
  65. package/dist/device/motion.js.map +1 -1
  66. package/dist/device/plug.d.ts +1 -0
  67. package/dist/device/plug.d.ts.map +1 -1
  68. package/dist/device/plug.js +54 -29
  69. package/dist/device/plug.js.map +1 -1
  70. package/dist/device/robotvacuumcleaner.d.ts +1 -0
  71. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  72. package/dist/device/robotvacuumcleaner.js +53 -28
  73. package/dist/device/robotvacuumcleaner.js.map +1 -1
  74. package/dist/device/waterdetector.d.ts +1 -0
  75. package/dist/device/waterdetector.d.ts.map +1 -1
  76. package/dist/device/waterdetector.js +53 -28
  77. package/dist/device/waterdetector.js.map +1 -1
  78. package/dist/irdevice/irdevice.d.ts.map +1 -1
  79. package/dist/irdevice/irdevice.js +30 -133
  80. package/dist/irdevice/irdevice.js.map +1 -1
  81. package/dist/platform.d.ts +19 -18
  82. package/dist/platform.d.ts.map +1 -1
  83. package/dist/platform.js +369 -524
  84. package/dist/platform.js.map +1 -1
  85. package/dist/settings.d.ts +1 -0
  86. package/dist/settings.d.ts.map +1 -1
  87. package/dist/settings.js.map +1 -1
  88. package/package.json +13 -13
  89. package/src/device/blindtilt.ts +59 -27
  90. package/src/device/bot.ts +98 -77
  91. package/src/device/ceilinglight.ts +51 -27
  92. package/src/device/colorbulb.ts +50 -26
  93. package/src/device/contact.ts +50 -26
  94. package/src/device/curtain.ts +71 -40
  95. package/src/device/device.ts +283 -395
  96. package/src/device/fan.ts +50 -26
  97. package/src/device/hub.ts +50 -26
  98. package/src/device/humidifier.ts +50 -26
  99. package/src/device/iosensor.ts +50 -26
  100. package/src/device/lightstrip.ts +50 -26
  101. package/src/device/lock.ts +51 -27
  102. package/src/device/meter.ts +50 -26
  103. package/src/device/meterplus.ts +50 -26
  104. package/src/device/motion.ts +50 -26
  105. package/src/device/plug.ts +51 -27
  106. package/src/device/robotvacuumcleaner.ts +50 -26
  107. package/src/device/waterdetector.ts +50 -26
  108. package/src/irdevice/irdevice.ts +54 -134
  109. package/src/platform.ts +379 -529
  110. package/src/settings.ts +1 -0
package/dist/platform.js CHANGED
@@ -40,6 +40,10 @@ import { readFileSync, writeFileSync } from 'fs';
40
40
  import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes';
41
41
  import { PLATFORM_NAME, PLUGIN_NAME, Devices, deleteWebhook, queryWebhook, setupWebhook, updateWebhook } from './settings.js';
42
42
  import { isBlindTiltDevice, isCurtainDevice, sleep } from './utils.js';
43
+ /*
44
+ * For Testing Locally:
45
+ * import { SwitchBotModel } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
46
+ */
43
47
  import { SwitchBotModel } from 'node-switchbot';
44
48
  /**
45
49
  * HomebridgePlatform
@@ -47,9 +51,11 @@ import { SwitchBotModel } from 'node-switchbot';
47
51
  * parse the user config and discover/register accessories with Homebridge.
48
52
  */
49
53
  export class SwitchBotPlatform {
50
- accessories;
54
+ // Platform properties
55
+ accessories = [];
51
56
  api;
52
57
  log;
58
+ // Configuration properties
53
59
  version;
54
60
  Logging;
55
61
  debugMode;
@@ -58,15 +64,16 @@ export class SwitchBotPlatform {
58
64
  platformConfig;
59
65
  platformLogging;
60
66
  config;
67
+ // MQTT and Webhook properties
61
68
  mqttClient = null;
62
69
  webhookEventListener = null;
70
+ // External APIs
63
71
  eve;
64
72
  fakegatoAPI;
73
+ // Event Handlers
65
74
  webhookEventHandler = {};
66
75
  bleEventHandler = {};
67
76
  constructor(log, config, api) {
68
- // initialize
69
- this.accessories = [];
70
77
  this.api = api;
71
78
  this.log = log;
72
79
  // only load if configured
@@ -104,27 +111,42 @@ export class SwitchBotPlatform {
104
111
  // in order to ensure they weren't added to homebridge already. This event can also be used
105
112
  // to start discovery of new accessories.
106
113
  this.api.on('didFinishLaunching', async () => {
107
- this.debugLog('Executed didFinishLaunching callback');
114
+ await this.debugLog('Executed didFinishLaunching callback');
108
115
  // run the method to discover / register your devices as accessories
109
116
  try {
110
117
  if (this.config.credentials?.openToken && !this.config.credentials.token) {
111
118
  await this.updateToken();
112
119
  }
113
120
  else if (this.config.credentials?.token && !this.config.credentials?.secret) {
114
- this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.');
121
+ await this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.');
115
122
  }
116
123
  else {
117
- this.discoverDevices();
124
+ await this.discoverDevices();
118
125
  }
119
126
  }
120
127
  catch (e) {
121
- this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
128
+ await this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
122
129
  await this.debugErrorLog(`Failed to Discover, Error: ${e}`);
123
130
  }
124
131
  });
125
- this.setupMqtt();
126
- this.setupwebhook();
127
- this.setupBlE();
132
+ try {
133
+ this.setupMqtt();
134
+ }
135
+ catch (e) {
136
+ this.errorLog(`Setup MQTT, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
137
+ }
138
+ try {
139
+ this.setupwebhook();
140
+ }
141
+ catch (e) {
142
+ this.errorLog(`Setup Webhook, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
143
+ }
144
+ try {
145
+ this.setupBlE();
146
+ }
147
+ catch (e) {
148
+ this.errorLog(`Setup Platform BLE, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
149
+ }
128
150
  }
129
151
  async setupMqtt() {
130
152
  if (this.config.options?.mqttURL) {
@@ -141,7 +163,7 @@ export class SwitchBotPlatform {
141
163
  this.mqttClient.subscribe('homebridge-switchbot/webhook/+');
142
164
  this.mqttClient.on('message', async (topic, message) => {
143
165
  try {
144
- await await this.debugLog(`Received Webhook via MQTT: ${topic}=${message}`);
166
+ await this.debugLog(`Received Webhook via MQTT: ${topic}=${message}`);
145
167
  const context = JSON.parse(message.toString());
146
168
  this.webhookEventHandler[context.deviceMac]?.(context);
147
169
  }
@@ -173,17 +195,14 @@ export class SwitchBotPlatform {
173
195
  const body = JSON.parse(data);
174
196
  await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`);
175
197
  if (this.config.options?.mqttURL) {
176
- const mac = body.context.deviceMac
177
- ?.toLowerCase()
178
- .match(/[\s\S]{1,2}/g)
179
- ?.join(':');
198
+ const mac = body.context.deviceMac?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
180
199
  const options = this.config.options?.mqttPubOptions || {};
181
200
  this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options);
182
201
  }
183
202
  this.webhookEventHandler[body.context.deviceMac]?.(body.context);
184
203
  }
185
204
  catch (e) {
186
- this.errorLog(`Failed to handle webhook event. Error:${e}`);
205
+ await this.errorLog(`Failed to handle webhook event. Error:${e}`);
187
206
  }
188
207
  });
189
208
  response.writeHead(200, { 'Content-Type': 'text/plain' });
@@ -200,7 +219,7 @@ export class SwitchBotPlatform {
200
219
  }).listen(port ? port : 80);
201
220
  }
202
221
  catch (e) {
203
- this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
222
+ await this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
204
223
  return;
205
224
  }
206
225
  try {
@@ -214,16 +233,14 @@ export class SwitchBotPlatform {
214
233
  }),
215
234
  });
216
235
  const response = await body.json();
217
- await this.debugLog(`setupWebhook: url:${url}`);
218
- await this.debugLog(`setupWebhook: body:${JSON.stringify(response)}`);
219
- await this.debugLog(`setupWebhook: statusCode:${statusCode}`);
236
+ await this.debugLog(`setupWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
220
237
  if (statusCode !== 200 || response?.statusCode !== 100) {
221
- this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
238
+ await this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
222
239
  + `message:${response?.message}`);
223
240
  }
224
241
  }
225
242
  catch (e) {
226
- this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
243
+ await this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
227
244
  }
228
245
  try {
229
246
  const { body, statusCode } = await request(updateWebhook, {
@@ -236,15 +253,13 @@ export class SwitchBotPlatform {
236
253
  }),
237
254
  });
238
255
  const response = await body.json();
239
- await this.debugLog(`updateWebhook: url:${url}`);
240
- await this.debugLog(`updateWebhook: body:${JSON.stringify(response)}`);
241
- await this.debugLog(`updateWebhook: statusCode:${statusCode}`);
256
+ await this.debugLog(`updateWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
242
257
  if (statusCode !== 200 || response?.statusCode !== 100) {
243
- this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
258
+ await this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
244
259
  }
245
260
  }
246
261
  catch (e) {
247
- this.errorLog(`Failed to update webhook. Error:${e.message}`);
262
+ await this.errorLog(`Failed to update webhook. Error:${e.message}`);
248
263
  }
249
264
  try {
250
265
  const { body, statusCode } = await request(queryWebhook, {
@@ -258,14 +273,14 @@ export class SwitchBotPlatform {
258
273
  await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`);
259
274
  await this.debugLog(`queryWebhook: statusCode:${statusCode}`);
260
275
  if (statusCode !== 200 || response?.statusCode !== 100) {
261
- this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
276
+ await this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
262
277
  }
263
278
  else {
264
- this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
279
+ await this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
265
280
  }
266
281
  }
267
282
  catch (e) {
268
- this.errorLog(`Failed to query webhook. Error:${e}`);
283
+ await this.errorLog(`Failed to query webhook. Error:${e}`);
269
284
  }
270
285
  this.api.on('shutdown', async () => {
271
286
  try {
@@ -280,14 +295,14 @@ export class SwitchBotPlatform {
280
295
  const response = await body.json();
281
296
  await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
282
297
  if (statusCode !== 200 || response?.statusCode !== 100) {
283
- this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
298
+ await this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
284
299
  }
285
300
  else {
286
- this.infoLog('Unregistered webhook to close listening.');
301
+ await this.infoLog('Unregistered webhook to close listening.');
287
302
  }
288
303
  }
289
304
  catch (e) {
290
- this.errorLog(`Failed to delete webhook. Error:${e.message}`);
305
+ await this.errorLog(`Failed to delete webhook. Error:${e.message}`);
291
306
  }
292
307
  });
293
308
  }
@@ -319,10 +334,10 @@ export class SwitchBotPlatform {
319
334
  this.api.on('shutdown', async () => {
320
335
  try {
321
336
  switchbot.stopScan();
322
- this.infoLog('Stopped BLE scanning to close listening.');
337
+ await this.infoLog('Stopped BLE scanning to close listening.');
323
338
  }
324
339
  catch (e) {
325
- this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message}`);
340
+ await this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message}`);
326
341
  }
327
342
  });
328
343
  }
@@ -336,7 +351,8 @@ export class SwitchBotPlatform {
336
351
  * It should be used to setup event handlers for characteristics and update respective values.
337
352
  */
338
353
  async configureAccessory(accessory) {
339
- await this.debugLog(`Loading accessory from cache: ${accessory.displayName}`);
354
+ const { displayName } = accessory;
355
+ await this.debugLog(`Loading accessory from cache: ${displayName}`);
340
356
  // add the restored accessory to the accessories cache so we can track if it has already been registered
341
357
  this.accessories.push(accessory);
342
358
  }
@@ -344,7 +360,7 @@ export class SwitchBotPlatform {
344
360
  * Verify the config passed to the plugin is valid
345
361
  */
346
362
  async verifyConfig() {
347
- this.debugLog('Verifying Config');
363
+ await this.debugLog('Verifying Config');
348
364
  this.config = this.config || {};
349
365
  this.config.options = this.config.options || {};
350
366
  const platformConfig = {};
@@ -379,10 +395,10 @@ export class SwitchBotPlatform {
379
395
  for (const irDeviceConfig of this.config.options.irdevices) {
380
396
  if (!irDeviceConfig.hide_device) {
381
397
  if (!irDeviceConfig.deviceId) {
382
- this.errorLog('The devices config section is missing the *Device ID* in the config. Please check your config.');
398
+ await this.errorLog('The devices config section is missing the *Device ID* in the config. Please check your config.');
383
399
  }
384
400
  if (!irDeviceConfig.deviceId && !irDeviceConfig.configRemoteType) {
385
- this.errorLog('The devices config section is missing the *Device Type* in the config. Please check your config.');
401
+ await this.errorLog('The devices config section is missing the *Device Type* in the config. Please check your config.');
386
402
  }
387
403
  }
388
404
  }
@@ -394,16 +410,16 @@ export class SwitchBotPlatform {
394
410
  if (!this.config.options.refreshRate) {
395
411
  // default 120 seconds (2 minutes)
396
412
  this.config.options.refreshRate = 120;
397
- this.debugWarnLog('Using Default Refresh Rate (2 minutes).');
413
+ await this.debugWarnLog('Using Default Refresh Rate (2 minutes).');
398
414
  }
399
415
  if (!this.config.options.pushRate) {
400
416
  // default 100 milliseconds
401
417
  this.config.options.pushRate = 0.1;
402
- this.debugWarnLog('Using Default Push Rate.');
418
+ await this.debugWarnLog('Using Default Push Rate.');
403
419
  }
404
420
  if (!this.config.options.maxRetries) {
405
421
  this.config.options.maxRetries = 5;
406
- this.debugWarnLog('Using Default Max Retries.');
422
+ await this.debugWarnLog('Using Default Max Retries.');
407
423
  }
408
424
  else {
409
425
  this.maxRetries = this.config.options.maxRetries;
@@ -411,23 +427,23 @@ export class SwitchBotPlatform {
411
427
  if (!this.config.options.delayBetweenRetries) {
412
428
  // default 3 seconds
413
429
  this.config.options.delayBetweenRetries = 3000;
414
- this.debugWarnLog('Using Default Delay Between Retries.');
430
+ await this.debugWarnLog('Using Default Delay Between Retries.');
415
431
  }
416
432
  else {
417
433
  this.delayBetweenRetries = this.config.options.delayBetweenRetries * 1000;
418
434
  }
419
435
  if (!this.config.credentials && !this.config.options) {
420
- this.debugWarnLog('Missing Credentials');
436
+ await this.debugWarnLog('Missing Credentials');
421
437
  }
422
438
  else if (this.config.credentials && !this.config.credentials.notice) {
423
439
  if (!this.config.credentials?.token) {
424
- this.debugErrorLog('Missing token');
425
- this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
440
+ await this.debugErrorLog('Missing token');
441
+ await this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
426
442
  }
427
443
  if (this.config.credentials?.token) {
428
444
  if (!this.config.credentials?.secret) {
429
- this.debugErrorLog('Missing secret');
430
- this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
445
+ await this.debugErrorLog('Missing secret');
446
+ await this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
431
447
  }
432
448
  }
433
449
  }
@@ -460,12 +476,12 @@ export class SwitchBotPlatform {
460
476
  }
461
477
  // Move openToken to token
462
478
  if (!this.config.credentials.secret) {
463
- this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken,'
479
+ await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken,'
464
480
  + ' "openToken" cconfig has been moved to the "token" config');
465
481
  this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.');
466
482
  }
467
483
  else {
468
- this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, '
484
+ await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, '
469
485
  + '"openToken" config has been moved to the "token" config, please restart Homebridge.');
470
486
  }
471
487
  // set the refresh token
@@ -476,10 +492,10 @@ export class SwitchBotPlatform {
476
492
  await this.debugWarnLog(`token: ${pluginConfig.credentials.token}`);
477
493
  // save the config, ensuring we maintain pretty json
478
494
  writeFileSync(this.api.user.configPath(), JSON.stringify(currentConfig, null, 4));
479
- this.verifyConfig();
495
+ await this.verifyConfig();
480
496
  }
481
497
  catch (e) {
482
- this.errorLog(`Update Token: ${e}`);
498
+ await this.errorLog(`Update Token: ${e}`);
483
499
  }
484
500
  }
485
501
  generateHeaders = () => {
@@ -499,326 +515,215 @@ export class SwitchBotPlatform {
499
515
  'Content-Type': 'application/json',
500
516
  };
501
517
  };
502
- /**
503
- * this method discovers devices
504
- *
505
- const t = `${Date.now()}`;
506
- const nonce = 'requestID';
507
- const data = this.config.credentials?.token + t + nonce;
508
- const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
509
- const sign = signTerm.toString('base64');
510
- */
511
518
  async discoverDevices() {
512
- if (this.config.credentials?.token) {
513
- let retryCount = 0;
514
- const maxRetries = this.maxRetries; // Maximum number of retries
515
- const delayBetweenRetries = this.delayBetweenRetries; // Delay between retries in milliseconds
516
- await this.debugWarnLog(`Retry Count: ${retryCount}`);
517
- await this.debugWarnLog(`Max Retries: ${maxRetries}`);
518
- await this.debugWarnLog(`Delay Between Retries: ${delayBetweenRetries}`);
519
- while (retryCount < maxRetries) {
520
- try {
521
- const { body, statusCode } = await request(Devices, {
522
- headers: this.generateHeaders(),
523
- });
524
- await this.debugWarnLog(`statusCode: ${statusCode}`);
525
- const devicesAPI = await body.json();
526
- await this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`);
527
- await this.debugWarnLog(`devicesAPI Body: ${JSON.stringify(devicesAPI.body)}`);
528
- await this.debugWarnLog(`devicesAPI StatusCode: ${devicesAPI.statusCode}`);
529
- if ((statusCode === 200 || statusCode === 100) && (devicesAPI.statusCode === 200 || devicesAPI.statusCode === 100)) {
530
- await this.debugErrorLog(`statusCode: ${statusCode} & devicesAPI StatusCode: ${devicesAPI.statusCode}`);
531
- // SwitchBot Devices
532
- const deviceLists = devicesAPI.body.deviceList;
533
- await this.debugWarnLog(`DeviceLists: ${JSON.stringify(deviceLists)}`);
534
- await this.debugWarnLog(`DeviceLists Length: ${deviceLists.length}`);
535
- if (!this.config.options?.devices) {
536
- await this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
537
- if (deviceLists.length === 0) {
538
- await this.debugLog(`SwitchBot API Has No Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`);
539
- }
540
- else {
541
- const devices = deviceLists.map((v) => v);
542
- for (const device of devices) {
543
- if (device.deviceType) {
544
- if (device.configDeviceName) {
545
- device.deviceName = device.configDeviceName;
546
- }
547
- this.createDevice(device);
548
- }
549
- }
550
- }
551
- }
552
- else if (this.config.credentials?.token && this.config.options.devices) {
553
- await this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
554
- if (deviceLists.length === 0) {
555
- await this.debugLog(`SwitchBot API Has No Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`);
556
- }
557
- else {
558
- const deviceConfigs = this.config.options?.devices;
559
- const mergeBydeviceId = (a1, a2) => a1.map((itm) => ({
560
- ...a2.find((item) => item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item),
561
- ...itm,
562
- }));
563
- const devices = mergeBydeviceId(deviceLists, deviceConfigs);
564
- await this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
565
- for (const device of devices) {
566
- if (!device.deviceType) {
567
- device.deviceType = device.configDeviceType;
568
- this.errorLog(`API has displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`);
569
- }
570
- if (device.deviceType) {
571
- if (device.configDeviceName) {
572
- device.deviceName = device.configDeviceName;
573
- }
574
- this.createDevice(device);
575
- }
576
- }
577
- }
578
- }
579
- else {
580
- this.errorLog('SwitchBot Token Supplied, Issue with Auth.');
581
- }
582
- if (devicesAPI.body.deviceList.length !== 0) {
583
- this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
584
- }
585
- else {
586
- await this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
587
- }
588
- // IR Devices
589
- const irDeviceLists = devicesAPI.body.infraredRemoteList;
590
- if (!this.config.options?.irdevices) {
591
- await this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
592
- const devices = irDeviceLists.map((v) => v);
593
- for (const device of devices) {
594
- if (device.remoteType) {
595
- this.createIRDevice(device);
596
- }
597
- }
598
- }
599
- else {
600
- await this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`);
601
- const irDeviceConfig = this.config.options?.irdevices;
602
- const mergeIRBydeviceId = (a1, a2) => a1.map((itm) => ({
603
- ...a2.find((item) => item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item),
604
- ...itm,
605
- }));
606
- const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig);
607
- await this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
608
- for (const device of devices) {
609
- this.createIRDevice(device);
610
- }
611
- }
612
- if (devicesAPI.body.infraredRemoteList.length !== 0) {
613
- this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
614
- }
615
- else {
616
- await this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
617
- }
618
- break;
619
- }
620
- else {
621
- this.statusCode(statusCode);
622
- this.statusCode(devicesAPI.statusCode);
623
- if (statusCode === 500) {
624
- retryCount++;
625
- this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount} of ${maxRetries}`);
626
- await sleep(delayBetweenRetries);
627
- }
628
- }
519
+ if (!this.config.credentials?.token) {
520
+ return this.handleManualConfig();
521
+ }
522
+ let retryCount = 0;
523
+ const maxRetries = this.maxRetries;
524
+ const delayBetweenRetries = this.delayBetweenRetries;
525
+ await this.debugWarnLog(`Retry Count: ${retryCount}`);
526
+ await this.debugWarnLog(`Max Retries: ${maxRetries}`);
527
+ await this.debugWarnLog(`Delay Between Retries: ${delayBetweenRetries}`);
528
+ while (retryCount < maxRetries) {
529
+ try {
530
+ const { body, statusCode } = await request(Devices, { headers: this.generateHeaders() });
531
+ await this.debugWarnLog(`statusCode: ${statusCode}`);
532
+ const devicesAPI = await body.json();
533
+ await this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`);
534
+ if (this.isSuccessfulResponse(statusCode, devicesAPI.statusCode)) {
535
+ await this.handleDevices(devicesAPI.body.deviceList);
536
+ await this.handleIRDevices(devicesAPI.body.infraredRemoteList);
537
+ break;
629
538
  }
630
- catch (e) {
539
+ else {
540
+ await this.handleErrorResponse(statusCode, devicesAPI.statusCode, retryCount, maxRetries, delayBetweenRetries);
631
541
  retryCount++;
632
- this.debugErrorLog(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
633
- await this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`);
634
542
  }
635
543
  }
544
+ catch (e) {
545
+ retryCount++;
546
+ await this.debugErrorLog(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)},`
547
+ + ' Submit Bugs Here: https://tinyurl.com/SwitchBotBug');
548
+ await this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`);
549
+ }
636
550
  }
637
- else if (!this.config.credentials?.token && this.config.options?.devices) {
551
+ }
552
+ async handleManualConfig() {
553
+ if (this.config.options?.devices) {
638
554
  await this.debugLog(`SwitchBot Device Manual Config Set: ${JSON.stringify(this.config.options?.devices)}`);
639
- const deviceConfigs = this.config.options?.devices;
640
- const devices = deviceConfigs.map((v) => v);
555
+ const devices = this.config.options.devices.map((v) => v);
641
556
  for (const device of devices) {
642
557
  device.deviceType = device.configDeviceType;
643
558
  device.deviceName = device.configDeviceName;
644
559
  if (device.deviceType) {
645
- this.createDevice(device);
560
+ await this.createDevice(device);
561
+ }
562
+ }
563
+ }
564
+ else {
565
+ await this.errorLog('Neither SwitchBot Token or Device Config are set.');
566
+ }
567
+ }
568
+ isSuccessfulResponse(statusCode, apiStatusCode) {
569
+ return (statusCode === 200 || statusCode === 100) && (apiStatusCode === 200 || apiStatusCode === 100);
570
+ }
571
+ async handleDevices(deviceLists) {
572
+ if (!this.config.options?.devices) {
573
+ await this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
574
+ if (deviceLists.length === 0) {
575
+ await this.debugLog('SwitchBot API Has No Devices With Cloud Services Enabled');
576
+ }
577
+ else {
578
+ for (const device of deviceLists) {
579
+ if (device.deviceType) {
580
+ if (device.configDeviceName) {
581
+ device.deviceName = device.configDeviceName;
582
+ }
583
+ await this.createDevice(device);
584
+ }
585
+ }
586
+ }
587
+ }
588
+ else {
589
+ await this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
590
+ const devices = this.mergeByDeviceId(deviceLists, this.config.options.devices);
591
+ await this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
592
+ for (const device of devices) {
593
+ if (!device.deviceType && device.configDeviceType) {
594
+ device.deviceType = device.configDeviceType;
595
+ await this.warnLog(`API is displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`);
596
+ }
597
+ else if (!device.deviceType && !device.configDeviceName) {
598
+ await this.errorLog('No deviceType or configDeviceType for device. No device will be created.');
599
+ }
600
+ if (device.deviceType) {
601
+ if (device.configDeviceName) {
602
+ device.deviceName = device.configDeviceName;
603
+ }
604
+ await this.createDevice(device);
605
+ }
606
+ }
607
+ }
608
+ }
609
+ async handleIRDevices(irDeviceLists) {
610
+ if (!this.config.options?.irdevices) {
611
+ await this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
612
+ for (const device of irDeviceLists) {
613
+ if (device.remoteType) {
614
+ await this.createIRDevice(device);
646
615
  }
647
616
  }
648
617
  }
649
618
  else {
650
- this.errorLog('Neither SwitchBot Token or Device Config are not set.');
619
+ await this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`);
620
+ const devices = this.mergeByDeviceId(irDeviceLists, this.config.options.irdevices);
621
+ await this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
622
+ for (const device of devices) {
623
+ await this.createIRDevice(device);
624
+ }
625
+ }
626
+ }
627
+ mergeByDeviceId(a1, a2) {
628
+ const normalizeDeviceId = (deviceId) => deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '');
629
+ return a1.map(itm => {
630
+ const matchingItem = a2.find(item => normalizeDeviceId(item.deviceId) === normalizeDeviceId(itm.deviceId));
631
+ return { ...matchingItem, ...itm };
632
+ });
633
+ }
634
+ async handleErrorResponse(statusCode, apiStatusCode, retryCount, maxRetries, delayBetweenRetries) {
635
+ await this.statusCode(statusCode);
636
+ await this.statusCode(apiStatusCode);
637
+ if (statusCode === 500) {
638
+ this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount + 1} of ${maxRetries}`);
639
+ await sleep(delayBetweenRetries);
651
640
  }
652
641
  }
653
642
  async createDevice(device) {
654
- switch (device.deviceType) {
655
- case 'Humidifier':
656
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
657
- this.createHumidifier(device);
658
- break;
659
- case 'Hub Mini':
660
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
661
- break;
662
- case 'Hub Plus':
663
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
664
- break;
665
- case 'Hub 2':
666
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
667
- this.createHub2(device);
668
- break;
669
- case 'Bot':
670
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
671
- this.createBot(device);
672
- break;
673
- case 'Meter':
674
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
675
- this.createMeter(device);
676
- break;
677
- case 'MeterPlus':
678
- case 'Meter Plus (JP)':
679
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
680
- this.createMeterPlus(device);
681
- break;
682
- case 'WoIOSensor':
683
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
684
- this.createIOSensor(device);
685
- break;
686
- case 'Water Detector':
687
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
688
- this.createWaterDetector(device);
689
- break;
690
- case 'Motion Sensor':
691
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
692
- this.createMotion(device);
693
- break;
694
- case 'Contact Sensor':
695
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
696
- this.createContact(device);
697
- break;
698
- case 'Curtain':
699
- case 'Curtain3':
700
- await this.debugLog(`Discovered ${device.deviceType} ${device.deviceName}: ${device.deviceId}`);
701
- this.createCurtain(device);
702
- break;
703
- case 'Blind Tilt':
704
- await this.debugLog(`Discovered ${device.deviceType} ${device.deviceName}: ${device.deviceId}`);
705
- this.createBlindTilt(device);
706
- break;
707
- case 'Plug':
708
- case 'Plug Mini (US)':
709
- case 'Plug Mini (JP)':
710
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
711
- this.createPlug(device);
712
- break;
713
- case 'Smart Lock':
714
- case 'Smart Lock Pro':
715
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
716
- this.createLock(device);
717
- break;
718
- case 'Color Bulb':
719
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
720
- this.createColorBulb(device);
721
- break;
722
- case 'K10+':
723
- case 'WoSweeper':
724
- case 'WoSweeperMini':
725
- case 'Robot Vacuum Cleaner S1':
726
- case 'Robot Vacuum Cleaner S1 Plus':
727
- case 'Robot Vacuum Cleaner S10':
728
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
729
- this.createRobotVacuumCleaner(device);
730
- break;
731
- case 'Ceiling Light':
732
- case 'Ceiling Light Pro':
733
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
734
- this.createCeilingLight(device);
735
- break;
736
- case 'Strip Light':
737
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
738
- this.createStripLight(device);
739
- break;
740
- case 'Battery Circulator Fan':
741
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
742
- this.createFan(device);
743
- break;
744
- case 'Remote':
745
- case 'Indoor Cam':
746
- case 'remote with screen+':
747
- await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}, is currently not supported, device: ${JSON.stringify(device)}`);
748
- break;
749
- default:
750
- this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`, +`Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`);
643
+ const deviceTypeHandlers = {
644
+ 'Humidifier': this.createHumidifier.bind(this),
645
+ 'Hub 2': this.createHub2.bind(this),
646
+ 'Bot': this.createBot.bind(this),
647
+ 'Meter': this.createMeter.bind(this),
648
+ 'MeterPlus': this.createMeterPlus.bind(this),
649
+ 'Meter Plus (JP)': this.createMeterPlus.bind(this),
650
+ 'WoIOSensor': this.createIOSensor.bind(this),
651
+ 'Water Detector': this.createWaterDetector.bind(this),
652
+ 'Motion Sensor': this.createMotion.bind(this),
653
+ 'Contact Sensor': this.createContact.bind(this),
654
+ 'Curtain': this.createCurtain.bind(this),
655
+ 'Curtain3': this.createCurtain.bind(this),
656
+ 'Blind Tilt': this.createBlindTilt.bind(this),
657
+ 'Plug': this.createPlug.bind(this),
658
+ 'Plug Mini (US)': this.createPlug.bind(this),
659
+ 'Plug Mini (JP)': this.createPlug.bind(this),
660
+ 'Smart Lock': this.createLock.bind(this),
661
+ 'Smart Lock Pro': this.createLock.bind(this),
662
+ 'Color Bulb': this.createColorBulb.bind(this),
663
+ 'K10+': this.createRobotVacuumCleaner.bind(this),
664
+ 'WoSweeper': this.createRobotVacuumCleaner.bind(this),
665
+ 'WoSweeperMini': this.createRobotVacuumCleaner.bind(this),
666
+ 'Robot Vacuum Cleaner S1': this.createRobotVacuumCleaner.bind(this),
667
+ 'Robot Vacuum Cleaner S1 Plus': this.createRobotVacuumCleaner.bind(this),
668
+ 'Robot Vacuum Cleaner S10': this.createRobotVacuumCleaner.bind(this),
669
+ 'Ceiling Light': this.createCeilingLight.bind(this),
670
+ 'Ceiling Light Pro': this.createCeilingLight.bind(this),
671
+ 'Strip Light': this.createStripLight.bind(this),
672
+ 'Battery Circulator Fan': this.createFan.bind(this),
673
+ };
674
+ if (deviceTypeHandlers[device.deviceType]) {
675
+ await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
676
+ await deviceTypeHandlers[device.deviceType](device);
677
+ }
678
+ else if (['Hub Mini', 'Hub Plus', 'Remote', 'Indoor Cam', 'remote with screen'].includes(device.deviceType)) {
679
+ await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}, is currently not supported, device: ${JSON.stringify(device)}`);
680
+ }
681
+ else {
682
+ await this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`
683
+ + ` Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`);
751
684
  }
752
685
  }
753
686
  async createIRDevice(device) {
754
- if (device.connectionType === undefined) {
755
- device.connectionType = 'OpenAPI';
756
- }
757
- switch (device.remoteType) {
758
- case 'TV':
759
- case 'DIY TV':
760
- case 'Projector':
761
- case 'DIY Projector':
762
- case 'Set Top Box':
763
- case 'DIY Set Top Box':
764
- case 'IPTV':
765
- case 'DIY IPTV':
766
- case 'DVD':
767
- case 'DIY DVD':
768
- case 'Speaker':
769
- case 'DIY Speaker':
770
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
771
- if (device.external === undefined) {
772
- device.external = true;
773
- this.createTV(device);
774
- }
775
- else {
776
- this.createTV(device);
777
- }
778
- break;
779
- case 'Fan':
780
- case 'DIY Fan':
781
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
782
- this.createIRFan(device);
783
- break;
784
- case 'Air Conditioner':
785
- case 'DIY Air Conditioner':
786
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
787
- this.createAirConditioner(device);
788
- break;
789
- case 'Light':
790
- case 'DIY Light':
791
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
792
- this.createLight(device);
793
- break;
794
- case 'Air Purifier':
795
- case 'DIY Air Purifier':
796
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
797
- this.createAirPurifier(device);
798
- break;
799
- case 'Water Heater':
800
- case 'DIY Water Heater':
801
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
802
- this.createWaterHeater(device);
803
- break;
804
- case 'Vacuum Cleaner':
805
- case 'DIY Vacuum Cleaner':
806
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
807
- this.createVacuumCleaner(device);
808
- break;
809
- case 'Camera':
810
- case 'DIY Camera':
811
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
812
- this.createCamera(device);
813
- break;
814
- case 'Others':
815
- await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
816
- this.createOthers(device);
817
- break;
818
- default:
819
- await this.debugLog(`Unsupported Device: ${JSON.stringify(device)}`);
820
- this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.remoteType}, is currently not supported.`);
821
- this.warnLog('Submit Feature Requests Here: ' + 'https://tinyurl.com/SwitchBotFeatureRequest');
687
+ device.connectionType = device.connectionType ?? 'OpenAPI';
688
+ const deviceTypeHandlers = {
689
+ 'TV': this.createTV.bind(this),
690
+ 'DIY TV': this.createTV.bind(this),
691
+ 'Projector': this.createTV.bind(this),
692
+ 'DIY Projector': this.createTV.bind(this),
693
+ 'Set Top Box': this.createTV.bind(this),
694
+ 'DIY Set Top Box': this.createTV.bind(this),
695
+ 'IPTV': this.createTV.bind(this),
696
+ 'DIY IPTV': this.createTV.bind(this),
697
+ 'DVD': this.createTV.bind(this),
698
+ 'DIY DVD': this.createTV.bind(this),
699
+ 'Speaker': this.createTV.bind(this),
700
+ 'DIY Speaker': this.createTV.bind(this),
701
+ 'Fan': this.createIRFan.bind(this),
702
+ 'DIY Fan': this.createIRFan.bind(this),
703
+ 'Air Conditioner': this.createAirConditioner.bind(this),
704
+ 'DIY Air Conditioner': this.createAirConditioner.bind(this),
705
+ 'Light': this.createLight.bind(this),
706
+ 'DIY Light': this.createLight.bind(this),
707
+ 'Air Purifier': this.createAirPurifier.bind(this),
708
+ 'DIY Air Purifier': this.createAirPurifier.bind(this),
709
+ 'Water Heater': this.createWaterHeater.bind(this),
710
+ 'DIY Water Heater': this.createWaterHeater.bind(this),
711
+ 'Vacuum Cleaner': this.createVacuumCleaner.bind(this),
712
+ 'DIY Vacuum Cleaner': this.createVacuumCleaner.bind(this),
713
+ 'Camera': this.createCamera.bind(this),
714
+ 'DIY Camera': this.createCamera.bind(this),
715
+ 'Others': this.createOthers.bind(this),
716
+ };
717
+ if (deviceTypeHandlers[device.remoteType]) {
718
+ await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
719
+ if (device.remoteType.startsWith('DIY') && device.external === undefined) {
720
+ device.external = true;
721
+ }
722
+ await deviceTypeHandlers[device.remoteType](device);
723
+ }
724
+ else {
725
+ await this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.remoteType}, is currently not supported.`
726
+ + ` Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`);
822
727
  }
823
728
  }
824
729
  async createHumidifier(device) {
@@ -1979,7 +1884,6 @@ export class SwitchBotPlatform {
1979
1884
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
1980
1885
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
1981
1886
  accessory.context.connectionType = await this.connectionType(device);
1982
- accessory.context.connectionType = device.connectionType;
1983
1887
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
1984
1888
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
1985
1889
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -1987,7 +1891,7 @@ export class SwitchBotPlatform {
1987
1891
  // this is imported from `platformAccessory.ts`
1988
1892
  new TV(this, accessory, device);
1989
1893
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
1990
- this.externalOrPlatformIR(device, accessory);
1894
+ this.externalOrPlatform(device, accessory);
1991
1895
  this.accessories.push(accessory);
1992
1896
  }
1993
1897
  else {
@@ -2037,7 +1941,6 @@ export class SwitchBotPlatform {
2037
1941
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2038
1942
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2039
1943
  accessory.context.connectionType = await this.connectionType(device);
2040
- accessory.context.connectionType = device.connectionType;
2041
1944
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2042
1945
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2043
1946
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2046,7 +1949,7 @@ export class SwitchBotPlatform {
2046
1949
  new IRFan(this, accessory, device);
2047
1950
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2048
1951
  // publish device externally or link the accessory to your platform
2049
- this.externalOrPlatformIR(device, accessory);
1952
+ this.externalOrPlatform(device, accessory);
2050
1953
  this.accessories.push(accessory);
2051
1954
  }
2052
1955
  else {
@@ -2096,7 +1999,6 @@ export class SwitchBotPlatform {
2096
1999
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2097
2000
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2098
2001
  accessory.context.connectionType = await this.connectionType(device);
2099
- accessory.context.connectionType = device.connectionType;
2100
2002
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2101
2003
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2102
2004
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2105,7 +2007,7 @@ export class SwitchBotPlatform {
2105
2007
  new Light(this, accessory, device);
2106
2008
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2107
2009
  // publish device externally or link the accessory to your platform
2108
- this.externalOrPlatformIR(device, accessory);
2010
+ this.externalOrPlatform(device, accessory);
2109
2011
  this.accessories.push(accessory);
2110
2012
  }
2111
2013
  else {
@@ -2155,7 +2057,6 @@ export class SwitchBotPlatform {
2155
2057
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2156
2058
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2157
2059
  accessory.context.connectionType = await this.connectionType(device);
2158
- accessory.context.connectionType = device.connectionType;
2159
2060
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2160
2061
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2161
2062
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2164,7 +2065,7 @@ export class SwitchBotPlatform {
2164
2065
  new AirConditioner(this, accessory, device);
2165
2066
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2166
2067
  // publish device externally or link the accessory to your platform
2167
- this.externalOrPlatformIR(device, accessory);
2068
+ this.externalOrPlatform(device, accessory);
2168
2069
  this.accessories.push(accessory);
2169
2070
  }
2170
2071
  else {
@@ -2214,7 +2115,6 @@ export class SwitchBotPlatform {
2214
2115
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2215
2116
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2216
2117
  accessory.context.connectionType = await this.connectionType(device);
2217
- accessory.context.connectionType = device.connectionType;
2218
2118
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2219
2119
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2220
2120
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2223,7 +2123,7 @@ export class SwitchBotPlatform {
2223
2123
  new AirPurifier(this, accessory, device);
2224
2124
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2225
2125
  // publish device externally or link the accessory to your platform
2226
- this.externalOrPlatformIR(device, accessory);
2126
+ this.externalOrPlatform(device, accessory);
2227
2127
  this.accessories.push(accessory);
2228
2128
  }
2229
2129
  else {
@@ -2273,7 +2173,6 @@ export class SwitchBotPlatform {
2273
2173
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2274
2174
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2275
2175
  accessory.context.connectionType = await this.connectionType(device);
2276
- accessory.context.connectionType = device.connectionType;
2277
2176
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2278
2177
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2279
2178
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2282,7 +2181,7 @@ export class SwitchBotPlatform {
2282
2181
  new WaterHeater(this, accessory, device);
2283
2182
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2284
2183
  // publish device externally or link the accessory to your platform
2285
- this.externalOrPlatformIR(device, accessory);
2184
+ this.externalOrPlatform(device, accessory);
2286
2185
  this.accessories.push(accessory);
2287
2186
  }
2288
2187
  else {
@@ -2332,7 +2231,6 @@ export class SwitchBotPlatform {
2332
2231
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2333
2232
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2334
2233
  accessory.context.connectionType = await this.connectionType(device);
2335
- accessory.context.connectionType = device.connectionType;
2336
2234
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2337
2235
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2338
2236
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2341,7 +2239,7 @@ export class SwitchBotPlatform {
2341
2239
  new VacuumCleaner(this, accessory, device);
2342
2240
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2343
2241
  // publish device externally or link the accessory to your platform
2344
- this.externalOrPlatformIR(device, accessory);
2242
+ this.externalOrPlatform(device, accessory);
2345
2243
  this.accessories.push(accessory);
2346
2244
  }
2347
2245
  else {
@@ -2391,7 +2289,6 @@ export class SwitchBotPlatform {
2391
2289
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2392
2290
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2393
2291
  accessory.context.connectionType = await this.connectionType(device);
2394
- accessory.context.connectionType = device.connectionType;
2395
2292
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2396
2293
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2397
2294
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2400,7 +2297,7 @@ export class SwitchBotPlatform {
2400
2297
  new Camera(this, accessory, device);
2401
2298
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2402
2299
  // publish device externally or link the accessory to your platform
2403
- this.externalOrPlatformIR(device, accessory);
2300
+ this.externalOrPlatform(device, accessory);
2404
2301
  this.accessories.push(accessory);
2405
2302
  }
2406
2303
  else {
@@ -2450,7 +2347,6 @@ export class SwitchBotPlatform {
2450
2347
  ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
2451
2348
  : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
2452
2349
  accessory.context.connectionType = await this.connectionType(device);
2453
- accessory.context.connectionType = device.connectionType;
2454
2350
  accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
2455
2351
  const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
2456
2352
  await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
@@ -2459,7 +2355,7 @@ export class SwitchBotPlatform {
2459
2355
  new Others(this, accessory, device);
2460
2356
  await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
2461
2357
  // publish device externally or link the accessory to your platform
2462
- this.externalOrPlatformIR(device, accessory);
2358
+ this.externalOrPlatform(device, accessory);
2463
2359
  this.accessories.push(accessory);
2464
2360
  }
2465
2361
  else {
@@ -2467,13 +2363,6 @@ export class SwitchBotPlatform {
2467
2363
  }
2468
2364
  }
2469
2365
  async registerCurtains(device) {
2470
- /*function isCurtainDevice(device: device & devicesConfig): device is (curtain | curtain3) & devicesConfig {
2471
- return device.deviceType === 'Curtain' || device.deviceType === 'Curtain3';
2472
- }
2473
-
2474
- function isBlindTiltDevice(device: device & devicesConfig): device is blindTilt & devicesConfig {
2475
- return device.deviceType === 'Blind Tilt';
2476
- }*/
2477
2366
  let registerWindowCovering;
2478
2367
  if (isCurtainDevice(device)) {
2479
2368
  await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds},`
@@ -2631,103 +2520,62 @@ export class SwitchBotPlatform {
2631
2520
  }
2632
2521
  return registerDevice;
2633
2522
  }
2634
- async externalOrPlatformIR(device, accessory) {
2635
- /**
2636
- * Publish as external accessory
2637
- * Only one TV can exist per bridge, to bypass this limitation, you should
2638
- * publish your TV as an external accessory.
2639
- */
2640
- if (device.external) {
2641
- await this.debugWarnLog(`${accessory.displayName} External Accessory Mode`);
2642
- this.externalAccessory(accessory);
2643
- }
2644
- else {
2645
- await this.debugLog(`${accessory.displayName} External Accessory Mode: ${device.external}`);
2646
- this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
2647
- }
2648
- }
2649
2523
  async externalOrPlatform(device, accessory) {
2650
- if (device.external) {
2651
- await this.debugWarnLog(`${accessory.displayName} External Accessory Mode`);
2652
- await this.externalAccessory(accessory);
2524
+ const { displayName } = accessory;
2525
+ const isExternal = device.external ?? false;
2526
+ if (isExternal) {
2527
+ await this.debugWarnLog(`${displayName} External Accessory Mode`);
2528
+ this.api.publishExternalAccessories(PLUGIN_NAME, [accessory]);
2653
2529
  }
2654
2530
  else {
2655
- await this.debugLog(`${accessory.displayName} External Accessory Mode: ${device.external}`);
2531
+ await this.debugLog(`${displayName} External Accessory Mode: ${isExternal}`);
2656
2532
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
2657
2533
  }
2658
2534
  }
2659
- async externalAccessory(accessory) {
2660
- this.api.publishExternalAccessories(PLUGIN_NAME, [accessory]);
2661
- }
2662
2535
  unregisterPlatformAccessories(existingAccessory) {
2536
+ const { displayName } = existingAccessory;
2663
2537
  // remove platform accessories when no longer present
2664
2538
  this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]);
2665
- this.warnLog(`Removing existing accessory from cache: ${existingAccessory.displayName}`);
2539
+ this.warnLog(`Removing existing accessory from cache: ${displayName}`);
2666
2540
  }
2541
+ /**
2542
+ * Handles the status codes returned by the device and logs appropriate messages.
2543
+ *
2544
+ * @param statusCode - The status code returned by the device.
2545
+ * @returns A promise that resolves when the logging is complete.
2546
+ */
2667
2547
  async statusCode(statusCode) {
2668
- switch (statusCode) {
2669
- case 151:
2670
- await this.errorLog(`Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here: ` +
2671
- 'https://tinyurl.com/SwitchBotFeatureRequest');
2672
- break;
2673
- case 152:
2674
- await this.errorLog(`Device not found, statusCode: ${statusCode}`);
2675
- break;
2676
- case 160:
2677
- await this.errorLog(`Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`);
2678
- break;
2679
- case 161:
2680
- await this.errorLog(`Device is offline, statusCode: ${statusCode}`);
2681
- break;
2682
- case 171:
2683
- await this.errorLog(`is offline, statusCode: ${statusCode}`);
2684
- break;
2685
- case 190:
2686
- await this.errorLog(`Requests reached the daily limit, statusCode: ${statusCode}`);
2687
- break;
2688
- case 100:
2689
- await this.debugLog(`Command successfully sent, statusCode: ${statusCode}`);
2690
- break;
2691
- case 200:
2692
- await this.debugLog(`Request successful, statusCode: ${statusCode}`);
2693
- break;
2694
- case 400:
2695
- await this.errorLog('Bad Request, The client has issued an invalid request. This is commonly used to specify validation errors in a request '
2696
- + `payload, statusCode: ${statusCode}`);
2697
- break;
2698
- case 401:
2699
- await this.errorLog('Unauthorized, Authorization for the API is required, but the request has not been authenticated, '
2700
- + `statusCode: ${statusCode}`);
2701
- break;
2702
- case 403:
2703
- await this.errorLog('Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not '
2704
- + `found, statusCode: ${statusCode}`);
2705
- break;
2706
- case 404:
2707
- await this.errorLog(`Not Found, Specifies the requested path does not exist, statusCode: ${statusCode}`);
2708
- break;
2709
- case 406:
2710
- await this.errorLog('Not Acceptable, The client has requested a MIME type via the Accept header for a value not supported by the server, '
2711
- + `statusCode: ${statusCode}`);
2712
- break;
2713
- case 415:
2714
- await this.errorLog('Unsupported Media Type, The client has defined a contentType header that is not supported by the server, '
2715
- + `statusCode: ${statusCode}`);
2716
- break;
2717
- case 422:
2718
- await this.errorLog('Unprocessable Entity, The client has made a valid request, but the server cannot process it. This is often used for '
2719
- + `APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
2720
- break;
2721
- case 429:
2722
- await this.errorLog('Too Many Requests, The client has exceeded the number of requests allowed for a given time window, '
2723
- + `statusCode: ${statusCode}`);
2724
- break;
2725
- case 500:
2726
- await this.errorLog('Internal Server Error, An unexpected error on the SmartThings servers has occurred. These errors should be rare, '
2727
- + `statusCode: ${statusCode}`);
2728
- break;
2729
- default:
2730
- await this.errorLog(`Unknown statusCode, statusCode: ${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`);
2548
+ const messages = {
2549
+ 151: `Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here:
2550
+ https://tinyurl.com/SwitchBotFeatureRequest`,
2551
+ 152: `Device not found, statusCode: ${statusCode}`,
2552
+ 160: `Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`,
2553
+ 161: `Device is offline, statusCode: ${statusCode}`,
2554
+ 171: `is offline, statusCode: ${statusCode}`,
2555
+ 190: `Requests reached the daily limit, statusCode: ${statusCode}`,
2556
+ 100: `Command successfully sent, statusCode: ${statusCode}`,
2557
+ 200: `Request successful, statusCode: ${statusCode}`,
2558
+ 400: `Bad Request, The client has issued an invalid request. This is commonly used to specify validation errors in a request payload,
2559
+ statusCode: ${statusCode}`,
2560
+ 401: `Unauthorized, Authorization for the API is required, but the request has not been authenticated, statusCode: ${statusCode}`,
2561
+ 403: `Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not found,
2562
+ statusCode: ${statusCode}`,
2563
+ 404: `Not Found, Specifies the requested path does not exist, statusCode: ${statusCode}`,
2564
+ 406: `Not Acceptable, The client has requested a MIME type via the Accept header for a value not supported by the server,
2565
+ statusCode: ${statusCode}`,
2566
+ 415: `Unsupported Media Type, The client has defined a contentType header that is not supported by the server, statusCode: ${statusCode}`,
2567
+ 422: `Unprocessable Entity, The client has made a valid request, but the server cannot process it. This is often used for APIs for which
2568
+ certain limits have been exceeded, statusCode: ${statusCode}`,
2569
+ 429: `Too Many Requests, The client has exceeded the number of requests allowed for a given time window, statusCode: ${statusCode}`,
2570
+ 500: `Internal Server Error, An unexpected error on the SmartThings servers has occurred. These errors should be rare,
2571
+ statusCode: ${statusCode}`,
2572
+ };
2573
+ const message = messages[statusCode] ?? `Unknown statusCode, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`;
2574
+ if ([100, 200].includes(statusCode)) {
2575
+ await this.debugLog(message);
2576
+ }
2577
+ else {
2578
+ await this.errorLog(message);
2731
2579
  }
2732
2580
  }
2733
2581
  async retryRequest(deviceMaxRetries, deviceDelayBetweenRetries, url, options) {
@@ -2755,57 +2603,43 @@ export class SwitchBotPlatform {
2755
2603
  }
2756
2604
  // BLE Connection
2757
2605
  async connectBLE(accessory, device) {
2758
- let switchbot;
2759
2606
  try {
2760
- const SwitchBot = (await import('node-switchbot')).SwitchBot;
2761
- queueScheduler.schedule(() => (switchbot = new SwitchBot()));
2607
+ const { SwitchBot } = await import('node-switchbot');
2608
+ const switchbot = new SwitchBot();
2609
+ queueScheduler.schedule(async () => switchbot);
2762
2610
  await this.debugLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${switchbot}`);
2611
+ return switchbot;
2763
2612
  }
2764
2613
  catch (e) {
2765
- switchbot = false;
2766
- await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${switchbot}, Error: ${e}`);
2614
+ await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' not found, Error: ${e}`);
2615
+ return false;
2767
2616
  }
2768
- return switchbot;
2769
2617
  }
2770
2618
  async getVersion() {
2771
2619
  const json = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
2772
2620
  await this.debugLog(`Plugin Version: ${json.version}`);
2773
2621
  this.version = json.version;
2622
+ return json.version;
2774
2623
  }
2775
2624
  async getPlatformConfigSettings() {
2625
+ const { options } = this.config;
2776
2626
  const platformConfig = {};
2777
- if (this.config.options) {
2778
- if (this.config.options.logging) {
2779
- platformConfig.logging = this.config.options.logging;
2780
- }
2781
- if (this.config.options.refreshRate) {
2782
- platformConfig.refreshRate = this.config.options.refreshRate;
2783
- }
2784
- if (this.config.options.updateRate) {
2785
- platformConfig.updateRate = this.config.options.updateRate;
2786
- }
2787
- if (this.config.options.pushRate) {
2788
- platformConfig.pushRate = this.config.options.pushRate;
2789
- }
2790
- if (this.config.options.maxRetries) {
2791
- this.maxRetries = this.config.options.maxRetries;
2792
- platformConfig.maxRetries = this.config.options.maxRetries;
2793
- }
2794
- else {
2795
- this.maxRetries = 3;
2796
- this.debugWarnLog('Using Default Max Retries');
2797
- platformConfig.maxRetries = this.maxRetries;
2798
- }
2799
- if (this.config.options.delayBetweenRetries) {
2800
- this.delayBetweenRetries = this.config.options.delayBetweenRetries * 1000;
2801
- platformConfig.delayBetweenRetries = this.config.options.delayBetweenRetries;
2802
- }
2803
- else {
2804
- this.delayBetweenRetries = 3000;
2805
- this.debugWarnLog('Using Default Delay Between Retries');
2806
- platformConfig.delayBetweenRetries = this.delayBetweenRetries / 1000;
2807
- }
2808
- if (Object.entries(platformConfig).length !== 0) {
2627
+ if (options) {
2628
+ platformConfig.logging = options.logging;
2629
+ platformConfig.refreshRate = options.refreshRate;
2630
+ platformConfig.updateRate = options.updateRate;
2631
+ platformConfig.pushRate = options.pushRate;
2632
+ this.maxRetries = options.maxRetries || 3;
2633
+ platformConfig.maxRetries = this.maxRetries;
2634
+ if (!options.maxRetries) {
2635
+ await this.debugWarnLog('Using Default Max Retries');
2636
+ }
2637
+ this.delayBetweenRetries = (options.delayBetweenRetries || 3) * 1000;
2638
+ platformConfig.delayBetweenRetries = this.delayBetweenRetries / 1000;
2639
+ if (!options.delayBetweenRetries) {
2640
+ await this.debugWarnLog('Using Default Delay Between Retries');
2641
+ }
2642
+ if (Object.keys(platformConfig).length) {
2809
2643
  await this.debugLog(`Platform Config: ${JSON.stringify(platformConfig)}`);
2810
2644
  }
2811
2645
  this.platformConfig = platformConfig;
@@ -2828,31 +2662,42 @@ export class SwitchBotPlatform {
2828
2662
  }
2829
2663
  /**
2830
2664
  * Validate and clean a string value for a Name Characteristic.
2831
- * @param displayName
2832
- * @param name
2833
- * @param value
2834
- * @returns string value
2835
- */
2665
+ * @param displayName - The display name of the accessory.
2666
+ * @param name - The name of the characteristic.
2667
+ * @param value - The value to be validated and cleaned.
2668
+ * @returns The cleaned string value.
2669
+ */
2836
2670
  async validateAndCleanDisplayName(displayName, name, value) {
2837
- const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9 ']*[a-zA-Z0-9]$/;
2838
- const invalidCharsPattern = /[^a-zA-Z0-9 ']/g;
2839
- const invalidStartEndPattern = /^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g;
2840
- if (!validPattern.test(value)) {
2841
- this.warnLog(`WARNING: The accessory '${displayName}' has an invalid '${name}' characteristic ('${value}'). Please use only alphanumeric,`
2842
- + ' space, and apostrophe characters. Ensure it starts and ends with an alphabetic or numeric character, and avoid emojis. This may'
2843
- + ' prevent the accessory from being added in the Home App or cause unresponsiveness.');
2844
- // Remove invalid characters
2845
- if (invalidCharsPattern.test(value)) {
2846
- this.warnLog(`Removing invalid characters from '${name}' characteristic`);
2847
- value = value.replace(invalidCharsPattern, '');
2848
- }
2849
- // Ensure it starts and ends with an alphanumeric character
2850
- if (invalidStartEndPattern.test(value)) {
2851
- this.warnLog(`Removing invalid starting or ending characters from '${name}' characteristic`);
2852
- value = value.replace(invalidStartEndPattern, '');
2853
- }
2854
- }
2855
- return value;
2671
+ if (this.config.options?.allowInvalidCharacters) {
2672
+ return value;
2673
+ }
2674
+ else {
2675
+ const validPattern = new RegExp(/^[\p{L}\p{N}][\p{L}\p{N} ']*[\p{L}\p{N}]$/u);
2676
+ const invalidCharsPattern = /[^\p{L}\p{N} ']/gu;
2677
+ const invalidStartEndPattern = /^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu;
2678
+ if (typeof value === 'string' && !validPattern.test(value)) {
2679
+ await this.warnLog(`WARNING: The accessory '${displayName}' has an invalid '${name}' characteristic ('${value}').`
2680
+ + ' Please use only alphanumeric, space, and apostrophe characters. Ensure it starts and ends with an alphabetic or numeric character,'
2681
+ + ' and avoid emojis. This may prevent the accessory from being added in the Home App or cause unresponsiveness.');
2682
+ // Remove invalid characters
2683
+ if (invalidCharsPattern.test(value)) {
2684
+ const before = value;
2685
+ await this.warnLog(`Removing invalid characters from '${name}' characteristic, if you feel this is incorrect,`
2686
+ + ' please enable \'allowInvalidCharacter\' in the config to allow all characters');
2687
+ value = value.replace(invalidCharsPattern, '');
2688
+ await this.warnLog(`${name} Before: '${before}' After: '${value}'`);
2689
+ }
2690
+ // Ensure it starts and ends with an alphanumeric character
2691
+ if (invalidStartEndPattern.test(value)) {
2692
+ const before = value;
2693
+ await this.warnLog(`Removing invalid starting or ending characters from '${name}' characteristic, if you feel this is incorrect,`
2694
+ + ' please enable \'allowInvalidCharacter\' in the config to allow all characters');
2695
+ value = value.replace(invalidStartEndPattern, '');
2696
+ await this.warnLog(`${name} Before: '${before}' After: '${value}'`);
2697
+ }
2698
+ }
2699
+ return value;
2700
+ }
2856
2701
  }
2857
2702
  /**
2858
2703
  * If device level logging is turned on, log to log.warn