@switchbot/homebridge-switchbot 3.5.1-beta.12 → 3.5.1-beta.13

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 (150) hide show
  1. package/config.schema.json +19 -3
  2. package/dist/device/blindtilt.d.ts +6 -4
  3. package/dist/device/blindtilt.d.ts.map +1 -1
  4. package/dist/device/blindtilt.js +57 -45
  5. package/dist/device/blindtilt.js.map +1 -1
  6. package/dist/device/bot.d.ts +6 -3
  7. package/dist/device/bot.d.ts.map +1 -1
  8. package/dist/device/bot.js +38 -24
  9. package/dist/device/bot.js.map +1 -1
  10. package/dist/device/ceilinglight.d.ts +6 -3
  11. package/dist/device/ceilinglight.d.ts.map +1 -1
  12. package/dist/device/ceilinglight.js +40 -21
  13. package/dist/device/ceilinglight.js.map +1 -1
  14. package/dist/device/colorbulb.d.ts +6 -3
  15. package/dist/device/colorbulb.d.ts.map +1 -1
  16. package/dist/device/colorbulb.js +42 -31
  17. package/dist/device/colorbulb.js.map +1 -1
  18. package/dist/device/contact.d.ts +6 -3
  19. package/dist/device/contact.d.ts.map +1 -1
  20. package/dist/device/contact.js +40 -28
  21. package/dist/device/contact.js.map +1 -1
  22. package/dist/device/curtain.d.ts +6 -4
  23. package/dist/device/curtain.d.ts.map +1 -1
  24. package/dist/device/curtain.js +35 -22
  25. package/dist/device/curtain.js.map +1 -1
  26. package/dist/device/device.d.ts +13 -2
  27. package/dist/device/device.d.ts.map +1 -1
  28. package/dist/device/device.js +223 -108
  29. package/dist/device/device.js.map +1 -1
  30. package/dist/device/fan.d.ts +6 -3
  31. package/dist/device/fan.d.ts.map +1 -1
  32. package/dist/device/fan.js +48 -29
  33. package/dist/device/fan.js.map +1 -1
  34. package/dist/device/hub.d.ts +6 -3
  35. package/dist/device/hub.d.ts.map +1 -1
  36. package/dist/device/hub.js +43 -26
  37. package/dist/device/hub.js.map +1 -1
  38. package/dist/device/humidifier.d.ts +6 -3
  39. package/dist/device/humidifier.d.ts.map +1 -1
  40. package/dist/device/humidifier.js +43 -26
  41. package/dist/device/humidifier.js.map +1 -1
  42. package/dist/device/iosensor.d.ts +6 -3
  43. package/dist/device/iosensor.d.ts.map +1 -1
  44. package/dist/device/iosensor.js +39 -24
  45. package/dist/device/iosensor.js.map +1 -1
  46. package/dist/device/lightstrip.d.ts +6 -3
  47. package/dist/device/lightstrip.d.ts.map +1 -1
  48. package/dist/device/lightstrip.js +43 -27
  49. package/dist/device/lightstrip.js.map +1 -1
  50. package/dist/device/lock.d.ts +6 -3
  51. package/dist/device/lock.d.ts.map +1 -1
  52. package/dist/device/lock.js +33 -22
  53. package/dist/device/lock.js.map +1 -1
  54. package/dist/device/meter.d.ts +6 -3
  55. package/dist/device/meter.d.ts.map +1 -1
  56. package/dist/device/meter.js +38 -24
  57. package/dist/device/meter.js.map +1 -1
  58. package/dist/device/meterplus.d.ts +6 -3
  59. package/dist/device/meterplus.d.ts.map +1 -1
  60. package/dist/device/meterplus.js +37 -24
  61. package/dist/device/meterplus.js.map +1 -1
  62. package/dist/device/motion.d.ts +6 -3
  63. package/dist/device/motion.d.ts.map +1 -1
  64. package/dist/device/motion.js +33 -22
  65. package/dist/device/motion.js.map +1 -1
  66. package/dist/device/plug.d.ts +6 -3
  67. package/dist/device/plug.d.ts.map +1 -1
  68. package/dist/device/plug.js +27 -16
  69. package/dist/device/plug.js.map +1 -1
  70. package/dist/device/robotvacuumcleaner.d.ts +6 -3
  71. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  72. package/dist/device/robotvacuumcleaner.js +34 -21
  73. package/dist/device/robotvacuumcleaner.js.map +1 -1
  74. package/dist/device/waterdetector.d.ts +6 -3
  75. package/dist/device/waterdetector.d.ts.map +1 -1
  76. package/dist/device/waterdetector.js +38 -23
  77. package/dist/device/waterdetector.js.map +1 -1
  78. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  79. package/dist/irdevice/airconditioner.js +19 -72
  80. package/dist/irdevice/airconditioner.js.map +1 -1
  81. package/dist/irdevice/airpurifier.js +4 -4
  82. package/dist/irdevice/airpurifier.js.map +1 -1
  83. package/dist/irdevice/camera.d.ts.map +1 -1
  84. package/dist/irdevice/camera.js +3 -10
  85. package/dist/irdevice/camera.js.map +1 -1
  86. package/dist/irdevice/fan.d.ts.map +1 -1
  87. package/dist/irdevice/fan.js +7 -25
  88. package/dist/irdevice/fan.js.map +1 -1
  89. package/dist/irdevice/irdevice.d.ts +1 -1
  90. package/dist/irdevice/irdevice.js +1 -1
  91. package/dist/irdevice/irdevice.js.map +1 -1
  92. package/dist/irdevice/light.d.ts.map +1 -1
  93. package/dist/irdevice/light.js +9 -27
  94. package/dist/irdevice/light.js.map +1 -1
  95. package/dist/irdevice/other.js +1 -1
  96. package/dist/irdevice/other.js.map +1 -1
  97. package/dist/irdevice/tv.d.ts.map +1 -1
  98. package/dist/irdevice/tv.js +3 -18
  99. package/dist/irdevice/tv.js.map +1 -1
  100. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  101. package/dist/irdevice/vacuumcleaner.js +2 -9
  102. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  103. package/dist/irdevice/waterheater.d.ts.map +1 -1
  104. package/dist/irdevice/waterheater.js +2 -9
  105. package/dist/irdevice/waterheater.js.map +1 -1
  106. package/dist/platform.d.ts +0 -11
  107. package/dist/platform.d.ts.map +1 -1
  108. package/dist/platform.js +11 -184
  109. package/dist/platform.js.map +1 -1
  110. package/dist/settings.d.ts +9 -8
  111. package/dist/settings.d.ts.map +1 -1
  112. package/dist/settings.js.map +1 -1
  113. package/dist/types/bledevicestatus.d.ts +2 -1
  114. package/dist/types/bledevicestatus.d.ts.map +1 -1
  115. package/dist/types/devicestatus.d.ts +1 -1
  116. package/dist/types/devicestatus.d.ts.map +1 -1
  117. package/package.json +9 -9
  118. package/src/device/blindtilt.ts +60 -47
  119. package/src/device/bot.ts +47 -25
  120. package/src/device/ceilinglight.ts +52 -21
  121. package/src/device/colorbulb.ts +45 -31
  122. package/src/device/contact.ts +43 -28
  123. package/src/device/curtain.ts +45 -23
  124. package/src/device/device.ts +226 -96
  125. package/src/device/fan.ts +61 -30
  126. package/src/device/hub.ts +55 -29
  127. package/src/device/humidifier.ts +55 -31
  128. package/src/device/iosensor.ts +45 -27
  129. package/src/device/lightstrip.ts +48 -28
  130. package/src/device/lock.ts +49 -23
  131. package/src/device/meter.ts +54 -26
  132. package/src/device/meterplus.ts +50 -26
  133. package/src/device/motion.ts +45 -24
  134. package/src/device/plug.ts +34 -17
  135. package/src/device/robotvacuumcleaner.ts +49 -23
  136. package/src/device/waterdetector.ts +56 -25
  137. package/src/irdevice/airconditioner.ts +19 -65
  138. package/src/irdevice/airpurifier.ts +4 -4
  139. package/src/irdevice/camera.ts +4 -9
  140. package/src/irdevice/fan.ts +10 -22
  141. package/src/irdevice/irdevice.ts +1 -1
  142. package/src/irdevice/light.ts +10 -24
  143. package/src/irdevice/other.ts +1 -1
  144. package/src/irdevice/tv.ts +5 -16
  145. package/src/irdevice/vacuumcleaner.ts +3 -8
  146. package/src/irdevice/waterheater.ts +3 -8
  147. package/src/platform.ts +11 -184
  148. package/src/settings.ts +10 -8
  149. package/src/types/bledevicestatus.ts +4 -2
  150. package/src/types/devicestatus.ts +1 -1
@@ -40,6 +40,15 @@ export class Curtain extends deviceBase {
40
40
  CurrentAmbientLightLevel?: CharacteristicValue;
41
41
  };
42
42
 
43
+ // OpenAPI
44
+ deviceStatus!: curtainStatus;
45
+
46
+ //Webhook
47
+ webhookContext!: curtainWebhookContext | curtain3WebhookContext;
48
+
49
+ // BLE
50
+ serviceData!: curtainServiceData | curtain3ServiceData;
51
+
43
52
  // Target
44
53
  setNewTarget!: boolean;
45
54
  setNewTargetTimer!: NodeJS.Timeout;
@@ -175,12 +184,11 @@ export class Curtain extends deviceBase {
175
184
  }
176
185
 
177
186
  // Retrieve initial values and updateHomekit
187
+ this.debugLog('Retrieve initial values and update Homekit');
178
188
  this.refreshStatus();
179
189
 
180
- // Update Homekit
181
- this.updateHomeKitCharacteristics();
182
-
183
190
  //regisiter webhook event handler
191
+ this.debugLog('Registering Webhook Event Handler');
184
192
  this.registerWebhook();
185
193
 
186
194
  // History
@@ -293,22 +301,24 @@ export class Curtain extends deviceBase {
293
301
  }, 10 * 60 * 1000);
294
302
  }
295
303
 
296
- async BLEparseStatus(serviceData: curtainServiceData | curtain3ServiceData): Promise<void> {
304
+ async BLEparseStatus(): Promise<void> {
297
305
  await this.debugLog('BLEparseStatus');
306
+ await this.debugLog(`(position, battery) = BLE:(${this.serviceData.position}, ${this.serviceData.battery}),`
307
+ + ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
298
308
  // CurrentPosition
299
- this.WindowCovering.CurrentPosition = 100 - serviceData.position;
309
+ this.WindowCovering.CurrentPosition = 100 -this.serviceData.position;
300
310
  await this.getCurrentPostion();
301
311
  // CurrentAmbientLightLevel
302
312
  if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) {
303
313
  const set_minLux = this.device.curtain?.set_minLux ?? 1;
304
314
  const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
305
- const lightLevel = Number(serviceData.lightLevel);
315
+ const lightLevel = this.serviceData.lightLevel;
306
316
  this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 19);
307
- this.debugLog(`LightLevel: ${serviceData.lightLevel},`
317
+ this.debugLog(`LightLevel: ${this.serviceData.lightLevel},`
308
318
  + ` CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
309
319
  }
310
320
  // BatteryLevel
311
- this.Battery.BatteryLevel = Number(serviceData.battery);
321
+ this.Battery.BatteryLevel = this.serviceData.battery;
312
322
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
313
323
  // StatusLowBattery
314
324
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
@@ -316,16 +326,18 @@ export class Curtain extends deviceBase {
316
326
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
317
327
  }
318
328
 
319
- async openAPIparseStatus(deviceStatus: curtainStatus): Promise<void> {
329
+ async openAPIparseStatus(): Promise<void> {
320
330
  await this.debugLog('openAPIparseStatus');
331
+ await this.debugLog(`(slidePosition, battery) = OpenAPI:(${this.deviceStatus.slidePosition}, ${this.deviceStatus.battery}),`
332
+ + ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
321
333
  // CurrentPosition
322
- this.WindowCovering.CurrentPosition = 100 - deviceStatus.slidePosition;
334
+ this.WindowCovering.CurrentPosition = 100 - this.deviceStatus.slidePosition;
323
335
  await this.setMinMax();
324
336
  await this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
325
337
  if (this.setNewTarget) {
326
338
  await this.infoLog('Checking Status ...');
327
339
  }
328
- if (this.setNewTarget && deviceStatus.moving) {
340
+ if (this.setNewTarget && this.deviceStatus.moving) {
329
341
  await this.setMinMax();
330
342
  if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
331
343
  await this.debugLog(`Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
@@ -351,25 +363,29 @@ export class Curtain extends deviceBase {
351
363
  }
352
364
  await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition},`
353
365
  + ` PositionState: ${this.WindowCovering.PositionState},`);
366
+
354
367
  // Brightness
355
368
  if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) {
356
369
  const set_minLux = this.device.curtain?.set_minLux ?? 1;
357
370
  const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
358
- const lightLevel = deviceStatus.lightLevel === 'bright' ? set_maxLux : set_minLux;
371
+ const lightLevel = this.deviceStatus.lightLevel === 'bright' ? set_maxLux : set_minLux;
359
372
  this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2);
360
373
  await this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
361
374
  + ` CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
362
375
  }
376
+
363
377
  // BatteryLevel
364
- this.Battery.BatteryLevel = Number(deviceStatus.battery);
378
+ this.Battery.BatteryLevel = this.deviceStatus.battery;
365
379
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
380
+
366
381
  // StatusLowBattery
367
382
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
368
383
  ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
369
384
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
385
+
370
386
  // Firmware Version
371
- if (deviceStatus.version) {
372
- const version = deviceStatus.version.toString();
387
+ if (this.deviceStatus.version) {
388
+ const version = this.deviceStatus.version.toString();
373
389
  await this.debugLog(`Firmware Version: ${version.replace(/^V|-.*$/g, '')}`);
374
390
  const deviceVersion = version.replace(/^V|-.*$/g, '') ?? '0.0.0';
375
391
  this.accessory
@@ -383,16 +399,19 @@ export class Curtain extends deviceBase {
383
399
  }
384
400
  }
385
401
 
386
- async parseStatusWebhook(context: curtainWebhookContext | curtain3WebhookContext): Promise<void> {
402
+ async parseStatusWebhook(): Promise<void> {
387
403
  await this.debugLog('parseStatusWebhook');
388
- await this.debugLog(`(slidePosition, battery) = Webhook:(${context.slidePosition}, ${context.battery}),`
404
+ await this.debugLog(`(slidePosition, battery) = Webhook:(${this.webhookContext.slidePosition}, ${this.webhookContext.battery}),`
389
405
  + ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
406
+
390
407
  // CurrentPosition
391
- this.WindowCovering.CurrentPosition = 100 - context.slidePosition;
408
+ this.WindowCovering.CurrentPosition = 100 - this.webhookContext.slidePosition;
392
409
  await this.getCurrentPostion();
410
+
393
411
  // BatteryLevel
394
- this.Battery.BatteryLevel = context.battery;
412
+ this.Battery.BatteryLevel = this.webhookContext.battery;
395
413
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
414
+
396
415
  // StatusLowBattery
397
416
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
398
417
  ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
@@ -429,7 +448,8 @@ export class Curtain extends deviceBase {
429
448
  // Update HomeKit
430
449
  if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3)
431
450
  && (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) {
432
- await this.BLEparseStatus(serviceData);
451
+ this.serviceData = serviceData;
452
+ await this.BLEparseStatus();
433
453
  await this.updateHomeKitCharacteristics();
434
454
  } else {
435
455
  await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
@@ -447,7 +467,8 @@ export class Curtain extends deviceBase {
447
467
  await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);;
448
468
  if (await this.successfulStatusCodes(statusCode, deviceStatus)) {
449
469
  await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
450
- await this.openAPIparseStatus(deviceStatus.body);
470
+ this.deviceStatus = deviceStatus.body;
471
+ await this.openAPIparseStatus();
451
472
  await this.updateHomeKitCharacteristics();
452
473
  } else {
453
474
  await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
@@ -462,10 +483,11 @@ export class Curtain extends deviceBase {
462
483
  async registerWebhook() {
463
484
  if (this.device.webhook) {
464
485
  await this.debugLog('is listening webhook.');
465
- this.platform.webhookEventHandler[this.device.deviceId] = async (context: curtainWebhookContext | curtain3WebhookContext) => {
486
+ this.webhookEventHandler[this.device.deviceId] = async (context: curtainWebhookContext | curtain3WebhookContext) => {
466
487
  try {
467
488
  await this.debugLog(`received Webhook: ${JSON.stringify(context)}`);
468
- await this.parseStatusWebhook(context);
489
+ this.webhookContext = context;
490
+ await this.parseStatusWebhook();
469
491
  await this.updateHomeKitCharacteristics();
470
492
  } catch (e: any) {
471
493
  await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
@@ -6,16 +6,18 @@
6
6
  import { hostname } from 'os';
7
7
  import { request } from 'undici';
8
8
  import asyncmqtt from 'async-mqtt';
9
- import { Devices } from '../settings.js';
9
+ import { createServer } from 'http';
10
10
  import { BlindTiltMappingMode, sleep } from '../utils.js';
11
+ import { Devices, deleteWebhook, queryWebhook, setupWebhook, updateWebhook } from '../settings.js';
12
+ import { SwitchBotModel, SwitchBotBLEModel, SwitchBotBLEModelName, SwitchBotBLEModelFriendlyName } from 'node-switchbot';
11
13
 
12
14
  import type { MqttClient } from 'mqtt';
13
15
  import type { device } from '../types/devicelist.js';
14
16
  import type { ad } from '../types/bledevicestatus.js';
15
17
  import type { SwitchBotPlatform } from '../platform.js';
18
+ import type { Server, IncomingMessage, ServerResponse } from 'http';
16
19
  import type { SwitchBotPlatformConfig, devicesConfig } from '../settings.js';
17
20
  import type { API, CharacteristicValue, HAP, Logging, PlatformAccessory, Service } from 'homebridge';
18
- import { SwitchBotModel, SwitchBotBLEModel, SwitchBotBLEModelName, SwitchBotBLEModelFriendlyName } from 'node-switchbot';
19
21
 
20
22
  export abstract class deviceBase {
21
23
  public readonly api: API;
@@ -39,6 +41,15 @@ export abstract class deviceBase {
39
41
  protected deviceModel!: SwitchBotModel;
40
42
  protected deviceBLEModel!: SwitchBotBLEModel;
41
43
 
44
+ // Webhook
45
+ protected readonly webhookEventHandler: { [x: string]: (context: any) => void } = {};
46
+ protected webhookEventListener: Server | null = null;
47
+
48
+ // MQTT
49
+ protected deviceMqttURL!: string;
50
+ protected deviceMqttOptions!: any;
51
+ protected deviceMqttPubOptions!: any;
52
+
42
53
  // BLE
43
54
  protected scanDuration!: number;
44
55
 
@@ -48,6 +59,8 @@ export abstract class deviceBase {
48
59
  //MQTT stuff
49
60
  protected mqttClient: MqttClient | null = null;
50
61
 
62
+
63
+
51
64
  constructor(
52
65
  protected readonly platform: SwitchBotPlatform,
53
66
  protected accessory: PlatformAccessory,
@@ -68,7 +81,9 @@ export abstract class deviceBase {
68
81
  this.getDeviceConfigSettings(accessory, device);
69
82
  this.getDeviceContext(accessory, device);
70
83
  this.getDeviceScanDuration(accessory, device);
84
+ this.getMqttSettings(device);
71
85
  this.setupMqtt(device);
86
+ this.setupwebhook();
72
87
 
73
88
  // Set accessory information
74
89
  accessory
@@ -76,82 +91,41 @@ export abstract class deviceBase {
76
91
  .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
77
92
  .setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760')
78
93
  .setCharacteristic(this.hap.Characteristic.Name, device.deviceName ?? accessory.displayName)
79
- .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName ?? accessory.displayName)
94
+ //.setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName ?? accessory.displayName)
80
95
  .setCharacteristic(this.hap.Characteristic.Model, device.model ?? accessory.context.model)
81
96
  .setCharacteristic(this.hap.Characteristic.ProductData, device.deviceId ?? accessory.context.deviceId)
82
97
  .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId);
83
98
  }
84
99
 
85
100
  async getDeviceLogSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
86
- if (this.platform.debugMode) {
87
- this.deviceLogging = accessory.context.logging = 'debugMode';
88
- this.debugWarnLog(`Using Debug Mode Logging: ${this.deviceLogging}`);
89
- } else if (device.logging) {
90
- this.deviceLogging = accessory.context.logging = device.logging;
91
- this.debugWarnLog(`Using Device Config Logging: ${this.deviceLogging}`);
92
- } else if (this.config.logging) {
93
- this.deviceLogging = accessory.context.logging = this.config.logging;
94
- this.debugWarnLog(`Using Platform Config Logging: ${this.deviceLogging}`);
95
- } else {
96
- this.deviceLogging = accessory.context.logging = 'standard';
97
- this.debugWarnLog(`Logging Not Set, Using: ${this.deviceLogging}`);
98
- }
101
+ this.deviceLogging = this.platform.debugMode ? 'debugMode' : device.logging ?? this.config.logging ?? 'standard';
102
+ const logging = this.platform.debugMode ? 'Debug Mode' : device.logging ? 'Device Config' : this.config.logging ? 'Platform Config' : 'Default';
103
+ accessory.context.deviceLogging = this.deviceLogging;
104
+ await this.debugLog(`Using ${logging} Logging: ${this.deviceLogging}`);
99
105
  }
100
106
 
101
107
  async getDeviceRateSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
102
108
  // refreshRate
103
- if (device.refreshRate) {
104
- this.deviceRefreshRate = device.refreshRate;
105
- this.debugLog(`Using Device Config refreshRate: ${this.deviceRefreshRate}`);
106
- } else if (this.config.options?.refreshRate) {
107
- this.deviceRefreshRate = this.config.options.refreshRate;
108
- this.debugLog(`Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
109
- } else {
110
- this.deviceRefreshRate = 5;
111
- this.debugLog(`Using Default refreshRate: ${this.deviceRefreshRate}`);
112
- }
109
+ this.deviceRefreshRate = device.refreshRate ?? this.config.options?.refreshRate ?? 5;
113
110
  accessory.context.deviceRefreshRate = this.deviceRefreshRate;
114
- // updateRate
115
- if (device.updateRate) {
116
- this.deviceUpdateRate = device.updateRate;
117
- this.debugLog(`Using Device Config updateRate: ${this.deviceUpdateRate}`);
118
- } else if (this.config.options?.updateRate) {
119
- this.deviceUpdateRate = this.config.options.updateRate;
120
- this.debugLog(`Using Platform Config updateRate: ${this.deviceUpdateRate}`);
121
- } else {
122
- this.deviceUpdateRate = 5;
123
- this.debugLog(`Using Default updateRate: ${this.deviceUpdateRate}`);
124
- }
111
+ const refreshRate = device.refreshRate ? 'Device Config' : this.config.options?.refreshRate ? 'Platform Config' : 'Default';
112
+ this.deviceUpdateRate = device.updateRate ?? this.config.options?.updateRate ?? 5;
125
113
  accessory.context.deviceUpdateRate = this.deviceUpdateRate;
126
- // pushRate
127
- if (device.pushRate) {
128
- this.devicePushRate = device.pushRate;
129
- this.debugLog(`Using Device Config pushRate: ${this.deviceUpdateRate}`);
130
- } else if (this.config.options?.pushRate) {
131
- this.devicePushRate = this.config.options.pushRate;
132
- this.debugLog(`Using Platform Config pushRate: ${this.deviceUpdateRate}`);
133
- } else {
134
- this.devicePushRate = 1;
135
- this.debugLog(`Using Default pushRate: ${this.deviceUpdateRate}`);
136
- }
114
+ const updateRate = device.updateRate ? 'Device Config' : this.config.options?.updateRate ? 'Platform Config' : 'Default';
115
+ this.devicePushRate = device.pushRate ?? this.config.options?.pushRate ?? 1;
137
116
  accessory.context.devicePushRate = this.devicePushRate;
117
+ const pushRate = device.pushRate ? 'Device Config' : this.config.options?.pushRate ? 'Platform Config' : 'Default';
118
+ await this.debugLog(`Using ${refreshRate} refreshRate: ${this.deviceRefreshRate}, ${updateRate} updateRate: ${this.deviceUpdateRate},`
119
+ + ` ${pushRate} pushRate: ${this.devicePushRate}`);
138
120
  }
139
121
 
140
122
  async getDeviceRetry(device: device & devicesConfig): Promise<void> {
141
- if (device.maxRetries) {
142
- this.deviceMaxRetries = device.maxRetries;
143
- this.debugLog(`Using Device Max Retries: ${this.deviceMaxRetries}`);
144
- } else {
145
- this.deviceMaxRetries = 5; // Maximum number of retries
146
- this.debugLog(`Max Retries Not Set, Using: ${this.deviceMaxRetries}`);
147
- }
148
- if (device.delayBetweenRetries) {
149
- this.deviceDelayBetweenRetries = device.delayBetweenRetries * 1000;
150
- this.debugLog(`Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
151
- } else {
152
- this.deviceDelayBetweenRetries = 3000; // Delay between retries in milliseconds
153
- this.debugLog(`Delay Between Retries Not Set, Using: ${this.deviceDelayBetweenRetries}`);
154
- }
123
+ this.deviceMaxRetries = device.maxRetries ?? 5;
124
+ const maxRetries = device.maxRetries ? 'Device' : 'Default';
125
+ this.deviceDelayBetweenRetries = device.delayBetweenRetries ? (device.delayBetweenRetries * 1000) : 3000;
126
+ const delayBetweenRetries = device.delayBetweenRetries ? 'Device' : 'Default';
127
+ await this.debugLog(`Using ${maxRetries} Max Retries: ${this.deviceMaxRetries},`
128
+ + ` ${delayBetweenRetries} Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
155
129
  }
156
130
 
157
131
  async retryBLE({ max, fn }: { max: number; fn: { (): any; (): Promise<any> } }): Promise<null> {
@@ -167,14 +141,12 @@ export abstract class deviceBase {
167
141
  }
168
142
 
169
143
  async maxRetryBLE(): Promise<number> {
170
- if (this.device.maxRetry) {
171
- return this.device.maxRetry;
172
- } else {
173
- return 5;
174
- }
144
+ return this.device.maxRetry ? this.device.maxRetry : 5;
175
145
  }
176
146
 
177
147
  async getDeviceScanDuration(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
148
+ this.scanDuration = device.scanDuration ? (this.deviceUpdateRate > device.scanDuration) ? this.deviceUpdateRate : device.scanDuration
149
+ ? (this.deviceUpdateRate > 1) ? this.deviceUpdateRate : 1 : this.deviceUpdateRate : 1;
178
150
  if (device.scanDuration) {
179
151
  if (this.deviceUpdateRate > device.scanDuration) {
180
152
  this.scanDuration = this.deviceUpdateRate;
@@ -319,21 +291,10 @@ export abstract class deviceBase {
319
291
  const numberOfLevels = spaceBetweenLevels + 1;
320
292
  this.debugLog(`LightLevel: ${lightLevel}, set_minLux: ${set_minLux}, set_maxLux: ${set_maxLux}, spaceBetweenLevels: ${spaceBetweenLevels},`
321
293
  + ` numberOfLevels: ${numberOfLevels}`);
322
- let CurrentAmbientLightLevel: number;
323
- switch (lightLevel) {
324
- case 1:
325
- CurrentAmbientLightLevel = set_minLux;
326
- this.debugLog(`LightLevel: ${lightLevel}, set_minLux: ${set_minLux}`);
327
- break;
328
- case numberOfLevels:
329
- CurrentAmbientLightLevel = set_maxLux;
330
- this.debugLog(`LightLevel: ${lightLevel}, set_maxLux: ${set_maxLux}`);
331
- break;
332
- default:
333
- CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(lightLevel) - 1);
334
- this.debugLog(`LightLevel: ${lightLevel}`);
335
- }
336
- this.debugLog(`CurrentAmbientLightLevel: ${CurrentAmbientLightLevel}`);
294
+ const CurrentAmbientLightLevel = lightLevel === 1 ? set_minLux : lightLevel = numberOfLevels
295
+ ? set_maxLux : ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(lightLevel) - 1);
296
+ await this.debugLog(`CurrentAmbientLightLevel: ${CurrentAmbientLightLevel}, LightLevel: ${lightLevel}, set_minLux: ${set_minLux},`
297
+ + ` set_maxLux: ${set_maxLux}`);
337
298
  return CurrentAmbientLightLevel;
338
299
  }
339
300
 
@@ -342,24 +303,31 @@ export abstract class deviceBase {
342
303
  * 'homebridge-switchbot/${this.device.deviceType}/xx:xx:xx:xx:xx:xx'
343
304
  */
344
305
  async mqttPublish(message: string, topic?: string) {
345
- const mac = this.device.deviceId
346
- ?.toLowerCase()
347
- .match(/[\s\S]{1,2}/g)
348
- ?.join(':');
349
- const options = this.device.mqttPubOptions || {};
350
- let mqttTopic: string;
351
- let mqttMessageTopic: string;
352
- if (topic) {
353
- mqttTopic = `/${topic}`;
354
- mqttMessageTopic = `${topic}/`;
355
- } else {
356
- mqttTopic = '';
357
- mqttMessageTopic = '';
358
- }
306
+ const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
307
+ const options = this.device.mqttPubOptions ?? {};
308
+ const mqttTopic = topic ? `/${topic}` : '';
309
+ const mqttMessageTopic = topic ? `${topic}/` : '';
359
310
  this.mqttClient?.publish(`homebridge-switchbot/${this.device.deviceType}/${mac}${mqttTopic}`, `${message}`, options);
360
311
  this.debugLog(`MQTT message: ${mqttMessageTopic}${message} options:${JSON.stringify(options)}`);
361
312
  }
362
313
 
314
+ /*
315
+ * MQTT Settings
316
+ */
317
+ async getMqttSettings(device: device & devicesConfig): Promise<void> {
318
+ // mqttURL
319
+ this.deviceMqttURL = device.mqttURL ?? this.config.options?.mqttURL ?? '';
320
+ const mqttURL = device.mqttURL ? 'Device Config' : this.config.options?.mqttURL ? 'Platform Config' : 'Default';
321
+ // mqttOptions
322
+ this.deviceMqttOptions = device.mqttOptions ?? this.config.options?.mqttOptions ?? {};
323
+ const mqttOptions = device.mqttOptions ? 'Device Config' : this.config.options?.mqttOptions ? 'Platform Config' : 'Default';
324
+ // mqttPubOptions
325
+ this.deviceMqttPubOptions = device.mqttPubOptions ?? this.config.options?.mqttPubOptions ?? {};
326
+ const mqttPubOptions = device.mqttPubOptions ? 'Device Config' : this.config.options?.mqttPubOptions ? 'Platform Config' : 'Default';
327
+ await this.debugLog(`Using ${mqttURL} MQTT URL: ${this.deviceMqttURL}, ${mqttOptions} mqttOptions: ${JSON.stringify(this.deviceMqttOptions)},`
328
+ + ` ${mqttPubOptions} mqttPubOptions: ${JSON.stringify(this.deviceMqttPubOptions)}`);
329
+ }
330
+
363
331
  /*
364
332
  * Setup MQTT hadler if URL is specified.
365
333
  */
@@ -379,6 +347,168 @@ export abstract class deviceBase {
379
347
  }
380
348
  }
381
349
 
350
+ async setupDeviceMqtt(): Promise<void> {
351
+ if (this.config.options?.mqttURL) {
352
+ try {
353
+ const { connectAsync } = asyncmqtt;
354
+ this.mqttClient = await connectAsync(this.config.options?.mqttURL, this.config.options.mqttOptions || {});
355
+ await this.debugLog('MQTT connection has been established successfully.');
356
+ this.mqttClient.on('error', async (e: Error) => {
357
+ await this.errorLog(`Failed to publish MQTT messages. ${e}`);
358
+ });
359
+ if (!this.config.options?.webhookURL) {
360
+ // receive webhook events via MQTT
361
+ await this.infoLog(`Webhook is configured to be received through ${this.config.options.mqttURL}/homebridge-switchbot/webhook.`);
362
+ this.mqttClient.subscribe('homebridge-switchbot/webhook/+');
363
+ this.mqttClient.on('message', async (topic: string, message) => {
364
+ try {
365
+ await this.debugLog(`Received Webhook via MQTT: ${topic}=${message}`);
366
+ const context = JSON.parse(message.toString());
367
+ this.webhookEventHandler[context.deviceMac]?.(context);
368
+ } catch (e: any) {
369
+ await this.errorLog(`Failed to handle webhook event. Error:${e}`);
370
+ }
371
+ });
372
+ }
373
+ } catch (e) {
374
+ this.mqttClient = null;
375
+ await this.errorLog(`Failed to establish MQTT connection. ${e}`);
376
+ }
377
+ }
378
+ }
379
+
380
+ async setupwebhook() {
381
+ //webhook configuration
382
+ if (this.config.options?.webhookURL) {
383
+ const url = this.config.options?.webhookURL;
384
+
385
+ try {
386
+ const xurl = new URL(url);
387
+ const port = Number(xurl.port);
388
+ const path = xurl.pathname;
389
+ this.webhookEventListener = createServer((request: IncomingMessage, response: ServerResponse) => {
390
+ try {
391
+ if (request.url === path && request.method === 'POST') {
392
+ request.on('data', async (data) => {
393
+ try {
394
+ const body = JSON.parse(data);
395
+ await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`);
396
+ if (this.config.options?.mqttURL) {
397
+ const mac = body.context.deviceMac
398
+ ?.toLowerCase()
399
+ .match(/[\s\S]{1,2}/g)
400
+ ?.join(':');
401
+ const options = this.config.options?.mqttPubOptions || {};
402
+ this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options);
403
+ }
404
+ this.webhookEventHandler[body.context.deviceMac]?.(body.context);
405
+ } catch (e: any) {
406
+ this.errorLog(`Failed to handle webhook event. Error:${e}`);
407
+ }
408
+ });
409
+ response.writeHead(200, { 'Content-Type': 'text/plain' });
410
+ response.end('OK');
411
+ }
412
+ // else {
413
+ // response.writeHead(403, {'Content-Type': 'text/plain'});
414
+ // response.end(`NG`);
415
+ // }
416
+ } catch (e: any) {
417
+ this.errorLog(`Failed to handle webhook event. Error:${e}`);
418
+ }
419
+ }).listen(port ? port : 80);
420
+ } catch (e: any) {
421
+ this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
422
+ return;
423
+ }
424
+
425
+ try {
426
+ const { body, statusCode } = await request(setupWebhook, {
427
+ method: 'POST',
428
+ headers: this.platform.generateHeaders(),
429
+ body: JSON.stringify({
430
+ 'action': 'setupWebhook',
431
+ 'url': url,
432
+ 'deviceList': 'ALL',
433
+ }),
434
+ });
435
+ const response: any = await body.json();
436
+ await this.debugLog(`setupWebhook: url:${url}`);
437
+ await this.debugLog(`setupWebhook: body:${JSON.stringify(response)}`);
438
+ await this.debugLog(`setupWebhook: statusCode:${statusCode}`);
439
+ if (statusCode !== 200 || response?.statusCode !== 100) {
440
+ this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
441
+ + `message:${response?.message}`);
442
+ }
443
+ } catch (e: any) {
444
+ this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
445
+ }
446
+
447
+ try {
448
+ const { body, statusCode } = await request(updateWebhook, {
449
+ method: 'POST', headers: this.platform.generateHeaders(), body: JSON.stringify({
450
+ 'action': 'updateWebhook',
451
+ 'config': {
452
+ 'url': url,
453
+ 'enable': true,
454
+ },
455
+ }),
456
+ });
457
+ const response: any = await body.json();
458
+ await this.debugLog(`updateWebhook: url:${url}`);
459
+ await this.debugLog(`updateWebhook: body:${JSON.stringify(response)}`);
460
+ await this.debugLog(`updateWebhook: statusCode:${statusCode}`);
461
+ if (statusCode !== 200 || response?.statusCode !== 100) {
462
+ this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
463
+ }
464
+ } catch (e: any) {
465
+ this.errorLog(`Failed to update webhook. Error:${e.message}`);
466
+ }
467
+
468
+ try {
469
+ const { body, statusCode } = await request(queryWebhook, {
470
+ method: 'POST',
471
+ headers: this.platform.generateHeaders(),
472
+ body: JSON.stringify({
473
+ 'action': 'queryUrl',
474
+ }),
475
+ });
476
+ const response: any = await body.json();
477
+ await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`);
478
+ await this.debugLog(`queryWebhook: statusCode:${statusCode}`);
479
+ if (statusCode !== 200 || response?.statusCode !== 100) {
480
+ this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
481
+ } else {
482
+ this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
483
+ }
484
+ } catch (e: any) {
485
+ this.errorLog(`Failed to query webhook. Error:${e}`);
486
+ }
487
+
488
+ this.api.on('shutdown', async () => {
489
+ try {
490
+ const { body, statusCode } = await request(deleteWebhook, {
491
+ method: 'POST',
492
+ headers: this.platform.generateHeaders(),
493
+ body: JSON.stringify({
494
+ 'action': 'deleteWebhook',
495
+ 'url': url,
496
+ }),
497
+ });
498
+ const response: any = await body.json();
499
+ await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
500
+ if (statusCode !== 200 || response?.statusCode !== 100) {
501
+ this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
502
+ } else {
503
+ this.infoLog('Unregistered webhook to close listening.');
504
+ }
505
+ } catch (e: any) {
506
+ this.errorLog(`Failed to delete webhook. Error:${e.message}`);
507
+ }
508
+ });
509
+ }
510
+ }
511
+
382
512
  /*
383
513
  * Setup EVE history graph feature if enabled.
384
514
  */