@switchbot/homebridge-switchbot 2.0.0-beta.19 → 2.0.0-beta.20

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