@switchbot/homebridge-switchbot 3.6.1-beta.1 → 3.6.1-beta.11

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 (136) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/device/blindtilt.d.ts.map +1 -1
  3. package/dist/device/blindtilt.js +44 -17
  4. package/dist/device/blindtilt.js.map +1 -1
  5. package/dist/device/bot.d.ts +1 -2
  6. package/dist/device/bot.d.ts.map +1 -1
  7. package/dist/device/bot.js +220 -397
  8. package/dist/device/bot.js.map +1 -1
  9. package/dist/device/ceilinglight.d.ts +3 -2
  10. package/dist/device/ceilinglight.d.ts.map +1 -1
  11. package/dist/device/ceilinglight.js +59 -31
  12. package/dist/device/ceilinglight.js.map +1 -1
  13. package/dist/device/colorbulb.d.ts +3 -2
  14. package/dist/device/colorbulb.d.ts.map +1 -1
  15. package/dist/device/colorbulb.js +57 -29
  16. package/dist/device/colorbulb.js.map +1 -1
  17. package/dist/device/contact.d.ts.map +1 -1
  18. package/dist/device/contact.js +47 -18
  19. package/dist/device/contact.js.map +1 -1
  20. package/dist/device/curtain.d.ts.map +1 -1
  21. package/dist/device/curtain.js +16 -5
  22. package/dist/device/curtain.js.map +1 -1
  23. package/dist/device/device.d.ts +9 -0
  24. package/dist/device/device.d.ts.map +1 -1
  25. package/dist/device/device.js +30 -3
  26. package/dist/device/device.js.map +1 -1
  27. package/dist/device/fan.d.ts.map +1 -1
  28. package/dist/device/fan.js +44 -18
  29. package/dist/device/fan.js.map +1 -1
  30. package/dist/device/hub.d.ts.map +1 -1
  31. package/dist/device/hub.js +46 -18
  32. package/dist/device/hub.js.map +1 -1
  33. package/dist/device/humidifier.d.ts.map +1 -1
  34. package/dist/device/humidifier.js +45 -20
  35. package/dist/device/humidifier.js.map +1 -1
  36. package/dist/device/iosensor.d.ts.map +1 -1
  37. package/dist/device/iosensor.js +46 -18
  38. package/dist/device/iosensor.js.map +1 -1
  39. package/dist/device/lightstrip.d.ts +4 -3
  40. package/dist/device/lightstrip.d.ts.map +1 -1
  41. package/dist/device/lightstrip.js +60 -31
  42. package/dist/device/lightstrip.js.map +1 -1
  43. package/dist/device/lock.d.ts.map +1 -1
  44. package/dist/device/lock.js +48 -17
  45. package/dist/device/lock.js.map +1 -1
  46. package/dist/device/meter.d.ts.map +1 -1
  47. package/dist/device/meter.js +46 -18
  48. package/dist/device/meter.js.map +1 -1
  49. package/dist/device/meterplus.d.ts.map +1 -1
  50. package/dist/device/meterplus.js +46 -18
  51. package/dist/device/meterplus.js.map +1 -1
  52. package/dist/device/motion.d.ts.map +1 -1
  53. package/dist/device/motion.js +45 -17
  54. package/dist/device/motion.js.map +1 -1
  55. package/dist/device/plug.d.ts.map +1 -1
  56. package/dist/device/plug.js +40 -18
  57. package/dist/device/plug.js.map +1 -1
  58. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  59. package/dist/device/robotvacuumcleaner.js +42 -17
  60. package/dist/device/robotvacuumcleaner.js.map +1 -1
  61. package/dist/device/waterdetector.d.ts.map +1 -1
  62. package/dist/device/waterdetector.js +41 -16
  63. package/dist/device/waterdetector.js.map +1 -1
  64. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  65. package/dist/irdevice/airconditioner.js +8 -1
  66. package/dist/irdevice/airconditioner.js.map +1 -1
  67. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  68. package/dist/irdevice/airpurifier.js +6 -0
  69. package/dist/irdevice/airpurifier.js.map +1 -1
  70. package/dist/irdevice/camera.d.ts.map +1 -1
  71. package/dist/irdevice/camera.js +3 -0
  72. package/dist/irdevice/camera.js.map +1 -1
  73. package/dist/irdevice/fan.d.ts.map +1 -1
  74. package/dist/irdevice/fan.js +3 -0
  75. package/dist/irdevice/fan.js.map +1 -1
  76. package/dist/irdevice/irdevice.d.ts +9 -0
  77. package/dist/irdevice/irdevice.d.ts.map +1 -1
  78. package/dist/irdevice/irdevice.js +29 -2
  79. package/dist/irdevice/irdevice.js.map +1 -1
  80. package/dist/irdevice/light.d.ts.map +1 -1
  81. package/dist/irdevice/light.js +9 -0
  82. package/dist/irdevice/light.js.map +1 -1
  83. package/dist/irdevice/other.d.ts +1 -0
  84. package/dist/irdevice/other.d.ts.map +1 -1
  85. package/dist/irdevice/other.js +216 -228
  86. package/dist/irdevice/other.js.map +1 -1
  87. package/dist/irdevice/tv.d.ts.map +1 -1
  88. package/dist/irdevice/tv.js +9 -0
  89. package/dist/irdevice/tv.js.map +1 -1
  90. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  91. package/dist/irdevice/vacuumcleaner.js +3 -0
  92. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  93. package/dist/irdevice/waterheater.js +1 -1
  94. package/dist/irdevice/waterheater.js.map +1 -1
  95. package/dist/platform.d.ts.map +1 -1
  96. package/dist/platform.js +11 -2
  97. package/dist/platform.js.map +1 -1
  98. package/dist/types/bledevicestatus.d.ts +1 -0
  99. package/dist/types/bledevicestatus.d.ts.map +1 -1
  100. package/dist/utils.d.ts +8 -0
  101. package/dist/utils.d.ts.map +1 -1
  102. package/dist/utils.js +16 -0
  103. package/dist/utils.js.map +1 -1
  104. package/package.json +6 -6
  105. package/src/device/blindtilt.ts +44 -17
  106. package/src/device/bot.ts +224 -407
  107. package/src/device/ceilinglight.ts +57 -33
  108. package/src/device/colorbulb.ts +55 -31
  109. package/src/device/contact.ts +48 -19
  110. package/src/device/curtain.ts +19 -6
  111. package/src/device/device.ts +36 -3
  112. package/src/device/fan.ts +44 -19
  113. package/src/device/hub.ts +46 -18
  114. package/src/device/humidifier.ts +44 -20
  115. package/src/device/iosensor.ts +46 -18
  116. package/src/device/lightstrip.ts +60 -33
  117. package/src/device/lock.ts +49 -17
  118. package/src/device/meter.ts +46 -18
  119. package/src/device/meterplus.ts +46 -18
  120. package/src/device/motion.ts +46 -18
  121. package/src/device/plug.ts +38 -18
  122. package/src/device/robotvacuumcleaner.ts +41 -17
  123. package/src/device/waterdetector.ts +40 -16
  124. package/src/irdevice/airconditioner.ts +11 -2
  125. package/src/irdevice/airpurifier.ts +8 -0
  126. package/src/irdevice/camera.ts +4 -0
  127. package/src/irdevice/fan.ts +4 -0
  128. package/src/irdevice/irdevice.ts +35 -2
  129. package/src/irdevice/light.ts +12 -0
  130. package/src/irdevice/other.ts +254 -283
  131. package/src/irdevice/tv.ts +12 -0
  132. package/src/irdevice/vacuumcleaner.ts +4 -0
  133. package/src/irdevice/waterheater.ts +1 -1
  134. package/src/platform.ts +11 -2
  135. package/src/types/bledevicestatus.ts +1 -0
  136. 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();
@@ -67,6 +68,10 @@ export class CeilingLight extends deviceBase {
67
68
 
68
69
  // Initialize LightBulb Service
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 CeilingLight 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
- this.accessory.configureController(this.AdaptiveLightingController);
93
- this.accessory.context.adaptiveLighting = true;
94
- this.debugLog(`adaptiveLighting: ${this.accessory.context.adaptiveLighting},`
95
- + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`);
96
+ accessory.configureController(this.AdaptiveLightingController);
97
+ accessory.context.adaptiveLighting = true;
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
@@ -166,9 +172,6 @@ export class CeilingLight extends deviceBase {
166
172
  this.debugLog('Registering Webhook Event Handler');
167
173
  this.registerWebhook();
168
174
 
169
- //regisiter webhook event handler
170
- this.registerWebhook();
171
-
172
175
  // Start an update interval
173
176
  interval(this.deviceRefreshRate * 1000)
174
177
  .pipe(skipWhile(() => this.ceilingLightUpdateInProgress))
@@ -288,25 +291,41 @@ export class CeilingLight extends deviceBase {
288
291
 
289
292
  async BLERefreshStatus(): Promise<void> {
290
293
  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;
294
+ if (this.config.options?.BLE) {
295
+ await this.debugLog('is listening to Platform BLE.');
296
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
297
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
298
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: ceilingLightServiceData) => {
299
+ try {
300
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
301
+ this.serviceData = context;
303
302
  await this.BLEparseStatus();
304
303
  await this.updateHomeKitCharacteristics();
305
- } else {
306
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
307
- await this.BLERefreshConnection(switchbot);
304
+ } catch (e: any) {
305
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
308
306
  }
309
- })();
307
+ };
308
+ } else {
309
+ await this.debugLog('is using Device BLE Scanning.');
310
+ const switchbot = await this.switchbotBLE();
311
+ if (switchbot === undefined) {
312
+ await this.BLERefreshConnection(switchbot);
313
+ } else {
314
+ (async () => {
315
+ // Start to monitor advertisement packets
316
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as unknown as ceilingLightServiceData;
317
+ // Update HomeKit
318
+ if ((serviceData.model === SwitchBotBLEModel.CeilingLight || SwitchBotBLEModel.CeilingLightPro)
319
+ && serviceData.modelName === SwitchBotBLEModelName.CeilingLight || SwitchBotBLEModelName.CeilingLightPro) {
320
+ this.serviceData = serviceData;
321
+ await this.BLEparseStatus();
322
+ await this.updateHomeKitCharacteristics();
323
+ } else {
324
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
325
+ await this.BLERefreshConnection(switchbot);
326
+ }
327
+ })();
328
+ }
310
329
  }
311
330
  }
312
331
 
@@ -687,13 +706,18 @@ export class CeilingLight extends deviceBase {
687
706
  this.LightBulb.Saturation, 'Saturation');
688
707
  }
689
708
 
690
- async adaptiveLighting(device: device & devicesConfig): Promise<void> {
709
+ async getAdaptiveLightingSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
710
+ // Adaptive Lighting
711
+ this.adaptiveLighting = accessory.context.adaptiveLighting ?? true;
712
+ await this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`);
713
+ // Adaptive Lighting Shift
691
714
  if (device.ceilinglight?.adaptiveLightingShift) {
692
715
  this.adaptiveLightingShift = device.ceilinglight.adaptiveLightingShift;
716
+ this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
693
717
  } else {
694
718
  this.adaptiveLightingShift = 0;
719
+ this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
695
720
  }
696
- await this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`);
697
721
  }
698
722
 
699
723
  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))
@@ -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() {
@@ -74,6 +74,10 @@ export class Contact extends deviceBase {
74
74
 
75
75
  // Initialize Contact Sensor Service
76
76
  accessory.context.ContactSensor = accessory.context.ContactSensor ?? {};
77
+ if (accessory.context.ContactSensor.Name) {
78
+ accessory.context.ContactSensor.Name = this.validateAndCleanString(accessory.context.ContactSensor.Name,
79
+ 'ContactSensor Name', accessory.context.ContactSensor.Name);
80
+ }
77
81
  this.ContactSensor = {
78
82
  Name: accessory.context.ContactSensor.Name ?? accessory.displayName,
79
83
  Service: accessory.getService(this.hap.Service.ContactSensor) ?? accessory.addService(this.hap.Service.ContactSensor) as Service,
@@ -92,6 +96,10 @@ export class Contact extends deviceBase {
92
96
 
93
97
  // Initialize Battery Service
94
98
  accessory.context.Battery = accessory.context.Battery ?? {};
99
+ if (accessory.context.Battery.Name) {
100
+ accessory.context.Battery.Name = this.validateAndCleanString(accessory.context.Battery.Name,
101
+ 'Battery Name', accessory.context.Battery.Name);
102
+ }
95
103
  this.Battery = {
96
104
  Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
97
105
  Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
@@ -125,6 +133,10 @@ export class Contact extends deviceBase {
125
133
  }
126
134
  } else {
127
135
  accessory.context.MotionSensor = accessory.context.MotionSensor ?? {};
136
+ if (accessory.context.MotionSensor.Name) {
137
+ accessory.context.MotionSensor.Name = this.validateAndCleanString(accessory.context.MotionSensor.Name,
138
+ 'MotionSensor Name', accessory.context.MotionSensor.Name);
139
+ }
128
140
  this.MotionSensor = {
129
141
  Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`,
130
142
  Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service,
@@ -151,6 +163,10 @@ export class Contact extends deviceBase {
151
163
  }
152
164
  } else {
153
165
  accessory.context.LightSensor = accessory.context.LightSensor ?? {};
166
+ if (accessory.context.LightSensor.Name) {
167
+ accessory.context.LightSensor.Name = this.validateAndCleanString(accessory.context.LightSensor.Name,
168
+ 'LightSensor Name', accessory.context.LightSensor.Name);
169
+ }
154
170
  this.LightSensor = {
155
171
  Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
156
172
  Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service,
@@ -176,9 +192,6 @@ export class Contact extends deviceBase {
176
192
  this.debugLog('Registering Webhook Event Handler');
177
193
  this.registerWebhook();
178
194
 
179
- //regisiter webhook event handler
180
- this.registerWebhook();
181
-
182
195
  // Start an update interval
183
196
  interval(this.deviceRefreshRate * 1000)
184
197
  .pipe(skipWhile(() => this.contactUpdateInProgress))
@@ -299,25 +312,41 @@ export class Contact extends deviceBase {
299
312
 
300
313
  async BLERefreshStatus(): Promise<void> {
301
314
  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;
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: contactSensorServiceData) => {
320
+ try {
321
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
322
+ this.serviceData = context;
314
323
  await this.BLEparseStatus();
315
324
  await this.updateHomeKitCharacteristics();
316
- } else {
317
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
318
- await this.BLERefreshConnection(switchbot);
325
+ } catch (e: any) {
326
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
319
327
  }
320
- })();
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 contactSensorServiceData;
339
+ // Update HomeKit
340
+ if (serviceData.model === SwitchBotBLEModel.ContactSensor && serviceData.modelName === SwitchBotBLEModelName.ContactSensor) {
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
+ }
321
350
  }
322
351
  }
323
352
 
@@ -73,6 +73,10 @@ export class Curtain extends deviceBase {
73
73
 
74
74
  // Initialize WindowCovering Service
75
75
  accessory.context.WindowCovering = accessory.context.WindowCovering ?? {};
76
+ if (accessory.context.WindowCovering.Name) {
77
+ accessory.context.WindowCovering.Name = this.validateAndCleanString(accessory.context.WindowCovering.Name,
78
+ 'WindowCovering Name', accessory.context.WindowCovering.Name);
79
+ }
76
80
  this.WindowCovering = {
77
81
  Name: accessory.context.WindowCovering.Name ?? accessory.displayName,
78
82
  Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service,
@@ -129,6 +133,10 @@ export class Curtain extends deviceBase {
129
133
 
130
134
  // Initialize Battery Service
131
135
  accessory.context.Battery = accessory.context.Battery ?? {};
136
+ if (accessory.context.Battery.Name) {
137
+ accessory.context.Battery.Name = this.validateAndCleanString(accessory.context.Battery.Name,
138
+ 'Battery Name', accessory.context.Battery.Name);
139
+ }
132
140
  this.Battery = {
133
141
  Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
134
142
  Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
@@ -168,6 +176,10 @@ export class Curtain extends deviceBase {
168
176
  }
169
177
  } else {
170
178
  accessory.context.LightSensor = accessory.context.LightSensor ?? {};
179
+ if (accessory.context.LightSensor.Name) {
180
+ accessory.context.LightSensor.Name = this.validateAndCleanString(accessory.context.LightSensor.Name,
181
+ 'LightSensor Name', accessory.context.LightSensor.Name);
182
+ }
171
183
  this.LightSensor = {
172
184
  Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
173
185
  Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service,
@@ -438,21 +450,22 @@ export class Curtain extends deviceBase {
438
450
  async BLERefreshStatus(): Promise<void> {
439
451
  await this.debugLog('BLERefreshStatus');
440
452
  if (this.config.options?.BLE) {
441
- await this.debugLog('is listening to BLE.');
442
- this.platform.bleEventHandler[this.device.deviceId] = async (context: curtainServiceData | curtain3ServiceData) => {
453
+ await this.debugLog('is listening to Platform BLE.');
454
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
455
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
456
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: curtainServiceData | curtain3ServiceData) => {
443
457
  try {
444
- await this.debugLog(`received Webhook: ${JSON.stringify(context)}`);
458
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
445
459
  this.serviceData = context;
446
- await this.parseStatusWebhook();
460
+ await this.BLEparseStatus();
447
461
  await this.updateHomeKitCharacteristics();
448
462
  } catch (e: any) {
449
463
  await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
450
464
  }
451
465
  };
452
466
  } else {
453
- await this.debugLog('is not listening to BLE.');
467
+ await this.debugLog('is using Device BLE Scanning.');
454
468
  const switchbot = await this.switchbotBLE();
455
-
456
469
  if (switchbot === undefined) {
457
470
  await this.BLERefreshConnection(switchbot);
458
471
  } else {
@@ -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';
@@ -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
@@ -67,6 +67,10 @@ export class Fan extends deviceBase {
67
67
 
68
68
  // Initialize Fan Service
69
69
  accessory.context.Fan = accessory.context.Fan ?? {};
70
+ if (accessory.context.Fan.Name) {
71
+ accessory.context.Fan.Name = this.validateAndCleanString(accessory.context.Fan.Name,
72
+ 'Fan Name', accessory.context.Fan.Name);
73
+ }
70
74
  this.Fan = {
71
75
  Name: accessory.context.Fan.Name ?? accessory.displayName,
72
76
  Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service,
@@ -102,6 +106,10 @@ export class Fan extends deviceBase {
102
106
 
103
107
  // Initialize Battery Service
104
108
  accessory.context.Battery = accessory.context.Battery ?? {};
109
+ if (accessory.context.Battery.Name) {
110
+ accessory.context.Battery.Name = this.validateAndCleanString(accessory.context.Battery.Name,
111
+ 'Battery Name', accessory.context.Battery.Name);
112
+ }
105
113
  this.Battery = {
106
114
  Name: accessory.context.Battery.Name ?? accessory.displayName,
107
115
  Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
@@ -135,6 +143,10 @@ export class Fan extends deviceBase {
135
143
 
136
144
  // Initialize LightBulb Service
137
145
  accessory.context.LightBulb = accessory.context.LightBulb ?? {};
146
+ if (accessory.context.LightBulb.Name) {
147
+ accessory.context.LightBulb.Name = this.validateAndCleanString(accessory.context.LightBulb.Name,
148
+ 'LightBulb Name', accessory.context.LightBulb.Name);
149
+ }
138
150
  this.LightBulb = {
139
151
  Name: accessory.context.LightBulb.Name ?? accessory.displayName,
140
152
  Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
@@ -168,9 +180,6 @@ export class Fan extends deviceBase {
168
180
  this.debugLog('Registering Webhook Event Handler');
169
181
  this.registerWebhook();
170
182
 
171
- //regisiter webhook event handler
172
- this.registerWebhook();
173
-
174
183
  // Start an update interval
175
184
  interval(this.deviceRefreshRate * 1000)
176
185
  .pipe(skipWhile(() => this.fanUpdateInProgress))
@@ -326,25 +335,41 @@ export class Fan extends deviceBase {
326
335
 
327
336
  async BLERefreshStatus(): Promise<void> {
328
337
  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;
338
+ if (this.config.options?.BLE) {
339
+ await this.debugLog('is listening to Platform BLE.');
340
+ this.device.bleMac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
341
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
342
+ this.platform.bleEventHandler[this.device.bleMac] = async (context: batteryCirculatorFanServiceData) => {
343
+ try {
344
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
345
+ this.serviceData = context;
341
346
  await this.BLEparseStatus();
342
347
  await this.updateHomeKitCharacteristics();
343
- } else {
344
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
345
- await this.BLERefreshConnection(switchbot);
348
+ } catch (e: any) {
349
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
346
350
  }
347
- })();
351
+ };
352
+ } else {
353
+ await this.debugLog('is using Device BLE Scanning.');
354
+ const switchbot = await this.switchbotBLE();
355
+ if (switchbot === undefined) {
356
+ await this.BLERefreshConnection(switchbot);
357
+ } else {
358
+ // Start to monitor advertisement packets
359
+ (async () => {
360
+ // Start to monitor advertisement packets
361
+ const serviceData = await this.monitorAdvertisementPackets(switchbot) as batteryCirculatorFanServiceData;
362
+ // Update HomeKit
363
+ if (serviceData.model === SwitchBotBLEModel.Unknown && SwitchBotBLEModelName.Unknown) {
364
+ this.serviceData = serviceData;
365
+ await this.BLEparseStatus();
366
+ await this.updateHomeKitCharacteristics();
367
+ } else {
368
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
369
+ await this.BLERefreshConnection(switchbot);
370
+ }
371
+ })();
372
+ }
348
373
  }
349
374
  }
350
375