@switchbot/homebridge-switchbot 0.2.0-alpha.1 → 1.0.1-beta.1

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 (99) hide show
  1. package/.github/workflows/nodejs-beta.yml +1 -1
  2. package/.github/workflows/nodejs.yml +1 -1
  3. package/.github/workflows/{prerelease.js → prerelease-beta.js} +0 -0
  4. package/CHANGELOG.md +1 -1
  5. package/README.md +9 -5
  6. package/config.schema.json +350 -126
  7. package/dist/devices/bots.d.ts +17 -33
  8. package/dist/devices/bots.d.ts.map +1 -1
  9. package/dist/devices/bots.js +103 -152
  10. package/dist/devices/bots.js.map +1 -1
  11. package/dist/devices/{bulb.d.ts → colorbulb.d.ts} +12 -19
  12. package/dist/devices/colorbulb.d.ts.map +1 -0
  13. package/dist/devices/{bulb.js → colorbulb.js} +32 -32
  14. package/dist/devices/colorbulb.js.map +1 -0
  15. package/dist/devices/contact.d.ts +18 -16
  16. package/dist/devices/contact.d.ts.map +1 -1
  17. package/dist/devices/contact.js +101 -44
  18. package/dist/devices/contact.js.map +1 -1
  19. package/dist/devices/curtains.d.ts +17 -51
  20. package/dist/devices/curtains.d.ts.map +1 -1
  21. package/dist/devices/curtains.js +168 -248
  22. package/dist/devices/curtains.js.map +1 -1
  23. package/dist/devices/humidifiers.d.ts +13 -4
  24. package/dist/devices/humidifiers.d.ts.map +1 -1
  25. package/dist/devices/humidifiers.js +78 -8
  26. package/dist/devices/humidifiers.js.map +1 -1
  27. package/dist/devices/indoorcam.d.ts +6 -13
  28. package/dist/devices/indoorcam.d.ts.map +1 -1
  29. package/dist/devices/indoorcam.js.map +1 -1
  30. package/dist/devices/meters.d.ts +12 -15
  31. package/dist/devices/meters.d.ts.map +1 -1
  32. package/dist/devices/meters.js +35 -44
  33. package/dist/devices/meters.js.map +1 -1
  34. package/dist/devices/motion.d.ts +17 -15
  35. package/dist/devices/motion.d.ts.map +1 -1
  36. package/dist/devices/motion.js +87 -40
  37. package/dist/devices/motion.js.map +1 -1
  38. package/dist/devices/plugs.d.ts +6 -13
  39. package/dist/devices/plugs.d.ts.map +1 -1
  40. package/dist/devices/plugs.js.map +1 -1
  41. package/dist/irdevices/airconditioners.d.ts +10 -10
  42. package/dist/irdevices/airconditioners.d.ts.map +1 -1
  43. package/dist/irdevices/airconditioners.js +29 -9
  44. package/dist/irdevices/airconditioners.js.map +1 -1
  45. package/dist/irdevices/airpurifiers.d.ts +11 -11
  46. package/dist/irdevices/airpurifiers.d.ts.map +1 -1
  47. package/dist/irdevices/airpurifiers.js.map +1 -1
  48. package/dist/irdevices/cameras.d.ts +3 -3
  49. package/dist/irdevices/cameras.d.ts.map +1 -1
  50. package/dist/irdevices/cameras.js.map +1 -1
  51. package/dist/irdevices/fans.d.ts +6 -6
  52. package/dist/irdevices/fans.d.ts.map +1 -1
  53. package/dist/irdevices/fans.js +10 -10
  54. package/dist/irdevices/fans.js.map +1 -1
  55. package/dist/irdevices/lights.d.ts +3 -3
  56. package/dist/irdevices/lights.d.ts.map +1 -1
  57. package/dist/irdevices/lights.js.map +1 -1
  58. package/dist/irdevices/others.d.ts +3 -3
  59. package/dist/irdevices/others.d.ts.map +1 -1
  60. package/dist/irdevices/others.js.map +1 -1
  61. package/dist/irdevices/tvs.d.ts +3 -3
  62. package/dist/irdevices/tvs.d.ts.map +1 -1
  63. package/dist/irdevices/tvs.js +7 -5
  64. package/dist/irdevices/tvs.js.map +1 -1
  65. package/dist/irdevices/vacuumcleaners.d.ts +3 -3
  66. package/dist/irdevices/vacuumcleaners.d.ts.map +1 -1
  67. package/dist/irdevices/vacuumcleaners.js.map +1 -1
  68. package/dist/irdevices/waterheaters.d.ts +3 -3
  69. package/dist/irdevices/waterheaters.d.ts.map +1 -1
  70. package/dist/irdevices/waterheaters.js.map +1 -1
  71. package/dist/platform.d.ts +3 -2
  72. package/dist/platform.d.ts.map +1 -1
  73. package/dist/platform.js +136 -46
  74. package/dist/platform.js.map +1 -1
  75. package/dist/settings.d.ts +50 -8
  76. package/dist/settings.d.ts.map +1 -1
  77. package/package.json +65 -65
  78. package/src/devices/bots.ts +126 -176
  79. package/src/devices/{bulb.ts → colorbulb.ts} +43 -51
  80. package/src/devices/contact.ts +124 -68
  81. package/src/devices/curtains.ts +189 -300
  82. package/src/devices/humidifiers.ts +93 -14
  83. package/src/devices/indoorcam.ts +10 -20
  84. package/src/devices/meters.ts +53 -69
  85. package/src/devices/motion.ts +108 -62
  86. package/src/devices/plugs.ts +8 -19
  87. package/src/irdevices/airconditioners.ts +43 -20
  88. package/src/irdevices/airpurifiers.ts +15 -11
  89. package/src/irdevices/cameras.ts +5 -3
  90. package/src/irdevices/fans.ts +22 -16
  91. package/src/irdevices/lights.ts +5 -3
  92. package/src/irdevices/others.ts +5 -3
  93. package/src/irdevices/tvs.ts +13 -7
  94. package/src/irdevices/vacuumcleaners.ts +5 -3
  95. package/src/irdevices/waterheaters.ts +5 -3
  96. package/src/platform.ts +148 -49
  97. package/src/settings.ts +79 -8
  98. package/dist/devices/bulb.d.ts.map +0 -1
  99. package/dist/devices/bulb.js.map +0 -1
@@ -2,7 +2,8 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, serviceData, ad, deviceStatusResponse } from '../settings';
6
+ import { AxiosResponse } from 'axios';
6
7
 
7
8
  /**
8
9
  * Platform Accessory
@@ -23,13 +24,21 @@ export class Humidifier {
23
24
  Active!: CharacteristicValue;
24
25
  WaterLevel!: CharacteristicValue;
25
26
 
26
- // Others
27
- deviceStatus!: any;
27
+ // OpenAPI
28
+ deviceStatus!: deviceStatusResponse;
29
+
30
+ // BLE Others
31
+ serviceData!: serviceData;
32
+ onState!: serviceData['onState'];
33
+ autoMode!: serviceData['autoMode'];
34
+ percentage!: serviceData['percentage'];
35
+
36
+ // Config
37
+ set_minStep?: number;
28
38
 
29
39
  // Updates
30
40
  humidifierUpdateInProgress!: boolean;
31
- doHumidifierUpdate;
32
- set_minStep: number | undefined;
41
+ doHumidifierUpdate!: Subject<void>;
33
42
 
34
43
  constructor(
35
44
  private readonly platform: SwitchBotPlatform,
@@ -180,7 +189,11 @@ export class Humidifier {
180
189
  */
181
190
  parseStatus() {
182
191
  // Current Relative Humidity
183
- this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!;
192
+ if (this.device.ble) {
193
+ this.CurrentRelativeHumidity = this.percentage!;
194
+ } else {
195
+ this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!;
196
+ }
184
197
  this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
185
198
  this.platform.device(JSON.stringify(this.deviceStatus.body));
186
199
  // Water Level
@@ -191,12 +204,16 @@ export class Humidifier {
191
204
  }
192
205
  this.platform.debug(`Humidifier ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`);
193
206
  // Active
194
- switch (this.deviceStatus.body.power) {
195
- case 'on':
196
- this.Active = this.platform.Characteristic.Active.ACTIVE;
197
- break;
198
- default:
199
- this.Active = this.platform.Characteristic.Active.INACTIVE;
207
+ if (this.device.ble) {
208
+ this.Active = this.State();
209
+ } else {
210
+ switch (this.deviceStatus.body.power) {
211
+ case 'on':
212
+ this.Active = this.platform.Characteristic.Active.ACTIVE;
213
+ break;
214
+ default:
215
+ this.Active = this.platform.Characteristic.Active.INACTIVE;
216
+ }
200
217
  }
201
218
  this.platform.debug(`Humidifier ${this.accessory.displayName} Active: ${this.Active}`);
202
219
  // Target Humidifier Dehumidifier State
@@ -226,15 +243,77 @@ export class Humidifier {
226
243
  this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`);
227
244
  // Current Temperature
228
245
  if (!this.device.humidifier?.hide_temperature) {
229
- this.CurrentTemperature = this.deviceStatus.body.temperature;
246
+ this.CurrentTemperature = Number(this.deviceStatus.body.temperature);
230
247
  this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
231
248
  }
232
249
  }
233
250
 
251
+ private State(): CharacteristicValue {
252
+ if (this.onState) {
253
+ return this.platform.Characteristic.Active.ACTIVE;
254
+ } else {
255
+ return this.platform.Characteristic.Active.INACTIVE;
256
+ }
257
+ }
258
+
234
259
  /**
235
260
  * Asks the SwitchBot API for the latest device information
236
261
  */
237
262
  async refreshStatus() {
263
+ if (this.device.ble) {
264
+ this.platform.device('BLE');
265
+ await this.BLERefreshStatus();
266
+ } else {
267
+ this.platform.device('OpenAPI');
268
+ await this.openAPIRefreshStatus();
269
+ }
270
+ }
271
+
272
+ private connectBLE() {
273
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
274
+ const Switchbot = require('node-switchbot');
275
+ const switchbot = new Switchbot();
276
+ const colon = this.device.deviceId!.match(/.{1,2}/g);
277
+ const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
278
+ this.device.bleMac = bleMac.toLowerCase();
279
+ this.platform.device(this.device.bleMac!);
280
+ return switchbot;
281
+ }
282
+
283
+ private async BLERefreshStatus() {
284
+ this.platform.device('Bot BLE Device refreshStatus');
285
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
286
+ const switchbot = this.connectBLE();
287
+ // Start to monitor advertisement packets
288
+ switchbot.startScan({
289
+ model: 'e',
290
+ id: this.device.bleMac,
291
+ }).then(() => {
292
+ // Set an event hander
293
+ switchbot.onadvertisement = (ad: ad) => {
294
+ this.serviceData = ad.serviceData;
295
+ this.autoMode = ad.serviceData.autoMode;
296
+ this.onState = ad.serviceData.onState;
297
+ this.percentage = ad.serviceData.percentage;
298
+ this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
299
+ this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},`
300
+ + `autoMode: ${ad.serviceData.autoMode}, onState: ${ad.serviceData.onState}, percentage: ${ad.serviceData.percentage}`);
301
+ };
302
+ // Wait 10 seconds
303
+ return switchbot.wait(10000);
304
+ }).then(() => {
305
+ // Stop to monitor
306
+ switchbot.stopScan();
307
+ this.parseStatus();
308
+ this.updateHomeKitCharacteristics();
309
+ }).catch(async (e: any) => {
310
+ this.platform.log.error(`BLE Connection Failed: ${e.message}`);
311
+ this.platform.log.warn('Using OpenAPI Connection');
312
+ await this.openAPIRefreshStatus();
313
+ });
314
+ }
315
+
316
+ private async openAPIRefreshStatus() {
238
317
  try {
239
318
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
240
319
  if (this.deviceStatus.message === 'success') {
@@ -436,7 +515,7 @@ export class Humidifier {
436
515
  }
437
516
  }
438
517
 
439
- private statusCode(push: { data: { statusCode: any; }; }) {
518
+ private statusCode(push: AxiosResponse<{ statusCode: number;}>) {
440
519
  switch (push.data.statusCode) {
441
520
  case 151:
442
521
  this.platform.log.error('Command not supported by this device type.');
@@ -1,8 +1,9 @@
1
- import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
1
+ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings';
6
+ import { AxiosResponse } from 'axios';
6
7
 
7
8
  export class IndoorCam {
8
9
  // Services
@@ -12,26 +13,15 @@ export class IndoorCam {
12
13
  On!: CharacteristicValue;
13
14
  OutletInUse!: CharacteristicValue;
14
15
 
15
- // Others
16
- deviceStatus!: any;
17
- switchbot!: {
18
- discover: (
19
- arg0:
20
- {
21
- duration: any;
22
- model: string;
23
- quick: boolean;
24
- id: MacAddress;
25
- }
26
- ) => Promise<any>;
27
- wait: (
28
- arg0: number
29
- ) => any;
30
- };
16
+ // OpenAPI Others
17
+ deviceStatus!: deviceStatusResponse;
18
+
19
+ // BLE Others
20
+ switchbot!: switchbot;
31
21
 
32
22
  // Updates
33
23
  cameraUpdateInProgress!: boolean;
34
- doCameraUpdate;
24
+ doCameraUpdate!: Subject<void>;
35
25
 
36
26
  constructor(
37
27
  private readonly platform: SwitchBotPlatform,
@@ -259,7 +249,7 @@ export class IndoorCam {
259
249
  }
260
250
 
261
251
 
262
- private statusCode(push: { data: { statusCode: any; }; }) {
252
+ private statusCode(push: AxiosResponse<{ statusCode: number;}>) {
263
253
  switch (push.data.statusCode) {
264
254
  case 151:
265
255
  this.platform.log.error('Command not supported by this device type.');
@@ -1,8 +1,8 @@
1
- import { Service, PlatformAccessory, Units, CharacteristicValue, MacAddress } from 'homebridge';
1
+ import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { skipWhile } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse } from '../settings';
6
6
 
7
7
  /**
8
8
  * Platform Accessory
@@ -24,28 +24,20 @@ export class Meter {
24
24
  Active!: CharacteristicValue;
25
25
  WaterLevel!: CharacteristicValue;
26
26
 
27
- // Others
28
- deviceStatus!: any;
29
- BLEtemperature!: number;
30
- BLEHumidity!: number;
31
- switchbot!: {
32
- discover: (
33
- arg0:
34
- {
35
- duration: any;
36
- model: string;
37
- quick: boolean;
38
- id: MacAddress;
39
- }
40
- ) => Promise<any>;
41
- wait: (
42
- arg0: number
43
- ) => any;
44
- };
27
+ // OpenAPI Others
28
+ deviceStatus!: deviceStatusResponse;
29
+
30
+ // BLE Others
31
+ switchbot!: switchbot;
32
+ serviceData!: serviceData;
33
+ battery!: serviceData['battery'];
34
+ humidity!: serviceData['humidity'];
35
+ fahrenheit!: serviceData['fahrenheit'];
36
+ temperature!: serviceData['temperature'];
45
37
 
46
38
  // Updates
47
39
  meterUpdateInProgress!: boolean;
48
- doMeterUpdate;
40
+ doMeterUpdate: Subject<void>;
49
41
 
50
42
  constructor(
51
43
  private readonly platform: SwitchBotPlatform,
@@ -211,7 +203,7 @@ export class Meter {
211
203
  // Current Relative Humidity
212
204
  if (!this.device.meter?.hide_humidity) {
213
205
  if (this.device.ble) {
214
- this.CurrentRelativeHumidity = this.BLEHumidity;
206
+ this.CurrentRelativeHumidity = Number(this.humidity);
215
207
  } else {
216
208
  this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!;
217
209
  }
@@ -221,14 +213,14 @@ export class Meter {
221
213
  // Current Temperature
222
214
  if (!this.device.meter?.hide_temperature) {
223
215
  if (this.device.ble) {
224
- this.CurrentTemperature = this.BLEtemperature;
216
+ this.CurrentTemperature = Number(this.temperature);
225
217
  } else {
226
218
  if (this.device.meter?.unit === 1) {
227
219
  this.CurrentTemperature = this.toFahrenheit(this.deviceStatus.body.temperature!);
228
220
  } else if (this.device.meter?.unit === 0) {
229
221
  this.CurrentTemperature = this.toCelsius(this.deviceStatus.body.temperature!);
230
222
  } else {
231
- this.CurrentTemperature = this.deviceStatus.body.temperature;
223
+ this.CurrentTemperature = Number(this.deviceStatus.body.temperature);
232
224
  }
233
225
  }
234
226
  this.platform.debug(`Meter ${this.accessory.displayName} - Temperature: ${this.CurrentTemperature}°c`);
@@ -248,8 +240,7 @@ export class Meter {
248
240
  }
249
241
  }
250
242
 
251
- private async BLErefreshStatus() {
252
- this.platform.debug('Meter BLE Device RefreshStatus');
243
+ private connectBLE() {
253
244
  // eslint-disable-next-line @typescript-eslint/no-var-requires
254
245
  const Switchbot = require('node-switchbot');
255
246
  const switchbot = new Switchbot();
@@ -257,49 +248,42 @@ export class Meter {
257
248
  const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
258
249
  this.device.bleMac = bleMac.toLowerCase();
259
250
  this.platform.device(this.device.bleMac!);
260
- switchbot.onadvertisement = (ad: any) => {
261
- this.platform.debug(JSON.stringify(ad, null, ' '));
262
- this.platform.device('ad:', JSON.stringify(ad));
263
- this.platform.device(`Temperature: ${ad.serviceData.temperature.c}`);
264
- this.platform.device(`Humidity: ${ad.serviceData.humidity}`);
265
- this.BLEtemperature = ad.serviceData.temperature.c;
266
- this.BLEHumidity = ad.serviceData.humidity;
267
- };
268
- this.parseStatus();
269
- this.updateHomeKitCharacteristics();
270
- switchbot
271
- .startScan({
272
- id: this.device.bleMac,
273
- })
274
- .then(() => {
275
- return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
276
- })
277
- .then(() => {
278
- switchbot.stopScan();
279
- })
280
- .catch(async (error: any) => {
281
- this.platform.log.error(error);
282
- await this.openAPIRefreshStatus();
283
- });
284
- setInterval(() => {
285
- this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
286
- switchbot
287
- .startScan({
288
- mode: 'T',
289
- id: bleMac,
290
- })
291
- .then(() => {
292
- return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
293
- })
294
- .then(() => {
295
- switchbot.stopScan();
296
- this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
297
- })
298
- .catch(async (error: any) => {
299
- this.platform.log.error(error);
300
- await this.openAPIRefreshStatus();
301
- });
302
- }, this.platform.config.options!.refreshRate! * 60000);
251
+ return switchbot;
252
+ }
253
+
254
+ private async BLErefreshStatus() {
255
+ this.platform.debug('Meter BLE Device RefreshStatus');
256
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
257
+ const switchbot = this.connectBLE();
258
+ // Start to monitor advertisement packets
259
+ switchbot.startScan({
260
+ model: 'e',
261
+ id: this.device.bleMac,
262
+ }).then(() => {
263
+ // Set an event hander
264
+ switchbot.onadvertisement = (ad: ad) => {
265
+ this.serviceData = ad.serviceData;
266
+ this.temperature = ad.serviceData.temperature;
267
+ this.fahrenheit = ad.serviceData.fahrenheit;
268
+ this.humidity = ad.serviceData.humidity;
269
+ this.battery = ad.serviceData.battery;
270
+ this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
271
+ this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},`
272
+ + `Temperature: ${ad.serviceData.temperature}, Fahrenheit: ${ad.serviceData.fahrenheit}, Humidity: ${ad.serviceData.humidity}`
273
+ + `Battery: ${ad.serviceData.battery}`);
274
+ };
275
+ // Wait 10 seconds
276
+ return switchbot.wait(10000);
277
+ }).then(() => {
278
+ // Stop to monitor
279
+ switchbot.stopScan();
280
+ this.parseStatus();
281
+ this.updateHomeKitCharacteristics();
282
+ }).catch(async (e: any) => {
283
+ this.platform.log.error(`BLE Connection Failed: ${e.message}`);
284
+ this.platform.log.warn('Using OpenAPI Connection');
285
+ await this.openAPIRefreshStatus();
286
+ });
303
287
  }
304
288
 
305
289
  private async openAPIRefreshStatus() {
@@ -1,8 +1,8 @@
1
- import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
1
+ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { skipWhile } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse } from '../settings';
6
6
 
7
7
  /**
8
8
  * Platform Accessory
@@ -12,31 +12,28 @@ import { DeviceURL, device, devicesConfig } from '../settings';
12
12
  export class Motion {
13
13
  // Services
14
14
  private service: Service;
15
+ private lightSensorService?: Service;
16
+ private batteryService?: Service;
15
17
 
16
18
  // Characteristic Values
17
19
  MotionDetected!: CharacteristicValue;
20
+ BatteryLevel?: CharacteristicValue;
21
+ StatusLowBattery?: CharacteristicValue;
22
+ CurrentAmbientLightLevel?: CharacteristicValue;
18
23
 
19
- // Others
20
- deviceStatus!: any;
21
- BLEmotion!: boolean;
22
- switchbot!: {
23
- discover: (
24
- arg0:
25
- {
26
- duration: any;
27
- model: string;
28
- quick: boolean;
29
- id: MacAddress;
30
- }
31
- ) => Promise<any>;
32
- wait: (
33
- arg0: number
34
- ) => any;
35
- };
24
+ // OpenAPI Others
25
+ deviceStatus!: deviceStatusResponse;
26
+
27
+ // BLE Others
28
+ switchbot!: switchbot;
29
+ serviceData!: serviceData;
30
+ battery!: serviceData['battery'];
31
+ movement!: serviceData['movement'];
32
+ lightLevel!: serviceData['lightLevel'];
36
33
 
37
34
  // Updates
38
35
  motionUbpdateInProgress!: boolean;
39
- doMotionUpdate;
36
+ doMotionUpdate!: Subject<void>;
40
37
 
41
38
  constructor(
42
39
  private readonly platform: SwitchBotPlatform,
@@ -77,6 +74,21 @@ export class Motion {
77
74
  accessory.getService(this.platform.Service.MotionSensor) ||
78
75
  accessory.addService(this.platform.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`;
79
76
 
77
+ if (device.ble) {
78
+ (this.lightSensorService =
79
+ accessory.getService(this.platform.Service.LightSensor) ||
80
+ accessory.addService(this.platform.Service.LightSensor)), `${accessory.displayName} Light Sensor`;
81
+
82
+ this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Light Sensor`);
83
+
84
+
85
+ (this.batteryService =
86
+ accessory.getService(this.platform.Service.Battery) ||
87
+ accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
88
+
89
+ this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
90
+ }
91
+
80
92
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
81
93
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
82
94
  // accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Motion, 'NAME', 'USER_DEFINED_SUBTYPE');
@@ -116,8 +128,24 @@ export class Motion {
116
128
  }
117
129
 
118
130
  private async BLEparseStatus() {
119
- this.MotionDetected = Boolean(this.BLEmotion);
131
+ this.MotionDetected = Boolean(this.movement);
120
132
  this.platform.debug(`${this.accessory.displayName}, MotionDetected: ${this.MotionDetected}`);
133
+ // Light Level
134
+ switch (this.lightLevel) {
135
+ case 'dark':
136
+ case 0:
137
+ this.CurrentAmbientLightLevel = 0.0001;
138
+ break;
139
+ default:
140
+ this.CurrentAmbientLightLevel = 100000;
141
+ }
142
+ // Battery
143
+ this.BatteryLevel = Number(this.battery);
144
+ if (this.BatteryLevel < 10) {
145
+ this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
146
+ } else {
147
+ this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
148
+ }
121
149
  }
122
150
 
123
151
  private async openAPIparseStatus() {
@@ -131,15 +159,14 @@ export class Motion {
131
159
  async refreshStatus() {
132
160
  if (this.device.ble) {
133
161
  this.platform.device('BLE');
134
- await this.BLErefreshStatus();
162
+ await this.BLERefreshStatus();
135
163
  } else {
136
164
  this.platform.device('OpenAPI');
137
165
  await this.openAPIRefreshStatus();
138
166
  }
139
167
  }
140
168
 
141
- private async BLErefreshStatus() {
142
- this.platform.debug('Motion BLE Device RefreshStatus');
169
+ private connectBLE() {
143
170
  // eslint-disable-next-line @typescript-eslint/no-var-requires
144
171
  const Switchbot = require('node-switchbot');
145
172
  const switchbot = new Switchbot();
@@ -147,46 +174,40 @@ export class Motion {
147
174
  const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
148
175
  this.device.bleMac = bleMac.toLowerCase();
149
176
  this.platform.device(this.device.bleMac!);
150
- switchbot.onadvertisement = (ad: any) => {
151
- this.platform.debug(JSON.stringify(ad, null, ' '));
152
- this.platform.device('ad:', JSON.stringify(ad));
153
- this.BLEmotion = ad.serviceData.motion;
154
- };
155
- switchbot
156
- .startScan({
157
- id: this.device.bleMac,
158
- })
159
- .then(() => {
160
- return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
161
- })
162
- .then(() => {
163
- switchbot.stopScan();
164
- })
165
- .catch(async (error: any) => {
166
- this.platform.log.error(error);
167
- await this.openAPIRefreshStatus();
168
- });
169
- setInterval(() => {
170
- this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
171
- switchbot
172
- .startScan({
173
- mode: 'S',
174
- id: bleMac,
175
- })
176
- .then(() => {
177
- return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
178
- })
179
- .then(() => {
180
- switchbot.stopScan();
181
- this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
182
- })
183
- .catch(async (error: any) => {
184
- this.platform.log.error(error);
185
- await this.openAPIRefreshStatus();
186
- });
177
+ return switchbot;
178
+ }
179
+
180
+ private async BLERefreshStatus() {
181
+ this.platform.debug('Motion BLE Device RefreshStatus');
182
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
183
+ const switchbot = this.connectBLE();
184
+ // Start to monitor advertisement packets
185
+ switchbot.startScan({
186
+ model: 's',
187
+ id: this.device.bleMac,
188
+ }).then(() => {
189
+ // Set an event hander
190
+ switchbot.onadvertisement = (ad: any) => {
191
+ this.serviceData = ad.serviceData;
192
+ this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
193
+ this.movement = ad.serviceData.movement;
194
+ this.battery = ad.serviceData.battery;
195
+ this.lightLevel = ad.serviceData.lightLevel;
196
+ this.platform.device(`${this.accessory.displayName}, Movement: ${ad.serviceData.movement}, Light Level: ${ad.serviceData.lightLevel},`
197
+ + ` Battery: ${ad.serviceData.battery}`);
198
+ };
199
+ // Wait 10 seconds
200
+ return switchbot.wait(10000);
201
+ }).then(() => {
202
+ // Stop to monitor
203
+ switchbot.stopScan();
187
204
  this.parseStatus();
188
205
  this.updateHomeKitCharacteristics();
189
- }, this.platform.config.options!.refreshRate! * 60000);
206
+ }).catch(async (e: any) => {
207
+ this.platform.log.error(`BLE Connection Failed: ${e.message}`);
208
+ this.platform.log.warn('Using OpenAPI Connection');
209
+ await this.openAPIRefreshStatus();
210
+ });
190
211
  }
191
212
 
192
213
  private async openAPIRefreshStatus() {
@@ -212,9 +233,34 @@ export class Motion {
212
233
  this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.MotionDetected);
213
234
  this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`);
214
235
  }
236
+ if (this.device.ble) {
237
+ if (this.BatteryLevel === undefined) {
238
+ this.platform.debug(`Motion ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
239
+ } else {
240
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
241
+ this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
242
+ }
243
+ if (this.StatusLowBattery === undefined) {
244
+ this.platform.debug(`Motion ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
245
+ } else {
246
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
247
+ this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
248
+ }
249
+ if (this.CurrentAmbientLightLevel === undefined) {
250
+ this.platform.debug(`Motion ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
251
+ } else {
252
+ this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
253
+ this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
254
+ }
255
+ }
215
256
  }
216
257
 
217
258
  public apiError(e: any) {
218
259
  this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, e);
260
+ if (this.device.ble) {
261
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
262
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
263
+ this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
264
+ }
219
265
  }
220
266
  }
@@ -1,8 +1,8 @@
1
- import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
1
+ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings';
6
6
 
7
7
  export class Plug {
8
8
  // Services
@@ -12,26 +12,15 @@ export class Plug {
12
12
  On!: CharacteristicValue;
13
13
  OutletInUse!: CharacteristicValue;
14
14
 
15
- // Others
16
- deviceStatus!: any;
17
- switchbot!: {
18
- discover: (
19
- arg0:
20
- {
21
- duration: any;
22
- model: string;
23
- quick: boolean;
24
- id: MacAddress;
25
- }
26
- ) => Promise<any>;
27
- wait: (
28
- arg0: number
29
- ) => any;
30
- };
15
+ // OpenAPI Others
16
+ deviceStatus!: deviceStatusResponse;
17
+
18
+ // BLE Others
19
+ switchbot!: switchbot;
31
20
 
32
21
  // Updates
33
22
  plugUpdateInProgress!: boolean;
34
- doPlugUpdate;
23
+ doPlugUpdate!: Subject<void>;
35
24
 
36
25
  constructor(
37
26
  private readonly platform: SwitchBotPlatform,