@switchbot/homebridge-switchbot 1.3.0 → 1.3.1-beta.11

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 (95) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/config.schema.json +12 -25
  3. package/dist/devices/bots.d.ts +2 -0
  4. package/dist/devices/bots.d.ts.map +1 -1
  5. package/dist/devices/bots.js +137 -96
  6. package/dist/devices/bots.js.map +1 -1
  7. package/dist/devices/colorbulb.d.ts +1 -0
  8. package/dist/devices/colorbulb.d.ts.map +1 -1
  9. package/dist/devices/colorbulb.js +18 -4
  10. package/dist/devices/colorbulb.js.map +1 -1
  11. package/dist/devices/contact.js +1 -1
  12. package/dist/devices/contact.js.map +1 -1
  13. package/dist/devices/curtains.d.ts.map +1 -1
  14. package/dist/devices/curtains.js +7 -2
  15. package/dist/devices/curtains.js.map +1 -1
  16. package/dist/devices/humidifiers.d.ts +1 -0
  17. package/dist/devices/humidifiers.d.ts.map +1 -1
  18. package/dist/devices/humidifiers.js +16 -3
  19. package/dist/devices/humidifiers.js.map +1 -1
  20. package/dist/devices/indoorcam.d.ts +32 -3
  21. package/dist/devices/indoorcam.d.ts.map +1 -1
  22. package/dist/devices/indoorcam.js +103 -45
  23. package/dist/devices/indoorcam.js.map +1 -1
  24. package/dist/devices/meters.d.ts +2 -11
  25. package/dist/devices/meters.d.ts.map +1 -1
  26. package/dist/devices/meters.js +8 -38
  27. package/dist/devices/meters.js.map +1 -1
  28. package/dist/devices/plugs.d.ts +2 -0
  29. package/dist/devices/plugs.d.ts.map +1 -1
  30. package/dist/devices/plugs.js +42 -17
  31. package/dist/devices/plugs.js.map +1 -1
  32. package/dist/irdevices/airconditioners.d.ts +2 -0
  33. package/dist/irdevices/airconditioners.d.ts.map +1 -1
  34. package/dist/irdevices/airconditioners.js +18 -2
  35. package/dist/irdevices/airconditioners.js.map +1 -1
  36. package/dist/irdevices/airpurifiers.d.ts +2 -0
  37. package/dist/irdevices/airpurifiers.d.ts.map +1 -1
  38. package/dist/irdevices/airpurifiers.js +18 -1
  39. package/dist/irdevices/airpurifiers.js.map +1 -1
  40. package/dist/irdevices/cameras.d.ts +1 -0
  41. package/dist/irdevices/cameras.d.ts.map +1 -1
  42. package/dist/irdevices/cameras.js +10 -1
  43. package/dist/irdevices/cameras.js.map +1 -1
  44. package/dist/irdevices/fans.d.ts +1 -0
  45. package/dist/irdevices/fans.d.ts.map +1 -1
  46. package/dist/irdevices/fans.js +10 -1
  47. package/dist/irdevices/fans.js.map +1 -1
  48. package/dist/irdevices/lights.d.ts +1 -0
  49. package/dist/irdevices/lights.d.ts.map +1 -1
  50. package/dist/irdevices/lights.js +10 -1
  51. package/dist/irdevices/lights.js.map +1 -1
  52. package/dist/irdevices/others.d.ts +1 -0
  53. package/dist/irdevices/others.d.ts.map +1 -1
  54. package/dist/irdevices/others.js +10 -1
  55. package/dist/irdevices/others.js.map +1 -1
  56. package/dist/irdevices/tvs.d.ts +1 -0
  57. package/dist/irdevices/tvs.d.ts.map +1 -1
  58. package/dist/irdevices/tvs.js +10 -1
  59. package/dist/irdevices/tvs.js.map +1 -1
  60. package/dist/irdevices/vacuumcleaners.d.ts +1 -0
  61. package/dist/irdevices/vacuumcleaners.d.ts.map +1 -1
  62. package/dist/irdevices/vacuumcleaners.js +10 -1
  63. package/dist/irdevices/vacuumcleaners.js.map +1 -1
  64. package/dist/irdevices/waterheaters.d.ts +1 -0
  65. package/dist/irdevices/waterheaters.d.ts.map +1 -1
  66. package/dist/irdevices/waterheaters.js +10 -1
  67. package/dist/irdevices/waterheaters.js.map +1 -1
  68. package/dist/platform.d.ts +2 -0
  69. package/dist/platform.d.ts.map +1 -1
  70. package/dist/platform.js +109 -23
  71. package/dist/platform.js.map +1 -1
  72. package/dist/settings.d.ts +6 -5
  73. package/dist/settings.d.ts.map +1 -1
  74. package/dist/settings.js.map +1 -1
  75. package/homebridge-ui/public/index.html +5 -1
  76. package/package.json +66 -66
  77. package/src/devices/bots.ts +125 -86
  78. package/src/devices/colorbulb.ts +18 -4
  79. package/src/devices/contact.ts +1 -1
  80. package/src/devices/curtains.ts +8 -3
  81. package/src/devices/humidifiers.ts +18 -4
  82. package/src/devices/indoorcam.ts +129 -50
  83. package/src/devices/meters.ts +10 -39
  84. package/src/devices/plugs.ts +46 -19
  85. package/src/irdevices/airconditioners.ts +18 -2
  86. package/src/irdevices/airpurifiers.ts +19 -1
  87. package/src/irdevices/cameras.ts +11 -1
  88. package/src/irdevices/fans.ts +11 -1
  89. package/src/irdevices/lights.ts +11 -1
  90. package/src/irdevices/others.ts +11 -1
  91. package/src/irdevices/tvs.ts +11 -1
  92. package/src/irdevices/vacuumcleaners.ts +11 -1
  93. package/src/irdevices/waterheaters.ts +11 -1
  94. package/src/platform.ts +109 -23
  95. package/src/settings.ts +7 -5
@@ -1,7 +1,7 @@
1
1
  import { AxiosResponse } from 'axios';
2
2
  import { interval, Subject } from 'rxjs';
3
3
  import { SwitchBotPlatform } from '../platform';
4
- import { debounceTime, skipWhile, tap } from 'rxjs/operators';
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 } from '../settings';
7
7
 
@@ -10,8 +10,13 @@ export class IndoorCam {
10
10
  private service: Service;
11
11
 
12
12
  // Characteristic Values
13
- On!: CharacteristicValue;
14
- OutletInUse!: CharacteristicValue;
13
+ Active!: CharacteristicValue;
14
+ SetupEndpoints!: CharacteristicValue;
15
+ StreamingStatus!: CharacteristicValue;
16
+ SupportedRTPConfiguration!: CharacteristicValue;
17
+ SelectedRTPStreamConfiguration!: CharacteristicValue;
18
+ SupportedVideoStreamConfiguration!: CharacteristicValue;
19
+ SupportedAudioStreamConfiguration!: CharacteristicValue;
15
20
 
16
21
  // OpenAPI Others
17
22
  deviceStatus!: deviceStatusResponse;
@@ -29,9 +34,12 @@ export class IndoorCam {
29
34
  private accessory: PlatformAccessory,
30
35
  public device: device & devicesConfig,
31
36
  ) {
37
+ // Indoor Cam Config
38
+ this.platform.device(`Indoor Cam: ${this.accessory.displayName} Config: (hide_device: ${device.hide_device})`);
39
+
40
+ this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} was added but will not work as OpenAPI doesn't support it yet.`);
32
41
  // default placeholders
33
- this.On = false;
34
- this.OutletInUse = true;
42
+ this.SelectedRTPStreamConfiguration;
35
43
 
36
44
  // this is subject we use to track when we need to POST changes to the SwitchBot API
37
45
  this.doCameraUpdate = new Subject();
@@ -50,8 +58,8 @@ export class IndoorCam {
50
58
  // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
51
59
  // you can create multiple services for each accessory
52
60
  (this.service =
53
- accessory.getService(this.platform.Service.Outlet) ||
54
- accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Indoor Camera`;
61
+ accessory.getService(this.platform.Service.CameraRTPStreamManagement) ||
62
+ accessory.addService(this.platform.Service.CameraRTPStreamManagement)), `${accessory.displayName} Indoor Camera`;
55
63
 
56
64
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
57
65
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
@@ -65,9 +73,26 @@ export class IndoorCam {
65
73
  // see https://developers.homebridge.io/#/service/WindowCovering
66
74
 
67
75
  // create handlers for required characteristics
68
- this.service.getCharacteristic(this.platform.Characteristic.On).onSet(this.OnSet.bind(this));
76
+ this.service.getCharacteristic(this.platform.Characteristic.Active)
77
+ .onSet(this.ActiveSet.bind(this));
78
+
79
+ this.service.getCharacteristic(this.platform.Characteristic.SelectedRTPStreamConfiguration)
80
+ .onSet(this.SelectedRTPStreamConfigurationSet.bind(this));
81
+
82
+ this.service.getCharacteristic(this.platform.Characteristic.SetupEndpoints)
83
+ .onSet(this.SetupEndpointsSet.bind(this));
84
+
85
+ this.service.getCharacteristic(this.platform.Characteristic.StreamingStatus)
86
+ .onSet(this.StreamingStatusSet.bind(this));
69
87
 
70
- this.service.setCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse || true);
88
+ this.service.getCharacteristic(this.platform.Characteristic.SupportedAudioStreamConfiguration)
89
+ .onSet(this.SupportedAudioStreamConfigurationSet.bind(this));
90
+
91
+ this.service.getCharacteristic(this.platform.Characteristic.SupportedRTPConfiguration)
92
+ .onSet(this.SupportedRTPConfigurationSet.bind(this));
93
+
94
+ this.service.getCharacteristic(this.platform.Characteristic.SupportedVideoStreamConfiguration)
95
+ .onSet(this.SupportedVideoStreamConfigurationSet.bind(this));
71
96
 
72
97
  // Update Homekit
73
98
  this.updateHomeKitCharacteristics();
@@ -108,14 +133,7 @@ export class IndoorCam {
108
133
  }
109
134
 
110
135
  parseStatus() {
111
- switch (this.deviceStatus.body.power) {
112
- case 'on':
113
- this.On = true;
114
- break;
115
- default:
116
- this.On = false;
117
- }
118
- this.platform.debug(`Indoor Cam ${this.accessory.displayName} On: ${this.On}`);
136
+ this.platform.debug(`Indoor Cam ${this.accessory.displayName} SelectedRTPStreamConfiguration: ${this.SelectedRTPStreamConfiguration}`);
119
137
  }
120
138
 
121
139
 
@@ -146,45 +164,42 @@ export class IndoorCam {
146
164
  * Camera - "command" "turnOn" "default" = set to ON state
147
165
  */
148
166
  async pushChanges() {
149
- const payload = {
150
- commandType: 'command',
151
- parameter: 'default',
152
- } as payload;
153
-
154
- if (this.On) {
155
- payload.command = 'turnOn';
167
+ if (!this.Active) {
168
+ const payload = {
169
+ commandType: 'command',
170
+ parameter: 'default',
171
+ command: 'turnOn',
172
+ } as payload;
173
+
174
+ this.platform.log.info(`Indoor Cam: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},`
175
+ + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`);
176
+
177
+ // Make the API request
178
+ const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
179
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`);
180
+ this.statusCode(push);
181
+ interval(5000)
182
+ .pipe(skipWhile(() => this.cameraUpdateInProgress))
183
+ .pipe(take(1))
184
+ .subscribe(() => {
185
+ this.refreshStatus();
186
+ });
156
187
  } else {
157
- payload.command = 'turnOff';
188
+ this.platform.device(`Indoor Cam: ${this.accessory.displayName} No pushChanges. Active: ${this.Active}`);
158
189
  }
159
-
160
- this.platform.log.info(`Indoor Cam: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},`
161
- + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`);
162
-
163
- // Make the API request
164
- const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
165
- this.platform.debug(`Indoor Cam: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`);
166
- this.statusCode(push);
167
- this.refreshStatus();
168
190
  }
169
191
 
170
192
  updateHomeKitCharacteristics() {
171
- if (this.On === undefined) {
172
- this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${this.On}`);
173
- } else {
174
- this.service.updateCharacteristic(this.platform.Characteristic.On, this.On);
175
- this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
176
- }
177
- if (this.OutletInUse === undefined) {
178
- this.platform.debug(`Indoor Cam: ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`);
193
+ if (this.SelectedRTPStreamConfiguration === undefined) {
194
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${this.SelectedRTPStreamConfiguration}`);
179
195
  } else {
180
- this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse);
181
- this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`);
196
+ this.service.updateCharacteristic(this.platform.Characteristic.SelectedRTPStreamConfiguration, this.SelectedRTPStreamConfiguration);
197
+ this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic On: ${this.SelectedRTPStreamConfiguration}`);
182
198
  }
183
199
  }
184
200
 
185
201
  public apiError(e: any) {
186
- this.service.updateCharacteristic(this.platform.Characteristic.On, e);
187
- this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e);
202
+ this.service.updateCharacteristic(this.platform.Characteristic.SelectedRTPStreamConfiguration, e);
188
203
  }
189
204
 
190
205
  private statusCode(push: AxiosResponse<{ statusCode: number; }>) {
@@ -200,9 +215,15 @@ export class IndoorCam {
200
215
  break;
201
216
  case 161:
202
217
  this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device is offline.`);
218
+ if (this.device.offline) {
219
+ this.Active = false;
220
+ }
203
221
  break;
204
222
  case 171:
205
- this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Hub Device is offline.`);
223
+ this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
224
+ if (this.device.offline) {
225
+ this.Active = false;
226
+ }
206
227
  break;
207
228
  case 190:
208
229
  this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,`
@@ -216,15 +237,73 @@ export class IndoorCam {
216
237
  }
217
238
  }
218
239
 
240
+ /**
241
+ * Handle requests to set the value of the "Active" characteristic
242
+ */
243
+ ActiveSet(value: CharacteristicValue) {
244
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} Active: ${value}`);
245
+
246
+ this.Active = value;
247
+ this.doCameraUpdate.next();
248
+ }
249
+
250
+ /**
251
+ * Handle requests to set the value of the "SelectedRTPStreamConfiguration" characteristic
252
+ */
253
+ SelectedRTPStreamConfigurationSet(value: CharacteristicValue) {
254
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SelectedRTPStreamConfiguration: ${value}`);
255
+
256
+ this.SelectedRTPStreamConfiguration = value;
257
+ this.doCameraUpdate.next();
258
+ }
259
+
260
+ /**
261
+ * Handle requests to set the value of the "On" characteristic
262
+ */
263
+ SetupEndpointsSet(value: CharacteristicValue) {
264
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SetupEndpoints: ${value}`);
265
+
266
+ this.SetupEndpoints = value;
267
+ this.doCameraUpdate.next();
268
+ }
269
+
219
270
  /**
220
271
  * Handle requests to set the value of the "On" characteristic
221
272
  */
222
- OnSet(value: CharacteristicValue) {
223
- this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${value}`);
273
+ SupportedAudioStreamConfigurationSet(value: CharacteristicValue) {
274
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SupportedAudioStreamConfiguration: ${value}`);
224
275
 
225
- this.On = value;
276
+ this.SupportedAudioStreamConfiguration = value;
226
277
  this.doCameraUpdate.next();
227
278
  }
228
279
 
280
+ /**
281
+ * Handle requests to set the value of the "On" characteristic
282
+ */
283
+ SupportedRTPConfigurationSet(value: CharacteristicValue) {
284
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SupportedRTPConfiguration: ${value}`);
285
+
286
+ this.SupportedRTPConfiguration = value;
287
+ this.doCameraUpdate.next();
288
+ }
229
289
 
290
+ /**
291
+ * Handle requests to set the value of the "On" characteristic
292
+ */
293
+ StreamingStatusSet(value: CharacteristicValue) {
294
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} StreamingStatus: ${value}`);
295
+
296
+ this.StreamingStatus = value;
297
+ this.doCameraUpdate.next();
298
+ }
299
+
300
+ /**
301
+ * Handle requests to set the value of the "On" characteristic
302
+ */
303
+ SupportedVideoStreamConfigurationSet(value: CharacteristicValue) {
304
+ this.platform.debug(`Indoor Cam: ${this.accessory.displayName} SupportedVideoStreamConfiguration: ${value}`);
305
+
306
+ this.SupportedVideoStreamConfiguration = value;
307
+ this.doCameraUpdate.next();
308
+ }
230
309
  }
@@ -3,7 +3,7 @@ import { interval, Subject } from 'rxjs';
3
3
  import { skipWhile } from 'rxjs/operators';
4
4
  import { SwitchBotPlatform } from '../platform';
5
5
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
6
- import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse } from '../settings';
6
+ import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature } from '../settings';
7
7
 
8
8
  /**
9
9
  * Platform Accessory
@@ -32,10 +32,9 @@ export class Meter {
32
32
  connected?: boolean;
33
33
  switchbot!: switchbot;
34
34
  serviceData!: serviceData;
35
+ temperature!: temperature['c'];
35
36
  battery!: serviceData['battery'];
36
37
  humidity!: serviceData['humidity'];
37
- fahrenheit!: serviceData['fahrenheit'];
38
- temperature!: serviceData['temperature'];
39
38
 
40
39
  // Config
41
40
  private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode;
@@ -51,8 +50,8 @@ export class Meter {
51
50
  public device: device & devicesConfig,
52
51
  ) {
53
52
  // Meter Config
54
- this.platform.device(`Meter: ${this.accessory.displayName} Config: (ble: ${device.ble}, unit: ${device.meter?.unit},`
55
- + ` hide_temperature: ${device.meter?.hide_temperature}, hide_humidity: ${device.meter?.hide_humidity})`);
53
+ this.platform.device(`Meter: ${this.accessory.displayName} Config: (ble: ${device.ble}, hide_temperature: ${device.meter?.hide_temperature},`
54
+ + ` hide_humidity: ${device.meter?.hide_humidity})`);
56
55
 
57
56
  // default placeholders
58
57
  this.BatteryLevel = 0;
@@ -191,13 +190,8 @@ export class Meter {
191
190
 
192
191
  // Current Temperature
193
192
  if (!this.device.meter?.hide_temperature) {
194
- if (this.device.meter?.unit === 1) {
195
- this.CurrentTemperature = this.toFahrenheit(this.temperature!.c);
196
- } else if (this.device.meter?.unit === 0) {
197
- this.CurrentTemperature = this.toCelsius(this.temperature!.c);
198
- } else {
199
- this.CurrentTemperature = Number(this.temperature?.c);
200
- }
193
+ this.temperature < 0 ? 0 : this.temperature > 100 ? 100 : this.temperature;
194
+ this.CurrentTemperature = this.temperature;
201
195
  this.platform.debug(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
202
196
  }
203
197
  }
@@ -223,13 +217,7 @@ export class Meter {
223
217
 
224
218
  // Current Temperature
225
219
  if (!this.device.meter?.hide_temperature) {
226
- if (this.device.meter?.unit === 1) {
227
- this.CurrentTemperature = this.toFahrenheit(this.deviceStatus.body.temperature!);
228
- } else if (this.device.meter?.unit === 0) {
229
- this.CurrentTemperature = this.toCelsius(this.deviceStatus.body.temperature!);
230
- } else {
231
- this.CurrentTemperature = Number(this.deviceStatus.body.temperature);
232
- }
220
+ this.CurrentTemperature = Number(this.deviceStatus.body.temperature);
233
221
  this.platform.debug(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
234
222
  }
235
223
  }
@@ -259,20 +247,18 @@ export class Meter {
259
247
  const switchbot = this.connectBLE();
260
248
  // Start to monitor advertisement packets
261
249
  switchbot.startScan({
262
- model: 'T',
263
250
  id: this.device.bleMac,
264
251
  }).then(() => {
265
252
  // Set an event hander
266
253
  switchbot.onadvertisement = (ad: ad) => {
267
254
  this.serviceData = ad.serviceData;
268
- this.temperature = ad.serviceData.temperature;
269
- this.fahrenheit = ad.serviceData.fahrenheit;
255
+ this.temperature = Number(ad.serviceData.temperature?.c);
270
256
  this.humidity = ad.serviceData.humidity;
271
257
  this.battery = ad.serviceData.battery;
272
258
  this.platform.device(`Meter: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
273
259
  this.platform.device(`Meter: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, `
274
- + `temperature: ${JSON.stringify(ad.serviceData.temperature)}, fahrenheit: ${ad.serviceData.fahrenheit}, `
275
- + `humidity: ${ad.serviceData.humidity}, battery: ${ad.serviceData.battery}`);
260
+ + `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, `
261
+ + `battery: ${ad.serviceData.battery}`);
276
262
 
277
263
  if (this.serviceData) {
278
264
  this.connected = true;
@@ -382,19 +368,4 @@ export class Meter {
382
368
  this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e);
383
369
  }
384
370
  }
385
-
386
- /**
387
- * Converts the value to celsius if the temperature units are in Fahrenheit
388
- */
389
- toCelsius(value: number) {
390
- // celsius should be to the nearest 0.5 degree
391
- return Math.round((5 / 9) * (value - 32) * 2) / 2;
392
- }
393
-
394
- /**
395
- * Converts the value to fahrenheit if the temperature units are in Fahrenheit
396
- */
397
- toFahrenheit(value: number) {
398
- return Math.round((value * 9) / 5 + 32);
399
- }
400
371
  }
@@ -2,7 +2,7 @@
2
2
  import { AxiosResponse } from 'axios';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { SwitchBotPlatform } from '../platform';
5
- import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
+ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
6
6
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
7
7
  import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload } from '../settings';
8
8
 
@@ -12,6 +12,7 @@ export class Plug {
12
12
 
13
13
  // Characteristic Values
14
14
  On!: CharacteristicValue;
15
+ OnCached!: CharacteristicValue;
15
16
 
16
17
  // OpenAPI Others
17
18
  deviceStatus!: deviceStatusResponse;
@@ -29,8 +30,15 @@ export class Plug {
29
30
  private accessory: PlatformAccessory,
30
31
  public device: device & devicesConfig,
31
32
  ) {
33
+ // Bot Config
34
+ this.platform.device(`Plug: ${this.accessory.displayName} Config: (offline: ${device.offline})`);
35
+
32
36
  // default placeholders
33
- this.On = false;
37
+ if (this.On === undefined) {
38
+ this.On = false;
39
+ } else {
40
+ this.On = this.accessory.context.On;
41
+ }
34
42
 
35
43
  // this is subject we use to track when we need to POST changes to the SwitchBot API
36
44
  this.doPlugUpdate = new Subject();
@@ -142,24 +150,34 @@ export class Plug {
142
150
  * Plug - "command" "turnOn" "default" = set to ON state
143
151
  */
144
152
  async pushChanges() {
145
- const payload = {
146
- commandType: 'command',
147
- parameter: 'default',
148
- } as payload;
149
-
150
- if (this.On) {
151
- payload.command = 'turnOn';
152
- } else {
153
- payload.command = 'turnOff';
154
- }
153
+ if (this.On !== this.OnCached) {
154
+ const payload = {
155
+ commandType: 'command',
156
+ parameter: 'default',
157
+ } as payload;
158
+
159
+ if (this.On) {
160
+ payload.command = 'turnOn';
161
+ } else {
162
+ payload.command = 'turnOff';
163
+ }
155
164
 
156
- this.platform.log.info(`Plug: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},`
157
- + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`);
165
+ this.platform.log.info(`Plug: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},`
166
+ + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`);
158
167
 
159
- // Make the API request
160
- const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
161
- this.platform.debug(`Plug: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`);
162
- this.statusCode(push);
168
+ // Make the API request
169
+ const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
170
+ this.platform.debug(`Plug: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`);
171
+ this.statusCode(push);
172
+ this.OnCached = this.On;
173
+ this.accessory.context.On = this.OnCached;
174
+ }
175
+ interval(5000)
176
+ .pipe(skipWhile(() => this.plugUpdateInProgress))
177
+ .pipe(take(1))
178
+ .subscribe(() => {
179
+ this.refreshStatus();
180
+ });
163
181
  }
164
182
 
165
183
  updateHomeKitCharacteristics() {
@@ -188,9 +206,11 @@ export class Plug {
188
206
  break;
189
207
  case 161:
190
208
  this.platform.log.error(`Plug: ${this.accessory.displayName} Device is offline.`);
209
+ this.offlineOff();
191
210
  break;
192
211
  case 171:
193
- this.platform.log.error(`Plug: ${this.accessory.displayName} Hub Device is offline.`);
212
+ this.platform.log.error(`Plug: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
213
+ this.offlineOff();
194
214
  break;
195
215
  case 190:
196
216
  this.platform.log.error(`Plug: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,`
@@ -204,6 +224,13 @@ export class Plug {
204
224
  }
205
225
  }
206
226
 
227
+ private offlineOff() {
228
+ if (this.device.offline) {
229
+ this.On = false;
230
+ this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On);
231
+ }
232
+ }
233
+
207
234
  /**
208
235
  * Handle requests to set the value of the "On" characteristic
209
236
  */
@@ -14,9 +14,11 @@ export class AirConditioner {
14
14
 
15
15
  // Characteristic Values
16
16
  Active!: CharacteristicValue;
17
+ ActiveCached!: CharacteristicValue;
17
18
  RotationSpeed!: CharacteristicValue;
18
19
  LastTemperature!: CharacteristicValue;
19
20
  CurrentTemperature!: CharacteristicValue;
21
+ CurrentTemperatureCached!: CharacteristicValue;
20
22
  TargetHeaterCoolerState?: CharacteristicValue;
21
23
  CurrentHeaterCoolerState!: CharacteristicValue;
22
24
  HeatingThresholdTemperature?: CharacteristicValue;
@@ -41,7 +43,17 @@ export class AirConditioner {
41
43
  private accessory: PlatformAccessory,
42
44
  public device: irdevice & irDevicesConfig,
43
45
  ) {
44
- this.CurrentTemperature = 24;
46
+ // default placeholders
47
+ if (this.Active === undefined) {
48
+ this.Active = this.platform.Characteristic.Active.INACTIVE;
49
+ } else {
50
+ this.Active = this.accessory.context.Active;
51
+ }
52
+ if (this.CurrentTemperature === undefined) {
53
+ this.CurrentTemperature = 24;
54
+ } else {
55
+ this.CurrentTemperature = this.accessory.context.CurrentTemperature;
56
+ }
45
57
 
46
58
  // set accessory information
47
59
  accessory
@@ -161,6 +173,8 @@ export class AirConditioner {
161
173
  this.platform.device(`Air Conditioner: ${this.accessory.displayName} pushAirConditionerOffChanges, Active: ${this.Active}`);
162
174
  }
163
175
  this.Active = value;
176
+ this.ActiveCached = this.Active;
177
+ this.accessory.context.Active = this.ActiveCached;
164
178
  }
165
179
 
166
180
  private updateHomeKitCharacteristics() {
@@ -349,6 +363,8 @@ export class AirConditioner {
349
363
  const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
350
364
  this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`);
351
365
  this.statusCode(push);
366
+ this.CurrentTemperatureCached = this.CurrentTemperature;
367
+ this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached;
352
368
  this.updateHomeKitCharacteristics();
353
369
  } catch (e: any) {
354
370
  this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} failed pushChanges`);
@@ -379,7 +395,7 @@ export class AirConditioner {
379
395
  this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device is offline.`);
380
396
  break;
381
397
  case 171:
382
- this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Hub Device is offline.`);
398
+ this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
383
399
  break;
384
400
  case 190:
385
401
  this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device internal error due to device states not synchronized`
@@ -15,11 +15,13 @@ export class AirPurifier {
15
15
  // Characteristic Values
16
16
  Active!: CharacteristicValue;
17
17
  APActive!: CharacteristicValue;
18
+ ActiveCached!: CharacteristicValue;
18
19
  CurrentAPTemp!: CharacteristicValue;
19
20
  CurrentAPMode!: CharacteristicValue;
20
21
  RotationSpeed!: CharacteristicValue;
21
22
  CurrentAPFanSpeed!: CharacteristicValue;
22
23
  CurrentTemperature!: CharacteristicValue;
24
+ CurrentTemperatureCached!: CharacteristicValue;
23
25
  CurrentAirPurifierState!: CharacteristicValue;
24
26
  CurrentHeaterCoolerState!: CharacteristicValue;
25
27
 
@@ -42,6 +44,18 @@ export class AirPurifier {
42
44
  private accessory: PlatformAccessory,
43
45
  public device: irdevice & irDevicesConfig,
44
46
  ) {
47
+ // default placeholders
48
+ if (this.Active === undefined) {
49
+ this.Active = this.platform.Characteristic.Active.INACTIVE;
50
+ } else {
51
+ this.Active = this.accessory.context.Active;
52
+ }
53
+ if (this.CurrentTemperature === undefined) {
54
+ this.CurrentTemperature = 24;
55
+ } else {
56
+ this.CurrentTemperature = this.accessory.context.CurrentTemperature;
57
+ }
58
+
45
59
  // set accessory information
46
60
  accessory
47
61
  .getService(this.platform.Service.AccessoryInformation)!
@@ -81,6 +95,8 @@ export class AirPurifier {
81
95
  this.pushAirConditionerOnChanges();
82
96
  }
83
97
  this.Active = value;
98
+ this.ActiveCached = this.Active;
99
+ this.accessory.context.Active = this.ActiveCached;
84
100
  }
85
101
 
86
102
  private updateHomeKitCharacteristics() {
@@ -210,6 +226,8 @@ export class AirPurifier {
210
226
  this.platform.debug(`Air Purifier: ${this.accessory.displayName} pushChanges: ${push.data}`);
211
227
  this.statusCode(push);
212
228
  this.updateHomeKitCharacteristics();
229
+ this.CurrentTemperatureCached = this.CurrentTemperature;
230
+ this.accessory.context.CurrentTemperature = this.CurrentTemperatureCached;
213
231
  } catch (e: any) {
214
232
  this.platform.log.error(`Air Purifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
215
233
  if (this.deviceDebug) {
@@ -239,7 +257,7 @@ export class AirPurifier {
239
257
  this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device is offline.`);
240
258
  break;
241
259
  case 171:
242
- this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Hub Device is offline.`);
260
+ this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
243
261
  break;
244
262
  case 190:
245
263
  this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device internal error due to device states not synchronized`
@@ -14,6 +14,7 @@ export class Camera {
14
14
 
15
15
  // Characteristic Values
16
16
  On!: CharacteristicValue;
17
+ OnCached!: CharacteristicValue;
17
18
 
18
19
  // Config
19
20
  private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode;
@@ -24,6 +25,13 @@ export class Camera {
24
25
  private accessory: PlatformAccessory,
25
26
  public device: irdevice & irDevicesConfig,
26
27
  ) {
28
+ // default placeholders
29
+ if (this.On === undefined) {
30
+ this.On = false;
31
+ } else {
32
+ this.On = this.accessory.context.On;
33
+ }
34
+
27
35
  // set accessory information
28
36
  accessory
29
37
  .getService(this.platform.Service.AccessoryInformation)!
@@ -110,6 +118,8 @@ export class Camera {
110
118
  this.platform.debug(`Camera: ${this.accessory.displayName} pushChanges: ${push.data}`);
111
119
  this.statusCode(push);
112
120
  this.updateHomeKitCharacteristics();
121
+ this.OnCached = this.On;
122
+ this.accessory.context.On = this.OnCached;
113
123
  } catch (e: any) {
114
124
  this.platform.log.error(`Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
115
125
  if (this.deviceDebug) {
@@ -139,7 +149,7 @@ export class Camera {
139
149
  this.platform.log.error(`Camera: ${this.accessory.displayName} Device is offline.`);
140
150
  break;
141
151
  case 171:
142
- this.platform.log.error(`Camera: ${this.accessory.displayName} Hub Device is offline.`);
152
+ this.platform.log.error(`Camera: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
143
153
  break;
144
154
  case 190:
145
155
  this.platform.log.error(`Camera: ${this.accessory.displayName} Device internal error due to device states not synchronized`
@@ -14,6 +14,7 @@ export class Fan {
14
14
 
15
15
  // Characteristic Values
16
16
  Active!: CharacteristicValue;
17
+ ActiveCached!: CharacteristicValue;
17
18
  ActiveIdentifier!: CharacteristicValue;
18
19
  RotationSpeed!: CharacteristicValue;
19
20
  SwingMode!: CharacteristicValue;
@@ -34,6 +35,13 @@ export class Fan {
34
35
  private accessory: PlatformAccessory,
35
36
  public device: irdevice & irDevicesConfig,
36
37
  ) {
38
+ // default placeholders
39
+ if (this.Active === undefined) {
40
+ this.Active = this.platform.Characteristic.Active.INACTIVE;
41
+ } else {
42
+ this.Active = this.accessory.context.Active;
43
+ }
44
+
37
45
  // set accessory information
38
46
  accessory
39
47
  .getService(this.platform.Service.AccessoryInformation)!
@@ -164,6 +172,8 @@ export class Fan {
164
172
  this.pushFanOnChanges();
165
173
  }
166
174
  this.Active = value;
175
+ this.ActiveCached = this.Active;
176
+ this.accessory.context.Active = this.ActiveCached;
167
177
  }
168
178
 
169
179
  /**
@@ -261,7 +271,7 @@ export class Fan {
261
271
  this.platform.log.error(`Fan: ${this.accessory.displayName} Device is offline.`);
262
272
  break;
263
273
  case 171:
264
- this.platform.log.error(`Fan: ${this.accessory.displayName} Hub Device is offline.`);
274
+ this.platform.log.error(`Fan: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
265
275
  break;
266
276
  case 190:
267
277
  this.platform.log.error(`Fan: ${this.accessory.displayName} Device internal error due to device states not synchronized`