@switchbot/homebridge-switchbot 2.0.0-beta.19 → 2.0.0-beta.20

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 (112) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/README.md +1 -2
  3. package/config.schema.json +51 -32
  4. package/dist/device/bot.d.ts +32 -29
  5. package/dist/device/bot.d.ts.map +1 -1
  6. package/dist/device/bot.js +549 -575
  7. package/dist/device/bot.js.map +1 -1
  8. package/dist/device/colorbulb.d.ts +33 -15
  9. package/dist/device/colorbulb.d.ts.map +1 -1
  10. package/dist/device/colorbulb.js +426 -341
  11. package/dist/device/colorbulb.js.map +1 -1
  12. package/dist/device/contact.d.ts +16 -9
  13. package/dist/device/contact.d.ts.map +1 -1
  14. package/dist/device/contact.js +246 -208
  15. package/dist/device/contact.js.map +1 -1
  16. package/dist/device/curtain.d.ts +21 -18
  17. package/dist/device/curtain.d.ts.map +1 -1
  18. package/dist/device/curtain.js +449 -449
  19. package/dist/device/curtain.js.map +1 -1
  20. package/dist/device/humidifier.d.ts +22 -20
  21. package/dist/device/humidifier.d.ts.map +1 -1
  22. package/dist/device/humidifier.js +346 -324
  23. package/dist/device/humidifier.js.map +1 -1
  24. package/dist/device/lock.d.ts +22 -10
  25. package/dist/device/lock.d.ts.map +1 -1
  26. package/dist/device/lock.js +257 -178
  27. package/dist/device/lock.js.map +1 -1
  28. package/dist/device/meter.d.ts +21 -14
  29. package/dist/device/meter.d.ts.map +1 -1
  30. package/dist/device/meter.js +287 -239
  31. package/dist/device/meter.js.map +1 -1
  32. package/dist/device/meterplus.d.ts +23 -10
  33. package/dist/device/meterplus.d.ts.map +1 -1
  34. package/dist/device/meterplus.js +308 -190
  35. package/dist/device/meterplus.js.map +1 -1
  36. package/dist/device/motion.d.ts +17 -10
  37. package/dist/device/motion.d.ts.map +1 -1
  38. package/dist/device/motion.js +225 -193
  39. package/dist/device/motion.js.map +1 -1
  40. package/dist/device/plug.d.ts +24 -17
  41. package/dist/device/plug.d.ts.map +1 -1
  42. package/dist/device/plug.js +270 -267
  43. package/dist/device/plug.js.map +1 -1
  44. package/dist/device/striplight.d.ts +36 -14
  45. package/dist/device/striplight.d.ts.map +1 -1
  46. package/dist/device/striplight.js +447 -299
  47. package/dist/device/striplight.js.map +1 -1
  48. package/dist/irdevice/airconditioner.d.ts +7 -14
  49. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  50. package/dist/irdevice/airconditioner.js +177 -169
  51. package/dist/irdevice/airconditioner.js.map +1 -1
  52. package/dist/irdevice/airpurifier.d.ts +3 -5
  53. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  54. package/dist/irdevice/airpurifier.js +72 -57
  55. package/dist/irdevice/airpurifier.js.map +1 -1
  56. package/dist/irdevice/camera.d.ts +3 -4
  57. package/dist/irdevice/camera.d.ts.map +1 -1
  58. package/dist/irdevice/camera.js +47 -33
  59. package/dist/irdevice/camera.js.map +1 -1
  60. package/dist/irdevice/fan.d.ts +3 -4
  61. package/dist/irdevice/fan.d.ts.map +1 -1
  62. package/dist/irdevice/fan.js +66 -48
  63. package/dist/irdevice/fan.js.map +1 -1
  64. package/dist/irdevice/light.d.ts +3 -4
  65. package/dist/irdevice/light.d.ts.map +1 -1
  66. package/dist/irdevice/light.js +48 -47
  67. package/dist/irdevice/light.js.map +1 -1
  68. package/dist/irdevice/other.d.ts +3 -4
  69. package/dist/irdevice/other.d.ts.map +1 -1
  70. package/dist/irdevice/other.js +47 -33
  71. package/dist/irdevice/other.js.map +1 -1
  72. package/dist/irdevice/tv.d.ts +10 -11
  73. package/dist/irdevice/tv.d.ts.map +1 -1
  74. package/dist/irdevice/tv.js +64 -48
  75. package/dist/irdevice/tv.js.map +1 -1
  76. package/dist/irdevice/vacuumcleaner.d.ts +7 -12
  77. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  78. package/dist/irdevice/vacuumcleaner.js +52 -41
  79. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  80. package/dist/irdevice/waterheater.d.ts +6 -11
  81. package/dist/irdevice/waterheater.d.ts.map +1 -1
  82. package/dist/irdevice/waterheater.js +52 -42
  83. package/dist/irdevice/waterheater.js.map +1 -1
  84. package/dist/platform.d.ts +0 -4
  85. package/dist/platform.d.ts.map +1 -1
  86. package/dist/platform.js +44 -66
  87. package/dist/platform.js.map +1 -1
  88. package/dist/settings.d.ts +3 -3
  89. package/dist/settings.d.ts.map +1 -1
  90. package/package.json +7 -6
  91. package/src/device/bot.ts +551 -589
  92. package/src/device/colorbulb.ts +441 -375
  93. package/src/device/contact.ts +249 -213
  94. package/src/device/curtain.ts +452 -461
  95. package/src/device/humidifier.ts +358 -365
  96. package/src/device/lock.ts +263 -179
  97. package/src/device/meter.ts +302 -253
  98. package/src/device/meterplus.ts +319 -194
  99. package/src/device/motion.ts +230 -199
  100. package/src/device/plug.ts +273 -274
  101. package/src/device/striplight.ts +460 -316
  102. package/src/irdevice/airconditioner.ts +181 -183
  103. package/src/irdevice/airpurifier.ts +70 -60
  104. package/src/irdevice/camera.ts +46 -36
  105. package/src/irdevice/fan.ts +64 -49
  106. package/src/irdevice/light.ts +47 -51
  107. package/src/irdevice/other.ts +46 -36
  108. package/src/irdevice/tv.ts +64 -51
  109. package/src/irdevice/vacuumcleaner.ts +52 -45
  110. package/src/irdevice/waterheater.ts +50 -45
  111. package/src/platform.ts +44 -68
  112. package/src/settings.ts +3 -3
@@ -10,6 +10,7 @@ import superStringify from 'super-stringify';
10
10
  import { SwitchBotPlatform } from '../platform';
11
11
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
12
12
  import { device, devicesConfig, serviceData, ad, switchbot, temperature, deviceStatus, HostDomain, DevicePath } from '../settings';
13
+ import { IncomingMessage } from 'http';
13
14
 
14
15
  /**
15
16
  * Platform Accessory
@@ -37,15 +38,20 @@ export class Meter {
37
38
  // BLE Others
38
39
  connected?: boolean;
39
40
  switchbot!: switchbot;
40
- SwitchToOpenAPI?: boolean;
41
41
  serviceData!: serviceData;
42
42
  address!: ad['address'];
43
+ SwitchToOpenAPI?: boolean;
43
44
  temperature!: temperature['c'];
44
45
  battery!: serviceData['battery'];
45
46
  humidity!: serviceData['humidity'];
46
47
 
48
+ //MQTT stuff
49
+ mqttClient: MqttClient | null = null;
50
+
51
+ // EVE history service handler
52
+ historyService: any;
53
+
47
54
  // Config
48
- OpenAPI?: boolean;
49
55
  scanDuration!: number;
50
56
  deviceLogging!: string;
51
57
  deviceRefreshRate!: number;
@@ -54,31 +60,19 @@ export class Meter {
54
60
  meterUpdateInProgress!: boolean;
55
61
  doMeterUpdate: Subject<void>;
56
62
 
57
- //MQTT stuff
58
- mqttClient: MqttClient | null = null;
59
-
60
- // EVE history service handler
61
- historyService: any;
63
+ // Connection
64
+ private readonly BLE = (this.device.connectionType === 'BLE' || this.device.connectionType === 'Both');
65
+ private readonly OpenAPI = (this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'Both');
62
66
 
63
67
  constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
64
68
  // default placeholders
65
69
  this.logs(device);
66
- this.openAPI();
67
70
  this.scan(device);
68
- this.setupHistoryService(device);
69
71
  this.refreshRate(device);
70
72
  this.config(device);
73
+ this.context();
74
+ this.setupHistoryService(device);
71
75
  this.setupMqtt(device);
72
- if (this.CurrentRelativeHumidity === undefined) {
73
- this.CurrentRelativeHumidity = 0;
74
- } else {
75
- this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
76
- }
77
- if (this.CurrentTemperature === undefined) {
78
- this.CurrentTemperature = 0;
79
- } else {
80
- this.CurrentTemperature = this.accessory.context.CurrentTemperature;
81
- }
82
76
 
83
77
  // this is subject we use to track when we need to POST changes to the SwitchBot API
84
78
  this.doMeterUpdate = new Subject();
@@ -152,11 +146,11 @@ export class Meter {
152
146
  }
153
147
 
154
148
  // Battery Service
155
- if (!device.ble) {
149
+ if (!this.BLE) {
156
150
  this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Battery Service`);
157
151
  this.batteryService = this.accessory.getService(this.platform.Service.Battery);
158
152
  accessory.removeService(this.batteryService!);
159
- } else if (device.ble && !this.batteryService) {
153
+ } else if (this.BLE && !this.batteryService) {
160
154
  this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Battery Service`);
161
155
  (this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery)),
162
156
  `${accessory.displayName} Battery`;
@@ -179,68 +173,23 @@ export class Meter {
179
173
  });
180
174
  }
181
175
 
182
- /*
183
- * Publish MQTT message for topics of
184
- * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
185
- */
186
- mqttPublish(message: any) {
187
- const mac = this.device.deviceId
188
- ?.toLowerCase()
189
- .match(/[\s\S]{1,2}/g)
190
- ?.join(':');
191
- const options = this.device.mqttPubOptions || {};
192
- this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
193
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${superStringify(options)}`);
194
- }
195
-
196
- /*
197
- * Setup MQTT hadler if URL is specifed.
198
- */
199
- async setupMqtt(device: device & devicesConfig): Promise<void> {
200
- if (device.mqttURL) {
201
- try {
202
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
203
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
204
- this.mqttClient.on('error', (e: Error) => {
205
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
206
- });
207
- } catch (e) {
208
- this.mqttClient = null;
209
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
210
- }
211
- }
212
- }
213
-
214
- /*
215
- * Setup EVE history graph feature if enabled.
216
- */
217
- async setupHistoryService(device: device & devicesConfig): Promise<void> {
218
- const mac = this.device
219
- .deviceId!.match(/.{1,2}/g)!
220
- .join(':')
221
- .toLowerCase();
222
- this.historyService = device.history
223
- ? new this.platform.fakegatoAPI('room', this.accessory, {
224
- log: this.platform.log,
225
- storage: 'fs',
226
- filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
227
- })
228
- : null;
229
- }
230
-
231
176
  /**
232
177
  * Parse the device status from the SwitchBot api
233
178
  */
234
179
  async parseStatus(): Promise<void> {
235
- if (this.SwitchToOpenAPI || !this.device.ble) {
180
+ if (this.BLE && !this.SwitchToOpenAPI) {
181
+ await this.BLEparseStatus();
182
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
183
+ this.SwitchToOpenAPI = false;
236
184
  await this.openAPIparseStatus();
237
185
  } else {
238
- await this.BLEparseStatus();
186
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
187
+ + ` ${this.device.connectionType}, parseStatus will not happen.`);
239
188
  }
240
189
  }
241
190
 
242
191
  async BLEparseStatus(): Promise<void> {
243
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE parseStatus`);
192
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
244
193
 
245
194
  // Battery
246
195
  this.BatteryLevel = Number(this.battery);
@@ -249,8 +198,7 @@ export class Meter {
249
198
  } else {
250
199
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
251
200
  }
252
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
253
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
201
+ this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
254
202
 
255
203
  // Humidity
256
204
  if (!this.device.meter?.hide_humidity) {
@@ -267,22 +215,17 @@ export class Meter {
267
215
  }
268
216
 
269
217
  async openAPIparseStatus(): Promise<void> {
270
- if (this.device.ble) {
271
- this.SwitchToOpenAPI = false;
218
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
219
+ // Current Relative Humidity
220
+ if (!this.device.meter?.hide_humidity) {
221
+ this.CurrentRelativeHumidity = this.Humidity!;
222
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
272
223
  }
273
- if (this.platform.config.credentials?.token) {
274
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI parseStatus`);
275
- // Current Relative Humidity
276
- if (!this.device.meter?.hide_humidity) {
277
- this.CurrentRelativeHumidity = this.Humidity!;
278
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
279
- }
280
224
 
281
- // Current Temperature
282
- if (!this.device.meter?.hide_temperature) {
283
- this.CurrentTemperature = this.Temperature!;
284
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
285
- }
225
+ // Current Temperature
226
+ if (!this.device.meter?.hide_temperature) {
227
+ this.CurrentTemperature = this.Temperature!;
228
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
286
229
  }
287
230
  }
288
231
 
@@ -290,15 +233,18 @@ export class Meter {
290
233
  * Asks the SwitchBot API for the latest device information
291
234
  */
292
235
  async refreshStatus(): Promise<void> {
293
- if (this.device.ble) {
294
- this.BLErefreshStatus();
236
+ if (this.BLE) {
237
+ await this.BLERefreshStatus();
238
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
239
+ await this.openAPIRefreshStatus();
295
240
  } else {
296
- this.openAPIRefreshStatus();
241
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
242
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
297
243
  }
298
244
  }
299
245
 
300
- async BLErefreshStatus(): Promise<void> {
301
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE RefreshStatus`);
246
+ async BLERefreshStatus(): Promise<void> {
247
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
302
248
  const switchbot = await this.platform.connectBLE();
303
249
  // Convert to BLE Address
304
250
  this.device.bleMac = this.device
@@ -326,11 +272,11 @@ export class Meter {
326
272
  }
327
273
  if (ad.serviceData.humidity! > 0) {
328
274
  // reject unreliable data
329
- this.serviceData = ad.serviceData;
330
- this.temperature = ad.serviceData.temperature!.c;
331
275
  this.humidity = ad.serviceData.humidity;
332
- this.battery = ad.serviceData.battery;
333
276
  }
277
+ this.serviceData = ad.serviceData;
278
+ this.temperature = ad.serviceData.temperature!.c;
279
+ this.battery = ad.serviceData.battery;
334
280
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${superStringify(ad.serviceData)}`);
335
281
  this.debugLog(
336
282
  `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` +
@@ -360,111 +306,66 @@ export class Meter {
360
306
  }
361
307
  })
362
308
  .catch(async (e: any) => {
363
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed refreshStatus with BLE Connection`);
364
- if (this.deviceLogging.includes('debug')) {
365
- this.errorLog(
366
- `${this.device.deviceType}: ${this.accessory.displayName} failed refreshStatus with BLE Connection,`
367
- + ` Error Message: ${superStringify(e.message)}`,
368
- );
369
- }
370
- if (this.platform.config.credentials?.token) {
371
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
372
- this.SwitchToOpenAPI = true;
373
- this.openAPIRefreshStatus();
374
- }
375
309
  this.apiError(e);
310
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}`
311
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
312
+ await this.BLEconnection(switchbot);
376
313
  });
377
314
  } else {
378
315
  await this.BLEconnection(switchbot);
379
316
  }
380
317
  }
381
318
 
382
- async getCustomBLEAddress(switchbot: any) {
383
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
384
- (async () => {
385
- // Start to monitor advertisement packets
386
- await switchbot.startScan({
387
- model: 'T',
388
- });
389
- // Set an event handler
390
- switchbot.onadvertisement = (ad: any) => {
391
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
392
- };
393
- await switchbot.wait(10000);
394
- // Stop to monitor
395
- switchbot.stopScan();
396
- })();
397
- }
398
- }
399
-
400
- async BLEconnection(switchbot: any): Promise<void> {
401
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
402
- if (this.platform.config.credentials?.token) {
403
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
404
- this.SwitchToOpenAPI = true;
405
- await this.openAPIRefreshStatus();
406
- }
407
- }
408
-
409
319
  async openAPIRefreshStatus(): Promise<void> {
410
- if (this.platform.config.credentials?.token && this.OpenAPI) {
411
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI RefreshStatus`);
412
- try {
413
- const t = Date.now();
414
- const nonce = 'requestID';
415
- const data = this.platform.config.credentials?.token + t + nonce;
416
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
417
- const sign = signTerm.toString('base64');
418
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
419
- const options = {
420
- hostname: HostDomain,
421
- port: 443,
422
- path: `${DevicePath}/${this.device.deviceId}/status`,
423
- method: 'GET',
424
- headers: {
425
- Authorization: this.platform.config.credentials?.token,
426
- sign: sign,
427
- nonce: nonce,
428
- t: t,
429
- 'Content-Type': 'application/json',
430
- },
431
- };
432
- const req = https.request(options, (res) => {
433
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
434
- let rawData = '';
435
- res.on('data', (d) => {
436
- rawData += d;
437
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
438
- });
439
- res.on('end', () => {
440
- try {
441
- this.deviceStatus = JSON.parse(rawData);
442
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus: ${superStringify(this.deviceStatus)}`);
443
- this.Humidity = this.deviceStatus.body.humidity!;
444
- this.Temperature = this.deviceStatus.body.temperature!;
445
- this.parseStatus();
446
- this.updateHomeKitCharacteristics();
447
- } catch (e: any) {
448
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
449
- }
450
- });
320
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
321
+ try {
322
+ const t = Date.now();
323
+ const nonce = 'requestID';
324
+ const data = this.platform.config.credentials?.token + t + nonce;
325
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
326
+ const sign = signTerm.toString('base64');
327
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
328
+ const options = {
329
+ hostname: HostDomain,
330
+ port: 443,
331
+ path: `${DevicePath}/${this.device.deviceId}/status`,
332
+ method: 'GET',
333
+ headers: {
334
+ Authorization: this.platform.config.credentials?.token,
335
+ sign: sign,
336
+ nonce: nonce,
337
+ t: t,
338
+ 'Content-Type': 'application/json',
339
+ },
340
+ };
341
+ const req = https.request(options, (res) => {
342
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus statusCode: ${res.statusCode}`);
343
+ let rawData = '';
344
+ res.on('data', (d) => {
345
+ rawData += d;
346
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
451
347
  });
452
- req.on('error', (e: any) => {
453
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
348
+ res.on('end', () => {
349
+ try {
350
+ this.deviceStatus = JSON.parse(rawData);
351
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus: ${superStringify(this.deviceStatus)}`);
352
+ this.Humidity = this.deviceStatus.body.humidity!;
353
+ this.Temperature = this.deviceStatus.body.temperature!;
354
+ this.parseStatus();
355
+ this.updateHomeKitCharacteristics();
356
+ } catch (e: any) {
357
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
358
+ }
454
359
  });
455
- req.end();
456
- } catch (e: any) {
457
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
458
- if (this.deviceLogging.includes('debug')) {
459
- this.errorLog(
460
- `${this.device.deviceType}: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,`
461
- + ` Error Message: ${superStringify(e.message)}`,
462
- );
463
- }
464
- this.apiError(e);
465
- }
466
- } else {
467
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI is disabled, commands will not be sent.`);
360
+ });
361
+ req.on('error', (e: any) => {
362
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
363
+ });
364
+ req.end();
365
+ } catch (e: any) {
366
+ this.apiError(e);
367
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
368
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
468
369
  }
469
370
  }
470
371
 
@@ -478,6 +379,7 @@ export class Meter {
478
379
  if (this.CurrentRelativeHumidity === undefined) {
479
380
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
480
381
  } else {
382
+ this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity;
481
383
  this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
482
384
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
483
385
  + ` CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
@@ -489,26 +391,30 @@ export class Meter {
489
391
  if (this.CurrentTemperature === undefined) {
490
392
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
491
393
  } else {
492
- this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature);
493
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
494
394
  mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
495
395
  entry['temp'] = this.CurrentTemperature;
396
+ this.accessory.context.CurrentTemperature = this.CurrentTemperature;
397
+ this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature);
398
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
399
+
496
400
  }
497
401
  }
498
- if (this.device.ble) {
402
+ if (this.BLE) {
499
403
  if (this.BatteryLevel === undefined) {
500
404
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
501
405
  } else {
406
+ mqttmessage.push(`"battery": ${this.BatteryLevel}`);
407
+ this.accessory.context.BatteryLevel = this.BatteryLevel;
502
408
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
503
409
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
504
- mqttmessage.push(`"battery": ${this.BatteryLevel}`);
505
410
  }
506
411
  if (this.StatusLowBattery === undefined) {
507
412
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
508
413
  } else {
414
+ mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
415
+ this.accessory.context.StatusLowBattery = this.StatusLowBattery;
509
416
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
510
417
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
511
- mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
512
418
  }
513
419
  }
514
420
  this.mqttPublish(`{${mqttmessage.join(',')}}`);
@@ -518,6 +424,150 @@ export class Meter {
518
424
  }
519
425
  }
520
426
 
427
+ /*
428
+ * Publish MQTT message for topics of
429
+ * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
430
+ */
431
+ mqttPublish(message: any) {
432
+ const mac = this.device.deviceId
433
+ ?.toLowerCase()
434
+ .match(/[\s\S]{1,2}/g)
435
+ ?.join(':');
436
+ const options = this.device.mqttPubOptions || {};
437
+ this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
438
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${superStringify(options)}`);
439
+ }
440
+
441
+ /*
442
+ * Setup MQTT hadler if URL is specifed.
443
+ */
444
+ async setupMqtt(device: device & devicesConfig): Promise<void> {
445
+ if (device.mqttURL) {
446
+ try {
447
+ this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
448
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
449
+ this.mqttClient.on('error', (e: Error) => {
450
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
451
+ });
452
+ } catch (e) {
453
+ this.mqttClient = null;
454
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
455
+ }
456
+ }
457
+ }
458
+
459
+ /*
460
+ * Setup EVE history graph feature if enabled.
461
+ */
462
+ async setupHistoryService(device: device & devicesConfig): Promise<void> {
463
+ const mac = this.device
464
+ .deviceId!.match(/.{1,2}/g)!
465
+ .join(':')
466
+ .toLowerCase();
467
+ this.historyService = device.history
468
+ ? new this.platform.fakegatoAPI('room', this.accessory, {
469
+ log: this.platform.log,
470
+ storage: 'fs',
471
+ filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
472
+ })
473
+ : null;
474
+ }
475
+
476
+ async getCustomBLEAddress(switchbot: any) {
477
+ if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
478
+ (async () => {
479
+ // Start to monitor advertisement packets
480
+ await switchbot.startScan({
481
+ model: 'T',
482
+ });
483
+ // Set an event handler
484
+ switchbot.onadvertisement = (ad: any) => {
485
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
486
+ };
487
+ await switchbot.wait(10000);
488
+ // Stop to monitor
489
+ switchbot.stopScan();
490
+ })();
491
+ }
492
+ }
493
+
494
+ async BLEconnection(switchbot: any): Promise<void> {
495
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
496
+ if (this.platform.config.credentials?.token && this.device.connectionType !== 'Both') {
497
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
498
+ this.SwitchToOpenAPI = true;
499
+ await this.openAPIRefreshStatus();
500
+ }
501
+ }
502
+
503
+ async scan(device: device & devicesConfig): Promise<void> {
504
+ if (device.scanDuration) {
505
+ this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
506
+ if (this.BLE) {
507
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
508
+ }
509
+ } else {
510
+ this.scanDuration = this.accessory.context.scanDuration = 1;
511
+ if (this.BLE) {
512
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
513
+ }
514
+ }
515
+ }
516
+
517
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
518
+ switch (res.statusCode) {
519
+ case 151:
520
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
521
+ break;
522
+ case 152:
523
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
524
+ break;
525
+ case 160:
526
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
527
+ break;
528
+ case 161:
529
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
530
+ this.offlineOff();
531
+ break;
532
+ case 171:
533
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} is offline. Hub: ${this.device.hubDeviceId}`);
534
+ this.offlineOff();
535
+ break;
536
+ case 190:
537
+ this.errorLog(
538
+ `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
539
+ ` Or command: ${superStringify(res)} format is invalid`,
540
+ );
541
+ break;
542
+ case 100:
543
+ if (this.platform.debugMode) {
544
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
545
+ }
546
+ break;
547
+ default:
548
+ if (this.platform.debugMode) {
549
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
550
+ }
551
+ }
552
+ }
553
+
554
+ async offlineOff(): Promise<void> {
555
+ if (this.device.offline) {
556
+ if (!this.device.meter?.hide_humidity) {
557
+ this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity,
558
+ this.accessory.context.CurrentRelativeHumidity);
559
+ }
560
+ if (!this.device.meter?.hide_temperature) {
561
+ this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.accessory.context.CurrentTemperature);
562
+ }
563
+ if (this.BLE) {
564
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.accessory.context.BatteryLevel);
565
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.accessory.context.StatusLowBattery);
566
+ }
567
+ await this.updateHomeKitCharacteristics();
568
+ }
569
+ }
570
+
521
571
  async apiError(e: any): Promise<void> {
522
572
  if (!this.device.meter?.hide_humidity) {
523
573
  this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, e);
@@ -525,35 +575,51 @@ export class Meter {
525
575
  if (!this.device.meter?.hide_temperature) {
526
576
  this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e);
527
577
  }
528
- if (this.device.ble) {
578
+ if (this.BLE) {
529
579
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
530
580
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
531
581
  }
532
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
533
582
  }
534
583
 
535
- async config(device: device & devicesConfig): Promise<void> {
536
- let config = {};
537
- if (device.meter) {
538
- config = device.meter;
539
- }
540
- if (device.ble !== undefined) {
541
- config['ble'] = device.ble;
542
- }
543
- if (device.openAPI !== undefined) {
544
- config['openAPI'] = device.openAPI;
545
- }
546
- if (device.logging !== undefined) {
547
- config['logging'] = device.logging;
584
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
585
+ let FirmwareRevision: string;
586
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
587
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
588
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
589
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
590
+ if (accessory.context.FirmwareRevision) {
591
+ FirmwareRevision = accessory.context.FirmwareRevision;
592
+ } else if (device.firmware) {
593
+ FirmwareRevision = device.firmware;
594
+ } else {
595
+ FirmwareRevision = this.platform.version;
548
596
  }
549
- if (device.refreshRate !== undefined) {
550
- config['refreshRate'] = device.refreshRate;
597
+ return FirmwareRevision;
598
+ }
599
+
600
+ async context() {
601
+ if (this.CurrentRelativeHumidity === undefined) {
602
+ this.CurrentRelativeHumidity = 0;
603
+ } else {
604
+ this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
551
605
  }
552
- if (device.scanDuration !== undefined) {
553
- config['scanDuration'] = device.scanDuration;
606
+ if (this.CurrentTemperature === undefined) {
607
+ this.CurrentTemperature = 0;
608
+ } else {
609
+ this.CurrentTemperature = this.accessory.context.CurrentTemperature;
554
610
  }
555
- if (Object.entries(config).length !== 0) {
556
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
611
+ if (this.BLE) {
612
+ if (this.BatteryLevel === undefined) {
613
+ this.BatteryLevel = 100;
614
+ } else {
615
+ this.BatteryLevel = this.accessory.context.BatteryLevel;
616
+ }
617
+ if (this.StatusLowBattery === undefined) {
618
+ this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
619
+ this.accessory.context.StatusLowBattery = this.StatusLowBattery;
620
+ } else {
621
+ this.StatusLowBattery = this.accessory.context.StatusLowBattery;
622
+ }
557
623
  }
558
624
  }
559
625
 
@@ -567,26 +633,25 @@ export class Meter {
567
633
  }
568
634
  }
569
635
 
570
- async openAPI() {
571
- if (!this.device.openAPI) {
572
- this.OpenAPI = true;
573
- } else {
574
- this.OpenAPI = this.device.openAPI;
636
+ async config(device: device & devicesConfig): Promise<void> {
637
+ let config = {};
638
+ if (device.meter) {
639
+ config = device.meter;
575
640
  }
576
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI: ${this.OpenAPI}`);
577
- }
578
-
579
- async scan(device: device & devicesConfig): Promise<void> {
580
- if (device.scanDuration) {
581
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
582
- if (device.ble) {
583
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
584
- }
585
- } else {
586
- this.scanDuration = this.accessory.context.scanDuration = 1;
587
- if (this.device.ble) {
588
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
589
- }
641
+ if (device.connectionType !== undefined) {
642
+ config['connectionType'] = device.connectionType;
643
+ }
644
+ if (device.logging !== undefined) {
645
+ config['logging'] = device.logging;
646
+ }
647
+ if (device.refreshRate !== undefined) {
648
+ config['refreshRate'] = device.refreshRate;
649
+ }
650
+ if (device.scanDuration !== undefined) {
651
+ config['scanDuration'] = device.scanDuration;
652
+ }
653
+ if (Object.entries(config).length !== 0) {
654
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
590
655
  }
591
656
  }
592
657
 
@@ -606,22 +671,6 @@ export class Meter {
606
671
  }
607
672
  }
608
673
 
609
- FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
610
- let FirmwareRevision: string;
611
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
612
- + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
613
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
614
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
615
- if (accessory.context.FirmwareRevision) {
616
- FirmwareRevision = accessory.context.FirmwareRevision;
617
- } else if (device.firmware) {
618
- FirmwareRevision = device.firmware;
619
- } else {
620
- FirmwareRevision = this.platform.version;
621
- }
622
- return FirmwareRevision;
623
- }
624
-
625
674
  /**
626
675
  * Logging for Device
627
676
  */