@switchbot/homebridge-switchbot 2.0.0-beta.3 → 2.0.0-beta.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -1
- package/README.md +1 -2
- package/config.schema.json +102 -20
- package/dist/device/bot.d.ts +34 -28
- package/dist/device/bot.d.ts.map +1 -1
- package/dist/device/bot.js +655 -649
- package/dist/device/bot.js.map +1 -1
- package/dist/device/colorbulb.d.ts +36 -19
- package/dist/device/colorbulb.d.ts.map +1 -1
- package/dist/device/colorbulb.js +483 -309
- package/dist/device/colorbulb.js.map +1 -1
- package/dist/device/contact.d.ts +18 -8
- package/dist/device/contact.d.ts.map +1 -1
- package/dist/device/contact.js +303 -237
- package/dist/device/contact.js.map +1 -1
- package/dist/device/curtain.d.ts +23 -16
- package/dist/device/curtain.d.ts.map +1 -1
- package/dist/device/curtain.js +541 -498
- package/dist/device/curtain.js.map +1 -1
- package/dist/device/humidifier.d.ts +24 -18
- package/dist/device/humidifier.d.ts.map +1 -1
- package/dist/device/humidifier.js +450 -369
- package/dist/device/humidifier.js.map +1 -1
- package/dist/device/lock.d.ts +27 -8
- package/dist/device/lock.d.ts.map +1 -1
- package/dist/device/lock.js +302 -148
- package/dist/device/lock.js.map +1 -1
- package/dist/device/meter.d.ts +22 -16
- package/dist/device/meter.d.ts.map +1 -1
- package/dist/device/meter.js +340 -262
- package/dist/device/meter.js.map +1 -1
- package/dist/device/meterplus.d.ts +24 -12
- package/dist/device/meterplus.d.ts.map +1 -1
- package/dist/device/meterplus.js +359 -213
- package/dist/device/meterplus.js.map +1 -1
- package/dist/device/motion.d.ts +18 -8
- package/dist/device/motion.d.ts.map +1 -1
- package/dist/device/motion.js +276 -216
- package/dist/device/motion.js.map +1 -1
- package/dist/device/plug.d.ts +27 -17
- package/dist/device/plug.d.ts.map +1 -1
- package/dist/device/plug.js +292 -251
- package/dist/device/plug.js.map +1 -1
- package/dist/device/striplight.d.ts +43 -19
- package/dist/device/striplight.d.ts.map +1 -1
- package/dist/device/striplight.js +503 -262
- package/dist/device/striplight.js.map +1 -1
- package/dist/irdevice/airconditioner.d.ts +9 -12
- package/dist/irdevice/airconditioner.d.ts.map +1 -1
- package/dist/irdevice/airconditioner.js +275 -230
- package/dist/irdevice/airconditioner.js.map +1 -1
- package/dist/irdevice/airpurifier.d.ts +5 -3
- package/dist/irdevice/airpurifier.d.ts.map +1 -1
- package/dist/irdevice/airpurifier.js +151 -104
- package/dist/irdevice/airpurifier.js.map +1 -1
- package/dist/irdevice/camera.d.ts +11 -8
- package/dist/irdevice/camera.d.ts.map +1 -1
- package/dist/irdevice/camera.js +130 -86
- package/dist/irdevice/camera.js.map +1 -1
- package/dist/irdevice/fan.d.ts +10 -7
- package/dist/irdevice/fan.d.ts.map +1 -1
- package/dist/irdevice/fan.js +157 -109
- package/dist/irdevice/fan.js.map +1 -1
- package/dist/irdevice/light.d.ts +11 -8
- package/dist/irdevice/light.d.ts.map +1 -1
- package/dist/irdevice/light.js +134 -103
- package/dist/irdevice/light.js.map +1 -1
- package/dist/irdevice/other.d.ts +11 -8
- package/dist/irdevice/other.d.ts.map +1 -1
- package/dist/irdevice/other.js +142 -98
- package/dist/irdevice/other.js.map +1 -1
- package/dist/irdevice/tv.d.ts +12 -9
- package/dist/irdevice/tv.d.ts.map +1 -1
- package/dist/irdevice/tv.js +140 -93
- package/dist/irdevice/tv.js.map +1 -1
- package/dist/irdevice/vacuumcleaner.d.ts +9 -10
- package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
- package/dist/irdevice/vacuumcleaner.js +129 -89
- package/dist/irdevice/vacuumcleaner.js.map +1 -1
- package/dist/irdevice/waterheater.d.ts +8 -9
- package/dist/irdevice/waterheater.d.ts.map +1 -1
- package/dist/irdevice/waterheater.js +129 -89
- package/dist/irdevice/waterheater.js.map +1 -1
- package/dist/platform.d.ts +14 -12
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +479 -434
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +18 -11
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/package.json +12 -10
- package/src/device/bot.ts +654 -660
- package/src/device/colorbulb.ts +487 -344
- package/src/device/contact.ts +306 -241
- package/src/device/curtain.ts +546 -512
- package/src/device/humidifier.ts +453 -403
- package/src/device/lock.ts +311 -149
- package/src/device/meter.ts +351 -275
- package/src/device/meterplus.ts +369 -219
- package/src/device/motion.ts +282 -222
- package/src/device/plug.ts +296 -260
- package/src/device/striplight.ts +519 -289
- package/src/irdevice/airconditioner.ts +279 -241
- package/src/irdevice/airpurifier.ts +148 -103
- package/src/irdevice/camera.ts +128 -85
- package/src/irdevice/fan.ts +154 -106
- package/src/irdevice/light.ts +132 -103
- package/src/irdevice/other.ts +140 -97
- package/src/irdevice/tv.ts +139 -93
- package/src/irdevice/vacuumcleaner.ts +128 -89
- package/src/irdevice/waterheater.ts +127 -89
- package/src/platform.ts +469 -456
- package/src/settings.ts +18 -11
package/src/device/lock.ts
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
238
|
+
this.openAPIparseStatus();
|
|
189
239
|
this.updateHomeKitCharacteristics();
|
|
190
240
|
} catch (e: any) {
|
|
191
|
-
this.errorLog(
|
|
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(
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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.BLERefreshStatus();
|
|
265
|
+
} else*/ if (this.OpenAPI && this.platform.config.credentials?.token) {
|
|
266
|
+
await this.openAPIpushChanges();
|
|
229
267
|
} else {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
408
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LockTargetState: ${this.LockTargetState}`);
|
|
287
409
|
}
|
|
288
410
|
if (this.LockCurrentState === undefined) {
|
|
289
|
-
this.debugLog(
|
|
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(
|
|
415
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic LockCurrentState: ${this.LockCurrentState}`);
|
|
293
416
|
}
|
|
294
417
|
}
|
|
295
418
|
|
|
296
|
-
async
|
|
297
|
-
if (
|
|
298
|
-
this.
|
|
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(
|
|
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(
|
|
454
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
|
|
312
455
|
break;
|
|
313
456
|
case 160:
|
|
314
|
-
this.errorLog(
|
|
457
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
|
|
315
458
|
break;
|
|
316
459
|
case 161:
|
|
317
|
-
this.errorLog(
|
|
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(
|
|
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
|
-
|
|
325
|
-
` Or command: ${
|
|
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(
|
|
474
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
|
|
330
475
|
break;
|
|
331
476
|
default:
|
|
332
|
-
this.debugLog(
|
|
477
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
|
|
333
478
|
}
|
|
334
479
|
}
|
|
335
480
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
343
|
-
this.
|
|
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,8 @@ export class Lock {
|
|
|
348
533
|
if (device.lock) {
|
|
349
534
|
config = device.lock;
|
|
350
535
|
}
|
|
351
|
-
if (device.
|
|
352
|
-
config['
|
|
536
|
+
if (device.connectionType !== undefined) {
|
|
537
|
+
config['connectionType'] = device.connectionType;
|
|
353
538
|
}
|
|
354
539
|
if (device.logging !== undefined) {
|
|
355
540
|
config['logging'] = device.logging;
|
|
@@ -361,65 +546,26 @@ export class Lock {
|
|
|
361
546
|
config['scanDuration'] = device.scanDuration;
|
|
362
547
|
}
|
|
363
548
|
if (Object.entries(config).length !== 0) {
|
|
364
|
-
this.infoLog(
|
|
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
|
-
}
|
|
549
|
+
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
|
|
389
550
|
}
|
|
390
551
|
}
|
|
391
552
|
|
|
392
553
|
async logs(device: device & devicesConfig): Promise<void> {
|
|
393
554
|
if (this.platform.debugMode) {
|
|
394
555
|
this.deviceLogging = this.accessory.context.logging = 'debugMode';
|
|
395
|
-
this.debugLog(
|
|
556
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
|
|
396
557
|
} else if (device.logging) {
|
|
397
558
|
this.deviceLogging = this.accessory.context.logging = device.logging;
|
|
398
|
-
this.debugLog(
|
|
559
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
|
|
399
560
|
} else if (this.platform.config.options?.logging) {
|
|
400
561
|
this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
|
|
401
|
-
this.debugLog(
|
|
562
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
|
|
402
563
|
} else {
|
|
403
564
|
this.deviceLogging = this.accessory.context.logging = 'standard';
|
|
404
|
-
this.debugLog(
|
|
565
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
|
|
405
566
|
}
|
|
406
567
|
}
|
|
407
568
|
|
|
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
569
|
/**
|
|
424
570
|
* Logging for Device
|
|
425
571
|
*/
|
|
@@ -435,12 +581,28 @@ export class Lock {
|
|
|
435
581
|
}
|
|
436
582
|
}
|
|
437
583
|
|
|
584
|
+
debugWarnLog(...log: any[]): void {
|
|
585
|
+
if (this.enablingDeviceLogging()) {
|
|
586
|
+
if (this.deviceLogging?.includes('debug')) {
|
|
587
|
+
this.platform.log.warn('[DEBUG]', String(...log));
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
438
592
|
errorLog(...log: any[]): void {
|
|
439
593
|
if (this.enablingDeviceLogging()) {
|
|
440
594
|
this.platform.log.error(String(...log));
|
|
441
595
|
}
|
|
442
596
|
}
|
|
443
597
|
|
|
598
|
+
debugErrorLog(...log: any[]): void {
|
|
599
|
+
if (this.enablingDeviceLogging()) {
|
|
600
|
+
if (this.deviceLogging?.includes('debug')) {
|
|
601
|
+
this.platform.log.error('[DEBUG]', String(...log));
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
444
606
|
debugLog(...log: any[]): void {
|
|
445
607
|
if (this.enablingDeviceLogging()) {
|
|
446
608
|
if (this.deviceLogging === 'debug') {
|