@switchbot/homebridge-switchbot 2.0.0-beta.2 → 2.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +1 -2
  3. package/config.schema.json +75 -20
  4. package/dist/device/bot.d.ts +32 -28
  5. package/dist/device/bot.d.ts.map +1 -1
  6. package/dist/device/bot.js +646 -648
  7. package/dist/device/bot.js.map +1 -1
  8. package/dist/device/colorbulb.d.ts +34 -19
  9. package/dist/device/colorbulb.d.ts.map +1 -1
  10. package/dist/device/colorbulb.js +429 -304
  11. package/dist/device/colorbulb.js.map +1 -1
  12. package/dist/device/contact.d.ts +16 -7
  13. package/dist/device/contact.d.ts.map +1 -1
  14. package/dist/device/contact.js +288 -230
  15. package/dist/device/contact.js.map +1 -1
  16. package/dist/device/curtain.d.ts +21 -16
  17. package/dist/device/curtain.d.ts.map +1 -1
  18. package/dist/device/curtain.js +519 -489
  19. package/dist/device/curtain.js.map +1 -1
  20. package/dist/device/humidifier.d.ts +22 -18
  21. package/dist/device/humidifier.d.ts.map +1 -1
  22. package/dist/device/humidifier.js +415 -360
  23. package/dist/device/humidifier.js.map +1 -1
  24. package/dist/device/lock.d.ts +22 -8
  25. package/dist/device/lock.d.ts.map +1 -1
  26. package/dist/device/lock.js +253 -147
  27. package/dist/device/lock.js.map +1 -1
  28. package/dist/device/meter.d.ts +21 -16
  29. package/dist/device/meter.d.ts.map +1 -1
  30. package/dist/device/meter.js +325 -255
  31. package/dist/device/meter.js.map +1 -1
  32. package/dist/device/meterplus.d.ts +23 -12
  33. package/dist/device/meterplus.d.ts.map +1 -1
  34. package/dist/device/meterplus.js +346 -208
  35. package/dist/device/meterplus.js.map +1 -1
  36. package/dist/device/motion.d.ts +17 -8
  37. package/dist/device/motion.d.ts.map +1 -1
  38. package/dist/device/motion.js +261 -209
  39. package/dist/device/motion.js.map +1 -1
  40. package/dist/device/plug.d.ts +24 -16
  41. package/dist/device/plug.d.ts.map +1 -1
  42. package/dist/device/plug.js +267 -242
  43. package/dist/device/plug.js.map +1 -1
  44. package/dist/device/striplight.d.ts +51 -16
  45. package/dist/device/striplight.d.ts.map +1 -1
  46. package/dist/device/striplight.js +527 -185
  47. package/dist/device/striplight.js.map +1 -1
  48. package/dist/irdevice/airconditioner.d.ts +7 -12
  49. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  50. package/dist/irdevice/airconditioner.js +261 -230
  51. package/dist/irdevice/airconditioner.js.map +1 -1
  52. package/dist/irdevice/airpurifier.d.ts +3 -3
  53. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  54. package/dist/irdevice/airpurifier.js +137 -104
  55. package/dist/irdevice/airpurifier.js.map +1 -1
  56. package/dist/irdevice/camera.d.ts +9 -8
  57. package/dist/irdevice/camera.d.ts.map +1 -1
  58. package/dist/irdevice/camera.js +116 -86
  59. package/dist/irdevice/camera.js.map +1 -1
  60. package/dist/irdevice/fan.d.ts +8 -7
  61. package/dist/irdevice/fan.d.ts.map +1 -1
  62. package/dist/irdevice/fan.js +143 -109
  63. package/dist/irdevice/fan.js.map +1 -1
  64. package/dist/irdevice/light.d.ts +9 -8
  65. package/dist/irdevice/light.d.ts.map +1 -1
  66. package/dist/irdevice/light.js +120 -103
  67. package/dist/irdevice/light.js.map +1 -1
  68. package/dist/irdevice/other.d.ts +9 -8
  69. package/dist/irdevice/other.d.ts.map +1 -1
  70. package/dist/irdevice/other.js +128 -98
  71. package/dist/irdevice/other.js.map +1 -1
  72. package/dist/irdevice/tv.d.ts +10 -9
  73. package/dist/irdevice/tv.d.ts.map +1 -1
  74. package/dist/irdevice/tv.js +126 -93
  75. package/dist/irdevice/tv.js.map +1 -1
  76. package/dist/irdevice/vacuumcleaner.d.ts +7 -10
  77. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  78. package/dist/irdevice/vacuumcleaner.js +115 -89
  79. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  80. package/dist/irdevice/waterheater.d.ts +6 -9
  81. package/dist/irdevice/waterheater.d.ts.map +1 -1
  82. package/dist/irdevice/waterheater.js +115 -89
  83. package/dist/irdevice/waterheater.js.map +1 -1
  84. package/dist/platform.d.ts +5 -5
  85. package/dist/platform.d.ts.map +1 -1
  86. package/dist/platform.js +227 -199
  87. package/dist/platform.js.map +1 -1
  88. package/dist/settings.d.ts +15 -11
  89. package/dist/settings.d.ts.map +1 -1
  90. package/dist/settings.js.map +1 -1
  91. package/package.json +12 -10
  92. package/src/device/bot.ts +641 -657
  93. package/src/device/colorbulb.ts +440 -339
  94. package/src/device/contact.ts +289 -233
  95. package/src/device/curtain.ts +523 -503
  96. package/src/device/humidifier.ts +421 -396
  97. package/src/device/lock.ts +257 -147
  98. package/src/device/meter.ts +335 -268
  99. package/src/device/meterplus.ts +354 -213
  100. package/src/device/motion.ts +264 -213
  101. package/src/device/plug.ts +270 -251
  102. package/src/device/striplight.ts +552 -207
  103. package/src/irdevice/airconditioner.ts +263 -241
  104. package/src/irdevice/airpurifier.ts +132 -103
  105. package/src/irdevice/camera.ts +112 -85
  106. package/src/irdevice/fan.ts +138 -106
  107. package/src/irdevice/light.ts +116 -103
  108. package/src/irdevice/other.ts +124 -97
  109. package/src/irdevice/tv.ts +123 -93
  110. package/src/irdevice/vacuumcleaner.ts +112 -89
  111. package/src/irdevice/waterheater.ts +111 -89
  112. package/src/platform.ts +216 -214
  113. package/src/settings.ts +15 -11
@@ -5,6 +5,7 @@ import { MqttClient } from 'mqtt';
5
5
  import { IncomingMessage } from 'http';
6
6
  import { interval, Subject } from 'rxjs';
7
7
  import { connectAsync } from 'async-mqtt';
8
+ import superStringify from 'super-stringify';
8
9
  import { SwitchBotPlatform } from '../platform';
9
10
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
10
11
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
@@ -35,10 +36,10 @@ export class Curtain {
35
36
  // BLE Others
36
37
  connected?: boolean;
37
38
  switchbot!: switchbot;
38
- SwitchToOpenAPI?: boolean;
39
39
  serviceData!: serviceData;
40
40
  spaceBetweenLevels!: number;
41
41
  address!: ad['address'];
42
+ SwitchToOpenAPI?: boolean;
42
43
  calibration: serviceData['calibration'];
43
44
  battery: serviceData['battery'];
44
45
  position: serviceData['position'];
@@ -48,6 +49,9 @@ export class Curtain {
48
49
  setNewTarget!: boolean;
49
50
  setNewTargetTimer!: NodeJS.Timeout;
50
51
 
52
+ //MQTT stuff
53
+ mqttClient: MqttClient | null = null;
54
+
51
55
  // Config
52
56
  set_minStep!: number;
53
57
  updateRate!: number;
@@ -63,8 +67,9 @@ export class Curtain {
63
67
  curtainUpdateInProgress!: boolean;
64
68
  doCurtainUpdate!: Subject<void>;
65
69
 
66
- //MQTT stuff
67
- mqttClient: MqttClient | null = null;
70
+ // Connection
71
+ private readonly BLE = (this.device.connectionType === 'BLE' || this.device.connectionType === 'Both');
72
+ private readonly OpenAPI = (this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'Both');
68
73
 
69
74
  constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
70
75
  // default placeholders
@@ -73,9 +78,7 @@ export class Curtain {
73
78
  this.scan(device);
74
79
  this.config(device);
75
80
  this.setupMqtt(device);
76
- this.CurrentPosition = 0;
77
- this.TargetPosition = 0;
78
- this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
81
+ this.context();
79
82
 
80
83
  // this is subject we use to track when we need to POST changes to the SwitchBot API
81
84
  this.doCurtainUpdate = new Subject();
@@ -137,33 +140,33 @@ export class Curtain {
137
140
 
138
141
  // Light Sensor Service
139
142
  if (device.curtain?.hide_lightsensor) {
140
- this.debugLog(`Curtain: ${accessory.displayName} Removing Light Sensor Service`);
143
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
141
144
  this.lightSensorService = this.accessory.getService(this.platform.Service.LightSensor);
142
145
  accessory.removeService(this.lightSensorService!);
143
146
  } else if (!this.lightSensorService) {
144
- this.debugLog(`Curtain: ${accessory.displayName} Add Light Sensor Service`);
147
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`);
145
148
  (this.lightSensorService =
146
149
  this.accessory.getService(this.platform.Service.LightSensor) || this.accessory.addService(this.platform.Service.LightSensor)),
147
150
  `${accessory.displayName} Light Sensor`;
148
151
 
149
152
  this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Light Sensor`);
150
153
  } else {
151
- this.debugLog(`Curtain: ${accessory.displayName} Light Sensor Service Not Added`);
154
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`);
152
155
  }
153
156
 
154
157
  // Battery Service
155
- if (!device.ble) {
156
- this.debugLog(`Curtain: ${accessory.displayName} Removing Battery Service`);
158
+ if (!this.BLE) {
159
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Battery Service`);
157
160
  this.batteryService = this.accessory.getService(this.platform.Service.Battery);
158
161
  accessory.removeService(this.batteryService!);
159
- } else if (device.ble && !this.batteryService) {
160
- this.debugLog(`Curtain: ${accessory.displayName} Add Battery Service`);
162
+ } else if (this.BLE && !this.batteryService) {
163
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Battery Service`);
161
164
  (this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery)),
162
165
  `${accessory.displayName} Battery`;
163
166
 
164
167
  this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
165
168
  } else {
166
- this.debugLog(`Curtain: ${accessory.displayName} Battery Service Not Added`);
169
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Battery Service Not Added`);
167
170
  }
168
171
 
169
172
  // Update Homekit
@@ -183,7 +186,7 @@ export class Curtain {
183
186
  if (this.PositionState === this.platform.Characteristic.PositionState.STOPPED) {
184
187
  return;
185
188
  }
186
- this.debugLog(`Curtain: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`);
189
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`);
187
190
  await this.refreshStatus();
188
191
  });
189
192
 
@@ -200,95 +203,65 @@ export class Curtain {
200
203
  try {
201
204
  await this.pushChanges();
202
205
  } catch (e: any) {
203
- this.errorLog(`Curtain: ${this.accessory.displayName} failed pushChanges`);
204
- if (this.deviceLogging.includes('debug')) {
205
- this.errorLog(`Curtain: ${this.accessory.displayName} failed pushChanges,` + ` Error Message: ${JSON.stringify(e.message)}`);
206
- }
207
206
  this.apiError(e);
207
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
208
+ + ` Error Message: ${superStringify(e.message)}`);
208
209
  }
209
210
  this.curtainUpdateInProgress = false;
210
211
  });
211
212
  }
212
213
 
213
- /*
214
- * Publish MQTT message for topics of
215
- * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
216
- */
217
- mqttPublish(topic: string, message: any) {
218
- const mac = this.device.deviceId
219
- ?.toLowerCase()
220
- .match(/[\s\S]{1,2}/g)
221
- ?.join(':');
222
- const options = this.device.mqttPubOptions || {};
223
- this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
224
- this.debugLog(`Meter: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
225
- }
226
-
227
- /*
228
- * Setup MQTT hadler if URL is specifed.
229
- */
230
- async setupMqtt(device: device & devicesConfig): Promise<void> {
231
- if (device.mqttURL) {
232
- try {
233
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
234
- this.debugLog(`Meter: ${this.accessory.displayName} MQTT connection has been established successfully.`);
235
- this.mqttClient.on('error', (e: Error) => {
236
- this.errorLog(`Meter: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
237
- });
238
- } catch (e) {
239
- this.mqttClient = null;
240
- this.errorLog(`Meter: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
241
- }
242
- }
243
- }
244
-
245
214
  /**
246
215
  * Parse the device status from the SwitchBot api
247
216
  */
248
217
  async parseStatus(): Promise<void> {
249
- if (this.SwitchToOpenAPI || !this.device.ble) {
218
+ if (this.BLE && !this.SwitchToOpenAPI) {
219
+ await this.BLEparseStatus();
220
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
221
+ this.SwitchToOpenAPI = false;
250
222
  await this.openAPIparseStatus();
251
223
  } else {
252
- await this.BLEparseStatus();
224
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
225
+ + ` ${this.device.connectionType}, parseStatus will not happen.`);
253
226
  }
254
227
  }
255
228
 
256
229
  async BLEparseStatus(): Promise<void> {
257
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE parseStatus`);
230
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
258
231
  // CurrentPosition
259
232
  this.CurrentPosition = 100 - Number(this.position);
260
233
  await this.setMinMax();
261
- this.debugLog(`Curtain: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
234
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
262
235
  if (this.setNewTarget) {
263
- this.infoLog(`Curtain: ${this.accessory.displayName} Checking Status ...`);
236
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
264
237
  }
265
238
 
266
239
  if (this.setNewTarget) {
267
240
  await this.setMinMax();
268
241
  if (this.TargetPosition > this.CurrentPosition) {
269
- this.debugLog(`Curtain: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`);
242
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`);
270
243
  this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
271
244
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
272
- this.debugLog(`Curtain: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
245
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
273
246
  } else if (this.TargetPosition < this.CurrentPosition) {
274
- this.debugLog(`Curtain: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`);
247
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`);
275
248
  this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
276
249
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
277
- this.debugLog(`Curtain: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
250
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
278
251
  } else {
279
- this.debugLog(`Curtain: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
252
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
280
253
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
281
254
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
282
- this.debugLog(`Curtain: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
255
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
283
256
  }
284
257
  } else {
285
- this.debugLog(`Curtain: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
258
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
286
259
  this.TargetPosition = this.CurrentPosition;
287
260
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
288
- this.debugLog(`Curtain: ${this.accessory.displayName} Stopped`);
261
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
289
262
  }
290
263
  this.debugLog(
291
- `Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
264
+ `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
292
265
  ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
293
266
  );
294
267
 
@@ -301,42 +274,42 @@ export class Curtain {
301
274
  switch (this.lightLevel) {
302
275
  case 1:
303
276
  this.CurrentAmbientLightLevel = this.set_minLux;
304
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
277
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
305
278
  break;
306
279
  case 2:
307
280
  this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
308
281
  this.debugLog(
309
- `Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` +
282
+ `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` +
310
283
  ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
311
284
  );
312
285
  break;
313
286
  case 3:
314
287
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2;
315
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
288
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
316
289
  break;
317
290
  case 4:
318
291
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3;
319
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
292
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
320
293
  break;
321
294
  case 5:
322
295
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4;
323
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
296
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
324
297
  break;
325
298
  case 6:
326
299
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5;
327
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
300
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
328
301
  break;
329
302
  case 7:
330
303
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6;
331
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
304
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
332
305
  break;
333
306
  case 8:
334
307
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7;
335
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
308
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
336
309
  break;
337
310
  case 9:
338
311
  this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8;
339
- this.debugLog(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
312
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`);
340
313
  break;
341
314
  case 10:
342
315
  default:
@@ -344,7 +317,8 @@ export class Curtain {
344
317
  this.debugLog();
345
318
  }
346
319
  this.debugLog(
347
- `Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` + ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
320
+ `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel},`
321
+ + ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
348
322
  );
349
323
  }
350
324
  // Battery
@@ -354,89 +328,87 @@ export class Curtain {
354
328
  } else {
355
329
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
356
330
  }
357
- this.debugLog(`Curtain: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
331
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
332
+ +` StatusLowBattery: ${this.StatusLowBattery}`);
358
333
  }
359
334
 
360
335
  async openAPIparseStatus(): Promise<void> {
361
- if (this.device.ble) {
362
- this.SwitchToOpenAPI = false;
336
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
337
+ // CurrentPosition
338
+ this.CurrentPosition = 100 - Number(this.slidePosition);
339
+ await this.setMinMax();
340
+ this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
341
+ if (this.setNewTarget) {
342
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
363
343
  }
364
- if (this.platform.config.credentials?.token) {
365
- this.debugLog(`Curtain: ${this.accessory.displayName} OpenAPI parseStatus`);
366
- // CurrentPosition
367
- this.CurrentPosition = 100 - Number(this.slidePosition);
368
- await this.setMinMax();
369
- this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
370
- if (this.setNewTarget) {
371
- this.infoLog(`Curtain: ${this.accessory.displayName} Checking Status ...`);
372
- }
373
344
 
374
- if (this.setNewTarget && this.moving) {
375
- await this.setMinMax();
376
- if (this.TargetPosition > this.CurrentPosition) {
377
- this.debugLog(`Curtain: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `);
378
- this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
379
- this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
380
- this.debugLog(`Curtain: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
381
- } else if (this.TargetPosition < this.CurrentPosition) {
382
- this.debugLog(`Curtain: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `);
383
- this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
384
- this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
385
- this.debugLog(`Curtain: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
386
- } else {
387
- this.debugLog(`Curtain: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
388
- this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
389
- this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
390
- this.debugLog(`Curtain: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
391
- }
345
+ if (this.setNewTarget && this.moving) {
346
+ await this.setMinMax();
347
+ if (this.TargetPosition > this.CurrentPosition) {
348
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `);
349
+ this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
350
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
351
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
352
+ } else if (this.TargetPosition < this.CurrentPosition) {
353
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `);
354
+ this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
355
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
356
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
392
357
  } else {
393
- this.debugLog(`Curtain: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
394
- this.TargetPosition = this.CurrentPosition;
358
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
395
359
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
396
- this.debugLog(`Curtain: ${this.accessory.displayName} Stopped`);
360
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
361
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
397
362
  }
398
- this.debugLog(
399
- `Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
363
+ } else {
364
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
365
+ this.TargetPosition = this.CurrentPosition;
366
+ this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
367
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
368
+ }
369
+ this.debugLog(
370
+ `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
400
371
  ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
401
- );
372
+ );
402
373
 
403
- if (!this.device.curtain?.hide_lightsensor) {
404
- this.set_minLux = this.minLux();
405
- this.set_maxLux = this.maxLux();
406
- // Brightness
407
- switch (this.brightness) {
408
- case 'dim':
409
- this.CurrentAmbientLightLevel = this.set_minLux;
410
- break;
411
- case 'bright':
412
- default:
413
- this.CurrentAmbientLightLevel = this.set_maxLux;
414
- }
415
- this.debugLog(`Curtain: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
374
+ if (!this.device.curtain?.hide_lightsensor) {
375
+ this.set_minLux = this.minLux();
376
+ this.set_maxLux = this.maxLux();
377
+ // Brightness
378
+ switch (this.brightness) {
379
+ case 'dim':
380
+ this.CurrentAmbientLightLevel = this.set_minLux;
381
+ break;
382
+ case 'bright':
383
+ default:
384
+ this.CurrentAmbientLightLevel = this.set_maxLux;
416
385
  }
386
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
417
387
  }
418
388
  }
419
389
 
420
390
  async refreshStatus(): Promise<void> {
421
- if (this.device.ble) {
391
+ if (this.BLE) {
422
392
  await this.BLERefreshStatus();
423
- } else {
393
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
424
394
  await this.openAPIRefreshStatus();
395
+ } else {
396
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
397
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
425
398
  }
426
399
  }
427
400
 
428
401
  async BLERefreshStatus(): Promise<void> {
429
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE refreshStatus`);
402
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
430
403
  const switchbot = await this.platform.connectBLE();
431
404
  // Convert to BLE Address
432
405
  this.device.bleMac = this.device
433
406
  .deviceId!.match(/.{1,2}/g)!
434
407
  .join(':')
435
408
  .toLowerCase();
436
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
409
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
437
410
  this.getCustomBLEAddress(switchbot);
438
411
  // Start to monitor advertisement packets
439
- this.errorLog(`Color Bulb: ${this.accessory.displayName} switchbot: ${JSON.stringify(switchbot)}`);
440
412
  if (switchbot !== false) {
441
413
  switchbot
442
414
  .startScan({
@@ -450,26 +422,26 @@ export class Curtain {
450
422
  if (this.deviceLogging.includes('debug')) {
451
423
  this.infoLog(this.address);
452
424
  this.infoLog(this.device.bleMac);
453
- this.infoLog(`Curtain: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
454
- this.infoLog(`Curtain: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
425
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
426
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
455
427
  }
456
428
  this.serviceData = ad.serviceData;
457
429
  this.calibration = ad.serviceData.calibration;
458
430
  this.battery = ad.serviceData.battery;
459
431
  this.position = ad.serviceData.position;
460
432
  this.lightLevel = ad.serviceData.lightLevel;
461
- this.debugLog(`Curtain: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
433
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${superStringify(ad.serviceData)}`);
462
434
  this.debugLog(
463
- `Curtain: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
435
+ `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
464
436
  `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`,
465
437
  );
466
438
 
467
439
  if (this.serviceData) {
468
440
  this.connected = true;
469
- this.debugLog(`Curtain: ${this.accessory.displayName} connected: ${this.connected}`);
441
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
470
442
  } else {
471
443
  this.connected = false;
472
- this.debugLog(`Curtain: ${this.accessory.displayName} connected: ${this.connected}`);
444
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
473
445
  }
474
446
  };
475
447
  // Wait 2 seconds
@@ -482,161 +454,101 @@ export class Curtain {
482
454
  this.parseStatus();
483
455
  this.updateHomeKitCharacteristics();
484
456
  } else {
485
- this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
486
- if (this.platform.config.credentials?.token) {
487
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
488
- this.SwitchToOpenAPI = true;
489
- await this.openAPIRefreshStatus();
490
- }
457
+ await this.BLEconnection(switchbot);
491
458
  }
492
459
  })
493
460
  .catch(async (e: any) => {
494
- this.errorLog(`Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection`);
495
- if (this.deviceLogging.includes('debug')) {
496
- this.errorLog(
497
- `Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
498
- );
499
- }
500
- if (this.platform.config.credentials?.token) {
501
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
502
- this.SwitchToOpenAPI = true;
503
- await this.openAPIRefreshStatus();
504
- }
505
461
  this.apiError(e);
462
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}`
463
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
464
+ await this.BLEconnection(switchbot);
506
465
  });
507
466
  } else {
508
467
  await this.BLEconnection(switchbot);
509
468
  }
510
469
  }
511
470
 
512
- async getCustomBLEAddress(switchbot: any) {
513
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
514
- (async () => {
515
- // Start to monitor advertisement packets
516
- await switchbot.startScan({
517
- model: 'c',
518
- });
519
- // Set an event handler
520
- switchbot.onadvertisement = (ad: any) => {
521
- this.warnLog(`Curtain: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
522
- };
523
- await switchbot.wait(10000);
524
- // Stop to monitor
525
- switchbot.stopScan();
526
- })();
527
- }
528
- }
529
-
530
- async BLEconnection(switchbot: any): Promise<void> {
531
- this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
532
- if (this.platform.config.credentials?.token) {
533
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
534
- this.SwitchToOpenAPI = true;
535
- await this.openAPIRefreshStatus();
536
- }
537
- }
538
-
539
471
  async openAPIRefreshStatus(): Promise<void> {
540
- if (this.platform.config.credentials?.token) {
541
- this.debugLog(`Curtain: ${this.accessory.displayName} OpenAPI refreshStatus`);
542
- try {
543
- const t = Date.now();
544
- const nonce = 'requestID';
545
- const data = this.platform.config.credentials?.token + t + nonce;
546
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
547
- const sign = signTerm.toString('base64');
548
- this.debugLog(`Curtain: ${this.accessory.displayName} sign: ${sign}`);
549
- const options = {
550
- hostname: HostDomain,
551
- port: 443,
552
- path: `${DevicePath}/${this.device.deviceId}/status`,
553
- method: 'GET',
554
- headers: {
555
- Authorization: this.platform.config.credentials?.token,
556
- sign: sign,
557
- nonce: nonce,
558
- t: t,
559
- 'Content-Type': 'application/json',
560
- },
561
- };
562
- const req = https.request(options, (res) => {
563
- this.debugLog(`Curtain: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
564
- let rawData = '';
565
- res.on('data', (d) => {
566
- rawData += d;
567
- this.debugLog(`Curtain: ${this.accessory.displayName} d: ${d}`);
568
- });
569
-
570
- res.on('end', () => {
571
- try {
572
- this.deviceStatus = JSON.parse(rawData);
573
- this.debugLog(`Curtain: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
574
- this.slidePosition = this.deviceStatus.body.slidePosition;
575
- this.moving = this.deviceStatus.body.moving;
576
- this.brightness = this.deviceStatus.body.brightness;
577
- this.parseStatus();
578
- this.updateHomeKitCharacteristics();
579
- } catch (e: any) {
580
- this.errorLog(`Curtain: ${this.accessory.displayName} error message: ${e.message}`);
581
- }
582
- });
472
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
473
+ try {
474
+ const t = Date.now();
475
+ const nonce = 'requestID';
476
+ const data = this.platform.config.credentials?.token + t + nonce;
477
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
478
+ const sign = signTerm.toString('base64');
479
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
480
+ const options = {
481
+ hostname: HostDomain,
482
+ port: 443,
483
+ path: `${DevicePath}/${this.device.deviceId}/status`,
484
+ method: 'GET',
485
+ headers: {
486
+ Authorization: this.platform.config.credentials?.token,
487
+ sign: sign,
488
+ nonce: nonce,
489
+ t: t,
490
+ 'Content-Type': 'application/json',
491
+ },
492
+ };
493
+ const req = https.request(options, (res) => {
494
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
495
+ let rawData = '';
496
+ res.on('data', (d) => {
497
+ rawData += d;
498
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
583
499
  });
584
- req.on('error', (e: any) => {
585
- this.errorLog(`Curtain: ${this.accessory.displayName} error message: ${e.message}`);
500
+ res.on('end', () => {
501
+ try {
502
+ this.deviceStatus = JSON.parse(rawData);
503
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus: ${superStringify(this.deviceStatus)}`);
504
+ this.slidePosition = this.deviceStatus.body.slidePosition;
505
+ this.moving = this.deviceStatus.body.moving;
506
+ this.brightness = this.deviceStatus.body.brightness;
507
+ this.parseStatus();
508
+ this.updateHomeKitCharacteristics();
509
+ } catch (e: any) {
510
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
511
+ }
586
512
  });
587
- req.end();
588
- } catch (e: any) {
589
- this.errorLog(`Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
590
- if (this.deviceLogging.includes('debug')) {
591
- this.errorLog(
592
- `Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
593
- );
594
- }
595
- this.apiError(e);
596
- }
513
+ });
514
+ req.on('error', (e: any) => {
515
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
516
+ });
517
+ req.end();
518
+ } catch (e: any) {
519
+ this.apiError(e);
520
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
521
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
597
522
  }
598
523
  }
599
524
 
600
525
  async pushChanges(): Promise<void> {
601
- if (this.device.ble) {
602
- await this.BLEpushChanges();
603
- } else {
526
+ if (this.BLE) {
527
+ await this.BLERefreshStatus();
528
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
604
529
  await this.openAPIpushChanges();
530
+ } else {
531
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
532
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
605
533
  }
606
534
  interval(this.updateRate * 1000)
607
- .pipe(take(2))
535
+ .pipe(take(1))
608
536
  .subscribe(async () => {
609
537
  await this.refreshStatus();
610
538
  });
611
539
  }
612
540
 
613
541
  async BLEpushChanges(): Promise<void> {
542
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
614
543
  if (this.TargetPosition !== this.CurrentPosition) {
615
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE pushChanges`);
616
544
  const switchbot = await this.platform.connectBLE();
617
545
  // Convert to BLE Address
618
546
  this.device.bleMac = this.device
619
547
  .deviceId!.match(/.{1,2}/g)!
620
548
  .join(':')
621
549
  .toLowerCase();
622
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
623
- if (this.TargetPosition > 50) {
624
- if (this.device.curtain?.setOpenMode === '1') {
625
- this.setPositionMode = 1;
626
- this.Mode = 'Silent Mode';
627
- } else {
628
- this.setPositionMode = 0;
629
- this.Mode = 'Performance Mode';
630
- }
631
- } else {
632
- if (this.device.curtain?.setCloseMode === '1') {
633
- this.setPositionMode = 1;
634
- this.Mode = 'Silent Mode';
635
- } else {
636
- this.setPositionMode = 0;
637
- this.Mode = 'Performance Mode';
638
- }
639
- }
550
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
551
+ this.SilentPerformance();
640
552
  const adjustedMode = this.setPositionMode || null;
641
553
  if (adjustedMode === null) {
642
554
  this.Mode = 'Default Mode';
@@ -645,256 +557,286 @@ export class Curtain {
645
557
  if (switchbot !== false) {
646
558
  switchbot
647
559
  .discover({ model: 'c', quick: true, id: this.device.bleMac })
648
- .then((device_list) => {
560
+ .then((device_list: any) => {
649
561
  this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);
650
562
  return device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode);
651
563
  })
652
564
  .then(() => {
653
- this.debugLog(`Curtain: ${this.accessory.displayName} Done.`);
565
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
654
566
  })
655
567
  .catch(async (e: any) => {
656
- this.errorLog(`Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection`);
657
- if (this.deviceLogging.includes('debug')) {
658
- this.errorLog(
659
- `Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
660
- );
661
- }
662
- if (this.platform.config.credentials?.token) {
663
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
568
+ this.apiError(e);
569
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
570
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
571
+ if (this.platform.config.credentials?.token && this.device.connectionType !== 'Both') {
572
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
664
573
  await this.openAPIpushChanges();
665
574
  }
666
- this.apiError(e);
667
575
  });
668
576
  } else {
669
- this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
577
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
670
578
  if (this.platform.config.credentials?.token) {
671
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
579
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
672
580
  await this.openAPIpushChanges();
673
581
  }
674
582
  }
675
583
  } else {
676
- this.debugLog(
677
- `Curtain: ${this.accessory.displayName} No BLE Changes, CurrentPosition & TargetPosition Are the Same.` +
584
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` +
678
585
  ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
679
586
  );
680
587
  }
681
588
  }
682
589
 
683
590
  async openAPIpushChanges(): Promise<void> {
684
- if (this.platform.config.credentials?.token) {
685
- try {
686
- this.debugLog(`Curtain: ${this.accessory.displayName} OpenAPI pushChanges`);
687
- if (this.TargetPosition !== this.CurrentPosition) {
688
- // Make Push On request to the API
689
- const t = Date.now();
690
- const nonce = 'requestID';
691
- const data = this.platform.config.credentials?.token + t + nonce;
692
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
693
- .update(Buffer.from(data, 'utf-8'))
694
- .digest();
695
- const sign = signTerm.toString('base64');
696
- this.debugLog(`Curtain: ${this.accessory.displayName} sign: ${sign}`);
697
- this.debugLog(`Pushing ${this.TargetPosition}`);
698
- const adjustedTargetPosition = 100 - Number(this.TargetPosition);
699
- if (this.TargetPosition > 50) {
700
- this.setPositionMode = this.device.curtain?.setOpenMode;
701
- } else {
702
- this.setPositionMode = this.device.curtain?.setCloseMode;
703
- }
704
- if (this.setPositionMode === '1') {
705
- this.Mode = 'Silent Mode';
706
- } else if (this.setPositionMode === '0') {
707
- this.Mode = 'Performance Mode';
708
- } else {
709
- this.Mode = 'Default Mode';
710
- }
711
- this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
712
- const adjustedMode = this.setPositionMode || 'ff';
713
- const body = JSON.stringify({
714
- 'command': 'setPosition',
715
- 'parameter': `0,${adjustedMode},${adjustedTargetPosition}`,
716
- 'commandType': 'command',
717
- });
718
- this.infoLog(`Curtain: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
719
- const options = {
720
- hostname: HostDomain,
721
- port: 443,
722
- path: `${DevicePath}/${this.device.deviceId}/commands`,
723
- method: 'POST',
724
- headers: {
725
- 'Authorization': this.platform.config.credentials?.token,
726
- 'sign': sign,
727
- 'nonce': nonce,
728
- 't': t,
729
- 'Content-Type': 'application/json',
730
- 'Content-Length': body.length,
731
- },
732
- };
733
- const req = https.request(options, res => {
734
- this.debugLog(`Curtain: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
735
- this.statusCode({ res });
736
- res.on('data', d => {
737
- this.debugLog(`Curtain: ${this.accessory.displayName} d: ${d}`);
738
- });
739
- });
740
- req.on('error', (e: any) => {
741
- this.errorLog(`Curtain: ${this.accessory.displayName} error message: ${e.message}`);
742
- });
743
- req.write(body);
744
- req.end();
745
- this.debugLog(`Curtain: ${this.accessory.displayName} openAPIpushChanges: ${JSON.stringify(req)}`);
591
+ try {
592
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI pushChanges`);
593
+ if (this.TargetPosition !== this.CurrentPosition) {
594
+ // Make Push On request to the API
595
+ const t = Date.now();
596
+ const nonce = 'requestID';
597
+ const data = this.platform.config.credentials?.token + t + nonce;
598
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
599
+ .update(Buffer.from(data, 'utf-8'))
600
+ .digest();
601
+ const sign = signTerm.toString('base64');
602
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
603
+ this.debugLog(`Pushing ${this.TargetPosition}`);
604
+ const adjustedTargetPosition = 100 - Number(this.TargetPosition);
605
+ if (this.TargetPosition > 50) {
606
+ this.setPositionMode = this.device.curtain?.setOpenMode;
746
607
  } else {
747
- this.debugLog(
748
- `Curtain: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` +
749
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
750
- );
608
+ this.setPositionMode = this.device.curtain?.setCloseMode;
751
609
  }
752
- } catch (e: any) {
753
- this.errorLog(`Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
754
- if (this.deviceLogging.includes('debug')) {
755
- this.errorLog(
756
- `Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
757
- );
610
+ if (this.setPositionMode === '1') {
611
+ this.Mode = 'Silent Mode';
612
+ } else if (this.setPositionMode === '0') {
613
+ this.Mode = 'Performance Mode';
614
+ } else {
615
+ this.Mode = 'Default Mode';
758
616
  }
759
- this.apiError(e);
617
+ this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
618
+ const adjustedMode = this.setPositionMode || 'ff';
619
+ const body = superStringify({
620
+ 'command': 'setPosition',
621
+ 'parameter': `0,${adjustedMode},${adjustedTargetPosition}`,
622
+ 'commandType': 'command',
623
+ });
624
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
625
+ const options = {
626
+ hostname: HostDomain,
627
+ port: 443,
628
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
629
+ method: 'POST',
630
+ headers: {
631
+ 'Authorization': this.platform.config.credentials?.token,
632
+ 'sign': sign,
633
+ 'nonce': nonce,
634
+ 't': t,
635
+ 'Content-Type': 'application/json',
636
+ 'Content-Length': body.length,
637
+ },
638
+ };
639
+ const req = https.request(options, res => {
640
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges statusCode: ${res.statusCode}`);
641
+ this.statusCode({ res });
642
+ res.on('data', d => {
643
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
644
+ });
645
+ });
646
+ req.on('error', (e: any) => {
647
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
648
+ });
649
+ req.write(body);
650
+ req.end();
651
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges: ${superStringify(req)}`);
652
+ } else {
653
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
654
+ +` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
655
+ );
760
656
  }
657
+ } catch (e: any) {
658
+ this.apiError(e);
659
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}`
660
+ + ` Connection, Error Message: ${superStringify(e.message)}`,
661
+ );
761
662
  }
762
663
  }
763
664
 
665
+ /**
666
+ * Handle requests to set the value of the "Target Position" characteristic
667
+ */
668
+ async TargetPositionSet(value: CharacteristicValue): Promise<void> {
669
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
670
+
671
+ this.TargetPosition = value;
672
+ this.mqttPublish('TargetPosition', this.TargetPosition);
673
+
674
+ await this.setMinMax();
675
+ if (value > this.CurrentPosition) {
676
+ this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
677
+ this.setNewTarget = true;
678
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
679
+ } else if (value < this.CurrentPosition) {
680
+ this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
681
+ this.setNewTarget = true;
682
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
683
+ } else {
684
+ this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
685
+ this.setNewTarget = false;
686
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
687
+ }
688
+ this.windowCoveringService.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
689
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
690
+
691
+ /**
692
+ * If Curtain movement time is short, the moving flag from backend is always false.
693
+ * The minimum time depends on the network control latency.
694
+ */
695
+ clearTimeout(this.setNewTargetTimer);
696
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
697
+ if (this.setNewTarget) {
698
+ this.setNewTargetTimer = setTimeout(() => {
699
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
700
+ this.setNewTarget = false;
701
+ }, this.updateRate * 1000);
702
+ }
703
+ this.doCurtainUpdate.next();
704
+ }
705
+
764
706
  async updateHomeKitCharacteristics(): Promise<void> {
765
707
  await this.setMinMax();
766
708
  if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) {
767
- this.debugLog(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
709
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
768
710
  } else {
711
+ this.accessory.context.CurrentPosition = this.CurrentPosition;
769
712
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, Number(this.CurrentPosition));
770
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
713
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
771
714
  this.mqttPublish('CurrentPosition', this.CurrentPosition);
772
715
  }
773
716
  if (this.PositionState === undefined) {
774
- this.debugLog(`Curtain: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
717
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
775
718
  } else {
719
+ this.accessory.context.PositionState = this.PositionState;
776
720
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, Number(this.PositionState));
777
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
721
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
778
722
  this.mqttPublish('PositionState', this.PositionState);
779
723
  }
780
724
  if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
781
- this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
725
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
782
726
  } else {
727
+ this.accessory.context.TargetPosition = this.TargetPosition;
783
728
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, Number(this.TargetPosition));
784
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
729
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
785
730
  this.mqttPublish('TargetPosition', this.TargetPosition);
786
731
  }
787
732
  if (!this.device.curtain?.hide_lightsensor) {
788
733
  if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
789
- this.debugLog(`Curtain: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
734
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
790
735
  } else {
736
+ this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
791
737
  this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
792
- this.debugLog(`Curtain: ${this.accessory.displayName}` + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
738
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
739
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
793
740
  this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
794
741
  }
795
742
  }
796
- if (this.device.ble) {
743
+ if (this.BLE) {
797
744
  if (this.BatteryLevel === undefined) {
798
- this.debugLog(`Curtain: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
745
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
799
746
  } else {
747
+ this.accessory.context.BatteryLevel = this.BatteryLevel;
800
748
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
801
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
749
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
802
750
  this.mqttPublish('BatteryLevel', this.BatteryLevel);
803
751
  }
804
752
  if (this.StatusLowBattery === undefined) {
805
- this.debugLog(`Curtain: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
753
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
806
754
  } else {
755
+ this.accessory.context.StatusLowBattery = this.StatusLowBattery;
807
756
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
808
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
757
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
809
758
  this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
810
759
  }
811
760
  }
812
761
  }
813
762
 
814
- async apiError(e: any): Promise<void> {
815
- this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e);
816
- this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, e);
817
- this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, e);
818
- if (!this.device.curtain?.hide_lightsensor) {
819
- this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
820
- }
821
- if (this.device.ble) {
822
- this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
823
- this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
824
- }
825
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
763
+ /*
764
+ * Publish MQTT message for topics of
765
+ * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
766
+ */
767
+ mqttPublish(topic: string, message: any) {
768
+ const mac = this.device.deviceId
769
+ ?.toLowerCase()
770
+ .match(/[\s\S]{1,2}/g)
771
+ ?.join(':');
772
+ const options = this.device.mqttPubOptions || {};
773
+ this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
774
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${superStringify(options)}`);
826
775
  }
827
776
 
828
- async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
829
- switch (res.statusCode) {
830
- case 151:
831
- this.errorLog(`Curtain: ${this.accessory.displayName} Command not supported by this device type.`);
832
- break;
833
- case 152:
834
- this.errorLog(`Curtain: ${this.accessory.displayName} Device not found.`);
835
- break;
836
- case 160:
837
- this.errorLog(`Curtain: ${this.accessory.displayName} Command is not supported.`);
838
- break;
839
- case 161:
840
- this.errorLog(`Curtain: ${this.accessory.displayName} Device is offline.`);
841
- break;
842
- case 171:
843
- this.errorLog(`Curtain: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
844
- break;
845
- case 190:
846
- this.errorLog(
847
- `Curtain: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
848
- ` Or command: ${JSON.stringify(res)} format is invalid`,
849
- );
850
- break;
851
- case 100:
852
- this.debugLog(`Curtain: ${this.accessory.displayName} Command successfully sent.`);
853
- break;
854
- default:
855
- this.debugLog(`Curtain: ${this.accessory.displayName} Unknown statusCode.`);
777
+ /*
778
+ * Setup MQTT hadler if URL is specifed.
779
+ */
780
+ async setupMqtt(device: device & devicesConfig): Promise<void> {
781
+ if (device.mqttURL) {
782
+ try {
783
+ this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
784
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
785
+ this.mqttClient.on('error', (e: Error) => {
786
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
787
+ });
788
+ } catch (e) {
789
+ this.mqttClient = null;
790
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
791
+ }
856
792
  }
857
793
  }
858
794
 
859
- /**
860
- * Handle requests to set the value of the "Target Position" characteristic
861
- */
862
- async TargetPositionSet(value: CharacteristicValue): Promise<void> {
863
- this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${value}`);
864
-
865
- this.TargetPosition = value;
866
- this.mqttPublish('TargetPosition', this.TargetPosition);
795
+ async getCustomBLEAddress(switchbot: any) {
796
+ if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
797
+ (async () => {
798
+ // Start to monitor advertisement packets
799
+ await switchbot.startScan({
800
+ model: 'c',
801
+ });
802
+ // Set an event handler
803
+ switchbot.onadvertisement = (ad: any) => {
804
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
805
+ };
806
+ await switchbot.wait(10000);
807
+ // Stop to monitor
808
+ switchbot.stopScan();
809
+ })();
810
+ }
811
+ }
867
812
 
868
- await this.setMinMax();
869
- if (value > this.CurrentPosition) {
870
- this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
871
- this.setNewTarget = true;
872
- this.debugLog(`Curtain: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
873
- } else if (value < this.CurrentPosition) {
874
- this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
875
- this.setNewTarget = true;
876
- this.debugLog(`Curtain: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
877
- } else {
878
- this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
879
- this.setNewTarget = false;
880
- this.debugLog(`Curtain: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
813
+ async BLEconnection(switchbot: any): Promise<void> {
814
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
815
+ if (this.platform.config.credentials?.token && this.device.connectionType !== 'Both') {
816
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
817
+ this.SwitchToOpenAPI = true;
818
+ await this.openAPIRefreshStatus();
881
819
  }
882
- this.windowCoveringService.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
883
- this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
820
+ }
884
821
 
885
- /**
886
- * If Curtain movement time is short, the moving flag from backend is always false.
887
- * The minimum time depends on the network control latency.
888
- */
889
- clearTimeout(this.setNewTargetTimer);
890
- this.debugLog(`Curtain: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
891
- if (this.setNewTarget) {
892
- this.setNewTargetTimer = setTimeout(() => {
893
- this.debugLog(`Curtain: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
894
- this.setNewTarget = false;
895
- }, this.updateRate * 1000);
822
+ async SilentPerformance() {
823
+ if (this.TargetPosition > 50) {
824
+ if (this.device.curtain?.setOpenMode === '1') {
825
+ this.setPositionMode = 1;
826
+ this.Mode = 'Silent Mode';
827
+ } else {
828
+ this.setPositionMode = 0;
829
+ this.Mode = 'Performance Mode';
830
+ }
831
+ } else {
832
+ if (this.device.curtain?.setCloseMode === '1') {
833
+ this.setPositionMode = 1;
834
+ this.Mode = 'Silent Mode';
835
+ } else {
836
+ this.setPositionMode = 0;
837
+ this.Mode = 'Performance Mode';
838
+ }
896
839
  }
897
- this.doCurtainUpdate.next();
898
840
  }
899
841
 
900
842
  async setMinMax(): Promise<void> {
@@ -910,92 +852,120 @@ export class Curtain {
910
852
  }
911
853
  }
912
854
 
913
- async config(device: device & devicesConfig): Promise<void> {
914
- let config = {};
915
- if (device.curtain) {
916
- config = device.curtain;
917
- }
918
- if (device.ble) {
919
- config['ble'] = device.ble;
920
- }
921
- if (device.logging !== undefined) {
922
- config['logging'] = device.logging;
923
- }
924
- if (device.refreshRate !== undefined) {
925
- config['refreshRate'] = device.refreshRate;
926
- }
927
- if (device.scanDuration !== undefined) {
928
- config['scanDuration'] = device.scanDuration;
929
- }
930
- if (Object.entries(config).length !== 0) {
931
- this.infoLog(`Curtain: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
855
+ minStep(device: device & devicesConfig): number {
856
+ if (device.curtain?.set_minStep) {
857
+ this.set_minStep = device.curtain?.set_minStep;
858
+ } else {
859
+ this.set_minStep = 1;
932
860
  }
861
+ return this.set_minStep;
933
862
  }
934
863
 
935
- async refreshRate(device: device & devicesConfig): Promise<void> {
936
- // refreshRate
937
- if (device.refreshRate) {
938
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
939
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
940
- } else if (this.platform.config.options!.refreshRate) {
941
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
942
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
864
+ minLux(): number {
865
+ if (this.device.curtain?.set_minLux) {
866
+ this.set_minLux = this.device.curtain?.set_minLux;
867
+ } else {
868
+ this.set_minLux = 1;
943
869
  }
944
- // updateRate
945
- if (device?.curtain?.updateRate) {
946
- this.updateRate = device?.curtain?.updateRate;
947
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config Curtain refreshRate: ${this.deviceRefreshRate}`);
870
+ return this.set_minLux;
871
+ }
872
+
873
+ maxLux(): number {
874
+ if (this.device.curtain?.set_maxLux) {
875
+ this.set_maxLux = this.device.curtain?.set_maxLux;
948
876
  } else {
949
- this.updateRate = 7;
950
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Default Curtain Refresh Rate.`);
877
+ this.set_maxLux = 6001;
951
878
  }
879
+ return this.set_maxLux;
952
880
  }
953
881
 
954
882
  async scan(device: device & devicesConfig): Promise<void> {
955
883
  if (device.scanDuration) {
956
884
  if (this.updateRate > device.scanDuration) {
957
885
  this.scanDuration = this.updateRate;
958
- this.warnLog(`Curtain: ${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
886
+ this.warnLog(`${this.device.deviceType}: `
887
+ + `${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
959
888
  } else {
960
889
  this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
961
890
  }
962
- if (device.ble) {
963
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
891
+ if (this.BLE) {
892
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
964
893
  }
965
894
  } else {
966
895
  if (this.updateRate > 1) {
967
896
  this.scanDuration = this.updateRate;
968
- this.warnLog(`Curtain: ${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
897
+ this.warnLog(`${this.device.deviceType}: `
898
+ + `${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
969
899
  } else {
970
900
  this.scanDuration = this.accessory.context.scanDuration = 1;
971
901
  }
972
- if (this.device.ble) {
973
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
902
+ if (this.BLE) {
903
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
974
904
  }
975
905
  }
976
906
  }
977
907
 
978
- async logs(device: device & devicesConfig): Promise<void> {
979
- if (this.platform.debugMode) {
980
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
981
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
982
- } else if (device.logging) {
983
- this.deviceLogging = this.accessory.context.logging = device.logging;
984
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
985
- } else if (this.platform.config.options?.logging) {
986
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
987
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
988
- } else {
989
- this.deviceLogging = this.accessory.context.logging = 'standard';
990
- this.debugLog(`Curtain: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
908
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
909
+ switch (res.statusCode) {
910
+ case 151:
911
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
912
+ break;
913
+ case 152:
914
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
915
+ break;
916
+ case 160:
917
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
918
+ break;
919
+ case 161:
920
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
921
+ this.offlineOff();
922
+ break;
923
+ case 171:
924
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
925
+ this.offlineOff();
926
+ break;
927
+ case 190:
928
+ this.errorLog(
929
+ `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
930
+ ` Or command: ${superStringify(res)} format is invalid`,
931
+ );
932
+ break;
933
+ case 100:
934
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
935
+ break;
936
+ default:
937
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
991
938
  }
992
939
  }
993
940
 
941
+ async offlineOff(): Promise<void> {
942
+ if (this.device.offline) {
943
+ this.CurrentPosition = 100;
944
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.On).updateValue(this.CurrentPosition);
945
+ await this.updateHomeKitCharacteristics();
946
+ }
947
+ }
948
+
949
+ async apiError(e: any): Promise<void> {
950
+ this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e);
951
+ this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, e);
952
+ this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, e);
953
+ if (!this.device.curtain?.hide_lightsensor) {
954
+ this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
955
+ }
956
+ if (this.BLE) {
957
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
958
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
959
+ }
960
+ //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
961
+ }
962
+
994
963
  FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
995
964
  let FirmwareRevision: string;
996
- this.debugLog(`Curtain: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
997
- this.debugLog(`Curtain: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
998
- this.debugLog(`Curtain: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
965
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
966
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
967
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
968
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
999
969
  if (accessory.context.FirmwareRevision) {
1000
970
  FirmwareRevision = accessory.context.FirmwareRevision;
1001
971
  } else if (device.firmware) {
@@ -1006,31 +976,81 @@ export class Curtain {
1006
976
  return FirmwareRevision;
1007
977
  }
1008
978
 
1009
- minStep(device: device & devicesConfig): number {
1010
- if (device.curtain?.set_minStep) {
1011
- this.set_minStep = device.curtain?.set_minStep;
979
+ async context() {
980
+ if (this.CurrentPosition === undefined) {
981
+ this.CurrentPosition = 0;
1012
982
  } else {
1013
- this.set_minStep = 1;
983
+ this.CurrentPosition = this.accessory.context.CurrentPosition;
984
+ }
985
+
986
+ if (this.TargetPosition === undefined) {
987
+ this.TargetPosition = 0;
988
+ } else {
989
+ this.TargetPosition = this.accessory.context.TargetPosition;
990
+ }
991
+
992
+ if (this.PositionState === undefined) {
993
+ this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
994
+ } else {
995
+ this.PositionState = this.accessory.context.PositionState;
1014
996
  }
1015
- return this.set_minStep;
1016
997
  }
1017
998
 
1018
- minLux(): number {
1019
- if (this.device.curtain?.set_minLux) {
1020
- this.set_minLux = this.device.curtain?.set_minLux;
999
+ async refreshRate(device: device & devicesConfig): Promise<void> {
1000
+ // refreshRate
1001
+ if (device.refreshRate) {
1002
+ this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
1003
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
1004
+ } else if (this.platform.config.options!.refreshRate) {
1005
+ this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
1006
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
1007
+ }
1008
+ // updateRate
1009
+ if (device?.curtain?.updateRate) {
1010
+ this.updateRate = device?.curtain?.updateRate;
1011
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain refreshRate: ${this.deviceRefreshRate}`);
1021
1012
  } else {
1022
- this.set_minLux = 1;
1013
+ this.updateRate = 7;
1014
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain Refresh Rate.`);
1023
1015
  }
1024
- return this.set_minLux;
1025
1016
  }
1026
1017
 
1027
- maxLux(): number {
1028
- if (this.device.curtain?.set_maxLux) {
1029
- this.set_maxLux = this.device.curtain?.set_maxLux;
1018
+ async config(device: device & devicesConfig): Promise<void> {
1019
+ let config = {};
1020
+ if (device.curtain) {
1021
+ config = device.curtain;
1022
+ }
1023
+ if (device.connectionType !== undefined) {
1024
+ config['connectionType'] = device.connectionType;
1025
+ }
1026
+ if (device.logging !== undefined) {
1027
+ config['logging'] = device.logging;
1028
+ }
1029
+ if (device.refreshRate !== undefined) {
1030
+ config['refreshRate'] = device.refreshRate;
1031
+ }
1032
+ if (device.scanDuration !== undefined) {
1033
+ config['scanDuration'] = device.scanDuration;
1034
+ }
1035
+ if (Object.entries(config).length !== 0) {
1036
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
1037
+ }
1038
+ }
1039
+
1040
+ async logs(device: device & devicesConfig): Promise<void> {
1041
+ if (this.platform.debugMode) {
1042
+ this.deviceLogging = this.accessory.context.logging = 'debugMode';
1043
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
1044
+ } else if (device.logging) {
1045
+ this.deviceLogging = this.accessory.context.logging = device.logging;
1046
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
1047
+ } else if (this.platform.config.options?.logging) {
1048
+ this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
1049
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
1030
1050
  } else {
1031
- this.set_maxLux = 6001;
1051
+ this.deviceLogging = this.accessory.context.logging = 'standard';
1052
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
1032
1053
  }
1033
- return this.set_maxLux;
1034
1054
  }
1035
1055
 
1036
1056
  /**