@switchbot/homebridge-switchbot 2.0.0-beta.2 → 2.0.0-beta.21

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 (113) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +1 -2
  3. package/config.schema.json +75 -20
  4. package/dist/device/bot.d.ts +32 -28
  5. package/dist/device/bot.d.ts.map +1 -1
  6. package/dist/device/bot.js +646 -648
  7. package/dist/device/bot.js.map +1 -1
  8. package/dist/device/colorbulb.d.ts +34 -19
  9. package/dist/device/colorbulb.d.ts.map +1 -1
  10. package/dist/device/colorbulb.js +429 -304
  11. package/dist/device/colorbulb.js.map +1 -1
  12. package/dist/device/contact.d.ts +16 -7
  13. package/dist/device/contact.d.ts.map +1 -1
  14. package/dist/device/contact.js +288 -230
  15. package/dist/device/contact.js.map +1 -1
  16. package/dist/device/curtain.d.ts +21 -16
  17. package/dist/device/curtain.d.ts.map +1 -1
  18. package/dist/device/curtain.js +519 -489
  19. package/dist/device/curtain.js.map +1 -1
  20. package/dist/device/humidifier.d.ts +22 -18
  21. package/dist/device/humidifier.d.ts.map +1 -1
  22. package/dist/device/humidifier.js +415 -360
  23. package/dist/device/humidifier.js.map +1 -1
  24. package/dist/device/lock.d.ts +22 -8
  25. package/dist/device/lock.d.ts.map +1 -1
  26. package/dist/device/lock.js +253 -147
  27. package/dist/device/lock.js.map +1 -1
  28. package/dist/device/meter.d.ts +21 -16
  29. package/dist/device/meter.d.ts.map +1 -1
  30. package/dist/device/meter.js +325 -255
  31. package/dist/device/meter.js.map +1 -1
  32. package/dist/device/meterplus.d.ts +23 -12
  33. package/dist/device/meterplus.d.ts.map +1 -1
  34. package/dist/device/meterplus.js +346 -208
  35. package/dist/device/meterplus.js.map +1 -1
  36. package/dist/device/motion.d.ts +17 -8
  37. package/dist/device/motion.d.ts.map +1 -1
  38. package/dist/device/motion.js +261 -209
  39. package/dist/device/motion.js.map +1 -1
  40. package/dist/device/plug.d.ts +24 -16
  41. package/dist/device/plug.d.ts.map +1 -1
  42. package/dist/device/plug.js +267 -242
  43. package/dist/device/plug.js.map +1 -1
  44. package/dist/device/striplight.d.ts +51 -16
  45. package/dist/device/striplight.d.ts.map +1 -1
  46. package/dist/device/striplight.js +527 -185
  47. package/dist/device/striplight.js.map +1 -1
  48. package/dist/irdevice/airconditioner.d.ts +7 -12
  49. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  50. package/dist/irdevice/airconditioner.js +261 -230
  51. package/dist/irdevice/airconditioner.js.map +1 -1
  52. package/dist/irdevice/airpurifier.d.ts +3 -3
  53. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  54. package/dist/irdevice/airpurifier.js +137 -104
  55. package/dist/irdevice/airpurifier.js.map +1 -1
  56. package/dist/irdevice/camera.d.ts +9 -8
  57. package/dist/irdevice/camera.d.ts.map +1 -1
  58. package/dist/irdevice/camera.js +116 -86
  59. package/dist/irdevice/camera.js.map +1 -1
  60. package/dist/irdevice/fan.d.ts +8 -7
  61. package/dist/irdevice/fan.d.ts.map +1 -1
  62. package/dist/irdevice/fan.js +143 -109
  63. package/dist/irdevice/fan.js.map +1 -1
  64. package/dist/irdevice/light.d.ts +9 -8
  65. package/dist/irdevice/light.d.ts.map +1 -1
  66. package/dist/irdevice/light.js +120 -103
  67. package/dist/irdevice/light.js.map +1 -1
  68. package/dist/irdevice/other.d.ts +9 -8
  69. package/dist/irdevice/other.d.ts.map +1 -1
  70. package/dist/irdevice/other.js +128 -98
  71. package/dist/irdevice/other.js.map +1 -1
  72. package/dist/irdevice/tv.d.ts +10 -9
  73. package/dist/irdevice/tv.d.ts.map +1 -1
  74. package/dist/irdevice/tv.js +126 -93
  75. package/dist/irdevice/tv.js.map +1 -1
  76. package/dist/irdevice/vacuumcleaner.d.ts +7 -10
  77. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  78. package/dist/irdevice/vacuumcleaner.js +115 -89
  79. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  80. package/dist/irdevice/waterheater.d.ts +6 -9
  81. package/dist/irdevice/waterheater.d.ts.map +1 -1
  82. package/dist/irdevice/waterheater.js +115 -89
  83. package/dist/irdevice/waterheater.js.map +1 -1
  84. package/dist/platform.d.ts +5 -5
  85. package/dist/platform.d.ts.map +1 -1
  86. package/dist/platform.js +227 -199
  87. package/dist/platform.js.map +1 -1
  88. package/dist/settings.d.ts +15 -11
  89. package/dist/settings.d.ts.map +1 -1
  90. package/dist/settings.js.map +1 -1
  91. package/package.json +12 -10
  92. package/src/device/bot.ts +641 -657
  93. package/src/device/colorbulb.ts +440 -339
  94. package/src/device/contact.ts +289 -233
  95. package/src/device/curtain.ts +523 -503
  96. package/src/device/humidifier.ts +421 -396
  97. package/src/device/lock.ts +257 -147
  98. package/src/device/meter.ts +335 -268
  99. package/src/device/meterplus.ts +354 -213
  100. package/src/device/motion.ts +264 -213
  101. package/src/device/plug.ts +270 -251
  102. package/src/device/striplight.ts +552 -207
  103. package/src/irdevice/airconditioner.ts +263 -241
  104. package/src/irdevice/airpurifier.ts +132 -103
  105. package/src/irdevice/camera.ts +112 -85
  106. package/src/irdevice/fan.ts +138 -106
  107. package/src/irdevice/light.ts +116 -103
  108. package/src/irdevice/other.ts +124 -97
  109. package/src/irdevice/tv.ts +123 -93
  110. package/src/irdevice/vacuumcleaner.ts +112 -89
  111. package/src/irdevice/waterheater.ts +111 -89
  112. package/src/platform.ts +216 -214
  113. package/src/settings.ts +15 -11
@@ -3,9 +3,11 @@ import crypto from 'crypto';
3
3
  import { Context } from 'vm';
4
4
  import { interval, Subject } from 'rxjs';
5
5
  import { skipWhile } from 'rxjs/operators';
6
+ import superStringify from 'super-stringify';
6
7
  import { SwitchBotPlatform } from '../platform';
7
8
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
8
9
  import { device, devicesConfig, serviceData, switchbot, deviceStatus, ad, HostDomain, DevicePath } from '../settings';
10
+ import { IncomingMessage } from 'http';
9
11
 
10
12
  /**
11
13
  * Platform Accessory
@@ -32,9 +34,9 @@ export class Motion {
32
34
  // BLE Others
33
35
  connected?: boolean;
34
36
  switchbot!: switchbot;
35
- SwitchToOpenAPI?: boolean;
36
37
  serviceData!: serviceData;
37
38
  address!: ad['address'];
39
+ SwitchToOpenAPI?: boolean;
38
40
  battery!: serviceData['battery'];
39
41
  movement!: serviceData['movement'];
40
42
  lightLevel!: serviceData['lightLevel'];
@@ -50,13 +52,17 @@ export class Motion {
50
52
  motionUbpdateInProgress!: boolean;
51
53
  doMotionUpdate!: Subject<void>;
52
54
 
55
+ // Connection
56
+ private readonly BLE = (this.device.connectionType === 'BLE' || this.device.connectionType === 'Both');
57
+ private readonly OpenAPI = (this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'Both');
58
+
53
59
  constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
54
60
  // default placeholders
55
61
  this.logs(device);
56
62
  this.scan(device);
57
63
  this.refreshRate(device);
58
64
  this.config(device);
59
- this.MotionDetected = false;
65
+ this.context();
60
66
 
61
67
  // this is subject we use to track when we need to POST changes to the SwitchBot API
62
68
  this.doMotionUpdate = new Subject();
@@ -93,33 +99,33 @@ export class Motion {
93
99
 
94
100
  // Light Sensor Service
95
101
  if (device.motion?.hide_lightsensor) {
96
- this.debugLog(`Motion Sensor: ${accessory.displayName} Removing Light Sensor Service`);
102
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
97
103
  this.lightSensorService = this.accessory.getService(this.platform.Service.LightSensor);
98
104
  accessory.removeService(this.lightSensorService!);
99
105
  } else if (!this.lightSensorService) {
100
- this.debugLog(`Motion Sensor: ${accessory.displayName} Add Light Sensor Service`);
106
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`);
101
107
  (this.lightSensorService =
102
108
  this.accessory.getService(this.platform.Service.LightSensor) || this.accessory.addService(this.platform.Service.LightSensor)),
103
109
  `${accessory.displayName} Light Sensor`;
104
110
 
105
111
  this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Light Sensor`);
106
112
  } else {
107
- this.debugLog(`Motion Sensor: ${accessory.displayName} Light Sensor Service Not Added`);
113
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`);
108
114
  }
109
115
 
110
116
  // Battery Service
111
- if (!device.ble) {
112
- this.debugLog(`Motion Sensor: ${accessory.displayName} Removing Battery Service`);
117
+ if (!this.BLE) {
118
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Battery Service`);
113
119
  this.batteryService = this.accessory.getService(this.platform.Service.Battery);
114
120
  accessory.removeService(this.batteryService!);
115
- } else if (device.ble && !this.batteryService) {
116
- this.debugLog(`Motion Sensor: ${accessory.displayName} Add Battery Service`);
121
+ } else if (this.BLE && !this.batteryService) {
122
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Battery Service`);
117
123
  (this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery)),
118
124
  `${accessory.displayName} Battery`;
119
125
 
120
126
  this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
121
127
  } else {
122
- this.debugLog(`Motion Sensor: ${accessory.displayName} Battery Service Not Added`);
128
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Battery Service Not Added`);
123
129
  }
124
130
 
125
131
  // Retrieve initial values and updateHomekit
@@ -137,18 +143,22 @@ export class Motion {
137
143
  * Parse the device status from the SwitchBot api
138
144
  */
139
145
  async parseStatus(): Promise<void> {
140
- if (this.SwitchToOpenAPI || !this.device.ble) {
146
+ if (this.BLE && !this.SwitchToOpenAPI) {
147
+ await this.BLEparseStatus();
148
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
149
+ this.SwitchToOpenAPI = false;
141
150
  await this.openAPIparseStatus();
142
151
  } else {
143
- await this.BLEparseStatus();
152
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
153
+ + ` ${this.device.connectionType}, parseStatus will not happen.`);
144
154
  }
145
155
  }
146
156
 
147
157
  async BLEparseStatus(): Promise<void> {
148
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE parseStatus`);
158
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
149
159
  // Movement
150
160
  this.MotionDetected = this.movement!;
151
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
161
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
152
162
  // Light Level
153
163
  if (!this.device.motion?.hide_lightsensor) {
154
164
  this.set_minLux = this.minLux();
@@ -162,7 +172,7 @@ export class Motion {
162
172
  this.CurrentAmbientLightLevel = this.set_maxLux;
163
173
  }
164
174
  this.debugLog(
165
- `Motion Sensor: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` +
175
+ `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` +
166
176
  ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
167
177
  );
168
178
  }
@@ -176,32 +186,28 @@ export class Motion {
176
186
  } else {
177
187
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
178
188
  }
179
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
189
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
190
+ +` StatusLowBattery: ${this.StatusLowBattery}`);
180
191
  }
181
192
 
182
193
  async openAPIparseStatus(): Promise<void> {
183
- if (this.device.ble) {
184
- this.SwitchToOpenAPI = false;
185
- }
186
- if (this.platform.config.credentials?.token) {
187
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} OpenAPI parseStatus`);
188
- // Motion State
189
- this.MotionDetected = this.moveDetected!;
190
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
191
- // Light Level
192
- if (!this.device.motion?.hide_lightsensor) {
193
- this.set_minLux = this.minLux();
194
- this.set_maxLux = this.maxLux();
195
- switch (this.brightness) {
196
- case 'dim':
197
- this.CurrentAmbientLightLevel = this.set_minLux;
198
- break;
199
- case 'bright':
200
- default:
201
- this.CurrentAmbientLightLevel = this.set_maxLux;
202
- }
203
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
194
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
195
+ // Motion State
196
+ this.MotionDetected = this.moveDetected!;
197
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
198
+ // Light Level
199
+ if (!this.device.motion?.hide_lightsensor) {
200
+ this.set_minLux = this.minLux();
201
+ this.set_maxLux = this.maxLux();
202
+ switch (this.brightness) {
203
+ case 'dim':
204
+ this.CurrentAmbientLightLevel = this.set_minLux;
205
+ break;
206
+ case 'bright':
207
+ default:
208
+ this.CurrentAmbientLightLevel = this.set_maxLux;
204
209
  }
210
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
205
211
  }
206
212
  }
207
213
 
@@ -209,15 +215,18 @@ export class Motion {
209
215
  * Asks the SwitchBot API for the latest device information
210
216
  */
211
217
  async refreshStatus(): Promise<void> {
212
- if (this.device.ble) {
218
+ if (this.BLE) {
213
219
  await this.BLERefreshStatus();
214
- } else {
220
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
215
221
  await this.openAPIRefreshStatus();
222
+ } else {
223
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
224
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
216
225
  }
217
226
  }
218
227
 
219
228
  async BLERefreshStatus(): Promise<void> {
220
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE RefreshStatus`);
229
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
221
230
  const switchbot = await this.platform.connectBLE();
222
231
  // Convert to BLE Address
223
232
  this.device.bleMac =
@@ -226,8 +235,7 @@ export class Motion {
226
235
  .deviceId!.match(/.{1,2}/g)!
227
236
  .join(':')
228
237
  .toLowerCase();
229
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
230
-
238
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
231
239
  this.getCustomBLEAddress(switchbot);
232
240
  // Start to monitor advertisement packets
233
241
  if (switchbot !== false) {
@@ -243,25 +251,25 @@ export class Motion {
243
251
  if (this.deviceLogging.includes('debug')) {
244
252
  this.infoLog(this.address);
245
253
  this.infoLog(this.device.bleMac);
246
- this.infoLog(`Motion Sensor: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
247
- this.infoLog(`Motion Sensor: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
254
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
255
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
248
256
  }
249
257
  this.serviceData = ad.serviceData;
250
258
  this.movement = ad.serviceData.movement;
251
259
  this.battery = ad.serviceData.battery;
252
260
  this.lightLevel = ad.serviceData.lightLevel;
253
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
261
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${superStringify(ad.serviceData)}`);
254
262
  this.debugLog(
255
- `Motion Sensor: ${this.accessory.displayName} movement: ${ad.serviceData.movement}, lightLevel: ` +
263
+ `${this.device.deviceType}: ${this.accessory.displayName} movement: ${ad.serviceData.movement}, lightLevel: ` +
256
264
  `${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`,
257
265
  );
258
266
 
259
267
  if (this.serviceData) {
260
268
  this.connected = true;
261
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} connected: ${this.connected}`);
269
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
262
270
  } else {
263
271
  this.connected = false;
264
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} connected: ${this.connected}`);
272
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
265
273
  }
266
274
  };
267
275
  // Wait 10 seconds
@@ -278,25 +286,109 @@ export class Motion {
278
286
  }
279
287
  })
280
288
  .catch(async (e: any) => {
281
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection`);
282
- if (this.deviceLogging.includes('debug')) {
283
- this.errorLog(
284
- `Motion Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` +
285
- ` Error Message: ${JSON.stringify(e.message)}`,
286
- );
287
- }
288
- if (this.platform.config.credentials?.token) {
289
- this.warnLog(`Motion Sensor: ${this.accessory.displayName} Using OpenAPI Connection`);
290
- this.SwitchToOpenAPI = true;
291
- await this.openAPIRefreshStatus();
292
- }
293
289
  this.apiError(e);
290
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}`
291
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
292
+ await this.BLEconnection(switchbot);
294
293
  });
295
294
  } else {
296
295
  await this.BLEconnection(switchbot);
297
296
  }
298
297
  }
299
298
 
299
+ async openAPIRefreshStatus(): Promise<void> {
300
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
301
+ try {
302
+ const t = Date.now();
303
+ const nonce = 'requestID';
304
+ const data = this.platform.config.credentials?.token + t + nonce;
305
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
306
+ const sign = signTerm.toString('base64');
307
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
308
+ const options = {
309
+ hostname: HostDomain,
310
+ port: 443,
311
+ path: `${DevicePath}/${this.device.deviceId}/status`,
312
+ method: 'GET',
313
+ headers: {
314
+ Authorization: this.platform.config.credentials?.token,
315
+ sign: sign,
316
+ nonce: nonce,
317
+ t: t,
318
+ 'Content-Type': 'application/json',
319
+ },
320
+ };
321
+ const req = https.request(options, (res) => {
322
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus statusCode: ${res.statusCode}`);
323
+ this.statusCode({ res });
324
+ let rawData = '';
325
+ res.on('data', (d) => {
326
+ rawData += d;
327
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
328
+ });
329
+ res.on('end', () => {
330
+ try {
331
+ this.deviceStatus = JSON.parse(rawData);
332
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus: ${superStringify(this.deviceStatus)}`);
333
+ this.moveDetected = this.deviceStatus.body.moveDetected;
334
+ this.brightness = this.deviceStatus.body.brightness;
335
+ this.parseStatus();
336
+ this.updateHomeKitCharacteristics();
337
+ } catch (e: any) {
338
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
339
+ }
340
+ });
341
+ });
342
+ req.on('error', (e: any) => {
343
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
344
+ });
345
+ req.end();
346
+ } catch (e: any) {
347
+ this.apiError(e);
348
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
349
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Updates the status for each of the HomeKit Characteristics
355
+ */
356
+ async updateHomeKitCharacteristics(): Promise<void> {
357
+ if (this.MotionDetected === undefined) {
358
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
359
+ } else {
360
+ this.accessory.context.MotionDetected = this.MotionDetected;
361
+ this.motionSensorService.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.MotionDetected);
362
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`);
363
+ }
364
+ if (this.BLE) {
365
+ if (this.BatteryLevel === undefined) {
366
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
367
+ } else {
368
+ this.accessory.context.BatteryLevel = this.BatteryLevel;
369
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
370
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
371
+ }
372
+ if (this.StatusLowBattery === undefined) {
373
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
374
+ } else {
375
+ this.accessory.context.StatusLowBattery = this.StatusLowBattery;
376
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
377
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
378
+ }
379
+ if (this.CurrentAmbientLightLevel === undefined) {
380
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
381
+ } else {
382
+ this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
383
+ this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
384
+ this.debugLog(
385
+ `${this.device.deviceType}: ${this.accessory.displayName}`
386
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
387
+ );
388
+ }
389
+ }
390
+ }
391
+
300
392
  async getCustomBLEAddress(switchbot: any) {
301
393
  if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
302
394
  (async () => {
@@ -306,7 +398,7 @@ export class Motion {
306
398
  });
307
399
  // Set an event handler
308
400
  switchbot.onadvertisement = (ad: any) => {
309
- this.warnLog(`Motion Sensor: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
401
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
310
402
  };
311
403
  await switchbot.wait(10000);
312
404
  // Stop to monitor
@@ -316,116 +408,132 @@ export class Motion {
316
408
  }
317
409
 
318
410
  async BLEconnection(switchbot: any): Promise<void> {
319
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
320
- if (this.platform.config.credentials?.token) {
321
- this.warnLog(`Motion Sensor: ${this.accessory.displayName} Using OpenAPI Connection`);
411
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
412
+ if (this.platform.config.credentials?.token && this.device.connectionType !== 'Both') {
413
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
322
414
  this.SwitchToOpenAPI = true;
323
415
  await this.openAPIRefreshStatus();
324
416
  }
325
417
  }
326
418
 
327
- async openAPIRefreshStatus(): Promise<void> {
328
- if (this.platform.config.credentials?.token) {
329
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} OpenAPI RefreshStatus`);
330
- try {
331
- const t = Date.now();
332
- const nonce = 'requestID';
333
- const data = this.platform.config.credentials?.token + t + nonce;
334
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
335
- const sign = signTerm.toString('base64');
336
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} sign: ${sign}`);
337
- const options = {
338
- hostname: HostDomain,
339
- port: 443,
340
- path: `${DevicePath}/${this.device.deviceId}/status`,
341
- method: 'GET',
342
- headers: {
343
- Authorization: this.platform.config.credentials?.token,
344
- sign: sign,
345
- nonce: nonce,
346
- t: t,
347
- 'Content-Type': 'application/json',
348
- },
349
- };
350
- const req = https.request(options, (res) => {
351
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
352
- let rawData = '';
353
- res.on('data', (d) => {
354
- rawData += d;
355
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} d: ${d}`);
356
- });
357
- res.on('end', () => {
358
- try {
359
- this.deviceStatus = JSON.parse(rawData);
360
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
361
- this.moveDetected = this.deviceStatus.body.moveDetected;
362
- this.brightness = this.deviceStatus.body.brightness;
363
- this.parseStatus();
364
- this.updateHomeKitCharacteristics();
365
- } catch (e: any) {
366
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} error message: ${e.message}`);
367
- }
368
- });
369
- });
370
- req.on('error', (e: any) => {
371
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} error message: ${e.message}`);
372
- });
373
- req.end();
374
- } catch (e: any) {
375
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
376
- if (this.deviceLogging.includes('debug')) {
377
- this.errorLog(
378
- `Motion Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` +
379
- ` Error Message: ${JSON.stringify(e.message)}`,
380
- );
381
- }
382
- this.apiError(e);
383
- }
419
+ minLux(): number {
420
+ if (this.device.motion?.set_minLux) {
421
+ this.set_minLux = this.device.motion!.set_minLux!;
422
+ } else {
423
+ this.set_minLux = 1;
384
424
  }
425
+ return this.set_minLux;
385
426
  }
386
427
 
387
- /**
388
- * Updates the status for each of the HomeKit Characteristics
389
- */
390
- async updateHomeKitCharacteristics(): Promise<void> {
391
- if (this.MotionDetected === undefined) {
392
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
428
+ maxLux(): number {
429
+ if (this.device.motion?.set_maxLux) {
430
+ this.set_maxLux = this.device.motion!.set_maxLux!;
393
431
  } else {
394
- this.motionSensorService.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.MotionDetected);
395
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`);
432
+ this.set_maxLux = 6001;
396
433
  }
397
- if (this.device.ble) {
398
- if (this.BatteryLevel === undefined) {
399
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
400
- } else {
401
- this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
402
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
434
+ return this.set_maxLux;
435
+ }
436
+
437
+ async scan(device: device & devicesConfig): Promise<void> {
438
+ if (device.scanDuration) {
439
+ this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
440
+ if (this.BLE) {
441
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
403
442
  }
404
- if (this.StatusLowBattery === undefined) {
405
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
406
- } else {
407
- this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
408
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
443
+ } else {
444
+ this.scanDuration = this.accessory.context.scanDuration = 1;
445
+ if (this.BLE) {
446
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
409
447
  }
410
- if (this.CurrentAmbientLightLevel === undefined) {
411
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
412
- } else {
413
- this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
414
- this.debugLog(
415
- `Motion Sensor: ${this.accessory.displayName}` + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
448
+ }
449
+ }
450
+
451
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
452
+ switch (res.statusCode) {
453
+ case 151:
454
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
455
+ break;
456
+ case 152:
457
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
458
+ break;
459
+ case 160:
460
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
461
+ break;
462
+ case 161:
463
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
464
+ this.offlineOff();
465
+ break;
466
+ case 171:
467
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} is offline. Hub: ${this.device.hubDeviceId}`);
468
+ this.offlineOff();
469
+ break;
470
+ case 190:
471
+ this.errorLog(
472
+ `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
473
+ ` Or command: ${superStringify(res)} format is invalid`,
416
474
  );
417
- }
475
+ break;
476
+ case 100:
477
+ if (this.platform.debugMode) {
478
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
479
+ }
480
+ break;
481
+ default:
482
+ if (this.platform.debugMode) {
483
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
484
+ }
485
+ }
486
+ }
487
+
488
+ async offlineOff(): Promise<void> {
489
+ if (this.device.offline) {
490
+ this.MotionDetected = false;
491
+ this.motionSensorService.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(this.MotionDetected);
492
+ await this.updateHomeKitCharacteristics();
418
493
  }
419
494
  }
420
495
 
421
496
  async apiError(e: any): Promise<void> {
422
497
  this.motionSensorService.updateCharacteristic(this.platform.Characteristic.MotionDetected, e);
423
- if (this.device.ble) {
498
+ if (this.BLE) {
424
499
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
425
500
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
426
501
  this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
427
502
  }
428
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
503
+ }
504
+
505
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
506
+ let FirmwareRevision: string;
507
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
508
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
509
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
510
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
511
+ if (accessory.context.FirmwareRevision) {
512
+ FirmwareRevision = accessory.context.FirmwareRevision;
513
+ } else if (device.firmware) {
514
+ FirmwareRevision = device.firmware;
515
+ } else {
516
+ FirmwareRevision = this.platform.version;
517
+ }
518
+ return FirmwareRevision;
519
+ }
520
+
521
+ async context() {
522
+ if (this.MotionDetected === undefined) {
523
+ this.MotionDetected = false;
524
+ } else {
525
+ this.MotionDetected = this.accessory.context.MotionDetected;
526
+ }
527
+ }
528
+
529
+ async refreshRate(device: device & devicesConfig): Promise<void> {
530
+ if (device.refreshRate) {
531
+ this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
532
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
533
+ } else if (this.platform.config.options!.refreshRate) {
534
+ this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
535
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
536
+ }
429
537
  }
430
538
 
431
539
  async config(device: device & devicesConfig): Promise<void> {
@@ -433,8 +541,8 @@ export class Motion {
433
541
  if (device.motion) {
434
542
  config = device.motion;
435
543
  }
436
- if (device.ble !== undefined) {
437
- config['ble'] = device.ble;
544
+ if (device.connectionType !== undefined) {
545
+ config['connectionType'] = device.connectionType;
438
546
  }
439
547
  if (device.logging !== undefined) {
440
548
  config['logging'] = device.logging;
@@ -449,83 +557,26 @@ export class Motion {
449
557
  config['offline'] = device.offline;
450
558
  }
451
559
  if (Object.entries(config).length !== 0) {
452
- this.infoLog(`Motion Sensor: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
453
- }
454
- }
455
-
456
- async refreshRate(device: device & devicesConfig): Promise<void> {
457
- if (device.refreshRate) {
458
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
459
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
460
- } else if (this.platform.config.options!.refreshRate) {
461
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
462
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
463
- }
464
- }
465
-
466
- async scan(device: device & devicesConfig): Promise<void> {
467
- if (device.scanDuration) {
468
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
469
- if (device.ble) {
470
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
471
- }
472
- } else {
473
- this.scanDuration = this.accessory.context.scanDuration = 1;
474
- if (this.device.ble) {
475
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
476
- }
560
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
477
561
  }
478
562
  }
479
563
 
480
564
  async logs(device: device & devicesConfig): Promise<void> {
481
565
  if (this.platform.debugMode) {
482
566
  this.deviceLogging = this.accessory.context.logging = 'debugMode';
483
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
567
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
484
568
  } else if (device.logging) {
485
569
  this.deviceLogging = this.accessory.context.logging = device.logging;
486
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
570
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
487
571
  } else if (this.platform.config.options?.logging) {
488
572
  this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
489
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
573
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
490
574
  } else {
491
575
  this.deviceLogging = this.accessory.context.logging = 'standard';
492
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
576
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
493
577
  }
494
578
  }
495
579
 
496
- FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
497
- let FirmwareRevision: string;
498
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
499
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
500
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
501
- if (accessory.context.FirmwareRevision) {
502
- FirmwareRevision = accessory.context.FirmwareRevision;
503
- } else if (device.firmware) {
504
- FirmwareRevision = device.firmware;
505
- } else {
506
- FirmwareRevision = this.platform.version;
507
- }
508
- return FirmwareRevision;
509
- }
510
-
511
- minLux(): number {
512
- if (this.device.motion?.set_minLux) {
513
- this.set_minLux = this.device.motion!.set_minLux!;
514
- } else {
515
- this.set_minLux = 1;
516
- }
517
- return this.set_minLux;
518
- }
519
-
520
- maxLux(): number {
521
- if (this.device.motion?.set_maxLux) {
522
- this.set_maxLux = this.device.motion!.set_maxLux!;
523
- } else {
524
- this.set_maxLux = 6001;
525
- }
526
- return this.set_maxLux;
527
- }
528
-
529
580
  /**
530
581
  * Logging for Device
531
582
  */