@switchbot/homebridge-switchbot 2.0.0-beta.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +3 -2
  3. package/config.schema.json +91 -19
  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 +657 -648
  7. package/dist/device/bot.js.map +1 -1
  8. package/dist/device/ceilinglight.d.ts +137 -0
  9. package/dist/device/ceilinglight.d.ts.map +1 -0
  10. package/dist/device/ceilinglight.js +1127 -0
  11. package/dist/device/ceilinglight.js.map +1 -0
  12. package/dist/device/colorbulb.d.ts +36 -14
  13. package/dist/device/colorbulb.d.ts.map +1 -1
  14. package/dist/device/colorbulb.js +472 -297
  15. package/dist/device/colorbulb.js.map +1 -1
  16. package/dist/device/contact.d.ts +18 -8
  17. package/dist/device/contact.d.ts.map +1 -1
  18. package/dist/device/contact.js +306 -237
  19. package/dist/device/contact.js.map +1 -1
  20. package/dist/device/curtain.d.ts +23 -16
  21. package/dist/device/curtain.d.ts.map +1 -1
  22. package/dist/device/curtain.js +566 -502
  23. package/dist/device/curtain.js.map +1 -1
  24. package/dist/device/humidifier.d.ts +24 -18
  25. package/dist/device/humidifier.d.ts.map +1 -1
  26. package/dist/device/humidifier.js +453 -369
  27. package/dist/device/humidifier.js.map +1 -1
  28. package/dist/device/lock.d.ts +27 -8
  29. package/dist/device/lock.d.ts.map +1 -1
  30. package/dist/device/lock.js +305 -148
  31. package/dist/device/lock.js.map +1 -1
  32. package/dist/device/meter.d.ts +22 -16
  33. package/dist/device/meter.d.ts.map +1 -1
  34. package/dist/device/meter.js +367 -267
  35. package/dist/device/meter.js.map +1 -1
  36. package/dist/device/meterplus.d.ts +24 -12
  37. package/dist/device/meterplus.d.ts.map +1 -1
  38. package/dist/device/meterplus.js +381 -213
  39. package/dist/device/meterplus.js.map +1 -1
  40. package/dist/device/motion.d.ts +18 -8
  41. package/dist/device/motion.d.ts.map +1 -1
  42. package/dist/device/motion.js +279 -216
  43. package/dist/device/motion.js.map +1 -1
  44. package/dist/device/plug.d.ts +27 -17
  45. package/dist/device/plug.d.ts.map +1 -1
  46. package/dist/device/plug.js +295 -251
  47. package/dist/device/plug.js.map +1 -1
  48. package/dist/device/striplight.d.ts +43 -19
  49. package/dist/device/striplight.d.ts.map +1 -1
  50. package/dist/device/striplight.js +506 -262
  51. package/dist/device/striplight.js.map +1 -1
  52. package/dist/irdevice/airconditioner.d.ts +9 -12
  53. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  54. package/dist/irdevice/airconditioner.js +278 -230
  55. package/dist/irdevice/airconditioner.js.map +1 -1
  56. package/dist/irdevice/airpurifier.d.ts +5 -3
  57. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  58. package/dist/irdevice/airpurifier.js +154 -104
  59. package/dist/irdevice/airpurifier.js.map +1 -1
  60. package/dist/irdevice/camera.d.ts +11 -8
  61. package/dist/irdevice/camera.d.ts.map +1 -1
  62. package/dist/irdevice/camera.js +133 -86
  63. package/dist/irdevice/camera.js.map +1 -1
  64. package/dist/irdevice/fan.d.ts +10 -7
  65. package/dist/irdevice/fan.d.ts.map +1 -1
  66. package/dist/irdevice/fan.js +160 -109
  67. package/dist/irdevice/fan.js.map +1 -1
  68. package/dist/irdevice/light.d.ts +11 -8
  69. package/dist/irdevice/light.d.ts.map +1 -1
  70. package/dist/irdevice/light.js +137 -103
  71. package/dist/irdevice/light.js.map +1 -1
  72. package/dist/irdevice/other.d.ts +11 -8
  73. package/dist/irdevice/other.d.ts.map +1 -1
  74. package/dist/irdevice/other.js +145 -98
  75. package/dist/irdevice/other.js.map +1 -1
  76. package/dist/irdevice/tv.d.ts +12 -9
  77. package/dist/irdevice/tv.d.ts.map +1 -1
  78. package/dist/irdevice/tv.js +143 -93
  79. package/dist/irdevice/tv.js.map +1 -1
  80. package/dist/irdevice/vacuumcleaner.d.ts +9 -10
  81. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  82. package/dist/irdevice/vacuumcleaner.js +132 -89
  83. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  84. package/dist/irdevice/waterheater.d.ts +8 -9
  85. package/dist/irdevice/waterheater.d.ts.map +1 -1
  86. package/dist/irdevice/waterheater.js +132 -89
  87. package/dist/irdevice/waterheater.js.map +1 -1
  88. package/dist/platform.d.ts +13 -12
  89. package/dist/platform.d.ts.map +1 -1
  90. package/dist/platform.js +524 -418
  91. package/dist/platform.js.map +1 -1
  92. package/dist/settings.d.ts +19 -11
  93. package/dist/settings.d.ts.map +1 -1
  94. package/dist/settings.js.map +1 -1
  95. package/homebridge-ui/public/index.html +1 -0
  96. package/package.json +11 -9
  97. package/src/device/bot.ts +656 -659
  98. package/src/device/ceilinglight.ts +1208 -0
  99. package/src/device/colorbulb.ts +482 -334
  100. package/src/device/contact.ts +309 -241
  101. package/src/device/curtain.ts +571 -516
  102. package/src/device/humidifier.ts +456 -403
  103. package/src/device/lock.ts +314 -149
  104. package/src/device/meter.ts +378 -280
  105. package/src/device/meterplus.ts +391 -219
  106. package/src/device/motion.ts +285 -222
  107. package/src/device/plug.ts +299 -260
  108. package/src/device/striplight.ts +522 -289
  109. package/src/irdevice/airconditioner.ts +282 -241
  110. package/src/irdevice/airpurifier.ts +151 -103
  111. package/src/irdevice/camera.ts +131 -85
  112. package/src/irdevice/fan.ts +157 -106
  113. package/src/irdevice/light.ts +135 -103
  114. package/src/irdevice/other.ts +143 -97
  115. package/src/irdevice/tv.ts +142 -93
  116. package/src/irdevice/vacuumcleaner.ts +131 -89
  117. package/src/irdevice/waterheater.ts +130 -89
  118. package/src/platform.ts +522 -446
  119. package/src/settings.ts +20 -11
  120. package/.github/npm-version-script.js +0 -81
@@ -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,86 +326,85 @@ 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
410
  if (switchbot !== false) {
@@ -446,29 +417,25 @@ export class Curtain {
446
417
  // Set an event hander
447
418
  switchbot.onadvertisement = (ad: any) => {
448
419
  this.address = ad.address;
449
- if (this.deviceLogging.includes('debug')) {
450
- this.infoLog(this.address);
451
- this.infoLog(this.device.bleMac);
452
- this.infoLog(`Curtain: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
453
- this.infoLog(`Curtain: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
454
- }
420
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},`
421
+ + ` BLE Address Found: ${this.address}`);
455
422
  this.serviceData = ad.serviceData;
456
423
  this.calibration = ad.serviceData.calibration;
457
424
  this.battery = ad.serviceData.battery;
458
425
  this.position = ad.serviceData.position;
459
426
  this.lightLevel = ad.serviceData.lightLevel;
460
- this.debugLog(`Curtain: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
427
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${superStringify(ad.serviceData)}`);
461
428
  this.debugLog(
462
- `Curtain: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
429
+ `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
463
430
  `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`,
464
431
  );
465
432
 
466
433
  if (this.serviceData) {
467
434
  this.connected = true;
468
- this.debugLog(`Curtain: ${this.accessory.displayName} connected: ${this.connected}`);
435
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
469
436
  } else {
470
437
  this.connected = false;
471
- this.debugLog(`Curtain: ${this.accessory.displayName} connected: ${this.connected}`);
438
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
472
439
  }
473
440
  };
474
441
  // Wait 2 seconds
@@ -478,164 +445,104 @@ export class Curtain {
478
445
  // Stop to monitor
479
446
  switchbot.stopScan();
480
447
  if (this.connected) {
481
- this.parseStatus();
448
+ this.BLEparseStatus();
482
449
  this.updateHomeKitCharacteristics();
483
450
  } else {
484
- this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
485
- if (this.platform.config.credentials?.token) {
486
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
487
- this.SwitchToOpenAPI = true;
488
- await this.openAPIRefreshStatus();
489
- }
451
+ await this.BLERefreshConnection(switchbot);
490
452
  }
491
453
  })
492
454
  .catch(async (e: any) => {
493
- this.errorLog(`Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection`);
494
- if (this.deviceLogging.includes('debug')) {
495
- this.errorLog(
496
- `Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
497
- );
498
- }
499
- if (this.platform.config.credentials?.token) {
500
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
501
- this.SwitchToOpenAPI = true;
502
- await this.openAPIRefreshStatus();
503
- }
504
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);
505
459
  });
506
460
  } else {
507
- await this.BLEconnection(switchbot);
508
- }
509
- }
510
-
511
- async getCustomBLEAddress(switchbot: any) {
512
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
513
- (async () => {
514
- // Start to monitor advertisement packets
515
- await switchbot.startScan({
516
- model: 'c',
517
- });
518
- // Set an event handler
519
- switchbot.onadvertisement = (ad: any) => {
520
- this.warnLog(`Curtain: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
521
- };
522
- await switchbot.wait(10000);
523
- // Stop to monitor
524
- switchbot.stopScan();
525
- })();
526
- }
527
- }
528
-
529
- async BLEconnection(switchbot: any): Promise<void> {
530
- this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
531
- if (this.platform.config.credentials?.token) {
532
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
533
- this.SwitchToOpenAPI = true;
534
- await this.openAPIRefreshStatus();
461
+ await this.BLERefreshConnection(switchbot);
535
462
  }
536
463
  }
537
464
 
538
465
  async openAPIRefreshStatus(): Promise<void> {
539
- if (this.platform.config.credentials?.token) {
540
- this.debugLog(`Curtain: ${this.accessory.displayName} OpenAPI refreshStatus`);
541
- try {
542
- const t = Date.now();
543
- const nonce = 'requestID';
544
- const data = this.platform.config.credentials?.token + t + nonce;
545
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
546
- const sign = signTerm.toString('base64');
547
- this.debugLog(`Curtain: ${this.accessory.displayName} sign: ${sign}`);
548
- const options = {
549
- hostname: HostDomain,
550
- port: 443,
551
- path: `${DevicePath}/${this.device.deviceId}/status`,
552
- method: 'GET',
553
- headers: {
554
- Authorization: this.platform.config.credentials?.token,
555
- sign: sign,
556
- nonce: nonce,
557
- t: t,
558
- 'Content-Type': 'application/json',
559
- },
560
- };
561
- const req = https.request(options, (res) => {
562
- this.debugLog(`Curtain: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
563
- let rawData = '';
564
- res.on('data', (d) => {
565
- rawData += d;
566
- this.debugLog(`Curtain: ${this.accessory.displayName} d: ${d}`);
567
- });
568
-
569
- res.on('end', () => {
570
- try {
571
- this.deviceStatus = JSON.parse(rawData);
572
- this.debugLog(`Curtain: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
573
- this.slidePosition = this.deviceStatus.body.slidePosition;
574
- this.moving = this.deviceStatus.body.moving;
575
- this.brightness = this.deviceStatus.body.brightness;
576
- this.parseStatus();
577
- this.updateHomeKitCharacteristics();
578
- } catch (e: any) {
579
- this.errorLog(`Curtain: ${this.accessory.displayName} error message: ${e.message}`);
580
- }
581
- });
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}`);
582
493
  });
583
- req.on('error', (e: any) => {
584
- 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
+ }
585
506
  });
586
- req.end();
587
- } catch (e: any) {
588
- this.errorLog(`Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
589
- if (this.deviceLogging.includes('debug')) {
590
- this.errorLog(
591
- `Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
592
- );
593
- }
594
- this.apiError(e);
595
- }
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)}`);
596
516
  }
597
517
  }
598
518
 
599
519
  async pushChanges(): Promise<void> {
600
- if (this.device.ble) {
520
+ if (this.BLE) {
601
521
  await this.BLEpushChanges();
602
- } else {
522
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
603
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.`);
604
527
  }
605
528
  interval(this.updateRate * 1000)
606
- .pipe(take(2))
529
+ .pipe(take(1))
607
530
  .subscribe(async () => {
608
531
  await this.refreshStatus();
609
532
  });
610
533
  }
611
534
 
612
535
  async BLEpushChanges(): Promise<void> {
536
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
613
537
  if (this.TargetPosition !== this.CurrentPosition) {
614
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE pushChanges`);
615
538
  const switchbot = await this.platform.connectBLE();
616
539
  // Convert to BLE Address
617
540
  this.device.bleMac = this.device
618
541
  .deviceId!.match(/.{1,2}/g)!
619
542
  .join(':')
620
543
  .toLowerCase();
621
- this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
622
- if (this.TargetPosition > 50) {
623
- if (this.device.curtain?.setOpenMode === '1') {
624
- this.setPositionMode = 1;
625
- this.Mode = 'Silent Mode';
626
- } else {
627
- this.setPositionMode = 0;
628
- this.Mode = 'Performance Mode';
629
- }
630
- } else {
631
- if (this.device.curtain?.setCloseMode === '1') {
632
- this.setPositionMode = 1;
633
- this.Mode = 'Silent Mode';
634
- } else {
635
- this.setPositionMode = 0;
636
- this.Mode = 'Performance Mode';
637
- }
638
- }
544
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
545
+ this.SilentPerformance();
639
546
  const adjustedMode = this.setPositionMode || null;
640
547
  if (adjustedMode === null) {
641
548
  this.Mode = 'Default Mode';
@@ -644,256 +551,304 @@ export class Curtain {
644
551
  if (switchbot !== false) {
645
552
  switchbot
646
553
  .discover({ model: 'c', quick: true, id: this.device.bleMac })
647
- .then((device_list) => {
554
+ .then((device_list: any) => {
648
555
  this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);
649
556
  return device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode);
650
557
  })
651
558
  .then(() => {
652
- this.debugLog(`Curtain: ${this.accessory.displayName} Done.`);
559
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
653
560
  })
654
561
  .catch(async (e: any) => {
655
- this.errorLog(`Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection`);
656
- if (this.deviceLogging.includes('debug')) {
657
- this.errorLog(
658
- `Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
659
- );
660
- }
661
- if (this.platform.config.credentials?.token) {
662
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
663
- await this.openAPIpushChanges();
664
- }
665
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();
666
566
  });
667
567
  } else {
668
- this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
669
- if (this.platform.config.credentials?.token) {
670
- this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`);
671
- await this.openAPIpushChanges();
672
- }
568
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection`);
569
+ await this.BLEPushConnection();
673
570
  }
674
571
  } else {
675
- this.debugLog(
676
- `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.` +
677
573
  ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
678
574
  );
679
575
  }
680
576
  }
681
577
 
682
578
  async openAPIpushChanges(): Promise<void> {
683
- if (this.platform.config.credentials?.token) {
684
- try {
685
- this.debugLog(`Curtain: ${this.accessory.displayName} OpenAPI pushChanges`);
686
- if (this.TargetPosition !== this.CurrentPosition) {
687
- // Make Push On request to the API
688
- const t = Date.now();
689
- const nonce = 'requestID';
690
- const data = this.platform.config.credentials?.token + t + nonce;
691
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
692
- .update(Buffer.from(data, 'utf-8'))
693
- .digest();
694
- const sign = signTerm.toString('base64');
695
- this.debugLog(`Curtain: ${this.accessory.displayName} sign: ${sign}`);
696
- this.debugLog(`Pushing ${this.TargetPosition}`);
697
- const adjustedTargetPosition = 100 - Number(this.TargetPosition);
698
- if (this.TargetPosition > 50) {
699
- this.setPositionMode = this.device.curtain?.setOpenMode;
700
- } else {
701
- this.setPositionMode = this.device.curtain?.setCloseMode;
702
- }
703
- if (this.setPositionMode === '1') {
704
- this.Mode = 'Silent Mode';
705
- } else if (this.setPositionMode === '0') {
706
- this.Mode = 'Performance Mode';
707
- } else {
708
- this.Mode = 'Default Mode';
709
- }
710
- this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
711
- const adjustedMode = this.setPositionMode || 'ff';
712
- const body = JSON.stringify({
713
- 'command': 'setPosition',
714
- 'parameter': `0,${adjustedMode},${adjustedTargetPosition}`,
715
- 'commandType': 'command',
716
- });
717
- this.infoLog(`Curtain: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
718
- const options = {
719
- hostname: HostDomain,
720
- port: 443,
721
- path: `${DevicePath}/${this.device.deviceId}/commands`,
722
- method: 'POST',
723
- headers: {
724
- 'Authorization': this.platform.config.credentials?.token,
725
- 'sign': sign,
726
- 'nonce': nonce,
727
- 't': t,
728
- 'Content-Type': 'application/json',
729
- 'Content-Length': body.length,
730
- },
731
- };
732
- const req = https.request(options, res => {
733
- this.debugLog(`Curtain: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
734
- this.statusCode({ res });
735
- res.on('data', d => {
736
- this.debugLog(`Curtain: ${this.accessory.displayName} d: ${d}`);
737
- });
738
- });
739
- req.on('error', (e: any) => {
740
- this.errorLog(`Curtain: ${this.accessory.displayName} error message: ${e.message}`);
741
- });
742
- req.write(body);
743
- req.end();
744
- 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;
745
595
  } else {
746
- this.debugLog(
747
- `Curtain: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` +
748
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
749
- );
596
+ this.setPositionMode = this.device.curtain?.setCloseMode;
750
597
  }
751
- } catch (e: any) {
752
- this.errorLog(`Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
753
- if (this.deviceLogging.includes('debug')) {
754
- this.errorLog(
755
- `Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
756
- );
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';
757
604
  }
758
- 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
+ );
759
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
+ );
760
650
  }
761
651
  }
762
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
+ if (this.device.mqttURL) {
665
+ this.mqttPublish('TargetPosition', this.TargetPosition);
666
+ }
667
+
668
+ await this.setMinMax();
669
+ if (value > this.CurrentPosition) {
670
+ this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
671
+ this.setNewTarget = true;
672
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
673
+ } else if (value < this.CurrentPosition) {
674
+ this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
675
+ this.setNewTarget = true;
676
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
677
+ } else {
678
+ this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
679
+ this.setNewTarget = false;
680
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
681
+ }
682
+ this.windowCoveringService.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
683
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
684
+
685
+ /**
686
+ * If Curtain movement time is short, the moving flag from backend is always false.
687
+ * The minimum time depends on the network control latency.
688
+ */
689
+ clearTimeout(this.setNewTargetTimer);
690
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
691
+ if (this.setNewTarget) {
692
+ this.setNewTargetTimer = setTimeout(() => {
693
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
694
+ this.setNewTarget = false;
695
+ }, this.updateRate * 1000);
696
+ }
697
+ this.doCurtainUpdate.next();
698
+ }
699
+
763
700
  async updateHomeKitCharacteristics(): Promise<void> {
764
701
  await this.setMinMax();
765
702
  if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) {
766
- this.debugLog(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
703
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
767
704
  } else {
705
+ if (this.device.mqttURL) {
706
+ this.mqttPublish('CurrentPosition', this.CurrentPosition);
707
+ }
708
+ this.accessory.context.CurrentPosition = this.CurrentPosition;
768
709
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, Number(this.CurrentPosition));
769
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
770
- this.mqttPublish('CurrentPosition', this.CurrentPosition);
710
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
771
711
  }
772
712
  if (this.PositionState === undefined) {
773
- this.debugLog(`Curtain: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
713
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
774
714
  } else {
715
+ if (this.device.mqttURL) {
716
+ this.mqttPublish('PositionState', this.PositionState);
717
+ }
718
+ this.accessory.context.PositionState = this.PositionState;
775
719
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, Number(this.PositionState));
776
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
777
- this.mqttPublish('PositionState', this.PositionState);
720
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
778
721
  }
779
722
  if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
780
- this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
723
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
781
724
  } else {
725
+ if (this.device.mqttURL) {
726
+ this.mqttPublish('TargetPosition', this.TargetPosition);
727
+ }
728
+ this.accessory.context.TargetPosition = this.TargetPosition;
782
729
  this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, Number(this.TargetPosition));
783
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
784
- this.mqttPublish('TargetPosition', this.TargetPosition);
730
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
785
731
  }
786
732
  if (!this.device.curtain?.hide_lightsensor) {
787
733
  if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
788
- this.debugLog(`Curtain: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
734
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
789
735
  } else {
736
+ if (this.device.mqttURL) {
737
+ this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
738
+ }
739
+ this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
790
740
  this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
791
- this.debugLog(`Curtain: ${this.accessory.displayName}` + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
792
- this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
741
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
742
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
793
743
  }
794
744
  }
795
- if (this.device.ble) {
745
+ if (this.BLE) {
796
746
  if (this.BatteryLevel === undefined) {
797
- this.debugLog(`Curtain: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
747
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
798
748
  } else {
749
+ if (this.device.mqttURL) {
750
+ this.mqttPublish('BatteryLevel', this.BatteryLevel);
751
+ }
752
+ this.accessory.context.BatteryLevel = this.BatteryLevel;
799
753
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
800
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
801
- this.mqttPublish('BatteryLevel', this.BatteryLevel);
754
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
802
755
  }
803
756
  if (this.StatusLowBattery === undefined) {
804
- this.debugLog(`Curtain: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
757
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
805
758
  } else {
759
+ if (this.device.mqttURL) {
760
+ this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
761
+ }
762
+ this.accessory.context.StatusLowBattery = this.StatusLowBattery;
806
763
  this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
807
- this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
808
- this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
764
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
809
765
  }
810
766
  }
811
767
  }
812
768
 
813
- async apiError(e: any): Promise<void> {
814
- this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e);
815
- this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, e);
816
- this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, e);
817
- if (!this.device.curtain?.hide_lightsensor) {
818
- this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
819
- }
820
- if (this.device.ble) {
821
- this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
822
- this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
823
- }
824
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
769
+ /*
770
+ * Publish MQTT message for topics of
771
+ * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
772
+ */
773
+ mqttPublish(topic: string, message: any) {
774
+ const mac = this.device.deviceId
775
+ ?.toLowerCase()
776
+ .match(/[\s\S]{1,2}/g)
777
+ ?.join(':');
778
+ const options = this.device.mqttPubOptions || {};
779
+ this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
780
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${superStringify(options)}`);
825
781
  }
826
782
 
827
- async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
828
- switch (res.statusCode) {
829
- case 151:
830
- this.errorLog(`Curtain: ${this.accessory.displayName} Command not supported by this device type.`);
831
- break;
832
- case 152:
833
- this.errorLog(`Curtain: ${this.accessory.displayName} Device not found.`);
834
- break;
835
- case 160:
836
- this.errorLog(`Curtain: ${this.accessory.displayName} Command is not supported.`);
837
- break;
838
- case 161:
839
- this.errorLog(`Curtain: ${this.accessory.displayName} Device is offline.`);
840
- break;
841
- case 171:
842
- this.errorLog(`Curtain: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
843
- break;
844
- case 190:
845
- this.errorLog(
846
- `Curtain: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
847
- ` Or command: ${JSON.stringify(res)} format is invalid`,
848
- );
849
- break;
850
- case 100:
851
- this.debugLog(`Curtain: ${this.accessory.displayName} Command successfully sent.`);
852
- break;
853
- default:
854
- this.debugLog(`Curtain: ${this.accessory.displayName} Unknown statusCode.`);
783
+ /*
784
+ * Setup MQTT hadler if URL is specifed.
785
+ */
786
+ async setupMqtt(device: device & devicesConfig): Promise<void> {
787
+ if (device.mqttURL) {
788
+ try {
789
+ this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
790
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
791
+ this.mqttClient.on('error', (e: Error) => {
792
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
793
+ });
794
+ } catch (e) {
795
+ this.mqttClient = null;
796
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
797
+ }
855
798
  }
856
799
  }
857
800
 
858
- /**
859
- * Handle requests to set the value of the "Target Position" characteristic
860
- */
861
- async TargetPositionSet(value: CharacteristicValue): Promise<void> {
862
- this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${value}`);
801
+ async getCustomBLEAddress(switchbot: any) {
802
+ if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
803
+ (async () => {
804
+ // Start to monitor advertisement packets
805
+ await switchbot.startScan({
806
+ model: 'c',
807
+ });
808
+ // Set an event handler
809
+ switchbot.onadvertisement = (ad: any) => {
810
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
811
+ };
812
+ await switchbot.wait(10000);
813
+ // Stop to monitor
814
+ switchbot.stopScan();
815
+ })();
816
+ }
817
+ }
863
818
 
864
- this.TargetPosition = value;
865
- this.mqttPublish('TargetPosition', this.TargetPosition);
819
+ async BLEPushConnection() {
820
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
821
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
822
+ await this.openAPIpushChanges();
823
+ }
824
+ }
866
825
 
867
- await this.setMinMax();
868
- if (value > this.CurrentPosition) {
869
- this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
870
- this.setNewTarget = true;
871
- this.debugLog(`Curtain: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
872
- } else if (value < this.CurrentPosition) {
873
- this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
874
- this.setNewTarget = true;
875
- this.debugLog(`Curtain: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
876
- } else {
877
- this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
878
- this.setNewTarget = false;
879
- this.debugLog(`Curtain: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
826
+ async BLERefreshConnection(switchbot: any): Promise<void> {
827
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
828
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
829
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
830
+ await this.openAPIRefreshStatus();
880
831
  }
881
- this.windowCoveringService.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
882
- this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
832
+ }
883
833
 
884
- /**
885
- * If Curtain movement time is short, the moving flag from backend is always false.
886
- * The minimum time depends on the network control latency.
887
- */
888
- clearTimeout(this.setNewTargetTimer);
889
- this.debugLog(`Curtain: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
890
- if (this.setNewTarget) {
891
- this.setNewTargetTimer = setTimeout(() => {
892
- this.debugLog(`Curtain: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
893
- this.setNewTarget = false;
894
- }, this.updateRate * 1000);
834
+ async SilentPerformance() {
835
+ if (this.TargetPosition > 50) {
836
+ if (this.device.curtain?.setOpenMode === '1') {
837
+ this.setPositionMode = 1;
838
+ this.Mode = 'Silent Mode';
839
+ } else {
840
+ this.setPositionMode = 0;
841
+ this.Mode = 'Performance Mode';
842
+ }
843
+ } else {
844
+ if (this.device.curtain?.setCloseMode === '1') {
845
+ this.setPositionMode = 1;
846
+ this.Mode = 'Silent Mode';
847
+ } else {
848
+ this.setPositionMode = 0;
849
+ this.Mode = 'Performance Mode';
850
+ }
895
851
  }
896
- this.doCurtainUpdate.next();
897
852
  }
898
853
 
899
854
  async setMinMax(): Promise<void> {
@@ -909,92 +864,120 @@ export class Curtain {
909
864
  }
910
865
  }
911
866
 
912
- async config(device: device & devicesConfig): Promise<void> {
913
- let config = {};
914
- if (device.curtain) {
915
- config = device.curtain;
916
- }
917
- if (device.ble) {
918
- config['ble'] = device.ble;
919
- }
920
- if (device.logging !== undefined) {
921
- config['logging'] = device.logging;
922
- }
923
- if (device.refreshRate !== undefined) {
924
- config['refreshRate'] = device.refreshRate;
925
- }
926
- if (device.scanDuration !== undefined) {
927
- config['scanDuration'] = device.scanDuration;
928
- }
929
- if (Object.entries(config).length !== 0) {
930
- this.infoLog(`Curtain: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
867
+ minStep(device: device & devicesConfig): number {
868
+ if (device.curtain?.set_minStep) {
869
+ this.set_minStep = device.curtain?.set_minStep;
870
+ } else {
871
+ this.set_minStep = 1;
931
872
  }
873
+ return this.set_minStep;
932
874
  }
933
875
 
934
- async refreshRate(device: device & devicesConfig): Promise<void> {
935
- // refreshRate
936
- if (device.refreshRate) {
937
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
938
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
939
- } else if (this.platform.config.options!.refreshRate) {
940
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
941
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
876
+ minLux(): number {
877
+ if (this.device.curtain?.set_minLux) {
878
+ this.set_minLux = this.device.curtain?.set_minLux;
879
+ } else {
880
+ this.set_minLux = 1;
942
881
  }
943
- // updateRate
944
- if (device?.curtain?.updateRate) {
945
- this.updateRate = device?.curtain?.updateRate;
946
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config Curtain refreshRate: ${this.deviceRefreshRate}`);
882
+ return this.set_minLux;
883
+ }
884
+
885
+ maxLux(): number {
886
+ if (this.device.curtain?.set_maxLux) {
887
+ this.set_maxLux = this.device.curtain?.set_maxLux;
947
888
  } else {
948
- this.updateRate = 7;
949
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Default Curtain Refresh Rate.`);
889
+ this.set_maxLux = 6001;
950
890
  }
891
+ return this.set_maxLux;
951
892
  }
952
893
 
953
894
  async scan(device: device & devicesConfig): Promise<void> {
954
895
  if (device.scanDuration) {
955
896
  if (this.updateRate > device.scanDuration) {
956
897
  this.scanDuration = this.updateRate;
957
- this.warnLog(`Curtain: ${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
898
+ this.warnLog(`${this.device.deviceType}: `
899
+ + `${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
958
900
  } else {
959
901
  this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
960
902
  }
961
- if (device.ble) {
962
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
903
+ if (this.BLE) {
904
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
963
905
  }
964
906
  } else {
965
907
  if (this.updateRate > 1) {
966
908
  this.scanDuration = this.updateRate;
967
- this.warnLog(`Curtain: ${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
909
+ this.warnLog(`${this.device.deviceType}: `
910
+ + `${this.accessory.displayName} scanDuration is less then updateRate, overriding scanDuration with updateRate`);
968
911
  } else {
969
912
  this.scanDuration = this.accessory.context.scanDuration = 1;
970
913
  }
971
- if (this.device.ble) {
972
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
914
+ if (this.BLE) {
915
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
973
916
  }
974
917
  }
975
918
  }
976
919
 
977
- async logs(device: device & devicesConfig): Promise<void> {
978
- if (this.platform.debugMode) {
979
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
980
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
981
- } else if (device.logging) {
982
- this.deviceLogging = this.accessory.context.logging = device.logging;
983
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
984
- } else if (this.platform.config.options?.logging) {
985
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
986
- this.debugLog(`Curtain: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
987
- } else {
988
- this.deviceLogging = this.accessory.context.logging = 'standard';
989
- this.debugLog(`Curtain: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
920
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
921
+ switch (res.statusCode) {
922
+ case 151:
923
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
924
+ break;
925
+ case 152:
926
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
927
+ break;
928
+ case 160:
929
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
930
+ break;
931
+ case 161:
932
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
933
+ this.offlineOff();
934
+ break;
935
+ case 171:
936
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
937
+ this.offlineOff();
938
+ break;
939
+ case 190:
940
+ this.errorLog(
941
+ `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
942
+ ` Or command: ${superStringify(res)} format is invalid`,
943
+ );
944
+ break;
945
+ case 100:
946
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
947
+ break;
948
+ default:
949
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
950
+ }
951
+ }
952
+
953
+ async offlineOff(): Promise<void> {
954
+ if (this.device.offline) {
955
+ this.CurrentPosition = 100;
956
+ this.windowCoveringService.getCharacteristic(this.platform.Characteristic.On).updateValue(this.CurrentPosition);
957
+ await this.updateHomeKitCharacteristics();
958
+ }
959
+ }
960
+
961
+ async apiError(e: any): Promise<void> {
962
+ this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e);
963
+ this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, e);
964
+ this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, e);
965
+ if (!this.device.curtain?.hide_lightsensor) {
966
+ this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
967
+ }
968
+ if (this.BLE) {
969
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
970
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
990
971
  }
972
+ //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
991
973
  }
992
974
 
993
975
  FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
994
976
  let FirmwareRevision: string;
995
- this.debugLog(`Curtain: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
996
- this.debugLog(`Curtain: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
997
- this.debugLog(`Curtain: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
977
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
978
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
979
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
980
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
998
981
  if (accessory.context.FirmwareRevision) {
999
982
  FirmwareRevision = accessory.context.FirmwareRevision;
1000
983
  } else if (device.firmware) {
@@ -1005,31 +988,87 @@ export class Curtain {
1005
988
  return FirmwareRevision;
1006
989
  }
1007
990
 
1008
- minStep(device: device & devicesConfig): number {
1009
- if (device.curtain?.set_minStep) {
1010
- this.set_minStep = device.curtain?.set_minStep;
991
+ async context() {
992
+ if (this.CurrentPosition === undefined) {
993
+ this.CurrentPosition = 0;
1011
994
  } else {
1012
- this.set_minStep = 1;
995
+ this.CurrentPosition = this.accessory.context.CurrentPosition;
996
+ }
997
+
998
+ if (this.TargetPosition === undefined) {
999
+ this.TargetPosition = 0;
1000
+ } else {
1001
+ this.TargetPosition = this.accessory.context.TargetPosition;
1002
+ }
1003
+
1004
+ if (this.PositionState === undefined) {
1005
+ this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
1006
+ } else {
1007
+ this.PositionState = this.accessory.context.PositionState;
1013
1008
  }
1014
- return this.set_minStep;
1015
1009
  }
1016
1010
 
1017
- minLux(): number {
1018
- if (this.device.curtain?.set_minLux) {
1019
- this.set_minLux = this.device.curtain?.set_minLux;
1011
+ async refreshRate(device: device & devicesConfig): Promise<void> {
1012
+ // refreshRate
1013
+ if (device.refreshRate) {
1014
+ this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
1015
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
1016
+ } else if (this.platform.config.options!.refreshRate) {
1017
+ this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
1018
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
1019
+ }
1020
+ // updateRate
1021
+ if (device?.curtain?.updateRate) {
1022
+ this.updateRate = device?.curtain?.updateRate;
1023
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain refreshRate: ${this.deviceRefreshRate}`);
1020
1024
  } else {
1021
- this.set_minLux = 1;
1025
+ this.updateRate = 7;
1026
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain Refresh Rate.`);
1022
1027
  }
1023
- return this.set_minLux;
1024
1028
  }
1025
1029
 
1026
- maxLux(): number {
1027
- if (this.device.curtain?.set_maxLux) {
1028
- this.set_maxLux = this.device.curtain?.set_maxLux;
1030
+ async config(device: device & devicesConfig): Promise<void> {
1031
+ let config = {};
1032
+ if (device.curtain) {
1033
+ config = device.curtain;
1034
+ }
1035
+ if (device.connectionType !== undefined) {
1036
+ config['connectionType'] = device.connectionType;
1037
+ }
1038
+ if (device.external !== undefined) {
1039
+ config['external'] = device.external;
1040
+ }
1041
+ if (device.mqttURL !== undefined) {
1042
+ config['mqttURL'] = device.mqttURL;
1043
+ }
1044
+ if (device.logging !== undefined) {
1045
+ config['logging'] = device.logging;
1046
+ }
1047
+ if (device.refreshRate !== undefined) {
1048
+ config['refreshRate'] = device.refreshRate;
1049
+ }
1050
+ if (device.scanDuration !== undefined) {
1051
+ config['scanDuration'] = device.scanDuration;
1052
+ }
1053
+ if (Object.entries(config).length !== 0) {
1054
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
1055
+ }
1056
+ }
1057
+
1058
+ async logs(device: device & devicesConfig): Promise<void> {
1059
+ if (this.platform.debugMode) {
1060
+ this.deviceLogging = this.accessory.context.logging = 'debugMode';
1061
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
1062
+ } else if (device.logging) {
1063
+ this.deviceLogging = this.accessory.context.logging = device.logging;
1064
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
1065
+ } else if (this.platform.config.options?.logging) {
1066
+ this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
1067
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
1029
1068
  } else {
1030
- this.set_maxLux = 6001;
1069
+ this.deviceLogging = this.accessory.context.logging = 'standard';
1070
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
1031
1071
  }
1032
- return this.set_maxLux;
1033
1072
  }
1034
1073
 
1035
1074
  /**
@@ -1047,12 +1086,28 @@ export class Curtain {
1047
1086
  }
1048
1087
  }
1049
1088
 
1089
+ debugWarnLog(...log: any[]): void {
1090
+ if (this.enablingDeviceLogging()) {
1091
+ if (this.deviceLogging?.includes('debug')) {
1092
+ this.platform.log.warn('[DEBUG]', String(...log));
1093
+ }
1094
+ }
1095
+ }
1096
+
1050
1097
  errorLog(...log: any[]): void {
1051
1098
  if (this.enablingDeviceLogging()) {
1052
1099
  this.platform.log.error(String(...log));
1053
1100
  }
1054
1101
  }
1055
1102
 
1103
+ debugErrorLog(...log: any[]): void {
1104
+ if (this.enablingDeviceLogging()) {
1105
+ if (this.deviceLogging?.includes('debug')) {
1106
+ this.platform.log.error('[DEBUG]', String(...log));
1107
+ }
1108
+ }
1109
+ }
1110
+
1056
1111
  debugLog(...log: any[]): void {
1057
1112
  if (this.enablingDeviceLogging()) {
1058
1113
  if (this.deviceLogging === 'debug') {