@switchbot/homebridge-switchbot 2.0.0-beta.3 → 2.0.0-beta.30

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