@switchbot/homebridge-switchbot 1.12.9-beta.8 → 1.13.1-beta.0

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 (74) hide show
  1. package/.github/workflows/codeql-analysis.yml +3 -3
  2. package/.github/workflows/dependabot.yml +17 -0
  3. package/.github/workflows/nodejs-beta.yml +0 -3
  4. package/.github/workflows/nodejs.yml +0 -3
  5. package/CHANGELOG.md +3 -1
  6. package/config.schema.json +1 -2
  7. package/dist/device/bot.d.ts +3 -0
  8. package/dist/device/bot.d.ts.map +1 -1
  9. package/dist/device/bot.js +18 -1
  10. package/dist/device/bot.js.map +1 -1
  11. package/dist/device/colorbulb.d.ts +3 -0
  12. package/dist/device/colorbulb.d.ts.map +1 -1
  13. package/dist/device/colorbulb.js +18 -1
  14. package/dist/device/colorbulb.js.map +1 -1
  15. package/dist/device/contact.d.ts +3 -0
  16. package/dist/device/contact.d.ts.map +1 -1
  17. package/dist/device/contact.js +18 -1
  18. package/dist/device/contact.js.map +1 -1
  19. package/dist/device/curtain.d.ts +6 -0
  20. package/dist/device/curtain.d.ts.map +1 -1
  21. package/dist/device/curtain.js +60 -4
  22. package/dist/device/curtain.js.map +1 -1
  23. package/dist/device/humidifier.d.ts +3 -0
  24. package/dist/device/humidifier.d.ts.map +1 -1
  25. package/dist/device/humidifier.js +18 -1
  26. package/dist/device/humidifier.js.map +1 -1
  27. package/dist/device/lock.d.ts +3 -0
  28. package/dist/device/lock.d.ts.map +1 -1
  29. package/dist/device/lock.js +18 -1
  30. package/dist/device/lock.js.map +1 -1
  31. package/dist/device/meter.d.ts +9 -0
  32. package/dist/device/meter.d.ts.map +1 -1
  33. package/dist/device/meter.js +82 -6
  34. package/dist/device/meter.js.map +1 -1
  35. package/dist/device/meterplus.d.ts +3 -0
  36. package/dist/device/meterplus.d.ts.map +1 -1
  37. package/dist/device/meterplus.js +18 -1
  38. package/dist/device/meterplus.js.map +1 -1
  39. package/dist/device/motion.d.ts +3 -0
  40. package/dist/device/motion.d.ts.map +1 -1
  41. package/dist/device/motion.js +18 -1
  42. package/dist/device/motion.js.map +1 -1
  43. package/dist/device/plug.d.ts +3 -0
  44. package/dist/device/plug.d.ts.map +1 -1
  45. package/dist/device/plug.js +18 -1
  46. package/dist/device/plug.js.map +1 -1
  47. package/dist/device/striplight.d.ts +3 -0
  48. package/dist/device/striplight.d.ts.map +1 -1
  49. package/dist/device/striplight.js +18 -1
  50. package/dist/device/striplight.js.map +1 -1
  51. package/dist/irdevice/other.js +3 -3
  52. package/dist/irdevice/other.js.map +1 -1
  53. package/dist/platform.d.ts +1 -0
  54. package/dist/platform.d.ts.map +1 -1
  55. package/dist/platform.js +43 -40
  56. package/dist/platform.js.map +1 -1
  57. package/dist/settings.d.ts +5 -0
  58. package/dist/settings.d.ts.map +1 -1
  59. package/dist/settings.js.map +1 -1
  60. package/package.json +15 -13
  61. package/src/device/bot.ts +18 -1
  62. package/src/device/colorbulb.ts +18 -1
  63. package/src/device/contact.ts +18 -1
  64. package/src/device/curtain.ts +63 -4
  65. package/src/device/humidifier.ts +18 -1
  66. package/src/device/lock.ts +18 -1
  67. package/src/device/meter.ts +90 -6
  68. package/src/device/meterplus.ts +18 -1
  69. package/src/device/motion.ts +18 -1
  70. package/src/device/plug.ts +18 -1
  71. package/src/device/striplight.ts +18 -1
  72. package/src/irdevice/other.ts +3 -3
  73. package/src/platform.ts +46 -40
  74. package/src/settings.ts +5 -0
@@ -4,6 +4,7 @@ import { SwitchBotPlatform } from '../platform';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
5
  import { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge';
6
6
  import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse, payload, hs2rgb, rgb2hs, m2hs, deviceStatus } from '../settings';
7
+ import { Context } from 'vm';
7
8
 
8
9
  /**
9
10
  * Platform Accessory
@@ -84,7 +85,8 @@ export class ColorBulb {
84
85
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
85
86
  .setCharacteristic(this.platform.Characteristic.Model, 'W1401400')
86
87
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
87
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
88
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
89
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
88
90
 
89
91
  // get the Lightbulb service if it exists, otherwise create a new Lightbulb service
90
92
  // you can create multiple services for each accessory
@@ -672,6 +674,21 @@ export class ColorBulb {
672
674
  }
673
675
  }
674
676
 
677
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
678
+ let FirmwareRevision: string;
679
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
680
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
681
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
682
+ if (accessory.context.FirmwareRevision) {
683
+ FirmwareRevision = accessory.context.FirmwareRevision;
684
+ } else if (device.firmware) {
685
+ FirmwareRevision = device.firmware;
686
+ } else {
687
+ FirmwareRevision = this.platform.version;
688
+ }
689
+ return FirmwareRevision;
690
+ }
691
+
675
692
  async adaptiveLighting(device: device & devicesConfig): Promise<void> {
676
693
  if (device.colorbulb?.adaptiveLightingShift) {
677
694
  this.adaptiveLightingShift = device.colorbulb.adaptiveLightingShift;
@@ -3,6 +3,7 @@ import { skipWhile } from 'rxjs/operators';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
5
5
  import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, deviceStatus, ad } from '../settings';
6
+ import { Context } from 'vm';
6
7
 
7
8
  /**
8
9
  * Platform Accessory
@@ -72,7 +73,8 @@ export class Contact {
72
73
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
73
74
  .setCharacteristic(this.platform.Characteristic.Model, 'W1201500')
74
75
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
75
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
76
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
77
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
76
78
 
77
79
  // get the Contact service if it exists, otherwise create a new Contact service
78
80
  // you can create multiple services for each accessory
@@ -510,6 +512,21 @@ export class Contact {
510
512
  }
511
513
  }
512
514
 
515
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
516
+ let FirmwareRevision: string;
517
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
518
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
519
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
520
+ if (accessory.context.FirmwareRevision) {
521
+ FirmwareRevision = accessory.context.FirmwareRevision;
522
+ } else if (device.firmware) {
523
+ FirmwareRevision = device.firmware;
524
+ } else {
525
+ FirmwareRevision = this.platform.version;
526
+ }
527
+ return FirmwareRevision;
528
+ }
529
+
513
530
  minLux(): number {
514
531
  if (this.device.contact?.set_minLux) {
515
532
  this.set_minLux = this.device.contact!.set_minLux!;
@@ -4,6 +4,9 @@ import { SwitchBotPlatform } from '../platform';
4
4
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
5
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
6
  import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, payload, deviceStatus, ad } from '../settings';
7
+ import { Context } from 'vm';
8
+ import { MqttClient } from 'mqtt';
9
+ import { connectAsync } from 'async-mqtt';
7
10
 
8
11
  export class Curtain {
9
12
  // Services
@@ -58,12 +61,16 @@ export class Curtain {
58
61
  curtainUpdateInProgress!: boolean;
59
62
  doCurtainUpdate!: Subject<void>;
60
63
 
64
+ //MQTT stuff
65
+ mqttClient: MqttClient | null = null;
66
+
61
67
  constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
62
68
  // default placeholders
63
69
  this.logs(device);
64
70
  this.refreshRate(device);
65
71
  this.scan(device);
66
72
  this.config(device);
73
+ this.setupMqtt(device);
67
74
  this.CurrentPosition = 0;
68
75
  this.TargetPosition = 0;
69
76
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
@@ -82,7 +89,8 @@ export class Curtain {
82
89
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
83
90
  .setCharacteristic(this.platform.Characteristic.Model, 'W0701600')
84
91
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId)
85
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
92
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
93
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
86
94
 
87
95
  // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
88
96
  // you can create multiple services for each accessory
@@ -199,6 +207,35 @@ export class Curtain {
199
207
  });
200
208
  }
201
209
 
210
+ /*
211
+ * Publish MQTT message for topics of
212
+ * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
213
+ */
214
+ mqttPublish(topic: string, message: any) {
215
+ const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
216
+ const options = this.device.mqttPubOptions || {};
217
+ this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
218
+ this.debugLog(`Meter: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
219
+ }
220
+
221
+ /*
222
+ * Setup MQTT hadler if URL is specifed.
223
+ */
224
+ async setupMqtt(device: device & devicesConfig): Promise<void> {
225
+ if (device.mqttURL) {
226
+ try {
227
+ this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
228
+ this.debugLog(`Meter: ${this.accessory.displayName} MQTT connection has been established successfully.`);
229
+ this.mqttClient.on('error', (e: Error) => {
230
+ this.errorLog(`Meter: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
231
+ });
232
+ } catch (e) {
233
+ this.mqttClient = null;
234
+ this.errorLog(`Meter: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
235
+ }
236
+ }
237
+ }
238
+
202
239
  /**
203
240
  * Parse the device status from the SwitchBot api
204
241
  */
@@ -226,17 +263,17 @@ export class Curtain {
226
263
  this.debugLog(`Curtain: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`);
227
264
  this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
228
265
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
229
- this.warnLog(`Curtain: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
266
+ this.debugLog(`Curtain: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
230
267
  } else if (this.TargetPosition < this.CurrentPosition) {
231
268
  this.debugLog(`Curtain: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`);
232
269
  this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
233
270
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
234
- this.warnLog(`Curtain: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
271
+ this.debugLog(`Curtain: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
235
272
  } else {
236
273
  this.debugLog(`Curtain: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
237
274
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
238
275
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
239
- this.warnLog(`Curtain: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
276
+ this.debugLog(`Curtain: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
240
277
  }
241
278
  } else {
242
279
  this.debugLog(`Curtain: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
@@ -654,18 +691,21 @@ export class Curtain {
654
691
  } else {
655
692
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, Number(this.CurrentPosition));
656
693
  this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
694
+ this.mqttPublish('CurrentPosition', this.CurrentPosition);
657
695
  }
658
696
  if (this.PositionState === undefined) {
659
697
  this.debugLog(`Curtain: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
660
698
  } else {
661
699
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, Number(this.PositionState));
662
700
  this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
701
+ this.mqttPublish('PositionState', this.PositionState);
663
702
  }
664
703
  if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
665
704
  this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
666
705
  } else {
667
706
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, Number(this.TargetPosition));
668
707
  this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
708
+ this.mqttPublish('TargetPosition', this.TargetPosition);
669
709
  }
670
710
  if (!this.device.curtain?.hide_lightsensor) {
671
711
  if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
@@ -673,6 +713,7 @@ export class Curtain {
673
713
  } else {
674
714
  this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
675
715
  this.debugLog(`Curtain: ${this.accessory.displayName}` + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
716
+ this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
676
717
  }
677
718
  }
678
719
  if (this.device.ble) {
@@ -681,12 +722,14 @@ export class Curtain {
681
722
  } else {
682
723
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
683
724
  this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
725
+ this.mqttPublish('BatteryLevel', this.BatteryLevel);
684
726
  }
685
727
  if (this.StatusLowBattery === undefined) {
686
728
  this.debugLog(`Curtain: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
687
729
  } else {
688
730
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
689
731
  this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
732
+ this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
690
733
  }
691
734
  }
692
735
  }
@@ -743,6 +786,7 @@ export class Curtain {
743
786
  this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${value}`);
744
787
 
745
788
  this.TargetPosition = value;
789
+ this.mqttPublish('TargetPosition', this.TargetPosition);
746
790
 
747
791
  await this.setMinMax();
748
792
  if (value > this.CurrentPosition) {
@@ -870,6 +914,21 @@ export class Curtain {
870
914
  }
871
915
  }
872
916
 
917
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
918
+ let FirmwareRevision: string;
919
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
920
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
921
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
922
+ if (accessory.context.FirmwareRevision) {
923
+ FirmwareRevision = accessory.context.FirmwareRevision;
924
+ } else if (device.firmware) {
925
+ FirmwareRevision = device.firmware;
926
+ } else {
927
+ FirmwareRevision = this.platform.version;
928
+ }
929
+ return FirmwareRevision;
930
+ }
931
+
873
932
  minStep(device: device & devicesConfig): number {
874
933
  if (device.curtain?.set_minStep) {
875
934
  this.set_minStep = device.curtain?.set_minStep;
@@ -4,6 +4,7 @@ import { SwitchBotPlatform } from '../platform';
4
4
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
5
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
6
  import { DeviceURL, device, devicesConfig, serviceData, ad, deviceStatusResponse, payload, deviceStatus } from '../settings';
7
+ import { Context } from 'vm';
7
8
 
8
9
  /**
9
10
  * Platform Accessory
@@ -78,7 +79,8 @@ export class Humidifier {
78
79
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
79
80
  .setCharacteristic(this.platform.Characteristic.Model, 'W0801800')
80
81
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
81
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
82
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
83
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
82
84
 
83
85
  // get the service if it exists, otherwise create a new service
84
86
  // you can create multiple services for each accessory
@@ -812,6 +814,21 @@ export class Humidifier {
812
814
  }
813
815
  }
814
816
 
817
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
818
+ let FirmwareRevision: string;
819
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
820
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
821
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
822
+ if (accessory.context.FirmwareRevision) {
823
+ FirmwareRevision = accessory.context.FirmwareRevision;
824
+ } else if (device.firmware) {
825
+ FirmwareRevision = device.firmware;
826
+ } else {
827
+ FirmwareRevision = this.platform.version;
828
+ }
829
+ return FirmwareRevision;
830
+ }
831
+
815
832
  minStep(): number {
816
833
  if (this.device.humidifier?.set_minStep) {
817
834
  this.set_minStep = this.device.humidifier?.set_minStep;
@@ -4,6 +4,7 @@ import { SwitchBotPlatform } from '../platform';
4
4
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
5
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
6
  import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload, deviceStatus } from '../settings';
7
+ import { Context } from 'vm';
7
8
 
8
9
  export class Lock {
9
10
  // Services
@@ -55,7 +56,8 @@ export class Lock {
55
56
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
56
57
  .setCharacteristic(this.platform.Characteristic.Model, 'W1601700')
57
58
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
58
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
59
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
60
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
59
61
 
60
62
  // get the LockMechanism service if it exists, otherwise create a new LockMechanism service
61
63
  // you can create multiple services for each accessory
@@ -339,6 +341,21 @@ export class Lock {
339
341
  }
340
342
  }
341
343
 
344
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
345
+ let FirmwareRevision: string;
346
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
347
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
348
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
349
+ if (accessory.context.FirmwareRevision) {
350
+ FirmwareRevision = accessory.context.FirmwareRevision;
351
+ } else if (device.firmware) {
352
+ FirmwareRevision = device.firmware;
353
+ } else {
354
+ FirmwareRevision = this.platform.version;
355
+ }
356
+ return FirmwareRevision;
357
+ }
358
+
342
359
  /**
343
360
  * Logging for Device
344
361
  */
@@ -3,6 +3,10 @@ import { skipWhile } from 'rxjs/operators';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
5
5
  import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature, deviceStatus } from '../settings';
6
+ import { Context } from 'vm';
7
+ import { MqttClient } from 'mqtt';
8
+ import { connectAsync } from 'async-mqtt';
9
+ import { hostname } from "os";
6
10
 
7
11
  /**
8
12
  * Platform Accessory
@@ -50,12 +54,20 @@ export class Meter {
50
54
  meterUpdateInProgress!: boolean;
51
55
  doMeterUpdate: Subject<void>;
52
56
 
57
+ //MQTT stuff
58
+ mqttClient: MqttClient | null = null;
59
+
60
+ // EVE history service handler
61
+ historyService: any;
62
+
53
63
  constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
54
64
  // default placeholders
55
65
  this.logs(device);
56
66
  this.scan(device);
67
+ this.setupHistoryService(device);
57
68
  this.refreshRate(device);
58
69
  this.config(device);
70
+ this.setupMqtt(device);
59
71
  if (this.CurrentRelativeHumidity === undefined) {
60
72
  this.CurrentRelativeHumidity = 0;
61
73
  } else {
@@ -80,7 +92,8 @@ export class Meter {
80
92
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
81
93
  .setCharacteristic(this.platform.Characteristic.Model, 'METERTH-S1')
82
94
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
83
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
95
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
96
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
84
97
 
85
98
  // Temperature Sensor Service
86
99
  if (device.meter?.hide_temperature) {
@@ -164,6 +177,48 @@ export class Meter {
164
177
  });
165
178
  }
166
179
 
180
+ /*
181
+ * Publish MQTT message for topics of
182
+ * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
183
+ */
184
+ mqttPublish(message: any) {
185
+ const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
186
+ const options = this.device.mqttPubOptions || {};
187
+ this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
188
+ this.debugLog(`Meter: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`);
189
+ }
190
+
191
+ /*
192
+ * Setup MQTT hadler if URL is specifed.
193
+ */
194
+ async setupMqtt(device: device & devicesConfig): Promise<void> {
195
+ if (device.mqttURL) {
196
+ try {
197
+ this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
198
+ this.debugLog(`Meter: ${this.accessory.displayName} MQTT connection has been established successfully.`)
199
+ this.mqttClient.on('error', (e: Error) => {
200
+ this.errorLog(`Meter: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`)
201
+ });
202
+ } catch (e) {
203
+ this.mqttClient = null;
204
+ this.errorLog(`Meter: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`)
205
+ }
206
+ }
207
+ }
208
+
209
+ /*
210
+ * Setup EVE history graph feature if enabled.
211
+ */
212
+ async setupHistoryService(device: device & devicesConfig): Promise<void> {
213
+ const mac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
214
+ this.historyService = device.history ?
215
+ new this.platform.fakegatoAPI('room', this.accessory,
216
+ {log: this.platform.log, storage: 'fs',
217
+ filename: `${hostname().split(".")[0]}_${mac}_persist.json`
218
+ }) :
219
+ null;
220
+ }
221
+
167
222
  /**
168
223
  * Parse the device status from the SwitchBot api
169
224
  */
@@ -185,7 +240,7 @@ export class Meter {
185
240
  } else {
186
241
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
187
242
  }
188
- this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
243
+ this.debugLog(`Meter: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
189
244
 
190
245
  // Humidity
191
246
  if (!this.device.meter?.hide_humidity) {
@@ -259,10 +314,12 @@ export class Meter {
259
314
  this.infoLog(`Meter: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
260
315
  this.infoLog(`Meter: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
261
316
  }
262
- this.serviceData = ad.serviceData;
263
- this.temperature = ad.serviceData.temperature!.c;
264
- this.humidity = ad.serviceData.humidity;
265
- this.battery = ad.serviceData.battery;
317
+ if (ad.serviceData.humidity! > 0) { // reject unreliable data
318
+ this.serviceData = ad.serviceData;
319
+ this.temperature = ad.serviceData.temperature!.c;
320
+ this.humidity = ad.serviceData.humidity;
321
+ this.battery = ad.serviceData.battery;
322
+ }
266
323
  this.debugLog(`Meter: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
267
324
  this.debugLog(
268
325
  `Meter: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` +
@@ -363,12 +420,16 @@ export class Meter {
363
420
  * Updates the status for each of the HomeKit Characteristics
364
421
  */
365
422
  async updateHomeKitCharacteristics(): Promise<void> {
423
+ let mqttmessage: string[] = [];
424
+ let entry = {time: Math.round(new Date().valueOf()/1000)};
366
425
  if (!this.device.meter?.hide_humidity) {
367
426
  if (this.CurrentRelativeHumidity === undefined) {
368
427
  this.debugLog(`Meter: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
369
428
  } else {
370
429
  this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
371
430
  this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
431
+ mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`);
432
+ entry["humidity"] = this.CurrentRelativeHumidity;
372
433
  }
373
434
  }
374
435
  if (!this.device.meter?.hide_temperature) {
@@ -377,6 +438,8 @@ export class Meter {
377
438
  } else {
378
439
  this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature);
379
440
  this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
441
+ mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
442
+ entry["temp"] = this.CurrentTemperature;
380
443
  }
381
444
  }
382
445
  if (this.device.ble) {
@@ -385,14 +448,20 @@ export class Meter {
385
448
  } else {
386
449
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
387
450
  this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
451
+ mqttmessage.push(`"battery": ${this.BatteryLevel}`);
388
452
  }
389
453
  if (this.StatusLowBattery === undefined) {
390
454
  this.debugLog(`Meter: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
391
455
  } else {
392
456
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
393
457
  this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
458
+ mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
394
459
  }
395
460
  }
461
+ this.mqttPublish(`{${mqttmessage.join(',')}}`)
462
+ if (this.CurrentRelativeHumidity > 0) { // reject unreliable data
463
+ this.historyService?.addEntry(entry);
464
+ }
396
465
  }
397
466
 
398
467
  async apiError(e: any): Promise<void> {
@@ -471,6 +540,21 @@ export class Meter {
471
540
  }
472
541
  }
473
542
 
543
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
544
+ let FirmwareRevision: string;
545
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
546
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
547
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
548
+ if (accessory.context.FirmwareRevision) {
549
+ FirmwareRevision = accessory.context.FirmwareRevision;
550
+ } else if (device.firmware) {
551
+ FirmwareRevision = device.firmware;
552
+ } else {
553
+ FirmwareRevision = this.platform.version;
554
+ }
555
+ return FirmwareRevision;
556
+ }
557
+
474
558
  /**
475
559
  * Logging for Device
476
560
  */
@@ -3,6 +3,7 @@ import { skipWhile } from 'rxjs/operators';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
5
5
  import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature, deviceStatus } from '../settings';
6
+ import { Context } from 'vm';
6
7
 
7
8
  /**
8
9
  * Platform Accessory
@@ -80,7 +81,8 @@ export class MeterPlus {
80
81
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
81
82
  .setCharacteristic(this.platform.Characteristic.Model, this.model(device))
82
83
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
83
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
84
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
85
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
84
86
 
85
87
  // Temperature Sensor Service
86
88
  if (device.meter?.hide_temperature) {
@@ -482,6 +484,21 @@ export class MeterPlus {
482
484
  }
483
485
  }
484
486
 
487
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
488
+ let FirmwareRevision: string;
489
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
490
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
491
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
492
+ if (accessory.context.FirmwareRevision) {
493
+ FirmwareRevision = accessory.context.FirmwareRevision;
494
+ } else if (device.firmware) {
495
+ FirmwareRevision = device.firmware;
496
+ } else {
497
+ FirmwareRevision = this.platform.version;
498
+ }
499
+ return FirmwareRevision;
500
+ }
501
+
485
502
  /**
486
503
  * Logging for Device
487
504
  */
@@ -3,6 +3,7 @@ import { skipWhile } from 'rxjs/operators';
3
3
  import { SwitchBotPlatform } from '../platform';
4
4
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
5
5
  import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, deviceStatus, ad } from '../settings';
6
+ import { Context } from 'vm';
6
7
 
7
8
  /**
8
9
  * Platform Accessory
@@ -68,7 +69,8 @@ export class Motion {
68
69
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
69
70
  .setCharacteristic(this.platform.Characteristic.Model, 'W1101500')
70
71
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
71
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
72
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
73
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
72
74
 
73
75
  // get the Battery service if it exists, otherwise create a new Motion service
74
76
  // you can create multiple services for each accessory
@@ -454,6 +456,21 @@ export class Motion {
454
456
  }
455
457
  }
456
458
 
459
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
460
+ let FirmwareRevision: string;
461
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
462
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
463
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
464
+ if (accessory.context.FirmwareRevision) {
465
+ FirmwareRevision = accessory.context.FirmwareRevision;
466
+ } else if (device.firmware) {
467
+ FirmwareRevision = device.firmware;
468
+ } else {
469
+ FirmwareRevision = this.platform.version;
470
+ }
471
+ return FirmwareRevision;
472
+ }
473
+
457
474
  minLux(): number {
458
475
  if (this.device.motion?.set_minLux) {
459
476
  this.set_minLux = this.device.motion!.set_minLux!;
@@ -4,6 +4,7 @@ import { SwitchBotPlatform } from '../platform';
4
4
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
5
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
6
  import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload, deviceStatus } from '../settings';
7
+ import { Context } from 'vm';
7
8
 
8
9
  export class Plug {
9
10
  // Services
@@ -51,7 +52,8 @@ export class Plug {
51
52
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
52
53
  .setCharacteristic(this.platform.Characteristic.Model, this.model(device))
53
54
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!)
54
- .setCharacteristic(this.platform.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
55
+ .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.FirmwareRevision(accessory, device))
56
+ .getCharacteristic(this.platform.Characteristic.FirmwareRevision).updateValue(this.FirmwareRevision(accessory, device));
55
57
 
56
58
  // get the Outlet service if it exists, otherwise create a new Outlet service
57
59
  // you can create multiple services for each accessory
@@ -316,6 +318,21 @@ export class Plug {
316
318
  }
317
319
  }
318
320
 
321
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
322
+ let FirmwareRevision: string;
323
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
324
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
325
+ this.debugLog(`Color Bulb: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
326
+ if (accessory.context.FirmwareRevision) {
327
+ FirmwareRevision = accessory.context.FirmwareRevision;
328
+ } else if (device.firmware) {
329
+ FirmwareRevision = device.firmware;
330
+ } else {
331
+ FirmwareRevision = this.platform.version;
332
+ }
333
+ return FirmwareRevision;
334
+ }
335
+
319
336
  /**
320
337
  * Logging for Device
321
338
  */