@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
@@ -3,6 +3,7 @@ import crypto from 'crypto';
3
3
  import { Context } from 'vm';
4
4
  import { IncomingMessage } from 'http';
5
5
  import { interval, Subject } from 'rxjs';
6
+ import superStringify from 'super-stringify';
6
7
  import { SwitchBotPlatform } from '../platform';
7
8
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
8
9
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
@@ -32,17 +33,17 @@ export class Lock {
32
33
  lockUpdateInProgress!: boolean;
33
34
  doLockUpdate!: Subject<void>;
34
35
 
36
+ // Connection
37
+ private readonly BLE = (this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI');
38
+ private readonly OpenAPI = (this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI');
39
+
35
40
  constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
36
41
  // default placeholders
37
42
  this.logs(device);
38
43
  this.scan(device);
39
44
  this.refreshRate(device);
40
45
  this.config(device);
41
- if (this.LockTargetState === undefined) {
42
- this.LockTargetState = false;
43
- } else {
44
- this.LockTargetState = this.accessory.context.On;
45
- }
46
+ this.conext();
46
47
 
47
48
  // this is subject we use to track when we need to POST changes to the SwitchBot API
48
49
  this.doLockUpdate = new Subject();
@@ -82,18 +83,18 @@ export class Lock {
82
83
 
83
84
  // Contact Sensor Service
84
85
  if (device.lock?.hide_contactsensor) {
85
- this.debugLog(`Lock: ${accessory.displayName} Removing Contact Sensor Service`);
86
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Contact Sensor Service`);
86
87
  this.contactSensorService = this.accessory.getService(this.platform.Service.ContactSensor);
87
88
  accessory.removeService(this.contactSensorService!);
88
89
  } else if (!this.contactSensorService) {
89
- this.debugLog(`Lock: ${accessory.displayName} Add Contact Sensor Service`);
90
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Contact Sensor Service`);
90
91
  (this.contactSensorService =
91
92
  this.accessory.getService(this.platform.Service.ContactSensor) || this.accessory.addService(this.platform.Service.ContactSensor)),
92
93
  `${accessory.displayName} Contact Sensor`;
93
94
 
94
95
  this.contactSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Contact Sensor`);
95
96
  } else {
96
- this.debugLog(`Lock: ${accessory.displayName} Contact Sensor Service Not Added`);
97
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Contact Sensor Service Not Added`);
97
98
  }
98
99
 
99
100
  // Update Homekit
@@ -119,19 +120,53 @@ export class Lock {
119
120
  try {
120
121
  await this.pushChanges();
121
122
  } catch (e: any) {
122
- this.errorLog(`Lock: ${this.accessory.displayName} failed pushChanges`);
123
- if (this.deviceLogging.includes('debug')) {
124
- this.errorLog(`Lock: ${this.accessory.displayName} failed pushChanges,` + ` Error Message: ${JSON.stringify(e.message)}`);
125
- }
126
123
  this.apiError(e);
124
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
125
+ + ` Error Message: ${superStringify(e.message)}`);
127
126
  }
128
127
  this.lockUpdateInProgress = false;
129
128
  });
130
129
  }
131
130
 
131
+ /**
132
+ * Parse the device status from the SwitchBot api
133
+ */
134
+ async parseStatus(): Promise<void> {
135
+ /*if (this.BLE) {
136
+ await this.BLEparseStatus();
137
+ } else*/ if (this.OpenAPI && this.platform.config.credentials?.token) {
138
+ await this.openAPIparseStatus();
139
+ } else {
140
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
141
+ + ` ${this.device.connectionType}, parseStatus will not happen.`);
142
+ }
143
+ }
144
+
145
+ async BLEparseStatus(): Promise<void> {
146
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
147
+ switch (this.lockState) {
148
+ case 'locked':
149
+ this.LockCurrentState = this.platform.Characteristic.LockCurrentState.SECURED;
150
+ this.LockTargetState = this.platform.Characteristic.LockTargetState.SECURED;
151
+ break;
152
+ default:
153
+ this.LockCurrentState = this.platform.Characteristic.LockCurrentState.UNSECURED;
154
+ this.LockTargetState = this.platform.Characteristic.LockTargetState.UNSECURED;
155
+ }
156
+ switch (this.doorState) {
157
+ case 'opened':
158
+ this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
159
+ break;
160
+ default:
161
+ this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED;
162
+ }
163
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockTargetState}`);
164
+ }
165
+
132
166
  //{"deviceId":"DBDC23B53139","deviceType":"Smart Lock","hubDeviceId":"E68B14109DA2","lockState":"locked","doorState":"opened","calibrate":true}
133
167
 
134
- async parseStatus(): Promise<void> {
168
+ async openAPIparseStatus(): Promise<void> {
169
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
135
170
  switch (this.lockState) {
136
171
  case 'locked':
137
172
  this.LockCurrentState = this.platform.Characteristic.LockCurrentState.SECURED;
@@ -148,17 +183,32 @@ export class Lock {
148
183
  default:
149
184
  this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED;
150
185
  }
151
- this.debugLog(`Lock: ${this.accessory.displayName} On: ${this.LockTargetState}`);
186
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockTargetState}`);
152
187
  }
153
188
 
189
+ /**
190
+ * Asks the SwitchBot API for the latest device information
191
+ */
154
192
  async refreshStatus(): Promise<void> {
193
+ /*if (this.BLE) {
194
+ await this.BLERefreshStatus();
195
+ } else*/ if (this.OpenAPI && this.platform.config.credentials?.token) {
196
+ await this.openAPIRefreshStatus();
197
+ } else {
198
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
199
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
200
+ }
201
+ }
202
+
203
+ async openAPIRefreshStatus(): Promise<void> {
204
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
155
205
  try {
156
206
  const t = Date.now();
157
207
  const nonce = 'requestID';
158
208
  const data = this.platform.config.credentials?.token + t + nonce;
159
209
  const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
160
210
  const sign = signTerm.toString('base64');
161
- this.debugLog(`Lock: ${this.accessory.displayName} sign: ${sign}`);
211
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
162
212
  const options = {
163
213
  hostname: HostDomain,
164
214
  port: 443,
@@ -173,37 +223,33 @@ export class Lock {
173
223
  },
174
224
  };
175
225
  const req = https.request(options, (res) => {
176
- this.debugLog(`Lock: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
226
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus statusCode: ${res.statusCode}`);
177
227
  let rawData = '';
178
228
  res.on('data', (d) => {
179
229
  rawData += d;
180
- this.debugLog(`Lock: ${this.accessory.displayName} d: ${d}`);
230
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
181
231
  });
182
232
  res.on('end', () => {
183
233
  try {
184
234
  this.deviceStatus = JSON.parse(rawData);
185
- this.debugLog(`Lock: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
235
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus: ${superStringify(this.deviceStatus)}`);
186
236
  this.lockState = this.deviceStatus.body.lockState;
187
237
  this.doorState = this.deviceStatus.body.doorState;
188
- this.parseStatus();
238
+ this.openAPIparseStatus();
189
239
  this.updateHomeKitCharacteristics();
190
240
  } catch (e: any) {
191
- this.errorLog(`Lock: ${this.accessory.displayName} error message: ${e.message}`);
241
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
192
242
  }
193
243
  });
194
244
  });
195
245
  req.on('error', (e: any) => {
196
- this.errorLog(`Lock: ${this.accessory.displayName} error message: ${e.message}`);
246
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
197
247
  });
198
248
  req.end();
199
249
  } catch (e: any) {
200
- this.errorLog(`Lock: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
201
- if (this.deviceLogging.includes('debug')) {
202
- this.errorLog(
203
- `Lock: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + ` Error Message: ${JSON.stringify(e.message)}`,
204
- );
205
- }
206
250
  this.apiError(e);
251
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
252
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
207
253
  }
208
254
  }
209
255
 
@@ -214,55 +260,14 @@ export class Lock {
214
260
  * Lock - "command" "unlock" "default" = set to ???? state - LockCurrentState
215
261
  */
216
262
  async pushChanges(): Promise<void> {
217
- // Make Push On request to the API
218
- const t = Date.now();
219
- const nonce = 'requestID';
220
- const data = this.platform.config.credentials?.token + t + nonce;
221
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
222
- .update(Buffer.from(data, 'utf-8'))
223
- .digest();
224
- const sign = signTerm.toString('base64');
225
- this.debugLog(`Lock: ${this.accessory.displayName} sign: ${sign}`);
226
- let command = '';
227
- if (this.LockTargetState) {
228
- command = 'lock';
263
+ /*if (this.BLE) {
264
+ await this.BLEpushChanges();
265
+ } else*/ if (this.OpenAPI && this.platform.config.credentials?.token) {
266
+ await this.openAPIpushChanges();
229
267
  } else {
230
- command = 'unlock';
231
- }
232
- const body = JSON.stringify({
233
- 'command': `${command}`,
234
- 'parameter': 'default',
235
- 'commandType': 'command',
236
- });
237
- this.infoLog(`Lock: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
238
- const options = {
239
- hostname: HostDomain,
240
- port: 443,
241
- path: `${DevicePath}/${this.device.deviceId}/commands`,
242
- method: 'POST',
243
- headers: {
244
- 'Authorization': this.platform.config.credentials?.token,
245
- 'sign': sign,
246
- 'nonce': nonce,
247
- 't': t,
248
- 'Content-Type': 'application/json',
249
- 'Content-Length': body.length,
250
- },
251
- };
252
- const req = https.request(options, res => {
253
- this.debugLog(`Lock: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
254
- this.statusCode({ res });
255
- res.on('data', d => {
256
- this.debugLog(`Lock: ${this.accessory.displayName} d: ${d}`);
257
- });
258
- });
259
- req.on('error', (e: any) => {
260
- this.errorLog(`Lock: ${this.accessory.displayName} error message: ${e.message}`);
261
- });
262
- req.write(body);
263
- req.end();
264
- this.debugLog(`Lock: ${this.accessory.displayName} pushchanges: ${JSON.stringify(req)}`);
265
- this.accessory.context.On = this.LockTargetState;
268
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
269
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
270
+ }
266
271
  interval(5000)
267
272
  .pipe(take(1))
268
273
  .subscribe(async () => {
@@ -270,77 +275,257 @@ export class Lock {
270
275
  });
271
276
  }
272
277
 
278
+ async BLEpushChanges(): Promise<void> {
279
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
280
+ if (this.LockTargetState !== this.accessory.context.LockTargetState) {
281
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges LockTargetState: ${this.LockTargetState}`
282
+ + ` LockTargetStateCached: ${this.accessory.context.LockTargetState}`);
283
+ const switchbot = await this.platform.connectBLE();
284
+ // Convert to BLE Address
285
+ this.device.bleMac = this.device
286
+ .deviceId!.match(/.{1,2}/g)!
287
+ .join(':')
288
+ .toLowerCase();
289
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
290
+ switchbot
291
+ .discover({
292
+ model: '',
293
+ id: this.device.bleMac,
294
+ })
295
+ .then(() => {
296
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
297
+ this.LockTargetState = this.platform.Characteristic.LockTargetState.SECURED;
298
+ })
299
+ .catch(async (e: any) => {
300
+ this.apiError(e);
301
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
302
+ + ` Connection, Error Message: ${superStringify(e.message)}`);
303
+ await this.BLEPushConnection();
304
+ });
305
+ } else {
306
+ this.debugLog(
307
+ `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `LockTargetState: ${this.LockTargetState}, `
308
+ +`LockTargetStateCached: ${this.accessory.context.LockTargetState}`,
309
+ );
310
+ }
311
+ }
312
+
313
+ async openAPIpushChanges(): Promise<void> {
314
+ try {
315
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
316
+ if (this.LockTargetState !== this.accessory.context.LockTargetState) {
317
+ // Make Push On request to the API
318
+ const t = Date.now();
319
+ const nonce = 'requestID';
320
+ const data = this.platform.config.credentials?.token + t + nonce;
321
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
322
+ .update(Buffer.from(data, 'utf-8'))
323
+ .digest();
324
+ const sign = signTerm.toString('base64');
325
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
326
+ let command = '';
327
+ if (this.LockTargetState) {
328
+ command = 'lock';
329
+ } else {
330
+ command = 'unlock';
331
+ }
332
+ const body = superStringify({
333
+ 'command': `${command}`,
334
+ 'parameter': 'default',
335
+ 'commandType': 'command',
336
+ });
337
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
338
+ const options = {
339
+ hostname: HostDomain,
340
+ port: 443,
341
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
342
+ method: 'POST',
343
+ headers: {
344
+ 'Authorization': this.platform.config.credentials?.token,
345
+ 'sign': sign,
346
+ 'nonce': nonce,
347
+ 't': t,
348
+ 'Content-Type': 'application/json',
349
+ 'Content-Length': body.length,
350
+ },
351
+ };
352
+ const req = https.request(options, res => {
353
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges statusCode: ${res.statusCode}`);
354
+ this.statusCode({ res });
355
+ res.on('data', d => {
356
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
357
+ });
358
+ });
359
+ req.on('error', (e: any) => {
360
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
361
+ });
362
+ req.write(body);
363
+ req.end();
364
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges: ${superStringify(req)}`);
365
+ } else {
366
+ this.debugLog(
367
+ `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` + `LockTargetState: ${this.LockTargetState}, `
368
+ +`LockTargetStateCached: ${this.accessory.context.LockTargetState}`,
369
+ );
370
+ }
371
+ } catch (e: any) {
372
+ this.apiError(e);
373
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
374
+ + ` Connection, Error Message: ${superStringify(e.message)}`,
375
+ );
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Handle requests to set the value of the "On" characteristic
381
+ */
382
+ async LockTargetStateSet(value: CharacteristicValue): Promise<void> {
383
+ if (this.LockTargetState === this.accessory.context.LockTargetState) {
384
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set LockTargetState: ${value}`);
385
+ } else {
386
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`);
387
+ }
388
+
389
+ this.LockTargetState = value;
390
+ this.doLockUpdate.next();
391
+ }
392
+
273
393
  async updateHomeKitCharacteristics(): Promise<void> {
274
394
  if (!this.device.lock?.hide_contactsensor) {
275
395
  if (this.ContactSensorState === undefined) {
276
- this.debugLog(`Lock: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
396
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
277
397
  } else {
398
+ this.accessory.context.ContactSensorState = this.ContactSensorState;
278
399
  this.contactSensorService?.updateCharacteristic(this.platform.Characteristic.ContactSensorState, this.ContactSensorState);
279
- this.debugLog(`Lock: ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`);
400
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`);
280
401
  }
281
402
  }
282
403
  if (this.LockTargetState === undefined) {
283
- this.debugLog(`Lock: ${this.accessory.displayName} LockTargetState: ${this.LockTargetState}`);
404
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockTargetState: ${this.LockTargetState}`);
284
405
  } else {
406
+ this.accessory.context.LockTargetState = this.LockTargetState;
285
407
  this.lockService.updateCharacteristic(this.platform.Characteristic.LockTargetState, this.LockTargetState);
286
- this.debugLog(`Lock: ${this.accessory.displayName} updateCharacteristic LockTargetState: ${this.LockTargetState}`);
408
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LockTargetState: ${this.LockTargetState}`);
287
409
  }
288
410
  if (this.LockCurrentState === undefined) {
289
- this.debugLog(`Lock: ${this.accessory.displayName} LockCurrentState: ${this.LockCurrentState}`);
411
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LockCurrentState: ${this.LockCurrentState}`);
290
412
  } else {
413
+ this.accessory.context.LockCurrentState = this.LockCurrentState;
291
414
  this.lockService.updateCharacteristic(this.platform.Characteristic.LockCurrentState, this.LockCurrentState);
292
- this.debugLog(`Lock: ${this.accessory.displayName} updateCharacteristic LockCurrentState: ${this.LockCurrentState}`);
415
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LockCurrentState: ${this.LockCurrentState}`);
293
416
  }
294
417
  }
295
418
 
296
- async apiError(e: any): Promise<void> {
297
- if (!this.device.lock?.hide_contactsensor) {
298
- this.contactSensorService?.updateCharacteristic(this.platform.Characteristic.ContactSensorState, e);
419
+ async BLEPushConnection() {
420
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
421
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
422
+ await this.openAPIpushChanges();
423
+ }
424
+ }
425
+
426
+ async BLERefreshConnection(switchbot: any): Promise<void> {
427
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
428
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
429
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
430
+ await this.openAPIRefreshStatus();
431
+ }
432
+ }
433
+
434
+ async scan(device: device & devicesConfig): Promise<void> {
435
+ if (device.scanDuration) {
436
+ this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
437
+ if (this.BLE) {
438
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
439
+ }
440
+ } else {
441
+ this.scanDuration = this.accessory.context.scanDuration = 1;
442
+ if (this.BLE) {
443
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
444
+ }
299
445
  }
300
- this.lockService.updateCharacteristic(this.platform.Characteristic.LockTargetState, e);
301
- this.lockService.updateCharacteristic(this.platform.Characteristic.LockCurrentState, e);
302
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
303
446
  }
304
447
 
305
448
  async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
306
449
  switch (res.statusCode) {
307
450
  case 151:
308
- this.errorLog(`Lock: ${this.accessory.displayName} Command not supported by this device type.`);
451
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
309
452
  break;
310
453
  case 152:
311
- this.errorLog(`Lock: ${this.accessory.displayName} Device not found.`);
454
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
312
455
  break;
313
456
  case 160:
314
- this.errorLog(`Lock: ${this.accessory.displayName} Command is not supported.`);
457
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
315
458
  break;
316
459
  case 161:
317
- this.errorLog(`Lock: ${this.accessory.displayName} Device is offline.`);
460
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
461
+ this.offlineOff();
318
462
  break;
319
463
  case 171:
320
- this.errorLog(`Lock: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
464
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
465
+ this.offlineOff();
321
466
  break;
322
467
  case 190:
323
468
  this.errorLog(
324
- `Lock: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
325
- ` Or command: ${JSON.stringify(res)} format is invalid`,
469
+ `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
470
+ ` Or command: ${superStringify(res)} format is invalid`,
326
471
  );
327
472
  break;
328
473
  case 100:
329
- this.debugLog(`Lock: ${this.accessory.displayName} Command successfully sent.`);
474
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
330
475
  break;
331
476
  default:
332
- this.debugLog(`Lock: ${this.accessory.displayName} Unknown statusCode.`);
477
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
333
478
  }
334
479
  }
335
480
 
336
- /**
337
- * Handle requests to set the value of the "On" characteristic
338
- */
339
- async LockTargetStateSet(value: CharacteristicValue): Promise<void> {
340
- this.debugLog(`Lock: ${this.accessory.displayName} Set LockTargetState: ${value}`);
481
+ async offlineOff(): Promise<void> {
482
+ if (this.device.offline) {
483
+ this.LockCurrentState = this.platform.Characteristic.LockCurrentState.SECURED;
484
+ this.lockService.getCharacteristic(this.platform.Characteristic.LockCurrentState).updateValue(this.LockCurrentState);
485
+ await this.updateHomeKitCharacteristics();
486
+ }
487
+ }
341
488
 
342
- this.LockTargetState = value;
343
- this.doLockUpdate.next();
489
+ async apiError(e: any): Promise<void> {
490
+ if (!this.device.lock?.hide_contactsensor) {
491
+ this.contactSensorService?.updateCharacteristic(this.platform.Characteristic.ContactSensorState, e);
492
+ }
493
+ this.lockService.updateCharacteristic(this.platform.Characteristic.LockTargetState, e);
494
+ this.lockService.updateCharacteristic(this.platform.Characteristic.LockCurrentState, e);
495
+ }
496
+
497
+ FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
498
+ let FirmwareRevision: string;
499
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
500
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
501
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
502
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
503
+ if (accessory.context.FirmwareRevision) {
504
+ FirmwareRevision = accessory.context.FirmwareRevision;
505
+ } else if (device.firmware) {
506
+ FirmwareRevision = device.firmware;
507
+ } else {
508
+ FirmwareRevision = this.platform.version;
509
+ }
510
+ return FirmwareRevision;
511
+ }
512
+
513
+ private conext() {
514
+ if (this.LockTargetState === undefined) {
515
+ this.LockTargetState = false;
516
+ } else {
517
+ this.LockTargetState = this.accessory.context.On;
518
+ }
519
+ }
520
+
521
+ async refreshRate(device: device & devicesConfig): Promise<void> {
522
+ if (device.refreshRate) {
523
+ this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
524
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
525
+ } else if (this.platform.config.options!.refreshRate) {
526
+ this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
527
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
528
+ }
344
529
  }
345
530
 
346
531
  async config(device: device & devicesConfig): Promise<void> {
@@ -348,8 +533,11 @@ export class Lock {
348
533
  if (device.lock) {
349
534
  config = device.lock;
350
535
  }
351
- if (device.ble !== undefined) {
352
- config['ble'] = device.ble;
536
+ if (device.connectionType !== undefined) {
537
+ config['connectionType'] = device.connectionType;
538
+ }
539
+ if (device.external !== undefined) {
540
+ config['external'] = device.external;
353
541
  }
354
542
  if (device.logging !== undefined) {
355
543
  config['logging'] = device.logging;
@@ -361,65 +549,26 @@ export class Lock {
361
549
  config['scanDuration'] = device.scanDuration;
362
550
  }
363
551
  if (Object.entries(config).length !== 0) {
364
- this.infoLog(`Lock: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
365
- }
366
- }
367
-
368
- async refreshRate(device: device & devicesConfig): Promise<void> {
369
- if (device.refreshRate) {
370
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
371
- this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
372
- } else if (this.platform.config.options!.refreshRate) {
373
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
374
- this.debugLog(`Lock: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
375
- }
376
- }
377
-
378
- async scan(device: device & devicesConfig): Promise<void> {
379
- if (device.scanDuration) {
380
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
381
- if (device.ble) {
382
- this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
383
- }
384
- } else {
385
- this.scanDuration = this.accessory.context.scanDuration = 1;
386
- if (this.device.ble) {
387
- this.debugLog(`Lock: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
388
- }
552
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
389
553
  }
390
554
  }
391
555
 
392
556
  async logs(device: device & devicesConfig): Promise<void> {
393
557
  if (this.platform.debugMode) {
394
558
  this.deviceLogging = this.accessory.context.logging = 'debugMode';
395
- this.debugLog(`Lock: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
559
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
396
560
  } else if (device.logging) {
397
561
  this.deviceLogging = this.accessory.context.logging = device.logging;
398
- this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
562
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
399
563
  } else if (this.platform.config.options?.logging) {
400
564
  this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
401
- this.debugLog(`Lock: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
565
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
402
566
  } else {
403
567
  this.deviceLogging = this.accessory.context.logging = 'standard';
404
- this.debugLog(`Lock: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
568
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
405
569
  }
406
570
  }
407
571
 
408
- FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
409
- let FirmwareRevision: string;
410
- this.debugLog(`Lock: ${this.accessory.displayName} accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
411
- this.debugLog(`Lock: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
412
- this.debugLog(`Lock: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
413
- if (accessory.context.FirmwareRevision) {
414
- FirmwareRevision = accessory.context.FirmwareRevision;
415
- } else if (device.firmware) {
416
- FirmwareRevision = device.firmware;
417
- } else {
418
- FirmwareRevision = this.platform.version;
419
- }
420
- return FirmwareRevision;
421
- }
422
-
423
572
  /**
424
573
  * Logging for Device
425
574
  */
@@ -435,12 +584,28 @@ export class Lock {
435
584
  }
436
585
  }
437
586
 
587
+ debugWarnLog(...log: any[]): void {
588
+ if (this.enablingDeviceLogging()) {
589
+ if (this.deviceLogging?.includes('debug')) {
590
+ this.platform.log.warn('[DEBUG]', String(...log));
591
+ }
592
+ }
593
+ }
594
+
438
595
  errorLog(...log: any[]): void {
439
596
  if (this.enablingDeviceLogging()) {
440
597
  this.platform.log.error(String(...log));
441
598
  }
442
599
  }
443
600
 
601
+ debugErrorLog(...log: any[]): void {
602
+ if (this.enablingDeviceLogging()) {
603
+ if (this.deviceLogging?.includes('debug')) {
604
+ this.platform.log.error('[DEBUG]', String(...log));
605
+ }
606
+ }
607
+ }
608
+
444
609
  debugLog(...log: any[]): void {
445
610
  if (this.enablingDeviceLogging()) {
446
611
  if (this.deviceLogging === 'debug') {