@switchbot/homebridge-switchbot 3.5.0-beta.9 → 3.5.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 (178) hide show
  1. package/.vscode/settings.json +4 -1
  2. package/CHANGELOG.md +4 -1
  3. package/config.schema.json +166 -24
  4. package/dist/device/blindtilt.d.ts +21 -96
  5. package/dist/device/blindtilt.d.ts.map +1 -1
  6. package/dist/device/blindtilt.js +439 -817
  7. package/dist/device/blindtilt.js.map +1 -1
  8. package/dist/device/bot.d.ts +27 -72
  9. package/dist/device/bot.d.ts.map +1 -1
  10. package/dist/device/bot.js +835 -865
  11. package/dist/device/bot.js.map +1 -1
  12. package/dist/device/ceilinglight.d.ts +15 -69
  13. package/dist/device/ceilinglight.d.ts.map +1 -1
  14. package/dist/device/ceilinglight.js +287 -653
  15. package/dist/device/ceilinglight.js.map +1 -1
  16. package/dist/device/colorbulb.d.ts +15 -76
  17. package/dist/device/colorbulb.d.ts.map +1 -1
  18. package/dist/device/colorbulb.js +354 -732
  19. package/dist/device/colorbulb.js.map +1 -1
  20. package/dist/device/contact.d.ts +14 -66
  21. package/dist/device/contact.d.ts.map +1 -1
  22. package/dist/device/contact.js +268 -576
  23. package/dist/device/contact.js.map +1 -1
  24. package/dist/device/curtain.d.ts +18 -89
  25. package/dist/device/curtain.d.ts.map +1 -1
  26. package/dist/device/curtain.js +431 -851
  27. package/dist/device/curtain.js.map +1 -1
  28. package/dist/device/device.d.ts +60 -0
  29. package/dist/device/device.d.ts.map +1 -0
  30. package/dist/device/device.js +739 -0
  31. package/dist/device/device.js.map +1 -0
  32. package/dist/device/fan.d.ts +63 -0
  33. package/dist/device/fan.d.ts.map +1 -0
  34. package/dist/device/fan.js +698 -0
  35. package/dist/device/fan.js.map +1 -0
  36. package/dist/device/hub.d.ts +15 -60
  37. package/dist/device/hub.d.ts.map +1 -1
  38. package/dist/device/hub.js +257 -631
  39. package/dist/device/hub.js.map +1 -1
  40. package/dist/device/humidifier.d.ts +11 -66
  41. package/dist/device/humidifier.d.ts.map +1 -1
  42. package/dist/device/humidifier.js +266 -629
  43. package/dist/device/humidifier.js.map +1 -1
  44. package/dist/device/iosensor.d.ts +12 -64
  45. package/dist/device/iosensor.d.ts.map +1 -1
  46. package/dist/device/iosensor.js +215 -615
  47. package/dist/device/iosensor.js.map +1 -1
  48. package/dist/device/lightstrip.d.ts +12 -70
  49. package/dist/device/lightstrip.d.ts.map +1 -1
  50. package/dist/device/lightstrip.js +336 -645
  51. package/dist/device/lightstrip.js.map +1 -1
  52. package/dist/device/lock.d.ts +17 -63
  53. package/dist/device/lock.d.ts.map +1 -1
  54. package/dist/device/lock.js +290 -595
  55. package/dist/device/lock.js.map +1 -1
  56. package/dist/device/meter.d.ts +15 -64
  57. package/dist/device/meter.d.ts.map +1 -1
  58. package/dist/device/meter.js +211 -608
  59. package/dist/device/meter.js.map +1 -1
  60. package/dist/device/meterplus.d.ts +15 -65
  61. package/dist/device/meterplus.d.ts.map +1 -1
  62. package/dist/device/meterplus.js +205 -615
  63. package/dist/device/meterplus.js.map +1 -1
  64. package/dist/device/motion.d.ts +12 -69
  65. package/dist/device/motion.d.ts.map +1 -1
  66. package/dist/device/motion.js +202 -563
  67. package/dist/device/motion.js.map +1 -1
  68. package/dist/device/plug.d.ts +10 -57
  69. package/dist/device/plug.d.ts.map +1 -1
  70. package/dist/device/plug.js +125 -484
  71. package/dist/device/plug.js.map +1 -1
  72. package/dist/device/robotvacuumcleaner.d.ts +13 -63
  73. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  74. package/dist/device/robotvacuumcleaner.js +233 -549
  75. package/dist/device/robotvacuumcleaner.js.map +1 -1
  76. package/dist/device/waterdetector.d.ts +11 -63
  77. package/dist/device/waterdetector.d.ts.map +1 -1
  78. package/dist/device/waterdetector.js +170 -493
  79. package/dist/device/waterdetector.js.map +1 -1
  80. package/dist/homebridge-ui/public/index.html +22 -16
  81. package/dist/homebridge-ui/server.js +1 -1
  82. package/dist/homebridge-ui/server.js.map +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +2 -2
  86. package/dist/index.js.map +1 -1
  87. package/dist/irdevice/airconditioner.d.ts +10 -50
  88. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  89. package/dist/irdevice/airconditioner.js +340 -468
  90. package/dist/irdevice/airconditioner.js.map +1 -1
  91. package/dist/irdevice/airpurifier.d.ts +8 -39
  92. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  93. package/dist/irdevice/airpurifier.js +105 -319
  94. package/dist/irdevice/airpurifier.js.map +1 -1
  95. package/dist/irdevice/camera.d.ts +7 -36
  96. package/dist/irdevice/camera.d.ts.map +1 -1
  97. package/dist/irdevice/camera.js +44 -285
  98. package/dist/irdevice/camera.js.map +1 -1
  99. package/dist/irdevice/fan.d.ts +7 -43
  100. package/dist/irdevice/fan.d.ts.map +1 -1
  101. package/dist/irdevice/fan.js +92 -350
  102. package/dist/irdevice/fan.js.map +1 -1
  103. package/dist/irdevice/irdevice.d.ts +40 -0
  104. package/dist/irdevice/irdevice.d.ts.map +1 -0
  105. package/dist/irdevice/irdevice.js +369 -0
  106. package/dist/irdevice/irdevice.js.map +1 -0
  107. package/dist/irdevice/light.d.ts +12 -45
  108. package/dist/irdevice/light.d.ts.map +1 -1
  109. package/dist/irdevice/light.js +118 -362
  110. package/dist/irdevice/light.js.map +1 -1
  111. package/dist/irdevice/other.d.ts +19 -48
  112. package/dist/irdevice/other.d.ts.map +1 -1
  113. package/dist/irdevice/other.js +438 -570
  114. package/dist/irdevice/other.js.map +1 -1
  115. package/dist/irdevice/tv.d.ts +8 -40
  116. package/dist/irdevice/tv.d.ts.map +1 -1
  117. package/dist/irdevice/tv.js +91 -339
  118. package/dist/irdevice/tv.js.map +1 -1
  119. package/dist/irdevice/vacuumcleaner.d.ts +7 -36
  120. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  121. package/dist/irdevice/vacuumcleaner.js +45 -285
  122. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  123. package/dist/irdevice/waterheater.d.ts +7 -36
  124. package/dist/irdevice/waterheater.d.ts.map +1 -1
  125. package/dist/irdevice/waterheater.js +47 -291
  126. package/dist/irdevice/waterheater.js.map +1 -1
  127. package/dist/platform.d.ts +22 -8
  128. package/dist/platform.d.ts.map +1 -1
  129. package/dist/platform.js +342 -191
  130. package/dist/platform.js.map +1 -1
  131. package/dist/settings.d.ts +64 -17
  132. package/dist/settings.d.ts.map +1 -1
  133. package/dist/settings.js +16 -552
  134. package/dist/settings.js.map +1 -1
  135. package/dist/utils.d.ts +77 -0
  136. package/dist/utils.d.ts.map +1 -1
  137. package/dist/utils.js +638 -1
  138. package/dist/utils.js.map +1 -1
  139. package/eslint.config.js +96 -0
  140. package/package.json +20 -16
  141. package/src/custom.d.ts +2 -1
  142. package/src/device/blindtilt.ts +472 -888
  143. package/src/device/bot.ts +893 -968
  144. package/src/device/ceilinglight.ts +304 -709
  145. package/src/device/colorbulb.ts +370 -803
  146. package/src/device/contact.ts +301 -611
  147. package/src/device/curtain.ts +460 -908
  148. package/src/device/device.ts +755 -0
  149. package/src/device/fan.ts +749 -0
  150. package/src/device/hub.ts +294 -664
  151. package/src/device/humidifier.ts +299 -696
  152. package/src/device/iosensor.ts +251 -644
  153. package/src/device/lightstrip.ts +362 -712
  154. package/src/device/lock.ts +328 -653
  155. package/src/device/meter.ts +247 -637
  156. package/src/device/meterplus.ts +240 -644
  157. package/src/device/motion.ts +230 -598
  158. package/src/device/plug.ts +139 -525
  159. package/src/device/robotvacuumcleaner.ts +258 -593
  160. package/src/device/waterdetector.ts +191 -522
  161. package/src/homebridge-ui/public/index.html +22 -16
  162. package/src/homebridge-ui/server.ts +1 -1
  163. package/src/index.ts +4 -3
  164. package/src/irdevice/airconditioner.ts +370 -527
  165. package/src/irdevice/airpurifier.ts +132 -356
  166. package/src/irdevice/camera.ts +58 -311
  167. package/src/irdevice/fan.ts +109 -379
  168. package/src/irdevice/irdevice.ts +382 -0
  169. package/src/irdevice/light.ts +157 -412
  170. package/src/irdevice/other.ts +507 -648
  171. package/src/irdevice/tv.ts +121 -375
  172. package/src/irdevice/vacuumcleaner.ts +59 -311
  173. package/src/irdevice/waterheater.ts +61 -320
  174. package/src/platform.ts +395 -259
  175. package/src/settings.ts +118 -609
  176. package/src/utils.ts +642 -1
  177. package/.github/npm-version-script.cjs +0 -81
  178. package/src/device/waterdetector.json +0 -11
@@ -1,112 +1,96 @@
1
- import asyncmqtt from 'async-mqtt';
2
- import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, HAP } from 'homebridge';
3
- import { MqttClient } from 'mqtt';
4
- import { hostname } from 'os';
5
- import { interval } from 'rxjs';
6
- import { request } from 'undici';
7
- import { SwitchBotPlatform } from '../platform.js';
8
- import { Devices, device, deviceStatus, devicesConfig, serviceData, temperature, SwitchBotPlatformConfig } from '../settings.js';
9
- import { sleep } from '../utils.js';
10
-
11
- export class Meter {
12
- public readonly api: API;
13
- public readonly log: Logging;
14
- public readonly config!: SwitchBotPlatformConfig;
15
- protected readonly hap: HAP;
1
+ /* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
+ *
3
+ * meter.ts: @switchbot/homebridge-switchbot.
4
+ */
5
+ import { Units } from 'homebridge';
6
+ import { Devices } from '../settings.js';
7
+ import { deviceBase } from './device.js';
8
+ import { convertUnits } from '../utils.js';
9
+ import { Subject, interval, skipWhile } from 'rxjs';
10
+
11
+ import type { SwitchBotPlatform } from '../platform.js';
12
+ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
13
+ import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
14
+
15
+ export class Meter extends deviceBase {
16
16
  // Services
17
- batteryService: Service;
18
- humidityService?: Service;
19
- temperatureService?: Service;
20
-
21
- // Characteristic Values
22
- BatteryLevel!: CharacteristicValue;
23
- FirmwareRevision!: CharacteristicValue;
24
- StatusLowBattery!: CharacteristicValue;
25
- CurrentTemperature?: CharacteristicValue;
26
- CurrentRelativeHumidity?: CharacteristicValue;
27
-
28
- // OpenAPI Status
29
- OpenAPI_BatteryLevel: deviceStatus['battery'];
30
- OpenAPI_FirmwareRevision: deviceStatus['version'];
31
- OpenAPI_CurrentTemperature: deviceStatus['temperature'];
32
- OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity'];
33
-
34
- // BLE Status
35
- BLE_Celsius!: temperature['c'];
36
- BLE_Fahrenheit!: temperature['f'];
37
- BLE_BatteryLevel!: serviceData['battery'];
38
- BLE_CurrentTemperature!: serviceData['temperature'];
39
- BLE_CurrentRelativeHumidity!: serviceData['humidity'];
40
-
41
- // BLE Others
42
- BLE_IsConnected?: boolean;
43
-
44
- //MQTT stuff
45
- mqttClient: MqttClient | null = null;
46
-
47
- // EVE history service handler
48
- historyService?: any;
49
-
50
- // Config
51
- scanDuration!: number;
52
- deviceLogging!: string;
53
- deviceRefreshRate!: number;
54
-
55
- // Connection
56
- private readonly OpenAPI: boolean;
57
- private readonly BLE: boolean;
17
+ private Battery: {
18
+ Name: CharacteristicValue;
19
+ Service: Service;
20
+ BatteryLevel: CharacteristicValue;
21
+ StatusLowBattery: CharacteristicValue;
22
+ };
23
+
24
+ private HumiditySensor?: {
25
+ Name: CharacteristicValue;
26
+ Service: Service;
27
+ CurrentRelativeHumidity: CharacteristicValue;
28
+ };
29
+
30
+ private TemperatureSensor?: {
31
+ Name: CharacteristicValue;
32
+ Service: Service;
33
+ CurrentTemperature: CharacteristicValue;
34
+ };
35
+
36
+ // Updates
37
+ meterUpdateInProgress!: boolean;
38
+ doMeterUpdate: Subject<void>;
58
39
 
59
40
  constructor(
60
- private readonly platform: SwitchBotPlatform,
61
- private accessory: PlatformAccessory,
62
- public device: device & devicesConfig,
41
+ readonly platform: SwitchBotPlatform,
42
+ accessory: PlatformAccessory,
43
+ device: device & devicesConfig,
63
44
  ) {
64
- this.api = this.platform.api;
65
- this.log = this.platform.log;
66
- this.config = this.platform.config;
67
- this.hap = this.api.hap;
68
- // Connection
69
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
70
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
71
- // default placeholders
72
- this.deviceLogs(device);
73
- this.scan(device);
74
- this.refreshRate(device);
75
- this.deviceContext();
76
- this.setupHistoryService(device);
77
- this.setupMqtt(device);
78
- this.deviceConfig(device);
79
-
80
- this.CurrentRelativeHumidity = accessory.context.CurrentRelativeHumidity;
81
- this.CurrentTemperature = accessory.context.CurrentTemperature;
82
-
83
- // Retrieve initial values and updateHomekit
84
- this.refreshStatus();
45
+ super(platform, accessory, device);
46
+ // this is subject we use to track when we need to POST changes to the SwitchBot API
47
+ this.doMeterUpdate = new Subject();
48
+ this.meterUpdateInProgress = false;
49
+
50
+ // Initialize Battery Service
51
+ accessory.context.Battery = accessory.context.Battery ?? {};
52
+ this.Battery = {
53
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
54
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
55
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
56
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
57
+ };
58
+ accessory.context.Battery = this.Battery as object;
59
+
60
+ // Initialize Battery Characteristics
61
+ this.Battery.Service
62
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
63
+ .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE)
64
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
65
+ .onGet(() => {
66
+ return this.Battery.BatteryLevel;
67
+ });
85
68
 
86
- // set accessory information
87
- accessory
88
- .getService(this.hap.Service.AccessoryInformation)!
89
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
90
- .setCharacteristic(this.hap.Characteristic.Model, 'METERTH-S1')
91
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
92
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
69
+ this.Battery.Service
70
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
71
+ .onGet(() => {
72
+ return this.Battery.StatusLowBattery;
73
+ });
93
74
 
94
- // Temperature Sensor Service
75
+ // Initialize Temperature Sensor Service
95
76
  if (device.meter?.hide_temperature) {
96
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
97
- this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor);
98
- accessory.removeService(this.temperatureService!);
99
- } else if (!this.temperatureService) {
100
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`);
101
- const temperatureService = `${accessory.displayName} Temperature Sensor`;
102
- (this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor)
103
- || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureService;
104
-
105
- this.temperatureService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
106
- if (!this.temperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
107
- this.temperatureService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`);
77
+ if (this.TemperatureSensor) {
78
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
79
+ this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service;
80
+ accessory.removeService(this.TemperatureSensor.Service);
108
81
  }
109
- this.temperatureService
82
+ } else {
83
+ accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {};
84
+ this.TemperatureSensor = {
85
+ Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`,
86
+ Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service,
87
+ CurrentTemperature: accessory.context.CurrentTemperature ?? 30,
88
+ };
89
+ accessory.context.TemperatureSensor = this.TemperatureSensor as object;
90
+
91
+ // Initialize Temperature Sensor Characteristics
92
+ this.TemperatureSensor.Service
93
+ .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name)
110
94
  .getCharacteristic(this.hap.Characteristic.CurrentTemperature)
111
95
  .setProps({
112
96
  unit: Units['CELSIUS'],
@@ -116,168 +100,122 @@ export class Meter {
116
100
  minStep: 0.1,
117
101
  })
118
102
  .onGet(() => {
119
- return this.CurrentTemperature!;
103
+ return this.TemperatureSensor!.CurrentTemperature!;
120
104
  });
121
- } else {
122
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`);
123
105
  }
124
-
125
- // Humidity Sensor Service
106
+ // Initialize Humidity Sensor Service
126
107
  if (device.meter?.hide_humidity) {
127
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
128
- this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor);
129
- accessory.removeService(this.humidityService!);
130
- } else if (!this.humidityService) {
131
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`);
132
- const humidityService = `${accessory.displayName} Humidity Sensor`;
133
- (this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor)
134
- || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityService;
135
-
136
- this.humidityService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`);
137
- if (!this.humidityService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
138
- this.humidityService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`);
108
+ if (this.HumiditySensor) {
109
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
110
+ this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service;
111
+ accessory.removeService(this.HumiditySensor.Service);
139
112
  }
140
- this.humidityService
113
+ } else {
114
+ accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {};
115
+ this.HumiditySensor = {
116
+ Name: accessory.context.HumiditySensorName ?? `${accessory.displayName} Humidity Sensor`,
117
+ Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service,
118
+ CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50,
119
+ };
120
+ accessory.context.HumiditySensor = this.HumiditySensor as object;
121
+
122
+ // Initialize Humidity Sensor Characteristics
123
+ this.HumiditySensor!.Service
124
+ .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name)
141
125
  .getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity)
142
126
  .setProps({
143
127
  minStep: 0.1,
144
128
  })
145
129
  .onGet(() => {
146
- return this.CurrentRelativeHumidity!;
130
+ return this.HumiditySensor!.CurrentRelativeHumidity!;
147
131
  });
148
- } else {
149
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`);
150
132
  }
151
133
 
152
- // Battery Service
153
- const batteryService = `${accessory.displayName} Battery`;
154
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
155
- || accessory.addService(this.hap.Service.Battery)), batteryService;
156
-
157
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
158
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
159
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
160
- }
161
- this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE);
134
+ // Retrieve initial values and updateHomekit
135
+ this.refreshStatus();
162
136
 
163
137
  // Retrieve initial values and update Homekit
164
138
  this.updateHomeKitCharacteristics();
165
139
 
166
140
  // Start an update interval
167
141
  interval(this.deviceRefreshRate * 1000)
142
+ .pipe(skipWhile(() => this.meterUpdateInProgress))
168
143
  .subscribe(async () => {
169
144
  await this.refreshStatus();
170
145
  });
171
146
 
172
147
  //regisiter webhook event handler
173
- if (this.device.webhook) {
174
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
175
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
176
- try {
177
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
178
- if (context.scale === 'CELSIUS') {
179
- const { temperature, humidity } = context;
180
- const { CurrentTemperature, CurrentRelativeHumidity } = this;
181
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
182
- '(temperature, humidity) = ' +
183
- `Webhook:(${temperature}, ${humidity}), ` +
184
- `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`);
185
- this.CurrentRelativeHumidity = humidity;
186
- this.CurrentTemperature = temperature;
187
- this.updateHomeKitCharacteristics();
188
- }
189
- } catch (e: any) {
190
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
191
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
192
- }
193
- };
194
- }
195
- }
196
-
197
- /**
198
- * Parse the device status from the SwitchBot api
199
- */
200
- async parseStatus(): Promise<void> {
201
- if (!this.device.enableCloudService && this.OpenAPI) {
202
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
203
- } else if (this.BLE) {
204
- await this.BLEparseStatus();
205
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
206
- await this.openAPIparseStatus();
207
- } else {
208
- await this.offlineOff();
209
- this.debugWarnLog(
210
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
211
- );
212
- }
148
+ this.registerWebhook(accessory, device);
213
149
  }
214
150
 
215
- async BLEparseStatus(): Promise<void> {
151
+ async BLEparseStatus(serviceData: serviceData): Promise<void> {
216
152
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
217
153
 
218
154
  // BatteryLevel
219
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
220
- if (this.BatteryLevel < 15) {
221
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
155
+ this.Battery.BatteryLevel = Number(serviceData.battery);
156
+ if (this.Battery.BatteryLevel < 15) {
157
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
222
158
  } else {
223
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
159
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
224
160
  }
225
- this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
161
+ this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`);
226
162
 
227
163
  // CurrentRelativeHumidity
228
164
  if (!this.device.meter?.hide_humidity) {
229
- this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!;
230
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
165
+ this.HumiditySensor!.CurrentRelativeHumidity = serviceData.humidity!;
166
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`);
231
167
  }
232
168
 
233
169
  // CurrentTemperature
234
170
  if (!this.device.meter?.hide_temperature) {
235
- this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius;
236
- this.CurrentTemperature = this.BLE_Celsius;
237
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
171
+ serviceData.temperature!.c < 0 ? 0 : serviceData.temperature!.c > 100 ? 100 : serviceData.temperature!.c;
172
+ this.TemperatureSensor!.CurrentTemperature = serviceData.temperature!.c;
173
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`);
238
174
  }
239
175
  }
240
176
 
241
- async openAPIparseStatus(): Promise<void> {
177
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise<void> {
242
178
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
243
179
 
244
- // BatteryLevel
245
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
246
- if (this.BatteryLevel < 15) {
247
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
180
+ // Battery
181
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
182
+ if (this.Battery.BatteryLevel < 10) {
183
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
248
184
  } else {
249
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
185
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
186
+ }
187
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
188
+ this.Battery.BatteryLevel = 100;
250
189
  }
251
- this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
190
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
191
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
252
192
 
253
193
  // CurrentRelativeHumidity
254
194
  if (!this.device.meter?.hide_humidity) {
255
- this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!;
256
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
195
+ this.HumiditySensor!.CurrentRelativeHumidity = deviceStatus.body.humidity!;
196
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`);
257
197
  }
258
198
 
259
199
  // CurrentTemperature
260
200
  if (!this.device.meter?.hide_temperature) {
261
- this.CurrentTemperature = this.OpenAPI_CurrentTemperature!;
262
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
201
+ this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!;
202
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`);
203
+ }
204
+
205
+ // Firmware Version
206
+ const version = deviceStatus.body.version?.toString();
207
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
208
+ if (deviceStatus.body.version) {
209
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
210
+ this.accessory
211
+ .getService(this.hap.Service.AccessoryInformation)!
212
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
213
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
214
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
215
+ .updateValue(deviceVersion);
216
+ this.accessory.context.deviceVersion = deviceVersion;
217
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
263
218
  }
264
-
265
- // Battery
266
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
267
- if (this.BatteryLevel < 10) {
268
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
269
- } else {
270
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
271
- }
272
- if (Number.isNaN(this.BatteryLevel)) {
273
- this.BatteryLevel = 100;
274
- }
275
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
276
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
277
-
278
- // FirmwareRevision
279
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
280
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
281
219
  }
282
220
 
283
221
  /**
@@ -292,10 +230,8 @@ export class Meter {
292
230
  await this.openAPIRefreshStatus();
293
231
  } else {
294
232
  await this.offlineOff();
295
- this.debugWarnLog(
296
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
297
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
298
- );
233
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
234
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
299
235
  }
300
236
  }
301
237
 
@@ -311,108 +247,44 @@ export class Meter {
311
247
  this.getCustomBLEAddress(switchbot);
312
248
  // Start to monitor advertisement packets
313
249
  (async () => {
314
- await switchbot.startScan({
315
- model: 'T',
316
- id: this.device.bleMac,
317
- });
250
+ // Start to monitor advertisement packets
251
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
318
252
  // Set an event handler
319
253
  switchbot.onadvertisement = (ad: any) => {
320
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
321
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
322
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'T') {
254
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
255
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
256
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
323
257
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
324
- this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity < 0 ? 0 : ad.serviceData.humidity > 100 ? 100 : ad.serviceData.humidity;
325
- this.BLE_CurrentTemperature = ad.serviceData.temperature;
326
- this.BLE_Celsius = ad.serviceData.temperature!.c;
327
- this.BLE_Fahrenheit = ad.serviceData.temperature!.f;
328
- this.BLE_BatteryLevel = ad.serviceData.battery;
329
258
  } else {
330
259
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
331
260
  }
332
261
  };
333
- // Wait 1 seconds
262
+ // Wait 10 seconds
334
263
  await switchbot.wait(this.scanDuration * 1000);
335
264
  // Stop to monitor
336
265
  await switchbot.stopScan();
337
266
  // Update HomeKit
338
- await this.BLEparseStatus();
267
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
339
268
  await this.updateHomeKitCharacteristics();
340
269
  })();
341
- /*if (switchbot !== false) {
342
- switchbot
343
- .startScan({
344
- model: 'T',
345
- id: this.device.bleMac,
346
- })
347
- .then(async () => {
348
- // Set an event handler
349
- switchbot.onadvertisement = async (ad: ad) => {
350
- this.debugLog(
351
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
352
- ` BLE Address Found: ${ad.address}`,
353
- );
354
- if (ad.serviceData.humidity! > 0) {
355
- // reject unreliable data
356
- this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity;
357
- }
358
- this.BLE_CurrentTemperature = ad.serviceData.temperature;
359
- this.BLE_Celsius = ad.serviceData.temperature!.c;
360
- this.BLE_Fahrenheit = ad.serviceData.temperature!.f;
361
- this.BLE_BatteryLevel = ad.serviceData.battery;
362
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
363
- this.debugLog(
364
- `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` +
365
- `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` +
366
- `battery: ${ad.serviceData.battery}`,
367
- );
368
-
369
- if (ad.serviceData) {
370
- this.BLE_IsConnected = true;
371
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
372
- await this.stopScanning(switchbot);
373
- } else {
374
- this.BLE_IsConnected = false;
375
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
376
- }
377
- };
378
- // Wait
379
- return await sleep(this.scanDuration * 1000);
380
- })
381
- .then(async () => {
382
- // Stop to monitor
383
- await this.stopScanning(switchbot);
384
- })
385
- .catch(async (e: any) => {
386
- this.apiError(e);
387
- this.errorLog(
388
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
389
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
390
- );
391
- await this.BLERefreshConnection(switchbot);
392
- });
393
- } else {
270
+ if (switchbot === undefined) {
394
271
  await this.BLERefreshConnection(switchbot);
395
- }*/
272
+ }
396
273
  }
397
274
 
398
275
  async openAPIRefreshStatus(): Promise<void> {
399
276
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
400
277
  try {
401
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
402
- headers: this.platform.generateHeaders(),
403
- });
278
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
279
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
404
280
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
405
281
  const deviceStatus: any = await body.json();
406
282
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
407
283
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
408
284
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
409
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
285
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
410
286
  + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
411
- this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!;
412
- this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!;
413
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
414
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
415
- this.openAPIparseStatus();
287
+ this.openAPIparseStatus(deviceStatus);
416
288
  this.updateHomeKitCharacteristics();
417
289
  } else {
418
290
  this.statusCode(statusCode);
@@ -420,10 +292,41 @@ export class Meter {
420
292
  }
421
293
  } catch (e: any) {
422
294
  this.apiError(e);
423
- this.errorLog(
424
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
425
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
426
- );
295
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
296
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
297
+ }
298
+ }
299
+
300
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
301
+ if (device.webhook) {
302
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
303
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
304
+ try {
305
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
306
+ const { temperature, humidity } = context;
307
+ const { CurrentTemperature } = this.TemperatureSensor ?? { CurrentTemperature: undefined };
308
+ const { CurrentRelativeHumidity } = this.HumiditySensor ?? { CurrentRelativeHumidity: undefined };
309
+ if (context.scale !== 'CELCIUS' && device.meter?.convertUnitTo === undefined) {
310
+ this.warnLog(`${device.deviceType}: ${accessory.displayName} received Webhook scale: `
311
+ + `${context.scale}, instead of CELCIUS. Use the *convertUnitsTo* config under Meter settings, if displaying incorrectly in HomeKit.`);
312
+ }
313
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ` +
314
+ '(scale, temperature, humidity) = '
315
+ + `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.meter?.convertUnitTo)}, ${humidity}), `
316
+ + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`);
317
+ if (!device.meter?.hide_humidity) {
318
+ this.HumiditySensor!.CurrentRelativeHumidity = humidity;
319
+ }
320
+ if (!device.meter?.hide_temperature) {
321
+ this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.meter?.convertUnitTo);
322
+ }
323
+ this.updateHomeKitCharacteristics();
324
+ } catch (e: any) {
325
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
326
+ }
327
+ };
328
+ } else {
329
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
427
330
  }
428
331
  }
429
332
 
@@ -436,64 +339,72 @@ export class Meter {
436
339
 
437
340
  // CurrentRelativeHumidity
438
341
  if (!this.device.meter?.hide_humidity) {
439
- if (this.CurrentRelativeHumidity === undefined) {
440
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
342
+ if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) {
343
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
344
+ + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`);
441
345
  } else {
346
+ this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity;
347
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity,
348
+ this.HumiditySensor!.CurrentRelativeHumidity);
349
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
350
+ + ` updateCharacteristic CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`);
442
351
  if (this.device.mqttURL) {
443
- mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`);
352
+ mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`);
444
353
  }
445
354
  if (this.device.history) {
446
- entry['humidity'] = this.CurrentRelativeHumidity;
355
+ entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity;
447
356
  }
448
- this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity;
449
- this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
450
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} `
451
- + `updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
452
357
  }
453
358
  }
359
+
454
360
  // CurrentTemperature
455
361
  if (!this.device.meter?.hide_temperature) {
456
- if (this.CurrentTemperature === undefined) {
457
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
362
+ if (this.TemperatureSensor!.CurrentTemperature === undefined) {
363
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
458
364
  } else {
365
+ this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature;
366
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature);
367
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
368
+ + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
459
369
  if (this.device.mqttURL) {
460
- mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
370
+ mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`);
461
371
  }
462
372
  if (this.device.history) {
463
- entry['temp'] = this.CurrentTemperature;
373
+ entry['temp'] = this.TemperatureSensor!.CurrentTemperature;
464
374
  }
465
- this.accessory.context.CurrentTemperature = this.CurrentTemperature;
466
- this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature);
467
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
468
375
  }
469
376
  }
377
+
470
378
  // BatteryLevel
471
- if (this.BatteryLevel === undefined) {
472
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
379
+ if (this.Battery.BatteryLevel === undefined) {
380
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
473
381
  } else {
382
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
383
+ this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
384
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
474
385
  if (this.device.mqttURL) {
475
- mqttmessage.push(`"battery": ${this.BatteryLevel}`);
386
+ mqttmessage.push(`"battery": ${this.Battery.BatteryLevel}`);
476
387
  }
477
- this.accessory.context.BatteryLevel = this.BatteryLevel;
478
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
479
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
480
388
  }
481
- if (this.StatusLowBattery === undefined) {
482
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
389
+ if (this.Battery.StatusLowBattery === undefined) {
390
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
483
391
  } else {
392
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
393
+ this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
394
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
395
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
484
396
  if (this.device.mqttURL) {
485
- mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
397
+ mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`);
486
398
  }
487
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
488
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
489
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
490
399
  }
491
400
 
492
- // MQTT
401
+ // MQTT Publish
493
402
  if (this.device.mqttURL) {
494
403
  this.mqttPublish(`{${mqttmessage.join(',')}}`);
495
404
  }
496
- if (Number(this.CurrentRelativeHumidity) > 0) {
405
+
406
+ // History Service
407
+ if (!this.device.meter?.hide_humidity && (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0)) {
497
408
  // reject unreliable data
498
409
  if (this.device.history) {
499
410
  this.historyService?.addEntry(entry);
@@ -501,84 +412,6 @@ export class Meter {
501
412
  }
502
413
  }
503
414
 
504
- /*
505
- * Publish MQTT message for topics of
506
- * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
507
- */
508
- mqttPublish(message: any) {
509
- const mac = this.device.deviceId
510
- ?.toLowerCase()
511
- .match(/[\s\S]{1,2}/g)
512
- ?.join(':');
513
- const options = this.device.mqttPubOptions || {};
514
- this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
515
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`);
516
- }
517
-
518
- /*
519
- * Setup MQTT hadler if URL is specified.
520
- */
521
- async setupMqtt(device: device & devicesConfig): Promise<void> {
522
- if (device.mqttURL) {
523
- try {
524
- const { connectAsync } = asyncmqtt;
525
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
526
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
527
- this.mqttClient.on('error', (e: Error) => {
528
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
529
- });
530
- } catch (e) {
531
- this.mqttClient = null;
532
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
533
- }
534
- }
535
- }
536
-
537
- /*
538
- * Setup EVE history graph feature if enabled.
539
- */
540
- async setupHistoryService(device: device & devicesConfig): Promise<void> {
541
- const mac = this.device
542
- .deviceId!.match(/.{1,2}/g)!
543
- .join(':')
544
- .toLowerCase();
545
- this.historyService = device.history
546
- ? new this.platform.fakegatoAPI('room', this.accessory, {
547
- log: this.platform.log,
548
- storage: 'fs',
549
- filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
550
- })
551
- : null;
552
- }
553
-
554
- async stopScanning(switchbot: any) {
555
- switchbot.stopScan();
556
- if (this.BLE_IsConnected) {
557
- await this.BLEparseStatus();
558
- await this.updateHomeKitCharacteristics();
559
- } else {
560
- await this.BLERefreshConnection(switchbot);
561
- }
562
- }
563
-
564
- async getCustomBLEAddress(switchbot: any) {
565
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
566
- (async () => {
567
- // Start to monitor advertisement packets
568
- await switchbot.startScan({
569
- model: 'T',
570
- });
571
- // Set an event handler
572
- switchbot.onadvertisement = (ad: any) => {
573
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
574
- };
575
- await sleep(10000);
576
- // Stop to monitor
577
- switchbot.stopScan();
578
- })();
579
- }
580
- }
581
-
582
415
  async BLERefreshConnection(switchbot: any): Promise<void> {
583
416
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
584
417
  + ` ${JSON.stringify(switchbot)}`);
@@ -588,249 +421,26 @@ export class Meter {
588
421
  }
589
422
  }
590
423
 
591
- async scan(device: device & devicesConfig): Promise<void> {
592
- if (device.scanDuration) {
593
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
594
- if (this.BLE) {
595
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
596
- }
597
- } else {
598
- this.scanDuration = this.accessory.context.scanDuration = 1;
599
- if (this.BLE) {
600
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
601
- }
602
- }
603
- }
604
-
605
- async statusCode(statusCode: number): Promise<void> {
606
- switch (statusCode) {
607
- case 151:
608
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
609
- break;
610
- case 152:
611
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
612
- break;
613
- case 160:
614
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
615
- break;
616
- case 161:
617
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
618
- this.offlineOff();
619
- break;
620
- case 171:
621
- this.errorLog(
622
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
623
- `Hub: ${this.device.hubDeviceId}`,
624
- );
625
- this.offlineOff();
626
- break;
627
- case 190:
628
- this.errorLog(
629
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
630
- ` Or command format is invalid, statusCode: ${statusCode}`,
631
- );
632
- break;
633
- case 100:
634
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
635
- break;
636
- case 200:
637
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
638
- break;
639
- case 400:
640
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
641
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
642
- break;
643
- case 401:
644
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
645
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
646
- break;
647
- case 403:
648
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
649
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
650
- break;
651
- case 404:
652
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
653
- + `statusCode: ${statusCode}`);
654
- break;
655
- case 406:
656
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
657
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
658
- break;
659
- case 415:
660
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
661
- + `header that is not supported by the server, statusCode: ${statusCode}`);
662
- break;
663
- case 422:
664
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
665
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
666
- break;
667
- case 429:
668
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
669
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
670
- break;
671
- case 500:
672
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
673
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
674
- break;
675
- default:
676
- this.infoLog(
677
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
678
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
679
- );
680
- }
681
- }
682
-
683
424
  async offlineOff(): Promise<void> {
684
425
  if (this.device.offline) {
685
- await this.deviceContext();
686
- await this.updateHomeKitCharacteristics();
426
+ if (!this.device.meter?.hide_humidity) {
427
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50);
428
+ }
429
+ if (!this.device.meter?.hide_temperature) {
430
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30);
431
+ }
432
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100);
687
433
  }
688
434
  }
689
435
 
690
436
  async apiError(e: any): Promise<void> {
691
437
  if (!this.device.meter?.hide_humidity) {
692
- this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
438
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
693
439
  }
694
440
  if (!this.device.meter?.hide_temperature) {
695
- this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
696
- }
697
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
698
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
699
- }
700
-
701
- async deviceContext() {
702
- if (this.CurrentRelativeHumidity === undefined) {
703
- this.CurrentRelativeHumidity = 0;
704
- } else {
705
- this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
706
- }
707
- if (this.CurrentTemperature === undefined) {
708
- this.CurrentTemperature = 0;
709
- } else {
710
- this.CurrentTemperature = this.accessory.context.CurrentTemperature;
711
- }
712
- if (this.BatteryLevel === undefined) {
713
- this.BatteryLevel = 100;
714
- } else {
715
- this.BatteryLevel = this.accessory.context.BatteryLevel;
716
- }
717
- if (this.StatusLowBattery === undefined) {
718
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
719
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
720
- } else {
721
- this.StatusLowBattery = this.accessory.context.StatusLowBattery;
722
- }
723
- if (this.FirmwareRevision === undefined) {
724
- this.FirmwareRevision = this.platform.version;
725
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
726
- }
727
- }
728
-
729
- async refreshRate(device: device & devicesConfig): Promise<void> {
730
- if (device.refreshRate) {
731
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
732
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
733
- } else if (this.platform.config.options!.refreshRate) {
734
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
735
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
736
- }
737
- }
738
-
739
- async deviceConfig(device: device & devicesConfig): Promise<void> {
740
- let config = {};
741
- if (device.meter) {
742
- config = device.meter;
743
- }
744
- if (device.connectionType !== undefined) {
745
- config['connectionType'] = device.connectionType;
746
- }
747
- if (device.external !== undefined) {
748
- config['external'] = device.external;
749
- }
750
- if (device.mqttURL !== undefined) {
751
- config['mqttURL'] = device.mqttURL;
752
- }
753
- if (device.logging !== undefined) {
754
- config['logging'] = device.logging;
755
- }
756
- if (device.refreshRate !== undefined) {
757
- config['refreshRate'] = device.refreshRate;
758
- }
759
- if (device.scanDuration !== undefined) {
760
- config['scanDuration'] = device.scanDuration;
761
- }
762
- if (device.webhook !== undefined) {
763
- config['webhook'] = device.webhook;
764
- }
765
- if (Object.entries(config).length !== 0) {
766
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
767
- }
768
- }
769
-
770
- async deviceLogs(device: device & devicesConfig): Promise<void> {
771
- if (this.platform.debugMode) {
772
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
773
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
774
- } else if (device.logging) {
775
- this.deviceLogging = this.accessory.context.logging = device.logging;
776
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
777
- } else if (this.platform.config.options?.logging) {
778
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
779
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
780
- } else {
781
- this.deviceLogging = this.accessory.context.logging = 'standard';
782
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
783
- }
784
- }
785
-
786
- /**
787
- * Logging for Device
788
- */
789
- infoLog(...log: any[]): void {
790
- if (this.enablingDeviceLogging()) {
791
- this.platform.log.info(String(...log));
441
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
792
442
  }
793
- }
794
-
795
- warnLog(...log: any[]): void {
796
- if (this.enablingDeviceLogging()) {
797
- this.platform.log.warn(String(...log));
798
- }
799
- }
800
-
801
- debugWarnLog(...log: any[]): void {
802
- if (this.enablingDeviceLogging()) {
803
- if (this.deviceLogging?.includes('debug')) {
804
- this.platform.log.warn('[DEBUG]', String(...log));
805
- }
806
- }
807
- }
808
-
809
- errorLog(...log: any[]): void {
810
- if (this.enablingDeviceLogging()) {
811
- this.platform.log.error(String(...log));
812
- }
813
- }
814
-
815
- debugErrorLog(...log: any[]): void {
816
- if (this.enablingDeviceLogging()) {
817
- if (this.deviceLogging?.includes('debug')) {
818
- this.platform.log.error('[DEBUG]', String(...log));
819
- }
820
- }
821
- }
822
-
823
- debugLog(...log: any[]): void {
824
- if (this.enablingDeviceLogging()) {
825
- if (this.deviceLogging === 'debug') {
826
- this.platform.log.info('[DEBUG]', String(...log));
827
- } else {
828
- this.platform.log.debug(String(...log));
829
- }
830
- }
831
- }
832
-
833
- enablingDeviceLogging(): boolean {
834
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
443
+ this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
444
+ this.Battery!.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
835
445
  }
836
446
  }