@switchbot/homebridge-switchbot 1.6.4-beta.5 → 1.7.1-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +23 -2
  2. package/config.schema.json +125 -33
  3. package/dist/devices/bots.d.ts +21 -9
  4. package/dist/devices/bots.d.ts.map +1 -1
  5. package/dist/devices/bots.js +352 -48
  6. package/dist/devices/bots.js.map +1 -1
  7. package/dist/devices/colorbulb.d.ts +6 -1
  8. package/dist/devices/colorbulb.d.ts.map +1 -1
  9. package/dist/devices/colorbulb.js +18 -11
  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 +5 -5
  79. package/src/devices/bots.ts +355 -52
  80. package/src/devices/colorbulb.ts +24 -14
  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
@@ -3,7 +3,7 @@ import { interval, Subject } from 'rxjs';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
5
  import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge';
6
- import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse, payload, hs2rgb, rgb2hs, m2hs } from '../settings';
6
+ import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse, payload, hs2rgb, rgb2hs, m2hs, deviceStatus } from '../settings';
7
7
 
8
8
  /**
9
9
  * Platform Accessory
@@ -27,6 +27,10 @@ export class ColorBulb {
27
27
  ColorTemperatureCached!: CharacteristicValue;
28
28
 
29
29
  // OpenAPI Others
30
+ power: deviceStatus['power'];
31
+ color: deviceStatus['color'];
32
+ brightness: deviceStatus['brightness'];
33
+ colorTemperature: deviceStatus['colorTemperature'];
30
34
  deviceStatus!: deviceStatusResponse;
31
35
 
32
36
  // BLE Others
@@ -57,6 +61,7 @@ export class ColorBulb {
57
61
  this.logs();
58
62
  this.refreshRate();
59
63
  this.adaptiveLighting();
64
+ this.config(device);
60
65
  if (this.On === undefined) {
61
66
  this.On = false;
62
67
  } else {
@@ -68,11 +73,6 @@ export class ColorBulb {
68
73
  this.ColorTemperature = 140;
69
74
  this.minKelvin = 2000;
70
75
  this.maxKelvin = 9000;
71
-
72
- // ColorBulb Config
73
- this.debugLog(`Color Bulb: ${this.accessory.displayName} Config: (ble: ${device.ble}, set_minStep: ${device.colorbulb?.set_minStep},`
74
- + ` adaptiveLighting: ${device.colorbulb?.adaptiveLightingShift})`);
75
-
76
76
  // this is subject we use to track when we need to POST changes to the SwitchBot API
77
77
  this.doColorBulbUpdate = new Subject();
78
78
  this.colorBulbUpdateInProgress = false;
@@ -214,6 +214,12 @@ export class ColorBulb {
214
214
  });
215
215
  }
216
216
 
217
+ config(device: device & devicesConfig) {
218
+ if (device.colorbulb !== undefined) {
219
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} Config: ${JSON.stringify(device.colorbulb)}`);
220
+ }
221
+ }
222
+
217
223
  refreshRate() {
218
224
  if (this.device.refreshRate) {
219
225
  this.deviceRefreshRate = this.accessory.context.refreshRate = this.device.refreshRate;
@@ -267,7 +273,7 @@ export class ColorBulb {
267
273
  }
268
274
 
269
275
  parseStatus() {
270
- switch (this.deviceStatus.body.power) {
276
+ switch (this.power) {
271
277
  case 'on':
272
278
  this.On = true;
273
279
  break;
@@ -277,13 +283,13 @@ export class ColorBulb {
277
283
  this.debugLog(`Color Bulb: ${this.accessory.displayName} On: ${this.On}`);
278
284
 
279
285
  // Brightness
280
- this.Brightness = Number(this.deviceStatus.body.brightness);
286
+ this.Brightness = Number(this.brightness);
281
287
  this.debugLog(`Color Bulb: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
282
288
 
283
289
  // Color, Hue & Brightness
284
- if (this.deviceStatus.body.color) {
285
- this.debugLog(`Color Bulb: ${this.accessory.displayName} color: ${JSON.stringify(this.deviceStatus.body.color)}`);
286
- const [red, green, blue] = this.deviceStatus.body.color!.split(':');
290
+ if (this.color) {
291
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} color: ${JSON.stringify(this.color)}`);
292
+ const [red, green, blue] = this.color!.split(':');
287
293
  this.debugLog(`Color Bulb: ${this.accessory.displayName} red: ${JSON.stringify(red)}`);
288
294
  this.debugLog(`Color Bulb: ${this.accessory.displayName} green: ${JSON.stringify(green)}`);
289
295
  this.debugLog(`Color Bulb: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`);
@@ -301,9 +307,9 @@ export class ColorBulb {
301
307
  }
302
308
 
303
309
  // ColorTemperature
304
- if (!Number.isNaN(this.deviceStatus.body.colorTemperature)) {
305
- this.debugLog(`Color Bulb: ${this.accessory.displayName} OpenAPI ColorTemperature: ${this.deviceStatus.body.colorTemperature}`);
306
- const mired = Math.round(1000000 / this.deviceStatus.body.colorTemperature!);
310
+ if (!Number.isNaN(this.colorTemperature)) {
311
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} OpenAPI ColorTemperature: ${this.colorTemperature}`);
312
+ const mired = Math.round(1000000 / this.colorTemperature!);
307
313
 
308
314
  this.ColorTemperature = Number(mired);
309
315
 
@@ -316,6 +322,10 @@ export class ColorBulb {
316
322
  try {
317
323
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
318
324
  this.debugLog(`Color Bulb: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
325
+ this.power = this.deviceStatus.body.power;
326
+ this.color = this.deviceStatus.body.color;
327
+ this.brightness = this.deviceStatus.body.brightness;
328
+ this.colorTemperature = this.deviceStatus.body.colorTemperature;
319
329
  this.parseStatus();
320
330
  this.updateHomeKitCharacteristics();
321
331
  } catch (e: any) {
@@ -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
@@ -24,11 +24,15 @@ export class Contact {
24
24
  StatusLowBattery!: CharacteristicValue;
25
25
 
26
26
  // OpenAPI others
27
+ openState: deviceStatus['openState'];
28
+ moveDetected: deviceStatus['moveDetected'];
29
+ brightness: deviceStatus['brightness'];
27
30
  deviceStatus!: deviceStatusResponse;
28
31
 
29
32
  // BLE Others
30
33
  connected?: boolean;
31
34
  switchbot!: switchbot;
35
+ SwitchToOpenAPI!: boolean;
32
36
  serviceData!: serviceData;
33
37
  battery!: serviceData['battery'];
34
38
  movement!: serviceData['movement'];
@@ -36,6 +40,8 @@ export class Contact {
36
40
  lightLevel!: serviceData['lightLevel'];
37
41
 
38
42
  // Config
43
+ set_minLux!: number;
44
+ set_maxLux!: number;
39
45
  scanDuration!: number;
40
46
  deviceLogging!: string;
41
47
  deviceRefreshRate!: number;
@@ -53,13 +59,9 @@ export class Contact {
53
59
  this.logs();
54
60
  this.scan();
55
61
  this.refreshRate();
62
+ this.config(device);
56
63
  this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED;
57
64
 
58
- // Contact Config
59
- this.debugLog(`Contact Sensor: ${this.accessory.displayName} Config: (ble: ${device.ble}, offline: ${device.offline},`
60
- + ` hide_lightsensor: ${device.contact?.hide_lightsensor}, hide_motionsensor: ${device.contact?.hide_motionsensor})`);
61
-
62
-
63
65
  // this is subject we use to track when we need to POST changes to the SwitchBot API
64
66
  this.doContactUpdate = new Subject();
65
67
  this.contactUbpdateInProgress = false;
@@ -89,7 +91,7 @@ export class Contact {
89
91
  this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
90
92
 
91
93
  // each service must implement at-minimum the "required characteristics" for the given service type
92
- // see https://developers.homebridge.io/#/service/MotionSensor
94
+ // see https://developers.homebridge.io/#/service/ContactSensor
93
95
 
94
96
  // Motion Sensor Service
95
97
  if (device.contact?.hide_motionsensor) {
@@ -125,12 +127,21 @@ export class Contact {
125
127
  this.debugLog(`Contact Sensor: ${accessory.displayName} Light Sensor Service Not Added`);
126
128
  }
127
129
 
128
- if (device.ble) {
130
+ // Battery Service
131
+ if (!device.ble) {
132
+ this.debugLog(`Contact Sensor: ${accessory.displayName} Removing Battery Service`);
133
+ this.batteryService = this.accessory.getService(this.platform.Service.Battery);
134
+ accessory.removeService(this.batteryService!);
135
+ } else if (device.ble && !this.batteryService) {
136
+ this.debugLog(`Contact Sensor: ${accessory.displayName} Add Battery Service`);
129
137
  (this.batteryService =
130
- accessory.getService(this.platform.Service.Battery) ||
131
- accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
138
+ this.accessory.getService(this.platform.Service.Battery) ||
139
+ this.accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
132
140
 
133
141
  this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
142
+
143
+ } else {
144
+ this.debugLog(`Contact Sensor: ${accessory.displayName} Battery Service Not Added`);
134
145
  }
135
146
 
136
147
  // Retrieve initial values and updateHomekit
@@ -144,6 +155,12 @@ export class Contact {
144
155
  });
145
156
  }
146
157
 
158
+ config(device: device & devicesConfig) {
159
+ if (device.contact !== undefined) {
160
+ this.warnLog(`Contact Sensor: ${this.accessory.displayName} Config: ${JSON.stringify(device.contact)}`);
161
+ }
162
+ }
163
+
147
164
  refreshRate() {
148
165
  if (this.device.refreshRate) {
149
166
  this.deviceRefreshRate = this.accessory.context.refreshRate = this.device.refreshRate;
@@ -191,23 +208,37 @@ export class Contact {
191
208
  }
192
209
  }
193
210
 
211
+ private minLux(): number {
212
+ if (this.device.contact?.set_minLux) {
213
+ this.set_minLux = this.device.contact!.set_minLux!;
214
+ } else {
215
+ this.set_minLux = 1;
216
+ }
217
+ return this.set_minLux;
218
+ }
219
+
220
+ private maxLux(): number {
221
+ if (this.device.contact?.set_maxLux) {
222
+ this.set_maxLux = this.device.contact!.set_maxLux!;
223
+ } else {
224
+ this.set_maxLux = 6001;
225
+ }
226
+ return this.set_maxLux;
227
+ }
228
+
194
229
  /**
195
230
  * Parse the device status from the SwitchBot api
196
231
  */
197
232
  async parseStatus() {
198
- if (this.device.ble) {
199
- await this.BLEparseStatus();
200
- } else {
233
+ if (this.SwitchToOpenAPI || !this.device.ble) {
201
234
  await this.openAPIparseStatus();
235
+ } else {
236
+ await this.BLEparseStatus();
202
237
  }
203
238
  }
204
239
 
205
240
  private async BLEparseStatus() {
206
241
  this.debugLog(`Contact Sensor: ${this.accessory.displayName} BLE parseStatus`);
207
- if (this.device.contact?.hide_motionsensor) {
208
- // Movement
209
- this.MotionDetected = Boolean(this.movement);
210
- }
211
242
  // Door State
212
243
  switch (this.doorState) {
213
244
  case 'open':
@@ -221,44 +252,74 @@ export class Contact {
221
252
  default:
222
253
  this.errorLog(`Contact Sensor: ${this.accessory.displayName} timeout no closed, doorstate: ${this.doorState}`);
223
254
  }
224
- if (this.device.contact?.hide_lightsensor) {// Light Level
255
+ // Movement
256
+ if (!this.device.contact?.hide_motionsensor) {
257
+ this.MotionDetected = Boolean(this.movement);
258
+ this.debugLog(`Contact Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
259
+ }
260
+ // Light Level
261
+ if (!this.device.contact?.hide_lightsensor) {
262
+ this.set_minLux = this.minLux();
263
+ this.set_maxLux = this.maxLux();
225
264
  switch (this.lightLevel) {
226
265
  case 'dark':
227
266
  case 0:
228
- this.CurrentAmbientLightLevel = 0.0001;
267
+ this.CurrentAmbientLightLevel = this.set_minLux;
229
268
  break;
230
269
  default:
231
- this.CurrentAmbientLightLevel = 100000;
270
+ this.CurrentAmbientLightLevel = this.set_maxLux;
232
271
  }
272
+ this.debugLog(`Contact Sensor: ${this.accessory.displayName} LightLevel: ${this.lightLevel},`
273
+ + ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
233
274
  }
234
275
  // Battery
276
+ if (this.battery === undefined) {
277
+ this.battery === 100;
278
+ }
235
279
  this.BatteryLevel = Number(this.battery);
236
280
  if (this.BatteryLevel < 10) {
237
281
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
238
282
  } else {
239
283
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
240
284
  }
241
-
242
- this.debugLog(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}, MotionDetected: `
243
- + `${this.MotionDetected}, CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}, BatteryLevel: ${this.BatteryLevel}`);
285
+ this.debugLog(`Contact Sensor: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
244
286
  }
245
287
 
246
288
  private async openAPIparseStatus() {
289
+ if (this.device.ble) {
290
+ this.SwitchToOpenAPI = false;
291
+ }
247
292
  if (this.platform.config.credentials?.openToken) {
248
293
  this.debugLog(`Contact Sensor: ${this.accessory.displayName} OpenAPI parseStatus`);
249
- if (this.deviceStatus.body.openState === 'open') {
294
+ // Contact State
295
+ if (this.openState === 'open') {
250
296
  this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
251
297
  this.debugLog(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
252
- } else if (this.deviceStatus.body.openState === 'close') {
298
+ } else if (this.openState === 'close') {
253
299
  this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED;
254
300
  this.debugLog(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
255
301
  } else {
256
- this.debugLog(`Contact Sensor: ${this.accessory.displayName} openState: ${this.deviceStatus.body.openState}`);
302
+ this.debugLog(`Contact Sensor: ${this.accessory.displayName} openState: ${this.openState}`);
257
303
  }
258
- if (this.device.contact?.hide_motionsensor) {
259
- this.MotionDetected = Boolean(this.deviceStatus.body.moveDetected);
304
+ // Motion State
305
+ if (!this.device.contact?.hide_motionsensor) {
306
+ this.MotionDetected = this.moveDetected!;
307
+ this.debugLog(`Contact Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
308
+ }
309
+ // Light Level
310
+ if (!this.device.contact?.hide_lightsensor) {
311
+ this.set_minLux = this.minLux();
312
+ this.set_maxLux = this.maxLux();
313
+ switch (this.brightness) {
314
+ case 'dim':
315
+ this.CurrentAmbientLightLevel = this.set_minLux;
316
+ break;
317
+ case 'bright':
318
+ default:
319
+ this.CurrentAmbientLightLevel = this.set_maxLux;
320
+ }
321
+ this.debugLog(`Contact Sensor: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
260
322
  }
261
- this.debugLog(`Contact Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
262
323
  }
263
324
  }
264
325
 
@@ -273,34 +334,12 @@ export class Contact {
273
334
  }
274
335
  }
275
336
 
276
- public async connectBLE() {
277
- let switchbot: any;
278
- try {
279
- // eslint-disable-next-line @typescript-eslint/no-var-requires
280
- const Switchbot = require('node-switchbot');
281
- switchbot = new Switchbot();
282
- // Convert to BLE Address
283
- this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
284
- this.debugLog(`Contact Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
285
- } catch (e: any) {
286
- switchbot = false;
287
- this.errorLog(`Contact Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`);
288
- if (this.deviceLogging === 'debug') {
289
- this.errorLog(`Contact Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
290
- + ` Error Message: ${JSON.stringify(e.message)}`);
291
- }
292
- if (this.platform.debugMode) {
293
- this.errorLog(`Contact Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
294
- + ` Error: ${JSON.stringify(e)}`);
295
- }
296
- }
297
- return switchbot;
298
- }
299
-
300
337
  private async BLERefreshStatus() {
301
338
  this.debugLog(`Contact Sensor: ${this.accessory.displayName} BLE refreshStatus`);
302
- // eslint-disable-next-line @typescript-eslint/no-var-requires
303
- const switchbot = await this.connectBLE();
339
+ const switchbot = await this.platform.connectBLE();
340
+ // Convert to BLE Address
341
+ this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
342
+ this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
304
343
  // Start to monitor advertisement packets
305
344
  if (switchbot !== false) {
306
345
  switchbot.startScan({
@@ -353,6 +392,7 @@ export class Contact {
353
392
  }
354
393
  if (this.platform.config.credentials?.openToken) {
355
394
  this.warnLog(`Contact Sensor: ${this.accessory.displayName} Using OpenAPI Connection`);
395
+ this.SwitchToOpenAPI = true;
356
396
  await this.openAPIRefreshStatus();
357
397
  }
358
398
  this.apiError(e);
@@ -366,6 +406,7 @@ export class Contact {
366
406
  this.errorLog(`Contact Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
367
407
  if (this.platform.config.credentials?.openToken) {
368
408
  this.warnLog(`Contact Sensor: ${this.accessory.displayName} Using OpenAPI Connection`);
409
+ this.SwitchToOpenAPI = true;
369
410
  await this.openAPIRefreshStatus();
370
411
  }
371
412
  }
@@ -376,6 +417,9 @@ export class Contact {
376
417
  try {
377
418
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
378
419
  this.debugLog(`Contact Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
420
+ this.openState = this.deviceStatus.body.openState;
421
+ this.moveDetected = this.deviceStatus.body.moveDetected;
422
+ this.brightness = this.deviceStatus.body.brightness;
379
423
  this.parseStatus();
380
424
  this.updateHomeKitCharacteristics();
381
425
  } catch (e: any) {
@@ -4,7 +4,7 @@ import { interval, Subject } from 'rxjs';
4
4
  import { SwitchBotPlatform } from '../platform';
5
5
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
6
6
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
7
- import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, payload } from '../settings';
7
+ import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, payload, deviceStatus } from '../settings';
8
8
 
9
9
  export class Curtain {
10
10
  // Services
@@ -22,11 +22,16 @@ export class Curtain {
22
22
 
23
23
  // OpenAPI Others
24
24
  deviceStatus!: deviceStatusResponse;
25
+ slidePosition: deviceStatus['slidePosition'];
26
+ moving: deviceStatus['moving'];
27
+ brightness: deviceStatus['brightness'];
25
28
 
26
29
  // BLE Others
27
30
  connected?: boolean;
28
31
  switchbot!: switchbot;
32
+ SwitchToOpenAPI?: boolean;
29
33
  serviceData!: serviceData;
34
+ spaceBetweenLevels!: number;
30
35
  calibration: serviceData['calibration'];
31
36
  battery: serviceData['battery'];
32
37
  position: serviceData['position'];
@@ -44,7 +49,6 @@ export class Curtain {
44
49
  scanDuration!: number;
45
50
  deviceLogging!: string;
46
51
  deviceRefreshRate!: number;
47
- spaceBetweenLevels!: number;
48
52
 
49
53
  // Updates
50
54
  curtainUpdateInProgress!: boolean;
@@ -60,6 +64,7 @@ export class Curtain {
60
64
  this.scan();
61
65
  this.setMinMax();
62
66
  this.refreshRate();
67
+ this.config(device);
63
68
  this.CurrentPosition = 0;
64
69
  this.TargetPosition = 0;
65
70
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
@@ -142,6 +147,23 @@ export class Curtain {
142
147
  this.debugLog(`Curtain: ${accessory.displayName} Light Sensor Service Not Added`);
143
148
  }
144
149
 
150
+ // Battery Service
151
+ if (!device.ble) {
152
+ this.debugLog(`Curtain: ${accessory.displayName} Removing Battery Service`);
153
+ this.batteryService = this.accessory.getService(this.platform.Service.Battery);
154
+ accessory.removeService(this.batteryService!);
155
+ } else if (device.ble && !this.batteryService) {
156
+ this.debugLog(`Curtain: ${accessory.displayName} Add Battery Service`);
157
+ (this.batteryService =
158
+ this.accessory.getService(this.platform.Service.Battery) ||
159
+ this.accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
160
+
161
+ this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
162
+
163
+ } else {
164
+ this.debugLog(`Curtain: ${accessory.displayName} Battery Service Not Added`);
165
+ }
166
+
145
167
 
146
168
  // Update Homekit
147
169
  this.updateHomeKitCharacteristics();
@@ -193,6 +215,12 @@ export class Curtain {
193
215
  });
194
216
  }
195
217
 
218
+ config(device: device & devicesConfig) {
219
+ if (device.curtain !== undefined || device.ble !== undefined) {
220
+ this.warnLog(`Curtain: ${this.accessory.displayName} Config: ${JSON.stringify(device.curtain)}, BLE: ${device.ble}`);
221
+ }
222
+ }
223
+
196
224
  refreshRate() {
197
225
  // refreshRate
198
226
  if (this.device.refreshRate) {
@@ -253,30 +281,6 @@ export class Curtain {
253
281
  }
254
282
  }
255
283
 
256
- public async connectBLE() {
257
- let switchbot: any;
258
- try {
259
- // eslint-disable-next-line @typescript-eslint/no-var-requires
260
- const Switchbot = require('node-switchbot');
261
- switchbot = new Switchbot();
262
- // Convert to BLE Address
263
- this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
264
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
265
- } catch (e: any) {
266
- switchbot = false;
267
- this.errorLog(`Curtain: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`);
268
- if (this.deviceLogging === 'debug') {
269
- this.errorLog(`Curtain: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
270
- + ` Error Message: ${JSON.stringify(e.message)}`);
271
- }
272
- if (this.platform.debugMode) {
273
- this.errorLog(`Curtain: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},`
274
- + ` Error: ${JSON.stringify(e)}`);
275
- }
276
- }
277
- return switchbot;
278
- }
279
-
280
284
  private minStep(): number {
281
285
  if (this.device.curtain?.set_minStep) {
282
286
  this.set_minStep = this.device.curtain?.set_minStep;
@@ -304,16 +308,20 @@ export class Curtain {
304
308
  return this.set_maxLux;
305
309
  }
306
310
 
311
+ /**
312
+ * Parse the device status from the SwitchBot api
313
+ */
307
314
  async parseStatus() {
308
- if (this.device.ble) {
309
- await this.BLEparseStatus();
310
- } else {
315
+ if (this.SwitchToOpenAPI || !this.device.ble) {
311
316
  await this.openAPIparseStatus();
317
+ } else {
318
+ await this.BLEparseStatus();
312
319
  }
313
320
  }
314
321
 
315
322
  private async BLEparseStatus() {
316
323
  this.debugLog(`Curtain: ${this.accessory.displayName} BLE parseStatus`);
324
+ // CurrentPosition
317
325
  this.setMinMax();
318
326
  this.CurrentPosition = 100 - Number(this.position);
319
327
  this.debugLog(`Curtain: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
@@ -339,6 +347,7 @@ export class Curtain {
339
347
  // If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage.
340
348
  this.TargetPosition = this.CurrentPosition;
341
349
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
350
+ this.debugLog(`Curtain: ${this.accessory.displayName} Stopped`);
342
351
  }
343
352
  }
344
353
  this.debugLog(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},`
@@ -407,17 +416,20 @@ export class Curtain {
407
416
  }
408
417
 
409
418
  private async openAPIparseStatus() {
419
+ if (this.device.ble) {
420
+ this.SwitchToOpenAPI = false;
421
+ }
410
422
  if (this.platform.config.credentials?.openToken) {
411
423
  this.debugLog(`Curtain: ${this.accessory.displayName} OpenAPI parseStatus`);
412
424
  // CurrentPosition
413
425
  this.setMinMax();
414
- this.CurrentPosition = 100 - this.deviceStatus.body.slidePosition!;
426
+ this.CurrentPosition = 100 - Number(this.slidePosition);
415
427
  this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
416
428
  if (this.setNewTarget) {
417
- this.infoLog(`Checking ${this.accessory.displayName} Status ...`);
429
+ this.infoLog(`Curtain: ${this.accessory.displayName} Checking Status ...`);
418
430
  }
419
431
 
420
- if (this.deviceStatus.body.moving) {
432
+ if (this.setNewTarget && this.moving) {
421
433
  this.setMinMax();
422
434
  if (this.TargetPosition > this.CurrentPosition) {
423
435
  this.debugLog(`Curtain: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `);
@@ -435,6 +447,7 @@ export class Curtain {
435
447
  /*If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage.*/
436
448
  this.TargetPosition = this.CurrentPosition;
437
449
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
450
+ this.debugLog(`Curtain: ${this.accessory.displayName} Stopped`);
438
451
  }
439
452
  }
440
453
  this.debugLog(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},`
@@ -444,7 +457,7 @@ export class Curtain {
444
457
  this.set_minLux = this.minLux();
445
458
  this.set_maxLux = this.maxLux();
446
459
  // Brightness
447
- switch (this.deviceStatus.body.brightness) {
460
+ switch (this.brightness) {
448
461
  case 'dim':
449
462
  this.CurrentAmbientLightLevel = this.set_minLux;
450
463
  break;
@@ -467,8 +480,10 @@ export class Curtain {
467
480
 
468
481
  private async BLERefreshStatus() {
469
482
  this.debugLog(`Curtain: ${this.accessory.displayName} BLE refreshStatus`);
470
- // eslint-disable-next-line @typescript-eslint/no-var-requires
471
- const switchbot = await this.connectBLE();
483
+ const switchbot = await this.platform.connectBLE();
484
+ // Convert to BLE Address
485
+ this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
486
+ this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
472
487
  // Start to monitor advertisement packets
473
488
  if (switchbot !== false) {
474
489
  switchbot.startScan({
@@ -506,6 +521,7 @@ export class Curtain {
506
521
  this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
507
522
  if (this.platform.config.credentials?.openToken) {
508
523
  this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
524
+ this.SwitchToOpenAPI = true;
509
525
  await this.openAPIRefreshStatus();
510
526
  }
511
527
  }
@@ -521,6 +537,7 @@ export class Curtain {
521
537
  }
522
538
  if (this.platform.config.credentials?.openToken) {
523
539
  this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
540
+ this.SwitchToOpenAPI = true;
524
541
  await this.openAPIRefreshStatus();
525
542
  }
526
543
  this.apiError(e);
@@ -534,6 +551,7 @@ export class Curtain {
534
551
  this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
535
552
  if (this.platform.config.credentials?.openToken) {
536
553
  this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
554
+ this.SwitchToOpenAPI = true;
537
555
  await this.openAPIRefreshStatus();
538
556
  }
539
557
  }
@@ -544,6 +562,9 @@ export class Curtain {
544
562
  try {
545
563
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
546
564
  this.debugLog(`Curtain: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
565
+ this.slidePosition = this.deviceStatus.body.slidePosition;
566
+ this.moving = this.deviceStatus.body.moving;
567
+ this.brightness = this.deviceStatus.body.brightness;
547
568
  this.parseStatus();
548
569
  this.updateHomeKitCharacteristics();
549
570
  } catch (e: any) {
@@ -578,7 +599,10 @@ export class Curtain {
578
599
  private async BLEpushChanges() {
579
600
  if (this.TargetPosition !== this.CurrentPosition) {
580
601
  this.debugLog(`Curtain: ${this.accessory.displayName} BLE pushChanges`);
581
- const switchbot = await this.connectBLE();
602
+ const switchbot = await this.platform.connectBLE();
603
+ // Convert to BLE Address
604
+ this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase();
605
+ this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
582
606
  if (switchbot !== false) {
583
607
  switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac }).then((device_list) => {
584
608
  this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);