@switchbot/homebridge-switchbot 1.7.0 → 1.7.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 (97) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/config.schema.json +101 -33
  3. package/dist/devices/bots.d.ts +9 -5
  4. package/dist/devices/bots.d.ts.map +1 -1
  5. package/dist/devices/bots.js +144 -77
  6. package/dist/devices/bots.js.map +1 -1
  7. package/dist/devices/colorbulb.d.ts +9 -4
  8. package/dist/devices/colorbulb.d.ts.map +1 -1
  9. package/dist/devices/colorbulb.js +40 -37
  10. package/dist/devices/colorbulb.js.map +1 -1
  11. package/dist/devices/contact.d.ts +10 -2
  12. package/dist/devices/contact.d.ts.map +1 -1
  13. package/dist/devices/contact.js +92 -51
  14. package/dist/devices/contact.js.map +1 -1
  15. package/dist/devices/curtains.d.ts +10 -3
  16. package/dist/devices/curtains.d.ts.map +1 -1
  17. package/dist/devices/curtains.js +52 -34
  18. package/dist/devices/curtains.js.map +1 -1
  19. package/dist/devices/humidifiers.d.ts +8 -2
  20. package/dist/devices/humidifiers.d.ts.map +1 -1
  21. package/dist/devices/humidifiers.js +28 -35
  22. package/dist/devices/humidifiers.js.map +1 -1
  23. package/dist/devices/meters.d.ts +14 -12
  24. package/dist/devices/meters.d.ts.map +1 -1
  25. package/dist/devices/meters.js +63 -103
  26. package/dist/devices/meters.js.map +1 -1
  27. package/dist/devices/motion.d.ts +10 -3
  28. package/dist/devices/motion.d.ts.map +1 -1
  29. package/dist/devices/motion.js +114 -68
  30. package/dist/devices/motion.js.map +1 -1
  31. package/dist/devices/plugs.d.ts +3 -1
  32. package/dist/devices/plugs.d.ts.map +1 -1
  33. package/dist/devices/plugs.js +8 -1
  34. package/dist/devices/plugs.js.map +1 -1
  35. package/dist/irdevices/airconditioners.d.ts +1 -0
  36. package/dist/irdevices/airconditioners.d.ts.map +1 -1
  37. package/dist/irdevices/airconditioners.js +6 -0
  38. package/dist/irdevices/airconditioners.js.map +1 -1
  39. package/dist/irdevices/airpurifiers.d.ts +1 -0
  40. package/dist/irdevices/airpurifiers.d.ts.map +1 -1
  41. package/dist/irdevices/airpurifiers.js +6 -0
  42. package/dist/irdevices/airpurifiers.js.map +1 -1
  43. package/dist/irdevices/cameras.d.ts +1 -0
  44. package/dist/irdevices/cameras.d.ts.map +1 -1
  45. package/dist/irdevices/cameras.js +6 -0
  46. package/dist/irdevices/cameras.js.map +1 -1
  47. package/dist/irdevices/fans.d.ts +1 -0
  48. package/dist/irdevices/fans.d.ts.map +1 -1
  49. package/dist/irdevices/fans.js +6 -0
  50. package/dist/irdevices/fans.js.map +1 -1
  51. package/dist/irdevices/lights.d.ts +1 -0
  52. package/dist/irdevices/lights.d.ts.map +1 -1
  53. package/dist/irdevices/lights.js +6 -0
  54. package/dist/irdevices/lights.js.map +1 -1
  55. package/dist/irdevices/others.d.ts +1 -0
  56. package/dist/irdevices/others.d.ts.map +1 -1
  57. package/dist/irdevices/others.js +6 -0
  58. package/dist/irdevices/others.js.map +1 -1
  59. package/dist/irdevices/tvs.d.ts +1 -0
  60. package/dist/irdevices/tvs.d.ts.map +1 -1
  61. package/dist/irdevices/tvs.js +6 -0
  62. package/dist/irdevices/tvs.js.map +1 -1
  63. package/dist/irdevices/vacuumcleaners.d.ts +1 -0
  64. package/dist/irdevices/vacuumcleaners.d.ts.map +1 -1
  65. package/dist/irdevices/vacuumcleaners.js +6 -0
  66. package/dist/irdevices/vacuumcleaners.js.map +1 -1
  67. package/dist/irdevices/waterheaters.d.ts +1 -0
  68. package/dist/irdevices/waterheaters.d.ts.map +1 -1
  69. package/dist/irdevices/waterheaters.js +6 -0
  70. package/dist/irdevices/waterheaters.js.map +1 -1
  71. package/dist/platform.d.ts +2 -1
  72. package/dist/platform.d.ts.map +1 -1
  73. package/dist/platform.js +14 -1
  74. package/dist/platform.js.map +1 -1
  75. package/dist/settings.d.ts +14 -0
  76. package/dist/settings.d.ts.map +1 -1
  77. package/dist/settings.js.map +1 -1
  78. package/package.json +71 -71
  79. package/src/devices/bots.ts +140 -74
  80. package/src/devices/colorbulb.ts +46 -40
  81. package/src/devices/contact.ts +99 -55
  82. package/src/devices/curtains.ts +60 -36
  83. package/src/devices/humidifiers.ts +36 -36
  84. package/src/devices/meters.ts +79 -113
  85. package/src/devices/motion.ts +125 -74
  86. package/src/devices/plugs.ts +11 -2
  87. package/src/irdevices/airconditioners.ts +7 -0
  88. package/src/irdevices/airpurifiers.ts +7 -0
  89. package/src/irdevices/cameras.ts +7 -0
  90. package/src/irdevices/fans.ts +7 -0
  91. package/src/irdevices/lights.ts +7 -0
  92. package/src/irdevices/others.ts +7 -0
  93. package/src/irdevices/tvs.ts +7 -0
  94. package/src/irdevices/vacuumcleaners.ts +7 -0
  95. package/src/irdevices/waterheaters.ts +7 -0
  96. package/src/platform.ts +14 -1
  97. package/src/settings.ts +15 -0
@@ -2,7 +2,7 @@ import { interval, Subject } from 'rxjs';
2
2
  import { skipWhile } from 'rxjs/operators';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
5
- import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature, deviceStatus } from '../settings';
6
6
 
7
7
  /**
8
8
  * Platform Accessory
@@ -11,28 +11,30 @@ import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceSta
11
11
  */
12
12
  export class Meter {
13
13
  // Services
14
- private service: Service;
15
- temperatureservice?: Service;
16
- humidityservice?: Service;
14
+ private batteryService?: Service;
15
+ private temperatureservice?: Service;
16
+ private humidityservice?: Service;
17
17
 
18
18
  // Characteristic Values
19
19
  CurrentRelativeHumidity!: CharacteristicValue;
20
20
  CurrentRelativeHumidityCached!: CharacteristicValue;
21
- CurrentTemperature!: CharacteristicValue;
22
- CurrentTemperatureCached!: CharacteristicValue;
23
- BatteryLevel!: CharacteristicValue;
24
- BatteryLevelCached!: CharacteristicValue;
25
- ChargingState!: CharacteristicValue;
26
- StatusLowBattery!: CharacteristicValue;
21
+ CurrentTemperature?: CharacteristicValue;
22
+ CurrentTemperatureCached?: CharacteristicValue;
23
+ BatteryLevel?: CharacteristicValue;
24
+ ChargingState?: CharacteristicValue;
25
+ StatusLowBattery?: CharacteristicValue;
27
26
  Active!: CharacteristicValue;
28
27
  WaterLevel!: CharacteristicValue;
29
28
 
30
29
  // OpenAPI Others
30
+ Temperature: deviceStatus['temperature'];
31
+ Humidity: deviceStatus['humidity'];
31
32
  deviceStatus!: deviceStatusResponse;
32
33
 
33
34
  // BLE Others
34
35
  connected?: boolean;
35
36
  switchbot!: switchbot;
37
+ SwitchToOpenAPI?: boolean;
36
38
  serviceData!: serviceData;
37
39
  temperature!: temperature['c'];
38
40
  battery!: serviceData['battery'];
@@ -56,17 +58,7 @@ export class Meter {
56
58
  this.logs();
57
59
  this.scan();
58
60
  this.refreshRate();
59
- if (this.BatteryLevel === undefined) {
60
- this.BatteryLevel = 100;
61
- } else {
62
- this.BatteryLevel = this.accessory.context.BatteryLevel;
63
- }
64
- if (this.BatteryLevel < 15) {
65
- this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
66
- } else {
67
- this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
68
- }
69
- this.ChargingState = this.platform.Characteristic.ChargingState.NOT_CHARGEABLE;
61
+ this.config(device);
70
62
  if (this.CurrentRelativeHumidity === undefined) {
71
63
  this.CurrentRelativeHumidity = 0;
72
64
  } else {
@@ -96,26 +88,6 @@ export class Meter {
96
88
  .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-METERTH-S1')
97
89
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
98
90
 
99
- // get the Battery service if it exists, otherwise create a new Battery service
100
- // you can create multiple services for each accessory
101
- (this.service =
102
- accessory.getService(this.platform.Service.Battery) ||
103
- accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
104
-
105
- // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
106
- // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
107
- // accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Battery, 'NAME', 'USER_DEFINED_SUBTYPE');
108
-
109
- // set the service name, this is what is displayed as the default name on the Home app
110
- // in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method.
111
- this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
112
-
113
- // each service must implement at-minimum the "required characteristics" for the given service type
114
- // see https://developers.homebridge.io/#/service/Battery
115
-
116
- // create handlers for required characteristics
117
- this.service.setCharacteristic(this.platform.Characteristic.ChargingState, 2);
118
-
119
91
  // Temperature Sensor Service
120
92
  if (device.meter?.hide_temperature) {
121
93
  this.debugLog(`Meter: ${accessory.displayName} Removing Temperature Sensor Service`);
@@ -127,7 +99,7 @@ export class Meter {
127
99
  this.accessory.getService(this.platform.Service.TemperatureSensor) ||
128
100
  this.accessory.addService(this.platform.Service.TemperatureSensor)), `${accessory.displayName} Temperature Sensor`;
129
101
 
130
- this.service.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
102
+ this.temperatureservice.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
131
103
 
132
104
  this.temperatureservice
133
105
  .getCharacteristic(this.platform.Characteristic.CurrentTemperature)
@@ -139,7 +111,7 @@ export class Meter {
139
111
  minStep: 0.1,
140
112
  })
141
113
  .onGet(() => {
142
- return this.CurrentTemperature;
114
+ return this.CurrentTemperature!;
143
115
  });
144
116
  } else {
145
117
  this.debugLog(`Meter: ${accessory.displayName} Temperature Sensor Service Not Added`);
@@ -156,7 +128,7 @@ export class Meter {
156
128
  this.accessory.getService(this.platform.Service.HumiditySensor) ||
157
129
  this.accessory.addService(this.platform.Service.HumiditySensor)), `${accessory.displayName} Humidity Sensor`;
158
130
 
159
- this.service.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Humidity Sensor`);
131
+ this.humidityservice.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Humidity Sensor`);
160
132
 
161
133
  this.humidityservice
162
134
  .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
@@ -170,6 +142,27 @@ export class Meter {
170
142
  this.debugLog(`Meter: ${accessory.displayName} Humidity Sensor Service Not Added`);
171
143
  }
172
144
 
145
+ // Battery Service
146
+ if (!device.ble) {
147
+ this.debugLog(`Meter: ${accessory.displayName} Removing Battery Service`);
148
+ this.batteryService = this.accessory.getService(this.platform.Service.Battery);
149
+ accessory.removeService(this.batteryService!);
150
+ } else if (device.ble && !this.batteryService) {
151
+ this.debugLog(`Meter: ${accessory.displayName} Add Battery Service`);
152
+ (this.batteryService =
153
+ this.accessory.getService(this.platform.Service.Battery) ||
154
+ this.accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
155
+
156
+ this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
157
+
158
+ this.batteryService.setCharacteristic(this.platform.Characteristic.ChargingState, this.platform.Characteristic.ChargingState.NOT_CHARGEABLE);
159
+
160
+ } else {
161
+ this.debugLog(`Meter: ${accessory.displayName} Battery Service Not Added`);
162
+ }
163
+
164
+
165
+
173
166
  // Retrieve initial values and updateHomekit
174
167
  this.updateHomeKitCharacteristics();
175
168
 
@@ -181,6 +174,12 @@ export class Meter {
181
174
  });
182
175
  }
183
176
 
177
+ config(device: device & devicesConfig) {
178
+ if (device.meter !== undefined || device.ble !== undefined) {
179
+ this.warnLog(`Meter: ${this.accessory.displayName} Config: ${JSON.stringify(device.meter)}, BLE: ${device.ble}`);
180
+ }
181
+ }
182
+
184
183
  refreshRate() {
185
184
  if (this.device.refreshRate) {
186
185
  this.deviceRefreshRate = this.accessory.context.refreshRate = this.device.refreshRate;
@@ -232,10 +231,10 @@ export class Meter {
232
231
  * Parse the device status from the SwitchBot api
233
232
  */
234
233
  async parseStatus() {
235
- if (this.device.ble) {
236
- await this.BLEparseStatus();
237
- } else {
234
+ if (this.SwitchToOpenAPI || !this.device.ble) {
238
235
  await this.openAPIparseStatus();
236
+ } else {
237
+ await this.BLEparseStatus();
239
238
  }
240
239
  }
241
240
 
@@ -251,9 +250,9 @@ export class Meter {
251
250
  }
252
251
  this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
253
252
 
253
+ // Humidity
254
254
  if (!this.device.meter?.hide_humidity) {
255
- // Humidity
256
- this.CurrentRelativeHumidity = Number(this.humidity);
255
+ this.CurrentRelativeHumidity = this.humidity!;
257
256
  this.debugLog(`Meter: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
258
257
  }
259
258
 
@@ -266,27 +265,20 @@ export class Meter {
266
265
  }
267
266
 
268
267
  private async openAPIparseStatus() {
268
+ if (this.device.ble) {
269
+ this.SwitchToOpenAPI = false;
270
+ }
269
271
  if (this.platform.config.credentials?.openToken) {
270
272
  this.debugLog(`Meter: ${this.accessory.displayName} OpenAPI parseStatus`);
271
- if (this.deviceStatus.body) {
272
- this.BatteryLevel = 100;
273
- } else {
274
- this.BatteryLevel = 10;
275
- }
276
- if (this.BatteryLevel < 15) {
277
- this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
278
- } else {
279
- this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
280
- }
281
273
  // Current Relative Humidity
282
274
  if (!this.device.meter?.hide_humidity) {
283
- this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!;
275
+ this.CurrentRelativeHumidity = this.Humidity!;
284
276
  this.debugLog(`Meter: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
285
277
  }
286
278
 
287
279
  // Current Temperature
288
280
  if (!this.device.meter?.hide_temperature) {
289
- this.CurrentTemperature = Number(this.deviceStatus.body.temperature);
281
+ this.CurrentTemperature = this.Temperature!;
290
282
  this.debugLog(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
291
283
  }
292
284
  }
@@ -303,34 +295,12 @@ export class Meter {
303
295
  }
304
296
  }
305
297
 
306
- private async connectBLE() {
307
- let switchbot: any;
308
- try {
309
- // eslint-disable-next-line @typescript-eslint/no-var-requires
310
- const Switchbot = require('node-switchbot');
311
- switchbot = new Switchbot();
312
- // Convert to BLE Address
313
- this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
314
- this.debugLog(`Meter: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
315
- } catch (e: any) {
316
- switchbot = false;
317
- this.errorLog(`Meter: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`);
318
- if (this.deviceLogging === 'debug') {
319
- this.errorLog(`Meter: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
320
- + ` Error Message: ${JSON.stringify(e.message)}`);
321
- }
322
- if (this.platform.debugMode) {
323
- this.errorLog(`Meter: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
324
- + ` Error: ${JSON.stringify(e)}`);
325
- }
326
- }
327
- return switchbot;
328
- }
329
-
330
298
  private async BLErefreshStatus() {
331
299
  this.debugLog(`Meter: ${this.accessory.displayName} BLE RefreshStatus`);
332
- // eslint-disable-next-line @typescript-eslint/no-var-requires
333
- const switchbot = await this.connectBLE();
300
+ const switchbot = await this.platform.connectBLE();
301
+ // Convert to BLE Address
302
+ this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
303
+ this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
334
304
  // Start to monitor advertisement packets
335
305
  if (switchbot !== false) {
336
306
  switchbot.startScan({
@@ -362,12 +332,6 @@ export class Meter {
362
332
  // Stop to monitor
363
333
  switchbot.stopScan();
364
334
  if (this.connected) {
365
- this.CurrentTemperatureCached = this.temperature;
366
- this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached;
367
- this.CurrentRelativeHumidityCached = this.humidity!;
368
- this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidityCached;
369
- this.BatteryLevelCached = this.battery!;
370
- this.accessory.context.BatteryLevel = this.BatteryLevelCached;
371
335
  this.parseStatus();
372
336
  this.updateHomeKitCharacteristics();
373
337
  } else {
@@ -385,6 +349,7 @@ export class Meter {
385
349
  }
386
350
  if (this.platform.config.credentials?.openToken) {
387
351
  this.warnLog(`Meter: ${this.accessory.displayName} Using OpenAPI Connection`);
352
+ this.SwitchToOpenAPI = true;
388
353
  this.openAPIRefreshStatus();
389
354
  }
390
355
  this.apiError(e);
@@ -398,6 +363,7 @@ export class Meter {
398
363
  this.errorLog(`Meter: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
399
364
  if (this.platform.config.credentials?.openToken) {
400
365
  this.warnLog(`Meter: ${this.accessory.displayName} Using OpenAPI Connection`);
366
+ this.SwitchToOpenAPI = true;
401
367
  await this.openAPIRefreshStatus();
402
368
  }
403
369
  }
@@ -408,12 +374,8 @@ export class Meter {
408
374
  try {
409
375
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
410
376
  this.debugLog(`Meter: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`);
411
- this.CurrentTemperatureCached = this.deviceStatus.body.humidity!;
412
- this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached;
413
- this.CurrentRelativeHumidityCached = this.deviceStatus.body.humidity!;
414
- this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidityCached;
415
- this.BatteryLevelCached = 100;
416
- this.accessory.context.BatteryLevel = this.BatteryLevelCached;
377
+ this.Humidity = this.deviceStatus.body.humidity!;
378
+ this.Temperature = this.deviceStatus.body.temperature!;
417
379
  this.parseStatus();
418
380
  this.updateHomeKitCharacteristics();
419
381
  } catch (e: any) {
@@ -435,18 +397,6 @@ export class Meter {
435
397
  * Updates the status for each of the HomeKit Characteristics
436
398
  */
437
399
  updateHomeKitCharacteristics() {
438
- if (this.StatusLowBattery === undefined) {
439
- this.debugLog(`Meter: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
440
- } else {
441
- this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
442
- this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
443
- }
444
- if (this.BatteryLevel === undefined) {
445
- this.debugLog(`Meter: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
446
- } else {
447
- this.service.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
448
- this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
449
- }
450
400
  if (!this.device.meter?.hide_humidity) {
451
401
  if (this.CurrentRelativeHumidity === undefined) {
452
402
  this.debugLog(`Meter: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
@@ -463,17 +413,33 @@ export class Meter {
463
413
  this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
464
414
  }
465
415
  }
416
+ if (this.device.ble) {
417
+ if (this.BatteryLevel === undefined) {
418
+ this.debugLog(`Bot: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
419
+ } else {
420
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
421
+ this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
422
+ }
423
+ if (this.StatusLowBattery === undefined) {
424
+ this.debugLog(`Bot: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
425
+ } else {
426
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
427
+ this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
428
+ }
429
+ }
466
430
  }
467
431
 
468
432
  public apiError(e: any) {
469
- this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
470
- this.service.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
471
433
  if (!this.device.meter?.hide_humidity) {
472
434
  this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, e);
473
435
  }
474
436
  if (!this.device.meter?.hide_temperature) {
475
437
  this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e);
476
438
  }
439
+ if (this.device.ble) {
440
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
441
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
442
+ }
477
443
  //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
478
444
  }
479
445
 
@@ -2,7 +2,7 @@ import { interval, Subject } from 'rxjs';
2
2
  import { skipWhile } from 'rxjs/operators';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
5
- import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, deviceStatus } from '../settings';
6
6
 
7
7
  /**
8
8
  * Platform Accessory
@@ -17,22 +17,27 @@ export class Motion {
17
17
 
18
18
  // Characteristic Values
19
19
  MotionDetected!: CharacteristicValue;
20
+ CurrentAmbientLightLevel!: CharacteristicValue;
20
21
  BatteryLevel?: CharacteristicValue;
21
22
  StatusLowBattery?: CharacteristicValue;
22
- CurrentAmbientLightLevel?: CharacteristicValue;
23
23
 
24
24
  // OpenAPI Others
25
25
  deviceStatus!: deviceStatusResponse;
26
+ moveDetected: deviceStatus['moveDetected'];
27
+ brightness: deviceStatus['brightness'];
26
28
 
27
29
  // BLE Others
28
30
  connected?: boolean;
29
31
  switchbot!: switchbot;
32
+ SwitchToOpenAPI?: boolean;
30
33
  serviceData!: serviceData;
31
34
  battery!: serviceData['battery'];
32
35
  movement!: serviceData['movement'];
33
36
  lightLevel!: serviceData['lightLevel'];
34
37
 
35
38
  // Config
39
+ set_minLux!: number;
40
+ set_maxLux!: number;
36
41
  scanDuration!: number;
37
42
  deviceLogging!: string;
38
43
  deviceRefreshRate!: number;
@@ -50,8 +55,14 @@ export class Motion {
50
55
  this.logs();
51
56
  this.scan();
52
57
  this.refreshRate();
58
+ this.config(device);
53
59
  this.MotionDetected = false;
54
60
 
61
+ // Motion Config
62
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} Config: (ble: ${device.ble}, offline: ${device.offline},`
63
+ + ` hide_lightsensor: ${device.motion?.hide_lightsensor},`
64
+ + ` set_minLux: ${this.device.motion?.set_minLux}, set_maxLux: ${this.device.motion?.set_maxLux})`);
65
+
55
66
  // this is subject we use to track when we need to POST changes to the SwitchBot API
56
67
  this.doMotionUpdate = new Subject();
57
68
  this.motionUbpdateInProgress = false;
@@ -72,21 +83,6 @@ export class Motion {
72
83
  accessory.getService(this.platform.Service.MotionSensor) ||
73
84
  accessory.addService(this.platform.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`;
74
85
 
75
- if (device.ble) {
76
- (this.lightSensorService =
77
- accessory.getService(this.platform.Service.LightSensor) ||
78
- accessory.addService(this.platform.Service.LightSensor)), `${accessory.displayName} Light Sensor`;
79
-
80
- this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Light Sensor`);
81
-
82
-
83
- (this.batteryService =
84
- accessory.getService(this.platform.Service.Battery) ||
85
- accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
86
-
87
- this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
88
- }
89
-
90
86
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
91
87
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
92
88
  // accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Motion, 'NAME', 'USER_DEFINED_SUBTYPE');
@@ -98,8 +94,39 @@ export class Motion {
98
94
  // each service must implement at-minimum the "required characteristics" for the given service type
99
95
  // see https://developers.homebridge.io/#/service/MotionSensor
100
96
 
101
- // create handlers for required characteristics
102
- //this.service.setCharacteristic(this.platform.Characteristic.ChargingState, 2);
97
+ // Light Sensor Service
98
+ if (device.motion?.hide_lightsensor) {
99
+ this.debugLog(`Motion Sensor: ${accessory.displayName} Removing Light Sensor Service`);
100
+ this.lightSensorService = this.accessory.getService(this.platform.Service.LightSensor);
101
+ accessory.removeService(this.lightSensorService!);
102
+ } else if (!this.lightSensorService) {
103
+ this.debugLog(`Motion Sensor: ${accessory.displayName} Add Light Sensor Service`);
104
+ (this.lightSensorService =
105
+ this.accessory.getService(this.platform.Service.LightSensor) ||
106
+ this.accessory.addService(this.platform.Service.LightSensor)), `${accessory.displayName} Light Sensor`;
107
+
108
+ this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Light Sensor`);
109
+
110
+ } else {
111
+ this.debugLog(`Motion Sensor: ${accessory.displayName} Light Sensor Service Not Added`);
112
+ }
113
+
114
+ // Battery Service
115
+ if (!device.ble) {
116
+ this.debugLog(`Motion Sensor: ${accessory.displayName} Removing Battery Service`);
117
+ this.batteryService = this.accessory.getService(this.platform.Service.Battery);
118
+ accessory.removeService(this.batteryService!);
119
+ } else if (device.ble && !this.batteryService) {
120
+ this.debugLog(`Motion Sensor: ${accessory.displayName} Add Battery Service`);
121
+ (this.batteryService =
122
+ this.accessory.getService(this.platform.Service.Battery) ||
123
+ this.accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
124
+
125
+ this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
126
+
127
+ } else {
128
+ this.debugLog(`Motion Sensor: ${accessory.displayName} Battery Service Not Added`);
129
+ }
103
130
 
104
131
  // Retrieve initial values and updateHomekit
105
132
  this.updateHomeKitCharacteristics();
@@ -112,6 +139,12 @@ export class Motion {
112
139
  });
113
140
  }
114
141
 
142
+ config(device: device & devicesConfig) {
143
+ if (device.motion !== undefined) {
144
+ this.warnLog(`Motion Sensor: ${this.accessory.displayName} Config: ${JSON.stringify(device.motion)}`);
145
+ }
146
+ }
147
+
115
148
  refreshRate() {
116
149
  if (this.device.refreshRate) {
117
150
  this.deviceRefreshRate = this.accessory.context.refreshRate = this.device.refreshRate;
@@ -159,59 +192,92 @@ export class Motion {
159
192
  }
160
193
  }
161
194
 
195
+ private minLux(): number {
196
+ if (this.device.motion?.set_minLux) {
197
+ this.set_minLux = this.device.motion!.set_minLux!;
198
+ } else {
199
+ this.set_minLux = 1;
200
+ }
201
+ return this.set_minLux;
202
+ }
203
+
204
+ private maxLux(): number {
205
+ if (this.device.motion?.set_maxLux) {
206
+ this.set_maxLux = this.device.motion!.set_maxLux!;
207
+ } else {
208
+ this.set_maxLux = 6001;
209
+ }
210
+ return this.set_maxLux;
211
+ }
212
+
162
213
  /**
163
214
  * Parse the device status from the SwitchBot api
164
215
  */
165
216
  async parseStatus() {
166
- if (this.device.ble) {
167
- await this.BLEparseStatus();
168
- } else {
217
+ if (this.SwitchToOpenAPI || !this.device.ble) {
169
218
  await this.openAPIparseStatus();
219
+ } else {
220
+ await this.BLEparseStatus();
170
221
  }
171
222
  }
172
223
 
173
224
  private async BLEparseStatus() {
174
225
  this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE parseStatus`);
175
- this.MotionDetected = Boolean(this.movement);
226
+ // Movement
227
+ this.MotionDetected = this.movement!;
176
228
  this.debugLog(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
177
229
  // Light Level
178
- switch (this.lightLevel) {
179
- case 'dark':
180
- case 0:
181
- this.CurrentAmbientLightLevel = 0.0001;
182
- break;
183
- default:
184
- this.CurrentAmbientLightLevel = 100000;
230
+ if (!this.device.motion?.hide_lightsensor) {
231
+ this.set_minLux = this.minLux();
232
+ this.set_maxLux = this.maxLux();
233
+ switch (this.lightLevel) {
234
+ case 'dark':
235
+ case 1:
236
+ this.CurrentAmbientLightLevel = this.set_minLux;
237
+ break;
238
+ default:
239
+ this.CurrentAmbientLightLevel = this.set_maxLux;
240
+ }
241
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} LightLevel: ${this.lightLevel},`
242
+ + ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
185
243
  }
186
244
  // Battery
187
- this.BatteryLevel = Number(this.battery);
245
+ if (this.battery === undefined) {
246
+ this.battery = 100;
247
+ }
248
+ this.BatteryLevel = this.battery!;
188
249
  if (this.BatteryLevel < 10) {
189
250
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
190
251
  } else {
191
252
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
192
253
  }
254
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
193
255
  }
194
256
 
195
257
  private async openAPIparseStatus() {
196
- try {
197
- if (this.platform.config.credentials?.openToken) {
198
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} OpenAPI parseStatus`);
199
- if (typeof this.deviceStatus.body.moveDetected === 'boolean') {
200
- this.MotionDetected = this.deviceStatus.body.moveDetected;
258
+ if (this.device.ble) {
259
+ this.SwitchToOpenAPI = false;
260
+ }
261
+ if (this.platform.config.credentials?.openToken) {
262
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} OpenAPI parseStatus`);
263
+ // Motion State
264
+ this.MotionDetected = this.moveDetected!;
265
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
266
+ // Light Level
267
+ if (!this.device.motion?.hide_lightsensor) {
268
+ this.set_minLux = this.minLux();
269
+ this.set_maxLux = this.maxLux();
270
+ switch (this.brightness) {
271
+ case 'dim':
272
+ this.CurrentAmbientLightLevel = this.set_minLux;
273
+ break;
274
+ case 'bright':
275
+ default:
276
+ this.CurrentAmbientLightLevel = this.set_maxLux;
201
277
  }
202
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
203
- }
204
- } catch (e: any) {
205
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed parseStatus with OpenAPI Connection`);
206
- if (this.deviceLogging === 'debug') {
207
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed parseStatus with OpenAPI Connection,`
208
- + ` Error Message: ${JSON.stringify(e.message)}`);
209
- }
210
- if (this.platform.debugMode) {
211
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed parseStatus with OpenAPI Connection,`
212
- + ` Error: ${JSON.stringify(e)}`);
278
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
279
+
213
280
  }
214
- this.apiError(e);
215
281
  }
216
282
  }
217
283
 
@@ -226,34 +292,12 @@ export class Motion {
226
292
  }
227
293
  }
228
294
 
229
- public async connectBLE() {
230
- let switchbot: any;
231
- try {
232
- // eslint-disable-next-line @typescript-eslint/no-var-requires
233
- const Switchbot = require('node-switchbot');
234
- switchbot = new Switchbot();
235
- // Convert to BLE Address
236
- this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
237
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
238
- } catch (e: any) {
239
- switchbot = false;
240
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`);
241
- if (this.deviceLogging === 'debug') {
242
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
243
- + ` Error Message: ${JSON.stringify(e.message)}`);
244
- }
245
- if (this.platform.debugMode) {
246
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
247
- + ` Error: ${JSON.stringify(e)}`);
248
- }
249
- }
250
- return switchbot;
251
- }
252
-
253
295
  private async BLERefreshStatus() {
254
296
  this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE RefreshStatus`);
255
- // eslint-disable-next-line @typescript-eslint/no-var-requires
256
- const switchbot = await this.connectBLE();
297
+ const switchbot = await this.platform.connectBLE();
298
+ // Convert to BLE Address
299
+ this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
300
+ this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
257
301
  // Start to monitor advertisement packets
258
302
  if (switchbot !== false) {
259
303
  switchbot.startScan({
@@ -301,6 +345,7 @@ export class Motion {
301
345
  }
302
346
  if (this.platform.config.credentials?.openToken) {
303
347
  this.warnLog(`Motion Sensor: ${this.accessory.displayName} Using OpenAPI Connection`);
348
+ this.SwitchToOpenAPI = true;
304
349
  await this.openAPIRefreshStatus();
305
350
  }
306
351
  this.apiError(e);
@@ -314,6 +359,7 @@ export class Motion {
314
359
  this.errorLog(`Motion Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
315
360
  if (this.platform.config.credentials?.openToken) {
316
361
  this.warnLog(`Motion Sensor: ${this.accessory.displayName} Using OpenAPI Connection`);
362
+ this.SwitchToOpenAPI = true;
317
363
  await this.openAPIRefreshStatus();
318
364
  }
319
365
  }
@@ -324,6 +370,11 @@ export class Motion {
324
370
  try {
325
371
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
326
372
  this.debugLog(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
373
+ if (this.deviceStatus.body.moveDetected === undefined) {
374
+ this.errorLog(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
375
+ }
376
+ this.moveDetected = this.deviceStatus.body.moveDetected;
377
+ this.brightness = this.deviceStatus.body.brightness;
327
378
  this.parseStatus();
328
379
  this.updateHomeKitCharacteristics();
329
380
  } catch (e: any) {