@switchbot/homebridge-switchbot 2.0.0-beta.3 → 2.0.0-beta.30

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