@switchbot/homebridge-switchbot 3.6.1-beta.0 → 3.6.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 (106) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/config.schema.json +11 -0
  3. package/dist/device/blindtilt.d.ts.map +1 -1
  4. package/dist/device/blindtilt.js +36 -18
  5. package/dist/device/blindtilt.js.map +1 -1
  6. package/dist/device/bot.d.ts +1 -2
  7. package/dist/device/bot.d.ts.map +1 -1
  8. package/dist/device/bot.js +186 -396
  9. package/dist/device/bot.js.map +1 -1
  10. package/dist/device/ceilinglight.d.ts +3 -2
  11. package/dist/device/ceilinglight.d.ts.map +1 -1
  12. package/dist/device/ceilinglight.js +57 -32
  13. package/dist/device/ceilinglight.js.map +1 -1
  14. package/dist/device/colorbulb.d.ts +3 -2
  15. package/dist/device/colorbulb.d.ts.map +1 -1
  16. package/dist/device/colorbulb.js +58 -30
  17. package/dist/device/colorbulb.js.map +1 -1
  18. package/dist/device/contact.d.ts.map +1 -1
  19. package/dist/device/contact.js +36 -19
  20. package/dist/device/contact.js.map +1 -1
  21. package/dist/device/curtain.d.ts.map +1 -1
  22. package/dist/device/curtain.js +37 -18
  23. package/dist/device/curtain.js.map +1 -1
  24. package/dist/device/device.d.ts +9 -0
  25. package/dist/device/device.d.ts.map +1 -1
  26. package/dist/device/device.js +32 -5
  27. package/dist/device/device.js.map +1 -1
  28. package/dist/device/fan.d.ts.map +1 -1
  29. package/dist/device/fan.js +36 -19
  30. package/dist/device/fan.js.map +1 -1
  31. package/dist/device/hub.d.ts.map +1 -1
  32. package/dist/device/hub.js +38 -19
  33. package/dist/device/hub.js.map +1 -1
  34. package/dist/device/humidifier.d.ts.map +1 -1
  35. package/dist/device/humidifier.js +40 -21
  36. package/dist/device/humidifier.js.map +1 -1
  37. package/dist/device/iosensor.d.ts.map +1 -1
  38. package/dist/device/iosensor.js +38 -19
  39. package/dist/device/iosensor.js.map +1 -1
  40. package/dist/device/lightstrip.d.ts +4 -3
  41. package/dist/device/lightstrip.d.ts.map +1 -1
  42. package/dist/device/lightstrip.js +58 -32
  43. package/dist/device/lightstrip.js.map +1 -1
  44. package/dist/device/lock.d.ts.map +1 -1
  45. package/dist/device/lock.js +37 -18
  46. package/dist/device/lock.js.map +1 -1
  47. package/dist/device/meter.d.ts.map +1 -1
  48. package/dist/device/meter.js +38 -19
  49. package/dist/device/meter.js.map +1 -1
  50. package/dist/device/meterplus.d.ts.map +1 -1
  51. package/dist/device/meterplus.js +38 -19
  52. package/dist/device/meterplus.js.map +1 -1
  53. package/dist/device/motion.d.ts.map +1 -1
  54. package/dist/device/motion.js +36 -17
  55. package/dist/device/motion.js.map +1 -1
  56. package/dist/device/plug.d.ts.map +1 -1
  57. package/dist/device/plug.js +37 -18
  58. package/dist/device/plug.js.map +1 -1
  59. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  60. package/dist/device/robotvacuumcleaner.js +36 -17
  61. package/dist/device/robotvacuumcleaner.js.map +1 -1
  62. package/dist/device/waterdetector.d.ts.map +1 -1
  63. package/dist/device/waterdetector.js +36 -17
  64. package/dist/device/waterdetector.js.map +1 -1
  65. package/dist/irdevice/other.d.ts +1 -0
  66. package/dist/irdevice/other.d.ts.map +1 -1
  67. package/dist/irdevice/other.js +186 -228
  68. package/dist/irdevice/other.js.map +1 -1
  69. package/dist/platform.d.ts +4 -0
  70. package/dist/platform.d.ts.map +1 -1
  71. package/dist/platform.js +41 -0
  72. package/dist/platform.js.map +1 -1
  73. package/dist/settings.d.ts +1 -0
  74. package/dist/settings.d.ts.map +1 -1
  75. package/dist/settings.js.map +1 -1
  76. package/dist/types/bledevicestatus.d.ts +1 -0
  77. package/dist/types/bledevicestatus.d.ts.map +1 -1
  78. package/dist/utils.d.ts +8 -0
  79. package/dist/utils.d.ts.map +1 -1
  80. package/dist/utils.js +16 -0
  81. package/dist/utils.js.map +1 -1
  82. package/package.json +8 -8
  83. package/src/device/blindtilt.ts +33 -18
  84. package/src/device/bot.ts +179 -406
  85. package/src/device/ceilinglight.ts +54 -34
  86. package/src/device/colorbulb.ts +56 -32
  87. package/src/device/contact.ts +33 -20
  88. package/src/device/curtain.ts +34 -18
  89. package/src/device/device.ts +38 -5
  90. package/src/device/fan.ts +33 -20
  91. package/src/device/hub.ts +35 -19
  92. package/src/device/humidifier.ts +37 -21
  93. package/src/device/iosensor.ts +35 -19
  94. package/src/device/lightstrip.ts +57 -34
  95. package/src/device/lock.ts +34 -18
  96. package/src/device/meter.ts +35 -19
  97. package/src/device/meterplus.ts +35 -19
  98. package/src/device/motion.ts +33 -17
  99. package/src/device/plug.ts +34 -18
  100. package/src/device/robotvacuumcleaner.ts +33 -17
  101. package/src/device/waterdetector.ts +33 -17
  102. package/src/irdevice/other.ts +214 -283
  103. package/src/platform.ts +39 -0
  104. package/src/settings.ts +1 -0
  105. package/src/types/bledevicestatus.ts +1 -0
  106. package/src/utils.ts +15 -0
@@ -42,8 +42,9 @@ export class CeilingLight extends deviceBase {
42
42
  serviceData!: ceilingLightServiceData | ceilingLightProServiceData;
43
43
 
44
44
  // Adaptive Lighting
45
+ adaptiveLighting!: boolean;
46
+ adaptiveLightingShift!: number;
45
47
  AdaptiveLightingController?: ControllerConstructor | Controller<ControllerServiceMap>;
46
- adaptiveLightingShift?: number;
47
48
 
48
49
  // Updates
49
50
  ceilingLightUpdateInProgress!: boolean;
@@ -59,7 +60,7 @@ export class CeilingLight extends deviceBase {
59
60
  accessory.category = this.hap.Categories.LIGHTBULB;
60
61
 
61
62
  // default placeholders
62
- this.adaptiveLighting(device);
63
+ this.getAdaptiveLightingSettings(accessory, device);
63
64
 
64
65
  // this is subject we use to track when we need to POST changes to the SwitchBot API
65
66
  this.doCeilingLightUpdate = new Subject();
@@ -78,23 +79,24 @@ export class CeilingLight extends deviceBase {
78
79
  };
79
80
  accessory.context.LightBulb = this.LightBulb as object;
80
81
 
81
- // Adaptive Lighting
82
- if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) {
82
+ if (this.adaptiveLighting && this.adaptiveLightingShift === -1 && this.LightBulb) {
83
83
  accessory.removeService(this.LightBulb.Service);
84
84
  this.LightBulb.Service = accessory.addService(this.hap.Service.Lightbulb);
85
85
  accessory.context.adaptiveLighting = false;
86
- this.debugLog(`adaptiveLighting: ${accessory.context.adaptiveLighting}`);
87
- }
88
- if (this.adaptiveLightingShift !== -1) {
86
+ this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`);
87
+ } else if (this.adaptiveLighting && this.adaptiveLightingShift >= 0 && this.LightBulb) {
89
88
  this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, {
89
+ controllerMode: this.hap.AdaptiveLightingControllerMode.AUTOMATIC,
90
90
  customTemperatureAdjustment: this.adaptiveLightingShift,
91
91
  });
92
- this.accessory.configureController(this.AdaptiveLightingController);
93
- this.accessory.context.adaptiveLighting = true;
94
- this.debugLog(`adaptiveLighting: ${this.accessory.context.adaptiveLighting},`
95
- + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`);
92
+ accessory.configureController(this.AdaptiveLightingController);
93
+ accessory.context.adaptiveLighting = true;
94
+ this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}, adaptiveLightingShift: ${this.adaptiveLightingShift}`,
95
+ );
96
+ } else {
97
+ accessory.context.adaptiveLighting = false;
98
+ this.debugLog(`adaptiveLighting: ${accessory.context.adaptiveLighting}`);
96
99
  }
97
- this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
98
100
 
99
101
  // Initialize LightBulb Characteristics
100
102
  this.LightBulb.Service
@@ -166,9 +168,6 @@ export class CeilingLight extends deviceBase {
166
168
  this.debugLog('Registering Webhook Event Handler');
167
169
  this.registerWebhook();
168
170
 
169
- //regisiter webhook event handler
170
- this.registerWebhook();
171
-
172
171
  // Start an update interval
173
172
  interval(this.deviceRefreshRate * 1000)
174
173
  .pipe(skipWhile(() => this.ceilingLightUpdateInProgress))
@@ -276,7 +275,7 @@ export class CeilingLight extends deviceBase {
276
275
  async refreshStatus(): Promise<void> {
277
276
  if (!this.device.enableCloudService && this.OpenAPI) {
278
277
  await this.errorLog(`refreshStatus enableCloudService: ${this.device.enableCloudService}`);
279
- } else if (this.BLE) {
278
+ } else if (this.BLE || this.config.options?.BLE) {
280
279
  await this.BLERefreshStatus();
281
280
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
282
281
  await this.openAPIRefreshStatus();
@@ -288,25 +287,41 @@ export class CeilingLight extends deviceBase {
288
287
 
289
288
  async BLERefreshStatus(): Promise<void> {
290
289
  await this.debugLog('BLERefreshStatus');
291
- const switchbot = await this.switchbotBLE();
292
-
293
- if (switchbot === undefined) {
294
- await this.BLERefreshConnection(switchbot);
295
- } else {
296
- (async () => {
297
- // Start to monitor advertisement packets
298
- const serviceData = await this.monitorAdvertisementPackets(switchbot) as unknown as ceilingLightServiceData;
299
- // Update HomeKit
300
- if ((serviceData.model === SwitchBotBLEModel.CeilingLight || SwitchBotBLEModel.CeilingLightPro)
301
- && serviceData.modelName === SwitchBotBLEModelName.CeilingLight || SwitchBotBLEModelName.CeilingLightPro) {
302
- this.serviceData = serviceData;
290
+ if (this.config.options?.BLE) {
291
+ await this.debugLog('is listening to Platform BLE.');
292
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
293
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
294
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: ceilingLightServiceData) => {
295
+ try {
296
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
297
+ this.serviceData = context;
303
298
  await this.BLEparseStatus();
304
299
  await this.updateHomeKitCharacteristics();
305
- } else {
306
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
307
- await this.BLERefreshConnection(switchbot);
300
+ } catch (e: any) {
301
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
308
302
  }
309
- })();
303
+ };
304
+ } else {
305
+ await this.debugLog('is using Device BLE Scanning.');
306
+ const switchbot = await this.switchbotBLE();
307
+ if (switchbot === undefined) {
308
+ await this.BLERefreshConnection(switchbot);
309
+ } else {
310
+ (async () => {
311
+ // Start to monitor advertisement packets
312
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as unknown as ceilingLightServiceData;
313
+ // Update HomeKit
314
+ if ((serviceData.model === SwitchBotBLEModel.CeilingLight || SwitchBotBLEModel.CeilingLightPro)
315
+ && serviceData.modelName === SwitchBotBLEModelName.CeilingLight || SwitchBotBLEModelName.CeilingLightPro) {
316
+ this.serviceData = serviceData;
317
+ await this.BLEparseStatus();
318
+ await this.updateHomeKitCharacteristics();
319
+ } else {
320
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
321
+ await this.BLERefreshConnection(switchbot);
322
+ }
323
+ })();
324
+ }
310
325
  }
311
326
  }
312
327
 
@@ -687,13 +702,18 @@ export class CeilingLight extends deviceBase {
687
702
  this.LightBulb.Saturation, 'Saturation');
688
703
  }
689
704
 
690
- async adaptiveLighting(device: device & devicesConfig): Promise<void> {
705
+ async getAdaptiveLightingSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
706
+ // Adaptive Lighting
707
+ this.adaptiveLighting = accessory.context.adaptiveLighting ?? true;
708
+ await this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`);
709
+ // Adaptive Lighting Shift
691
710
  if (device.ceilinglight?.adaptiveLightingShift) {
692
711
  this.adaptiveLightingShift = device.ceilinglight.adaptiveLightingShift;
712
+ this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
693
713
  } else {
694
714
  this.adaptiveLightingShift = 0;
715
+ this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
695
716
  }
696
- await this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
697
717
  }
698
718
 
699
719
  async BLEPushConnection() {
@@ -42,8 +42,9 @@ export class ColorBulb extends deviceBase {
42
42
  serviceData!: colorBulbServiceData;
43
43
 
44
44
  // Adaptive Lighting
45
+ adaptiveLighting!: boolean;
46
+ adaptiveLightingShift!: number;
45
47
  AdaptiveLightingController?: ControllerConstructor | Controller<ControllerServiceMap>;
46
- adaptiveLightingShift?: number;
47
48
 
48
49
  // Updates
49
50
  colorBulbUpdateInProgress!: boolean;
@@ -59,7 +60,7 @@ export class ColorBulb extends deviceBase {
59
60
  accessory.category = this.hap.Categories.LIGHTBULB;
60
61
 
61
62
  // default placeholders
62
- this.adaptiveLighting(device);
63
+ this.getAdaptiveLightingSettings(accessory, device);
63
64
 
64
65
  // this is subject we use to track when we need to POST changes to the SwitchBot API
65
66
  this.doColorBulbUpdate = new Subject();
@@ -67,6 +68,10 @@ export class ColorBulb extends deviceBase {
67
68
 
68
69
  // Initialize LightBulb property
69
70
  accessory.context.LightBulb = accessory.context.LightBulb ?? {};
71
+ if (accessory.context.LightBulb.Name) {
72
+ accessory.context.LightBulb.Name = this.validateAndCleanString(accessory.context.LightBulb.Name,
73
+ 'LightBulb Name', accessory.context.LightBulb.Name);
74
+ }
70
75
  this.LightBulb = {
71
76
  Name: accessory.context.LightBulb.Name ?? accessory.displayName,
72
77
  Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
@@ -78,23 +83,24 @@ export class ColorBulb extends deviceBase {
78
83
  };
79
84
  accessory.context.LightBulb = this.LightBulb as object;
80
85
 
81
- // Adaptive Lighting
82
- if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) {
86
+ if (this.adaptiveLighting && this.adaptiveLightingShift === -1 && this.LightBulb) {
83
87
  accessory.removeService(this.LightBulb.Service);
84
88
  this.LightBulb.Service = accessory.addService(this.hap.Service.Lightbulb);
85
89
  accessory.context.adaptiveLighting = false;
86
- this.debugLog(`adaptiveLighting: ${accessory.context.adaptiveLighting}`);
87
- }
88
- if (this.adaptiveLightingShift !== -1) {
90
+ this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`);
91
+ } else if (this.adaptiveLighting && this.adaptiveLightingShift >= 0 && this.LightBulb) {
89
92
  this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, {
93
+ controllerMode: this.hap.AdaptiveLightingControllerMode.AUTOMATIC,
90
94
  customTemperatureAdjustment: this.adaptiveLightingShift,
91
95
  });
92
96
  accessory.configureController(this.AdaptiveLightingController);
93
97
  accessory.context.adaptiveLighting = true;
94
- this.debugLog(`adaptiveLighting: ${accessory.context.adaptiveLighting},`
95
- + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`);
98
+ this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}, adaptiveLightingShift: ${this.adaptiveLightingShift}`,
99
+ );
100
+ } else {
101
+ accessory.context.adaptiveLighting = false;
102
+ this.debugLog(`adaptiveLighting: ${accessory.context.adaptiveLighting}`);
96
103
  }
97
- this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
98
104
 
99
105
  // Initialize LightBulb Characteristics
100
106
  this.LightBulb.Service
@@ -162,9 +168,6 @@ export class ColorBulb extends deviceBase {
162
168
  this.debugLog('Registering Webhook Event Handler');
163
169
  this.registerWebhook();
164
170
 
165
- //regisiter webhook event handler
166
- this.registerWebhook();
167
-
168
171
  // Start an update interval
169
172
  interval(this.deviceRefreshRate * 1000)
170
173
  .pipe(skipWhile(() => this.colorBulbUpdateInProgress))
@@ -297,7 +300,7 @@ export class ColorBulb extends deviceBase {
297
300
  async refreshStatus(): Promise<void> {
298
301
  if (!this.device.enableCloudService && this.OpenAPI) {
299
302
  await this.errorLog(`refreshStatus enableCloudService: ${this.device.enableCloudService}`);
300
- } else if (this.BLE) {
303
+ } else if (this.BLE || this.config.options?.BLE) {
301
304
  await this.BLERefreshStatus();
302
305
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
303
306
  await this.openAPIRefreshStatus();
@@ -309,25 +312,41 @@ export class ColorBulb extends deviceBase {
309
312
 
310
313
  async BLERefreshStatus(): Promise<void> {
311
314
  await this.debugLog('BLERefreshStatus');
312
- const switchbot = await this.switchbotBLE();
313
-
314
- if (switchbot === undefined) {
315
- await this.BLERefreshConnection(switchbot);
316
- } else {
317
- // Start to monitor advertisement packets
318
- (async () => {
319
- // Start to monitor advertisement packets
320
- const serviceData = await this.monitorAdvertisementPackets(switchbot) as colorBulbServiceData;
321
- // Update HomeKit
322
- if (serviceData.model === SwitchBotBLEModel.ColorBulb && serviceData.modelName === SwitchBotBLEModelName.ColorBulb) {
323
- this.serviceData = serviceData;
315
+ if (this.config.options?.BLE) {
316
+ await this.debugLog('is listening to Platform BLE.');
317
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
318
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
319
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: colorBulbServiceData) => {
320
+ try {
321
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
322
+ this.serviceData = context;
324
323
  await this.BLEparseStatus();
325
324
  await this.updateHomeKitCharacteristics();
326
- } else {
327
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
328
- await this.BLERefreshConnection(switchbot);
325
+ } catch (e: any) {
326
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
329
327
  }
330
- })();
328
+ };
329
+ } else {
330
+ await this.debugLog('is using Device BLE Scanning.');
331
+ const switchbot = await this.switchbotBLE();
332
+ if (switchbot === undefined) {
333
+ await this.BLERefreshConnection(switchbot);
334
+ } else {
335
+ // Start to monitor advertisement packets
336
+ (async () => {
337
+ // Start to monitor advertisement packets
338
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as colorBulbServiceData;
339
+ // Update HomeKit
340
+ if (serviceData.model === SwitchBotBLEModel.ColorBulb && serviceData.modelName === SwitchBotBLEModelName.ColorBulb) {
341
+ this.serviceData = serviceData;
342
+ await this.BLEparseStatus();
343
+ await this.updateHomeKitCharacteristics();
344
+ } else {
345
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
346
+ await this.BLERefreshConnection(switchbot);
347
+ }
348
+ })();
349
+ }
331
350
  }
332
351
  }
333
352
 
@@ -820,13 +839,18 @@ export class ColorBulb extends deviceBase {
820
839
  this.LightBulb.Saturation, 'Saturation');
821
840
  }
822
841
 
823
- async adaptiveLighting(device: device & devicesConfig): Promise<void> {
842
+ async getAdaptiveLightingSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
843
+ // Adaptive Lighting
844
+ this.adaptiveLighting = accessory.context.adaptiveLighting ?? true;
845
+ await this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`);
846
+ // Adaptive Lighting Shift
824
847
  if (device.colorbulb?.adaptiveLightingShift) {
825
848
  this.adaptiveLightingShift = device.colorbulb.adaptiveLightingShift;
849
+ this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
826
850
  } else {
827
851
  this.adaptiveLightingShift = 0;
852
+ this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
828
853
  }
829
- await this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
830
854
  }
831
855
 
832
856
  async BLEPushConnection() {
@@ -176,9 +176,6 @@ export class Contact extends deviceBase {
176
176
  this.debugLog('Registering Webhook Event Handler');
177
177
  this.registerWebhook();
178
178
 
179
- //regisiter webhook event handler
180
- this.registerWebhook();
181
-
182
179
  // Start an update interval
183
180
  interval(this.deviceRefreshRate * 1000)
184
181
  .pipe(skipWhile(() => this.contactUpdateInProgress))
@@ -287,7 +284,7 @@ export class Contact extends deviceBase {
287
284
  async refreshStatus(): Promise<void> {
288
285
  if (!this.device.enableCloudService && this.OpenAPI) {
289
286
  await this.errorLog(`refreshStatus enableCloudService: ${this.device.enableCloudService}`);
290
- } else if (this.BLE) {
287
+ } else if (this.BLE || this.config.options?.BLE) {
291
288
  await this.BLERefreshStatus();
292
289
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
293
290
  await this.openAPIRefreshStatus();
@@ -299,25 +296,41 @@ export class Contact extends deviceBase {
299
296
 
300
297
  async BLERefreshStatus(): Promise<void> {
301
298
  await this.debugLog('BLERefreshStatus');
302
- const switchbot = await this.switchbotBLE();
303
-
304
- if (switchbot === undefined) {
305
- await this.BLERefreshConnection(switchbot);
306
- } else {
307
- // Start to monitor advertisement packets
308
- (async () => {
309
- // Start to monitor advertisement packets
310
- const serviceData = await this.monitorAdvertisementPackets(switchbot) as contactSensorServiceData;
311
- // Update HomeKit
312
- if (serviceData.model === SwitchBotBLEModel.ContactSensor && serviceData.modelName === SwitchBotBLEModelName.ContactSensor) {
313
- this.serviceData = serviceData;
299
+ if (this.config.options?.BLE) {
300
+ await this.debugLog('is listening to Platform BLE.');
301
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
302
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
303
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: contactSensorServiceData) => {
304
+ try {
305
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
306
+ this.serviceData = context;
314
307
  await this.BLEparseStatus();
315
308
  await this.updateHomeKitCharacteristics();
316
- } else {
317
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
318
- await this.BLERefreshConnection(switchbot);
309
+ } catch (e: any) {
310
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
319
311
  }
320
- })();
312
+ };
313
+ } else {
314
+ await this.debugLog('is using Device BLE Scanning.');
315
+ const switchbot = await this.switchbotBLE();
316
+ if (switchbot === undefined) {
317
+ await this.BLERefreshConnection(switchbot);
318
+ } else {
319
+ // Start to monitor advertisement packets
320
+ (async () => {
321
+ // Start to monitor advertisement packets
322
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as contactSensorServiceData;
323
+ // Update HomeKit
324
+ if (serviceData.model === SwitchBotBLEModel.ContactSensor && serviceData.modelName === SwitchBotBLEModelName.ContactSensor) {
325
+ this.serviceData = serviceData;
326
+ await this.BLEparseStatus();
327
+ await this.updateHomeKitCharacteristics();
328
+ } else {
329
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
330
+ await this.BLERefreshConnection(switchbot);
331
+ }
332
+ })();
333
+ }
321
334
  }
322
335
  }
323
336
 
@@ -425,7 +425,7 @@ export class Curtain extends deviceBase {
425
425
  async refreshStatus(): Promise<void> {
426
426
  if (!this.device.enableCloudService && this.OpenAPI) {
427
427
  await this.errorLog(`refreshStatus enableCloudService: ${this.device.enableCloudService}`);
428
- } else if (this.BLE) {
428
+ } else if (this.BLE || this.config.options?.BLE) {
429
429
  await this.BLERefreshStatus();
430
430
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
431
431
  await this.openAPIRefreshStatus();
@@ -437,26 +437,42 @@ export class Curtain extends deviceBase {
437
437
 
438
438
  async BLERefreshStatus(): Promise<void> {
439
439
  await this.debugLog('BLERefreshStatus');
440
- const switchbot = await this.switchbotBLE();
441
-
442
- if (switchbot === undefined) {
443
- await this.BLERefreshConnection(switchbot);
444
- } else {
445
- // Start to monitor advertisement packets
446
- (async () => {
447
- // Start to monitor advertisement packets
448
- const serviceData = await this.monitorAdvertisementPackets(switchbot) as curtainServiceData | curtain3ServiceData;
449
- // Update HomeKit
450
- if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3)
451
- && (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) {
452
- this.serviceData = serviceData;
440
+ if (this.config.options?.BLE) {
441
+ await this.debugLog('is listening to Platform BLE.');
442
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
443
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
444
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: curtainServiceData | curtain3ServiceData) => {
445
+ try {
446
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
447
+ this.serviceData = context;
453
448
  await this.BLEparseStatus();
454
449
  await this.updateHomeKitCharacteristics();
455
- } else {
456
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
457
- await this.BLERefreshConnection(switchbot);
450
+ } catch (e: any) {
451
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
458
452
  }
459
- })();
453
+ };
454
+ } else {
455
+ await this.debugLog('is using Device BLE Scanning.');
456
+ const switchbot = await this.switchbotBLE();
457
+ if (switchbot === undefined) {
458
+ await this.BLERefreshConnection(switchbot);
459
+ } else {
460
+ // Start to monitor advertisement packets
461
+ (async () => {
462
+ // Start to monitor advertisement packets
463
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as curtainServiceData | curtain3ServiceData;
464
+ // Update HomeKit
465
+ if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3)
466
+ && (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) {
467
+ this.serviceData = serviceData;
468
+ await this.BLEparseStatus();
469
+ await this.updateHomeKitCharacteristics();
470
+ } else {
471
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
472
+ await this.BLERefreshConnection(switchbot);
473
+ }
474
+ })();
475
+ }
460
476
  }
461
477
  }
462
478
 
@@ -79,13 +79,20 @@ export abstract class deviceBase {
79
79
  .getService(this.hap.Service.AccessoryInformation)!
80
80
  .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
81
81
  .setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760')
82
- .setCharacteristic(this.hap.Characteristic.Name, device.configDeviceName ?? device.deviceName ?? accessory.displayName)
83
- .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.configDeviceName ?? device.deviceName ?? accessory.displayName)
82
+ .setCharacteristic(this.hap.Characteristic.Name, this.validName(accessory, device))
83
+ .setCharacteristic(this.hap.Characteristic.ConfiguredName, this.validName(accessory, device))
84
84
  .setCharacteristic(this.hap.Characteristic.Model, device.model ?? accessory.context.model)
85
85
  .setCharacteristic(this.hap.Characteristic.ProductData, device.deviceId ?? accessory.context.deviceId)
86
86
  .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId);
87
87
  }
88
88
 
89
+ public validName(accessory: PlatformAccessory, device: device & devicesConfig) {
90
+ const Name = device.configDeviceName ?? device.deviceName ?? accessory.displayName;
91
+ const NameOfName = device.configDeviceName ? 'configDeviceName' : device.deviceName ? 'deviceName' : 'displayName';
92
+ const validName = this.validateAndCleanString(Name, NameOfName, Name);
93
+ return validName;
94
+ }
95
+
89
96
  async getDeviceLogSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
90
97
  this.deviceLogging = this.platform.debugMode ? 'debugMode' : device.logging ?? this.config.logging ?? 'standard';
91
98
  const logging = this.platform.debugMode ? 'Debug Mode' : device.logging ? 'Device Config' : this.config.logging ? 'Platform Config' : 'Default';
@@ -355,11 +362,11 @@ export abstract class deviceBase {
355
362
  .deviceId!.match(/.{1,2}/g)!
356
363
  .join(':')
357
364
  .toLowerCase();
358
- this.debugLog(`BLE Address: ${this.device.bleMac}`);
365
+ await this.debugLog(`BLE Address: ${this.device.bleMac}`);
359
366
  }
360
367
 
361
368
  async monitorAdvertisementPackets(switchbot: any) {
362
- this.debugLog(`Scanning for ${this.device.bleModelName} devices...`);
369
+ await this.debugLog(`Scanning for ${this.device.bleModelName} devices...`);
363
370
  await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
364
371
  // Set an event handler
365
372
  let serviceData = { model: this.device.bleModel, modelName: this.device.bleModelName } as ad['serviceData'];
@@ -374,7 +381,7 @@ export abstract class deviceBase {
374
381
  this.debugLog(`serviceData: ${JSON.stringify(ad.serviceData)}`);
375
382
  }
376
383
  };
377
- // Wait 10 seconds
384
+ // Wait
378
385
  await switchbot.wait(this.scanDuration * 1000);
379
386
  // Stop to monitor
380
387
  await switchbot.stopScan();
@@ -765,6 +772,32 @@ export abstract class deviceBase {
765
772
  }
766
773
  }
767
774
 
775
+ /**
776
+ * Validate and clean a string value for a Name Characteristic.
777
+ * @param displayName
778
+ * @param name
779
+ * @param value
780
+ * @returns string value
781
+ */
782
+ validateAndCleanString(displayName: string, name: string, value: string): string {
783
+ const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9 ']*[a-zA-Z0-9]$/;
784
+ const invalidCharsPattern = /[^a-zA-Z0-9 ']/g;
785
+
786
+ if (!validPattern.test(value)) {
787
+ this.warnLog(`WARNING: The accessory '${displayName}' has an invalid '${name}' characteristic ('${value}'). Please use only alphanumeric,`
788
+ + ' space, and apostrophe characters. Ensure it starts and ends with an alphabetic or numeric character, and avoid emojis. This may'
789
+ + ' prevent the accessory from being added in the Home App or cause unresponsiveness.');
790
+
791
+ // Remove invalid characters
792
+ value = value.replace(invalidCharsPattern, '');
793
+
794
+ // Ensure it starts and ends with an alphanumeric character
795
+ value = value.replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, '');
796
+ }
797
+
798
+ return value;
799
+ }
800
+
768
801
  /**
769
802
  * Logging for Device
770
803
  */
package/src/device/fan.ts CHANGED
@@ -168,9 +168,6 @@ export class Fan extends deviceBase {
168
168
  this.debugLog('Registering Webhook Event Handler');
169
169
  this.registerWebhook();
170
170
 
171
- //regisiter webhook event handler
172
- this.registerWebhook();
173
-
174
171
  // Start an update interval
175
172
  interval(this.deviceRefreshRate * 1000)
176
173
  .pipe(skipWhile(() => this.fanUpdateInProgress))
@@ -314,7 +311,7 @@ export class Fan extends deviceBase {
314
311
  async refreshStatus(): Promise<void> {
315
312
  if (!this.device.enableCloudService && this.OpenAPI) {
316
313
  await this.errorLog(`refreshStatus enableCloudService: ${this.device.enableCloudService}`);
317
- } else if (this.BLE) {
314
+ } else if (this.BLE || this.config.options?.BLE) {
318
315
  await this.BLERefreshStatus();
319
316
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
320
317
  await this.openAPIRefreshStatus();
@@ -326,25 +323,41 @@ export class Fan extends deviceBase {
326
323
 
327
324
  async BLERefreshStatus(): Promise<void> {
328
325
  await this.debugLog('BLERefreshStatus');
329
- const switchbot = await this.switchbotBLE();
330
-
331
- if (switchbot === undefined) {
332
- await this.BLERefreshConnection(switchbot);
333
- } else {
334
- // Start to monitor advertisement packets
335
- (async () => {
336
- // Start to monitor advertisement packets
337
- const serviceData = await this.monitorAdvertisementPackets(switchbot) as batteryCirculatorFanServiceData;
338
- // Update HomeKit
339
- if (serviceData.model === SwitchBotBLEModel.Unknown && SwitchBotBLEModelName.Unknown) {
340
- this.serviceData = serviceData;
326
+ if (this.config.options?.BLE) {
327
+ await this.debugLog('is listening to Platform BLE.');
328
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
329
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
330
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: batteryCirculatorFanServiceData) => {
331
+ try {
332
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
333
+ this.serviceData = context;
341
334
  await this.BLEparseStatus();
342
335
  await this.updateHomeKitCharacteristics();
343
- } else {
344
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
345
- await this.BLERefreshConnection(switchbot);
336
+ } catch (e: any) {
337
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
346
338
  }
347
- })();
339
+ };
340
+ } else {
341
+ await this.debugLog('is using Device BLE Scanning.');
342
+ const switchbot = await this.switchbotBLE();
343
+ if (switchbot === undefined) {
344
+ await this.BLERefreshConnection(switchbot);
345
+ } else {
346
+ // Start to monitor advertisement packets
347
+ (async () => {
348
+ // Start to monitor advertisement packets
349
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as batteryCirculatorFanServiceData;
350
+ // Update HomeKit
351
+ if (serviceData.model === SwitchBotBLEModel.Unknown && SwitchBotBLEModelName.Unknown) {
352
+ this.serviceData = serviceData;
353
+ await this.BLEparseStatus();
354
+ await this.updateHomeKitCharacteristics();
355
+ } else {
356
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
357
+ await this.BLERefreshConnection(switchbot);
358
+ }
359
+ })();
360
+ }
348
361
  }
349
362
  }
350
363