@switchbot/homebridge-switchbot 0.1.2-beta.0 → 0.1.2-beta.12

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 (90) hide show
  1. package/.github/ISSUE_TEMPLATE/bug-report.yml +1 -1
  2. package/.github/ISSUE_TEMPLATE/config.yml +6 -6
  3. package/.github/ISSUE_TEMPLATE/support-request.yml +1 -1
  4. package/.github/PULL_REQUEST_TEMPLATE/pull_requests.md +26 -6
  5. package/.github/dependabot.yml +2 -0
  6. package/.github/workflows/codeql-analysis.yml +71 -0
  7. package/.github/workflows/issue-validate.yml +3 -3
  8. package/.github/workflows/nodejs-beta.yml +4 -4
  9. package/.github/workflows/nodejs.yml +4 -4
  10. package/CHANGELOG.md +5 -1
  11. package/config.schema.json +18 -15
  12. package/dist/devices/bots.d.ts +1 -0
  13. package/dist/devices/bots.d.ts.map +1 -1
  14. package/dist/devices/bots.js +72 -24
  15. package/dist/devices/bots.js.map +1 -1
  16. package/dist/devices/bulb.d.ts +10 -1
  17. package/dist/devices/bulb.d.ts.map +1 -1
  18. package/dist/devices/bulb.js +13 -2
  19. package/dist/devices/bulb.js.map +1 -1
  20. package/dist/devices/contact.d.ts +13 -3
  21. package/dist/devices/contact.d.ts.map +1 -1
  22. package/dist/devices/contact.js +78 -3
  23. package/dist/devices/contact.js.map +1 -1
  24. package/dist/devices/curtains.d.ts +19 -8
  25. package/dist/devices/curtains.d.ts.map +1 -1
  26. package/dist/devices/curtains.js +98 -21
  27. package/dist/devices/curtains.js.map +1 -1
  28. package/dist/devices/humidifiers.d.ts.map +1 -1
  29. package/dist/devices/humidifiers.js +4 -4
  30. package/dist/devices/humidifiers.js.map +1 -1
  31. package/dist/devices/indoorcam.d.ts +42 -0
  32. package/dist/devices/indoorcam.d.ts.map +1 -0
  33. package/dist/devices/indoorcam.js +235 -0
  34. package/dist/devices/indoorcam.js.map +1 -0
  35. package/dist/devices/meters.d.ts +13 -4
  36. package/dist/devices/meters.d.ts.map +1 -1
  37. package/dist/devices/meters.js +36 -17
  38. package/dist/devices/meters.js.map +1 -1
  39. package/dist/devices/motion.d.ts +11 -3
  40. package/dist/devices/motion.d.ts.map +1 -1
  41. package/dist/devices/motion.js +70 -8
  42. package/dist/devices/motion.js.map +1 -1
  43. package/dist/devices/plugs.d.ts +11 -1
  44. package/dist/devices/plugs.d.ts.map +1 -1
  45. package/dist/devices/plugs.js +67 -1
  46. package/dist/devices/plugs.js.map +1 -1
  47. package/dist/irdevices/airconditioners.js +2 -2
  48. package/dist/irdevices/airconditioners.js.map +1 -1
  49. package/dist/irdevices/airpurifiers.js +2 -2
  50. package/dist/irdevices/airpurifiers.js.map +1 -1
  51. package/dist/irdevices/cameras.js +1 -1
  52. package/dist/irdevices/cameras.js.map +1 -1
  53. package/dist/irdevices/fans.js +1 -1
  54. package/dist/irdevices/fans.js.map +1 -1
  55. package/dist/irdevices/lights.js +3 -3
  56. package/dist/irdevices/lights.js.map +1 -1
  57. package/dist/irdevices/others.js +1 -1
  58. package/dist/irdevices/others.js.map +1 -1
  59. package/dist/irdevices/tvs.d.ts.map +1 -1
  60. package/dist/irdevices/tvs.js +2 -3
  61. package/dist/irdevices/tvs.js.map +1 -1
  62. package/dist/irdevices/vacuumcleaners.js +1 -1
  63. package/dist/irdevices/vacuumcleaners.js.map +1 -1
  64. package/dist/platform.d.ts +1 -0
  65. package/dist/platform.d.ts.map +1 -1
  66. package/dist/platform.js +104 -93
  67. package/dist/platform.js.map +1 -1
  68. package/dist/settings.d.ts +1 -2
  69. package/dist/settings.d.ts.map +1 -1
  70. package/package.json +9 -9
  71. package/src/devices/bots.ts +87 -37
  72. package/src/devices/bulb.ts +33 -3
  73. package/src/devices/contact.ts +102 -10
  74. package/src/devices/curtains.ts +137 -37
  75. package/src/devices/humidifiers.ts +9 -4
  76. package/src/devices/indoorcam.ts +304 -0
  77. package/src/devices/meters.ts +58 -22
  78. package/src/devices/motion.ts +93 -13
  79. package/src/devices/plugs.ts +89 -4
  80. package/src/irdevices/airconditioners.ts +2 -2
  81. package/src/irdevices/airpurifiers.ts +2 -2
  82. package/src/irdevices/cameras.ts +1 -1
  83. package/src/irdevices/fans.ts +1 -1
  84. package/src/irdevices/lights.ts +3 -3
  85. package/src/irdevices/others.ts +1 -1
  86. package/src/irdevices/tvs.ts +2 -4
  87. package/src/irdevices/vacuumcleaners.ts +1 -1
  88. package/src/platform.ts +120 -95
  89. package/src/settings.ts +1 -2
  90. package/.github/ISSUE_TEMPLATE.md +0 -48
@@ -0,0 +1,304 @@
1
+ import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
2
+ import { SwitchBotPlatform } from '../platform';
3
+ import { interval, Subject } from 'rxjs';
4
+ import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
+ import { DeviceURL, device, deviceStatusResponse } from '../settings';
6
+ import { AxiosResponse } from 'axios';
7
+
8
+ export class IndoorCam {
9
+ // Services
10
+ private service: Service;
11
+
12
+ // Characteristic Values
13
+ On!: CharacteristicValue;
14
+ OutletInUse!: CharacteristicValue;
15
+
16
+ // Others
17
+ deviceStatus!: deviceStatusResponse;
18
+ switchbot!: {
19
+ discover: (
20
+ arg0:
21
+ {
22
+ duration: any;
23
+ model: string;
24
+ quick: boolean;
25
+ id: MacAddress;
26
+ }
27
+ ) => Promise<any>;
28
+ wait: (
29
+ arg0: number
30
+ ) => any;
31
+ };
32
+
33
+ // Updates
34
+ cameraUpdateInProgress!: boolean;
35
+ doCameraUpdate;
36
+
37
+ constructor(
38
+ private readonly platform: SwitchBotPlatform,
39
+ private accessory: PlatformAccessory,
40
+ public device: device,
41
+ ) {
42
+ // default placeholders
43
+ this.On = false;
44
+ this.OutletInUse = true;
45
+
46
+ // BLE Connections
47
+ if (this.platform.config.options?.ble?.includes(this.device.deviceId!)) {
48
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
49
+ const SwitchBot = require('node-switchbot');
50
+ this.switchbot = new SwitchBot();
51
+ const colon = device.deviceId!.match(/.{1,2}/g);
52
+ const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
53
+ this.device.bleMac = bleMac.toLowerCase();
54
+ if (this.platform.config.options.debug === 'device') {
55
+ this.platform.debug(this.device.bleMac.toLowerCase());
56
+ }
57
+ }
58
+
59
+ // this is subject we use to track when we need to POST changes to the SwitchBot API
60
+ this.doCameraUpdate = new Subject();
61
+ this.cameraUpdateInProgress = false;
62
+
63
+ // Retrieve initial values and updateHomekit
64
+ this.refreshStatus();
65
+
66
+ // set accessory information
67
+ accessory
68
+ .getService(this.platform.Service.AccessoryInformation)!
69
+ .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
70
+ .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-CAMERA-')
71
+ .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
72
+
73
+ // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
74
+ // you can create multiple services for each accessory
75
+ (this.service =
76
+ accessory.getService(this.platform.Service.Outlet) ||
77
+ accessory.addService(this.platform.Service.Outlet)), `${device.deviceName} ${device.deviceType}`;
78
+
79
+ // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
80
+ // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
81
+ // accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE');
82
+
83
+ // set the service name, this is what is displayed as the default name on the Home app
84
+ // in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method.
85
+ this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
86
+
87
+ // each service must implement at-minimum the "required characteristics" for the given service type
88
+ // see https://developers.homebridge.io/#/service/WindowCovering
89
+
90
+ // create handlers for required characteristics
91
+ this.service.getCharacteristic(this.platform.Characteristic.On).onSet(this.OnSet.bind(this));
92
+
93
+ this.service.setCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse || true);
94
+
95
+ // Update Homekit
96
+ this.updateHomeKitCharacteristics();
97
+
98
+ // Start an update interval
99
+ interval(this.platform.config.options!.refreshRate! * 1000)
100
+ .pipe(skipWhile(() => this.cameraUpdateInProgress))
101
+ .subscribe(() => {
102
+ this.refreshStatus();
103
+ });
104
+
105
+ // Watch for Camera change events
106
+ // We put in a debounce of 100ms so we don't make duplicate calls
107
+ this.doCameraUpdate
108
+ .pipe(
109
+ tap(() => {
110
+ this.cameraUpdateInProgress = true;
111
+ }),
112
+ debounceTime(this.platform.config.options!.pushRate! * 1000),
113
+ )
114
+ .subscribe(async () => {
115
+ try {
116
+ await this.pushChanges();
117
+ } catch (e: any) {
118
+ this.platform.log.error(JSON.stringify(e.message));
119
+ this.platform.debug(`Camera ${accessory.displayName} - ${JSON.stringify(e)}`);
120
+ this.apiError(e);
121
+ }
122
+ this.cameraUpdateInProgress = false;
123
+ });
124
+ }
125
+
126
+ parseStatus() {
127
+ switch (this.deviceStatus.body.power) {
128
+ case 'on':
129
+ this.On = true;
130
+ break;
131
+ default:
132
+ this.On = false;
133
+ }
134
+ this.platform.debug(`Camera ${this.accessory.displayName} On: ${this.On}`);
135
+ }
136
+
137
+ async refreshStatus() {
138
+ if (this.platform.config.options?.ble?.includes(this.device.deviceId!)) {
139
+ this.platform.log.warn('BLE DEVICE-REFRESH');
140
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
141
+ const Switchbot = require('node-switchbot');
142
+ const switchbot = new Switchbot();
143
+ const colon = this.device.deviceId!.match(/.{1,2}/g);
144
+ const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
145
+ this.device.bleMac = bleMac.toLowerCase();
146
+ if (this.platform.config.options.debug === 'device') {
147
+ this.platform.debug(this.device.bleMac!);
148
+ }
149
+ switchbot.onadvertisement = (ad: any) => {
150
+ this.platform.log.info(JSON.stringify(ad, null, ' '));
151
+ this.platform.log.warn('ad:', JSON.stringify(ad));
152
+ };
153
+ switchbot
154
+ .startScan({
155
+ id: this.device.bleMac,
156
+ })
157
+ .then(() => {
158
+ return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
159
+ })
160
+ .then(() => {
161
+ switchbot.stopScan();
162
+ })
163
+ .catch(async (error: any) => {
164
+ this.platform.log.error(error);
165
+ await this.openAPIRefreshStatus();
166
+ });
167
+ setInterval(() => {
168
+ this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
169
+ switchbot
170
+ .startScan({
171
+ mode: 'T',
172
+ id: bleMac,
173
+ })
174
+ .then(() => {
175
+ return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
176
+ })
177
+ .then(() => {
178
+ switchbot.stopScan();
179
+ this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
180
+ })
181
+ .catch(async (error: any) => {
182
+ this.platform.log.error(error);
183
+ await this.openAPIRefreshStatus();
184
+ });
185
+ }, this.platform.config.options!.refreshRate! * 60000);
186
+ } else {
187
+ await this.openAPIRefreshStatus();
188
+ }
189
+ }
190
+
191
+ private async openAPIRefreshStatus() {
192
+ try {
193
+ this.platform.debug('Camera - Reading', `${DeviceURL}/${this.device.deviceId}/status`);
194
+ const deviceStatus: deviceStatusResponse = (
195
+ await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)
196
+ ).data;
197
+ if (deviceStatus.message === 'success') {
198
+ this.deviceStatus = deviceStatus;
199
+ this.platform.log.warn(`Camera ${this.accessory.displayName} refreshStatus - ${JSON.stringify(this.deviceStatus)}`);
200
+ this.parseStatus();
201
+ this.updateHomeKitCharacteristics();
202
+ }
203
+ } catch (e: any) {
204
+ this.platform.log.error(
205
+ `Camera - Failed to refresh status of ${this.device.deviceName}`,
206
+ JSON.stringify(e.message),
207
+ this.platform.debug(`Camera ${this.accessory.displayName} - ${JSON.stringify(e)}`));
208
+ this.apiError(e);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Pushes the requested changes to the SwitchBot API
214
+ * deviceType commandType Command command parameter Description
215
+ * Camera - "command" "turnOff" "default" = set to OFF state
216
+ * Camera - "command" "turnOn" "default" = set to ON state
217
+ */
218
+ async pushChanges() {
219
+ const payload = {
220
+ commandType: 'command',
221
+ parameter: 'default',
222
+ } as any;
223
+
224
+ if (this.On) {
225
+ payload.command = 'turnOn';
226
+ } else {
227
+ payload.command = 'turnOff';
228
+ }
229
+
230
+ this.platform.log.info(
231
+ 'Sending request for',
232
+ this.accessory.displayName,
233
+ 'to SwitchBot API. command:',
234
+ payload.command,
235
+ 'parameter:',
236
+ payload.parameter,
237
+ 'commandType:',
238
+ payload.commandType,
239
+ );
240
+ this.platform.debug(`Camera ${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`);
241
+
242
+ // Make the API request
243
+ const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
244
+ this.platform.debug(`Camera ${this.accessory.displayName} Changes pushed - ${push.data}`);
245
+ this.statusCode(push);
246
+ }
247
+
248
+ updateHomeKitCharacteristics() {
249
+ if (this.On !== undefined) {
250
+ this.service.updateCharacteristic(this.platform.Characteristic.On, this.On);
251
+ }
252
+ if (this.OutletInUse !== undefined) {
253
+ this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse);
254
+ }
255
+ }
256
+
257
+ public apiError(e: any) {
258
+ this.service.updateCharacteristic(this.platform.Characteristic.On, e);
259
+ this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e);
260
+ }
261
+
262
+
263
+ private statusCode(push: AxiosResponse<any>) {
264
+ switch (push.data.statusCode) {
265
+ case 151:
266
+ this.platform.log.error('Command not supported by this device type.');
267
+ break;
268
+ case 152:
269
+ this.platform.log.error('Device not found.');
270
+ break;
271
+ case 160:
272
+ this.platform.log.error('Command is not supported.');
273
+ break;
274
+ case 161:
275
+ this.platform.log.error('Device is offline.');
276
+ break;
277
+ case 171:
278
+ this.platform.log.error('Hub Device is offline.');
279
+ break;
280
+ case 190:
281
+ this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.');
282
+ break;
283
+ case 100:
284
+ if (this.platform.config.options?.debug) {
285
+ this.platform.log.info('Command successfully sent.');
286
+ }
287
+ break;
288
+ default:
289
+ this.platform.debug('Unknown statusCode.');
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Handle requests to set the value of the "On" characteristic
295
+ */
296
+ OnSet(value: CharacteristicValue) {
297
+ this.platform.debug(`Camera ${this.accessory.displayName} - Set On: ${value}`);
298
+
299
+ this.On = value;
300
+ this.doCameraUpdate.next();
301
+ }
302
+
303
+
304
+ }
@@ -1,4 +1,4 @@
1
- import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
1
+ import { Service, PlatformAccessory, Units, CharacteristicValue, MacAddress } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { skipWhile } from 'rxjs/operators';
@@ -10,10 +10,12 @@ import { DeviceURL, device, deviceStatusResponse } from '../settings';
10
10
  * Each accessory may expose multiple services of different service types.
11
11
  */
12
12
  export class Meter {
13
+ // Services
13
14
  private service: Service;
14
15
  temperatureservice?: Service;
15
16
  humidityservice?: Service;
16
17
 
18
+ // Characteristic Values
17
19
  CurrentRelativeHumidity!: CharacteristicValue;
18
20
  CurrentTemperature!: CharacteristicValue;
19
21
  BatteryLevel!: CharacteristicValue;
@@ -21,13 +23,29 @@ export class Meter {
21
23
  StatusLowBattery!: CharacteristicValue;
22
24
  Active!: CharacteristicValue;
23
25
  WaterLevel!: CharacteristicValue;
26
+
27
+ // Others
24
28
  deviceStatus!: deviceStatusResponse;
25
- switchbot;
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
+ };
26
45
 
46
+ // Updates
27
47
  meterUpdateInProgress!: boolean;
28
48
  doMeterUpdate;
29
- BLEtemperature: any;
30
- BLEHumidity: any;
31
49
 
32
50
  constructor(
33
51
  private readonly platform: SwitchBotPlatform,
@@ -40,6 +58,17 @@ export class Meter {
40
58
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
41
59
  this.CurrentRelativeHumidity = 0;
42
60
  this.CurrentTemperature = 0;
61
+ if (this.platform.config.options?.ble?.includes(this.device.deviceId!)) {
62
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
63
+ const SwitchBot = require('node-switchbot');
64
+ this.switchbot = new SwitchBot();
65
+ const colon = device.deviceId!.match(/.{1,2}/g);
66
+ const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
67
+ this.device.bleMac = bleMac.toLowerCase();
68
+ if (this.platform.config.options.debug === 'device') {
69
+ this.platform.debug(this.device.bleMac.toLowerCase());
70
+ }
71
+ }
43
72
 
44
73
  // this is subject we use to track when we need to POST changes to the SwitchBot API
45
74
  this.doMeterUpdate = new Subject();
@@ -59,7 +88,7 @@ export class Meter {
59
88
  // you can create multiple services for each accessory
60
89
  (this.service =
61
90
  accessory.getService(this.platform.Service.Battery) ||
62
- accessory.addService(this.platform.Service.Battery)), '%s %s', device.deviceName, device.deviceType;
91
+ accessory.addService(this.platform.Service.Battery)), `${device.deviceName} ${device.deviceType}`;
63
92
 
64
93
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
65
94
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
@@ -77,18 +106,18 @@ export class Meter {
77
106
 
78
107
  // Temperature Sensor Service
79
108
  if (this.platform.config.options?.meter?.hide_temperature) {
80
- if (this.platform.config.options.debug) {
109
+ if (this.platform.config.options.debug === 'device') {
81
110
  this.platform.log.error('Removing service');
82
111
  }
83
112
  this.temperatureservice = this.accessory.getService(this.platform.Service.TemperatureSensor);
84
113
  accessory.removeService(this.temperatureservice!);
85
114
  } else if (!this.temperatureservice) {
86
- if (this.platform.config.options?.debug) {
115
+ if (this.platform.config.options?.debug === 'device') {
87
116
  this.platform.log.warn('Adding service');
88
117
  }
89
118
  (this.temperatureservice =
90
119
  this.accessory.getService(this.platform.Service.TemperatureSensor) ||
91
- this.accessory.addService(this.platform.Service.TemperatureSensor)), '%s %s TemperatureSensor', device.deviceName, device.deviceType;
120
+ this.accessory.addService(this.platform.Service.TemperatureSensor)), `${device.deviceName} ${device.deviceType} TemperatureSensor`;
92
121
 
93
122
  this.temperatureservice
94
123
  .getCharacteristic(this.platform.Characteristic.CurrentTemperature)
@@ -102,16 +131,16 @@ export class Meter {
102
131
  .onGet(() => {
103
132
  return this.CurrentTemperature;
104
133
  });
105
- this.platform.log.info(this.device.deviceName + ' current temperature: ' + this.CurrentTemperature + '\u2103');
134
+ //this.platform.log.info(this.device.deviceName + ' current temperature: ' + this.CurrentTemperature + '\u2103');
106
135
  } else {
107
- if (this.platform.config.options?.debug) {
136
+ if (this.platform.config.options?.debug === 'device') {
108
137
  this.platform.log.warn('TemperatureSensor not added.');
109
138
  }
110
139
  }
111
140
 
112
141
  // Humidity Sensor Service
113
142
  if (this.platform.config.options?.meter?.hide_humidity) {
114
- if (this.platform.config.options.debug) {
143
+ if (this.platform.config.options.debug === 'device') {
115
144
  this.platform.log.error('Removing service');
116
145
  }
117
146
  this.humidityservice = this.accessory.getService(this.platform.Service.HumiditySensor);
@@ -119,7 +148,7 @@ export class Meter {
119
148
  } else if (!this.humidityservice) {
120
149
  (this.humidityservice =
121
150
  this.accessory.getService(this.platform.Service.HumiditySensor) ||
122
- this.accessory.addService(this.platform.Service.HumiditySensor)), '%s %s HumiditySensor', device.deviceName, device.deviceType;
151
+ this.accessory.addService(this.platform.Service.HumiditySensor)), `${device.deviceName} ${device.deviceType} HumiditySensor`;
123
152
 
124
153
  this.humidityservice
125
154
  .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
@@ -129,7 +158,7 @@ export class Meter {
129
158
  .onGet(() => {
130
159
  return this.CurrentRelativeHumidity;
131
160
  });
132
- this.platform.log.info(this.device.deviceName + ' current humidity: ' + this.CurrentRelativeHumidity + '%');
161
+ //this.platform.log.info(this.device.deviceName + ' current humidity: ' + this.CurrentRelativeHumidity + '%');
133
162
  } else {
134
163
  if (this.platform.config.options?.debug) {
135
164
  this.platform.log.warn('HumiditySensor not added.');
@@ -194,14 +223,15 @@ export class Meter {
194
223
  */
195
224
  async refreshStatus() {
196
225
  if (this.platform.config.options?.ble?.includes(this.device.deviceId!)) {
226
+ this.platform.debug('Meter BLE Device RefreshStatus');
197
227
  // eslint-disable-next-line @typescript-eslint/no-var-requires
198
228
  const Switchbot = require('node-switchbot');
199
229
  const switchbot = new Switchbot();
200
230
  const colon = this.device.deviceId!.match(/.{1,2}/g);
201
231
  const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
202
232
  this.device.bleMac = bleMac.toLowerCase();
203
- if (this.platform.config.options.debug) {
204
- this.platform.log.warn(this.device.bleMac!);
233
+ if (this.platform.config.options.debug === 'device') {
234
+ this.platform.debug(this.device.bleMac!);
205
235
  }
206
236
  switchbot.onadvertisement = (ad: any) => {
207
237
  this.platform.log.info(JSON.stringify(ad, null, ' '));
@@ -221,14 +251,15 @@ export class Meter {
221
251
  .then(() => {
222
252
  switchbot.stopScan();
223
253
  })
224
- .catch((error: any) => {
254
+ .catch(async (error: any) => {
225
255
  this.platform.log.error(error);
256
+ await this.openAPIRefreshStatus();
226
257
  });
227
258
  setInterval(() => {
228
- // log.info("Start scan " + name + "(" + bleMac + ")");
259
+ this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
229
260
  switchbot
230
261
  .startScan({
231
- // mode: 'T',
262
+ mode: 'T',
232
263
  id: bleMac,
233
264
  })
234
265
  .then(() => {
@@ -236,20 +267,26 @@ export class Meter {
236
267
  })
237
268
  .then(() => {
238
269
  switchbot.stopScan();
239
- this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + bleMac + ')');
270
+ this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
240
271
  })
241
- .catch((error: any) => {
272
+ .catch(async (error: any) => {
242
273
  this.platform.log.error(error);
274
+ await this.openAPIRefreshStatus();
243
275
  });
244
276
  }, this.platform.config.options!.refreshRate! * 60000);
277
+ } else {
278
+ await this.openAPIRefreshStatus();
245
279
  }
280
+ }
281
+
282
+ private async openAPIRefreshStatus() {
246
283
  try {
247
284
  const deviceStatus: deviceStatusResponse = (
248
285
  await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)
249
286
  ).data;
250
287
  if (deviceStatus.message === 'success') {
251
288
  this.deviceStatus = deviceStatus;
252
- this.platform.debug(`Meter ${this.accessory.displayName} refreshStatus - ${JSON.stringify(this.deviceStatus)}`);
289
+ this.platform.debug(`Meter ${this.accessory.displayName} openAPIRefreshStatus - ${JSON.stringify(this.deviceStatus)}`);
253
290
  this.parseStatus();
254
291
  this.updateHomeKitCharacteristics();
255
292
  }
@@ -262,7 +299,6 @@ export class Meter {
262
299
  );
263
300
  this.apiError(e);
264
301
  }
265
-
266
302
  }
267
303
 
268
304
  /**
@@ -1,4 +1,4 @@
1
- import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
1
+ import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { skipWhile } from 'rxjs/operators';
@@ -10,13 +10,30 @@ import { DeviceURL, device, deviceStatusResponse } from '../settings';
10
10
  * Each accessory may expose multiple services of different service types.
11
11
  */
12
12
  export class Motion {
13
+ // Services
13
14
  private service: Service;
14
- temperatureservice?: Service;
15
- humidityservice?: Service;
16
15
 
16
+ // Characteristic Values
17
17
  MotionDetected!: CharacteristicValue;
18
- deviceStatus!: deviceStatusResponse;
19
18
 
19
+ // Others
20
+ deviceStatus!: deviceStatusResponse;
21
+ switchbot!: {
22
+ discover: (
23
+ arg0:
24
+ {
25
+ duration: any;
26
+ model: string;
27
+ quick: boolean;
28
+ id: MacAddress;
29
+ }
30
+ ) => Promise<any>;
31
+ wait: (
32
+ arg0: number
33
+ ) => any;
34
+ };
35
+
36
+ // Updates
20
37
  motionUbpdateInProgress!: boolean;
21
38
  doMotionUpdate;
22
39
 
@@ -28,6 +45,19 @@ export class Motion {
28
45
  // default placeholders
29
46
  this.MotionDetected = false;
30
47
 
48
+ // BLE Connection
49
+ if (this.platform.config.options?.ble?.includes(this.device.deviceId!)) {
50
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
51
+ const SwitchBot = require('node-switchbot');
52
+ this.switchbot = new SwitchBot();
53
+ const colon = device.deviceId!.match(/.{1,2}/g);
54
+ const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
55
+ this.device.bleMac = bleMac.toLowerCase();
56
+ if (this.platform.config.options.debug === 'device') {
57
+ this.platform.debug(this.device.bleMac.toLowerCase());
58
+ }
59
+ }
60
+
31
61
  // this is subject we use to track when we need to POST changes to the SwitchBot API
32
62
  this.doMotionUpdate = new Subject();
33
63
  this.motionUbpdateInProgress = false;
@@ -39,14 +69,14 @@ export class Motion {
39
69
  accessory
40
70
  .getService(this.platform.Service.AccessoryInformation)!
41
71
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
42
- .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-MOTION-')
72
+ .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-MOTION-W1101500')
43
73
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId);
44
74
 
45
75
  // get the Battery service if it exists, otherwise create a new Motion service
46
76
  // you can create multiple services for each accessory
47
77
  (this.service =
48
78
  accessory.getService(this.platform.Service.MotionSensor) ||
49
- accessory.addService(this.platform.Service.MotionSensor)), '%s %s', device.deviceName, device.deviceType;
79
+ accessory.addService(this.platform.Service.MotionSensor)), `${device.deviceName} ${device.deviceType}`;
50
80
 
51
81
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
52
82
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
@@ -78,17 +108,68 @@ export class Motion {
78
108
  */
79
109
  parseStatus() {
80
110
  // Set Room Sensor State
81
- if (this.deviceStatus.body.moveDetected) {
82
- this.MotionDetected = true;
83
- } else {
84
- this.MotionDetected = false;
85
- }
111
+ this.MotionDetected = Boolean(this.deviceStatus.body.moveDetected);
112
+ this.platform.debug(`${this.accessory.displayName}, MotionDetected: ${this.MotionDetected}`);
86
113
  }
87
114
 
88
115
  /**
89
116
  * Asks the SwitchBot API for the latest device information
90
117
  */
91
118
  async refreshStatus() {
119
+ if (this.platform.config.options?.ble?.includes(this.device.deviceId!)) {
120
+ this.platform.debug('Motion BLE Device RefreshStatus');
121
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
122
+ const Switchbot = require('node-switchbot');
123
+ const switchbot = new Switchbot();
124
+ const colon = this.device.deviceId!.match(/.{1,2}/g);
125
+ const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
126
+ this.device.bleMac = bleMac.toLowerCase();
127
+ if (this.platform.config.options.debug === 'device') {
128
+ this.platform.debug(this.device.bleMac!);
129
+ }
130
+ switchbot.onadvertisement = (ad: any) => {
131
+ this.platform.log.info(JSON.stringify(ad, null, ' '));
132
+ this.platform.log.warn('ad:', JSON.stringify(ad));
133
+ };
134
+ switchbot
135
+ .startScan({
136
+ id: this.device.bleMac,
137
+ })
138
+ .then(() => {
139
+ return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
140
+ })
141
+ .then(() => {
142
+ switchbot.stopScan();
143
+ })
144
+ .catch(async (error: any) => {
145
+ this.platform.log.error(error);
146
+ await this.openAPIRefreshStatus();
147
+ });
148
+ setInterval(() => {
149
+ this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
150
+ switchbot
151
+ .startScan({
152
+ mode: 'S',
153
+ id: bleMac,
154
+ })
155
+ .then(() => {
156
+ return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
157
+ })
158
+ .then(() => {
159
+ switchbot.stopScan();
160
+ this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
161
+ })
162
+ .catch(async (error: any) => {
163
+ this.platform.log.error(error);
164
+ await this.openAPIRefreshStatus();
165
+ });
166
+ }, this.platform.config.options!.refreshRate! * 60000);
167
+ } else {
168
+ await this.openAPIRefreshStatus();
169
+ }
170
+ }
171
+
172
+ private async openAPIRefreshStatus() {
92
173
  try {
93
174
  const deviceStatus: deviceStatusResponse = (
94
175
  await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)
@@ -105,8 +186,7 @@ export class Motion {
105
186
  'Motion - Failed to update status of',
106
187
  this.device.deviceName,
107
188
  JSON.stringify(e.message),
108
- this.platform.debug(`Motion ${this.accessory.displayName} - ${JSON.stringify(e)}`),
109
- );
189
+ this.platform.debug(`Motion ${this.accessory.displayName} - ${JSON.stringify(e)}`));
110
190
  this.apiError(e);
111
191
  }
112
192
  }