node-switchbot 2.5.0-beta.9 → 3.0.1
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/.github/workflows/beta-release.yml +25 -2
- package/.github/workflows/release.yml +13 -0
- package/BLE.md +1144 -0
- package/CHANGELOG.md +22 -0
- package/OpenAPI.md +185 -0
- package/README.md +20 -1189
- package/branding/Node_x_SwitchBot.svg +9 -0
- package/branding/icon.png +0 -0
- package/branding/switchbot.png +0 -0
- package/dist/advertising.d.ts +3 -3
- package/dist/advertising.d.ts.map +1 -1
- package/dist/advertising.js +26 -26
- package/dist/advertising.js.map +1 -1
- package/dist/device/woblindtilt.d.ts +19 -50
- package/dist/device/woblindtilt.d.ts.map +1 -1
- package/dist/device/woblindtilt.js +41 -100
- package/dist/device/woblindtilt.js.map +1 -1
- package/dist/device/wobulb.d.ts +41 -15
- package/dist/device/wobulb.d.ts.map +1 -1
- package/dist/device/wobulb.js +72 -128
- package/dist/device/wobulb.js.map +1 -1
- package/dist/device/woceilinglight.d.ts +47 -21
- package/dist/device/woceilinglight.d.ts.map +1 -1
- package/dist/device/woceilinglight.js +96 -171
- package/dist/device/woceilinglight.js.map +1 -1
- package/dist/device/wocontact.d.ts +11 -1
- package/dist/device/wocontact.d.ts.map +1 -1
- package/dist/device/wocontact.js +23 -22
- package/dist/device/wocontact.js.map +1 -1
- package/dist/device/wocurtain.d.ts +39 -1
- package/dist/device/wocurtain.d.ts.map +1 -1
- package/dist/device/wocurtain.js +69 -103
- package/dist/device/wocurtain.js.map +1 -1
- package/dist/device/wohand.d.ts +37 -2
- package/dist/device/wohand.d.ts.map +1 -1
- package/dist/device/wohand.js +55 -89
- package/dist/device/wohand.js.map +1 -1
- package/dist/device/wohub2.d.ts +11 -1
- package/dist/device/wohub2.d.ts.map +1 -1
- package/dist/device/wohub2.js +21 -29
- package/dist/device/wohub2.js.map +1 -1
- package/dist/device/wohumi.d.ts +37 -2
- package/dist/device/wohumi.d.ts.map +1 -1
- package/dist/device/wohumi.js +52 -83
- package/dist/device/wohumi.js.map +1 -1
- package/dist/device/woiosensorth.d.ts +12 -1
- package/dist/device/woiosensorth.d.ts.map +1 -1
- package/dist/device/woiosensorth.js +25 -29
- package/dist/device/woiosensorth.js.map +1 -1
- package/dist/device/woplugmini.d.ts +45 -12
- package/dist/device/woplugmini.d.ts.map +1 -1
- package/dist/device/woplugmini.js +71 -77
- package/dist/device/woplugmini.js.map +1 -1
- package/dist/device/wopresence.d.ts +11 -1
- package/dist/device/wopresence.d.ts.map +1 -1
- package/dist/device/wopresence.js +21 -27
- package/dist/device/wopresence.js.map +1 -1
- package/dist/device/wosensorth.d.ts +18 -2
- package/dist/device/wosensorth.d.ts.map +1 -1
- package/dist/device/wosensorth.js +34 -50
- package/dist/device/wosensorth.js.map +1 -1
- package/dist/device/wosmartlock.d.ts +63 -13
- package/dist/device/wosmartlock.d.ts.map +1 -1
- package/dist/device/wosmartlock.js +114 -195
- package/dist/device/wosmartlock.js.map +1 -1
- package/dist/device/wosmartlockpro.d.ts +16 -12
- package/dist/device/wosmartlockpro.d.ts.map +1 -1
- package/dist/device/wosmartlockpro.js +34 -29
- package/dist/device/wosmartlockpro.js.map +1 -1
- package/dist/device/wostrip.d.ts +4 -3
- package/dist/device/wostrip.d.ts.map +1 -1
- package/dist/device/wostrip.js +5 -4
- package/dist/device/wostrip.js.map +1 -1
- package/dist/device.d.ts +13 -5
- package/dist/device.d.ts.map +1 -1
- package/dist/device.js +13 -2
- package/dist/device.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/parameter-checker.d.ts +10 -13
- package/dist/parameter-checker.d.ts.map +1 -1
- package/dist/parameter-checker.js +16 -1
- package/dist/parameter-checker.js.map +1 -1
- package/dist/settings.d.ts +47 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +49 -0
- package/dist/settings.js.map +1 -1
- package/dist/{switchbot.d.ts → switchbot-ble.d.ts} +12 -9
- package/dist/switchbot-ble.d.ts.map +1 -0
- package/dist/switchbot-ble.js +426 -0
- package/dist/switchbot-ble.js.map +1 -0
- package/dist/switchbot-openapi.d.ts +131 -0
- package/dist/switchbot-openapi.d.ts.map +1 -0
- package/dist/switchbot-openapi.js +311 -0
- package/dist/switchbot-openapi.js.map +1 -0
- package/dist/test/advertising.test.d.ts +2 -0
- package/dist/test/advertising.test.d.ts.map +1 -0
- package/dist/test/advertising.test.js +99 -0
- package/dist/test/advertising.test.js.map +1 -0
- package/dist/test/device.test.d.ts +2 -0
- package/dist/test/device.test.d.ts.map +1 -0
- package/dist/test/device.test.js +38 -0
- package/dist/test/device.test.js.map +1 -0
- package/dist/test/index.test.d.ts +2 -0
- package/dist/test/index.test.d.ts.map +1 -0
- package/dist/test/index.test.js +35 -0
- package/dist/test/index.test.js.map +1 -0
- package/dist/test/parameter-checker.test.d.ts +2 -0
- package/dist/test/parameter-checker.test.d.ts.map +1 -0
- package/dist/test/parameter-checker.test.js +108 -0
- package/dist/test/parameter-checker.test.js.map +1 -0
- package/dist/test/settings.test.d.ts +2 -0
- package/dist/test/settings.test.d.ts.map +1 -0
- package/dist/test/settings.test.js +49 -0
- package/dist/test/settings.test.js.map +1 -0
- package/dist/test/switchbot-openapi.test.d.ts +2 -0
- package/dist/test/switchbot-openapi.test.d.ts.map +1 -0
- package/dist/test/switchbot-openapi.test.js +96 -0
- package/dist/test/switchbot-openapi.test.js.map +1 -0
- package/dist/test/switchbot.test.d.ts +2 -0
- package/dist/test/switchbot.test.d.ts.map +1 -0
- package/dist/test/switchbot.test.js +106 -0
- package/dist/test/switchbot.test.js.map +1 -0
- package/dist/test/woblindtilt.test.js +70 -20
- package/dist/test/woblindtilt.test.js.map +1 -1
- package/dist/test/wobulb.test.js +87 -44
- package/dist/test/wobulb.test.js.map +1 -1
- package/dist/test/woceilinglight.test.js +72 -54
- package/dist/test/woceilinglight.test.js.map +1 -1
- package/dist/test/wocontact.test.js +51 -20
- package/dist/test/wocontact.test.js.map +1 -1
- package/dist/test/wocurtain.test.js +65 -23
- package/dist/test/wocurtain.test.js.map +1 -1
- package/dist/test/wohand.test.js +34 -40
- package/dist/test/wohand.test.js.map +1 -1
- package/dist/test/wohub2.test.d.ts +2 -0
- package/dist/test/wohub2.test.d.ts.map +1 -0
- package/dist/test/wohub2.test.js +80 -0
- package/dist/test/wohub2.test.js.map +1 -0
- package/dist/test/wohumi.test.js +70 -49
- package/dist/test/wohumi.test.js.map +1 -1
- package/dist/test/woiosensorth.test.js +33 -32
- package/dist/test/woiosensorth.test.js.map +1 -1
- package/dist/test/woplugmini.test.js +80 -57
- package/dist/test/woplugmini.test.js.map +1 -1
- package/dist/test/wopresence.test.js +44 -18
- package/dist/test/wopresence.test.js.map +1 -1
- package/dist/test/wosensorth.test.js +44 -52
- package/dist/test/wosensorth.test.js.map +1 -1
- package/dist/test/wosmartlock.test.js +126 -59
- package/dist/test/wosmartlock.test.js.map +1 -1
- package/dist/test/wosmartlockpro.test.js +96 -69
- package/dist/test/wosmartlockpro.test.js.map +1 -1
- package/dist/test/wostrip.test.js +70 -83
- package/dist/test/wostrip.test.js.map +1 -1
- package/dist/types/bledevicestatus.d.ts +4 -12
- package/dist/types/bledevicestatus.d.ts.map +1 -1
- package/dist/types/devicelist.d.ts.map +1 -1
- package/dist/types/devicelist.js +0 -4
- package/dist/types/devicelist.js.map +1 -1
- package/dist/types/devicepush.d.ts +13 -0
- package/dist/types/devicepush.d.ts.map +1 -0
- package/dist/types/devicepush.js +2 -0
- package/dist/types/devicepush.js.map +1 -0
- package/dist/types/deviceresponse.d.ts +2 -3
- package/dist/types/deviceresponse.d.ts.map +1 -1
- package/dist/types/devicestatus.d.ts.map +1 -1
- package/dist/types/devicewebhookstatus.d.ts +32 -0
- package/dist/types/devicewebhookstatus.d.ts.map +1 -1
- package/dist/types/irdevicelist.d.ts.map +1 -1
- package/dist/types/types.d.ts +24 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +14 -0
- package/dist/types/types.js.map +1 -1
- package/docs/assets/highlight.css +12 -47
- package/docs/assets/main.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/SwitchBotBLE.html +62 -0
- package/docs/classes/SwitchBotOpenAPI.html +47 -0
- package/docs/classes/SwitchbotDevice.html +32 -33
- package/docs/enums/LogLevel.html +10 -0
- package/docs/enums/SwitchBotBLEModel.html +2 -2
- package/docs/enums/SwitchBotBLEModelFriendlyName.html +2 -2
- package/docs/enums/SwitchBotBLEModelName.html +2 -2
- package/docs/enums/SwitchBotModel.html +2 -2
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +16 -993
- package/docs/interfaces/Ad-1.html +5 -0
- package/docs/interfaces/AdvertisementData.html +3 -0
- package/docs/interfaces/Chars.html +4 -0
- package/docs/interfaces/ErrorObject.html +3 -0
- package/docs/interfaces/Params.html +6 -0
- package/docs/interfaces/Rule.html +9 -0
- package/docs/interfaces/ServiceData.html +2 -0
- package/docs/interfaces/SwitchBotBLEDevice.html +21 -0
- package/docs/interfaces/WebhookDetail.html +6 -0
- package/docs/interfaces/ad.html +5 -0
- package/docs/interfaces/body.html +3 -0
- package/docs/interfaces/bodyChange.html +4 -0
- package/docs/interfaces/deleteWebhookResponse.html +4 -0
- package/docs/interfaces/device.html +7 -0
- package/docs/interfaces/deviceList.html +2 -0
- package/docs/interfaces/deviceStatus.html +7 -0
- package/docs/interfaces/deviceStatusRequest.html +4 -0
- package/docs/interfaces/deviceWebhook.html +4 -0
- package/docs/interfaces/deviceWebhookContext.html +4 -0
- package/docs/interfaces/devices.html +4 -0
- package/docs/interfaces/infraredRemoteList.html +2 -0
- package/docs/interfaces/irdevice.html +5 -0
- package/docs/interfaces/pushResponse.html +4 -0
- package/docs/interfaces/queryWebhookResponse.html +4 -0
- package/docs/interfaces/setupWebhookResponse.html +4 -0
- package/docs/interfaces/switchbot.html +3 -0
- package/docs/interfaces/updateWebhookResponse.html +4 -0
- package/docs/interfaces/webhookRequest.html +4 -0
- package/docs/media/BLE.md +1144 -0
- package/docs/media/OpenAPI.md +185 -0
- package/docs/modules.html +137 -3
- package/docs/types/MacAddress.html +1 -0
- package/docs/types/batteryCirculatorFan.html +1 -0
- package/docs/types/batteryCirculatorFanServiceData.html +1 -0
- package/docs/types/batteryCirculatorFanStatus.html +1 -0
- package/docs/types/batteryCirculatorFanWebhookContext.html +1 -0
- package/docs/types/blindTilt.html +1 -0
- package/docs/types/blindTiltServiceData.html +1 -0
- package/docs/types/blindTiltStatus.html +1 -0
- package/docs/types/blindTiltWebhookContext.html +1 -0
- package/docs/types/bot.html +1 -0
- package/docs/types/botServiceData.html +1 -0
- package/docs/types/botStatus.html +1 -0
- package/docs/types/botWebhookContext.html +1 -0
- package/docs/types/ceilingLight.html +1 -0
- package/docs/types/ceilingLightPro.html +1 -0
- package/docs/types/ceilingLightProServiceData.html +1 -0
- package/docs/types/ceilingLightProStatus.html +1 -0
- package/docs/types/ceilingLightProWebhookContext.html +1 -0
- package/docs/types/ceilingLightServiceData.html +1 -0
- package/docs/types/ceilingLightStatus.html +1 -0
- package/docs/types/ceilingLightWebhookContext.html +1 -0
- package/docs/types/colorBulb.html +1 -0
- package/docs/types/colorBulbServiceData.html +1 -0
- package/docs/types/colorBulbStatus.html +1 -0
- package/docs/types/colorBulbWebhookContext.html +1 -0
- package/docs/types/contactSensor.html +1 -0
- package/docs/types/contactSensorServiceData.html +1 -0
- package/docs/types/contactSensorStatus.html +1 -0
- package/docs/types/contactSensorWebhookContext.html +1 -0
- package/docs/types/curtain.html +1 -0
- package/docs/types/curtain3.html +1 -0
- package/docs/types/curtain3ServiceData.html +1 -0
- package/docs/types/curtain3WebhookContext.html +1 -0
- package/docs/types/curtainServiceData.html +1 -0
- package/docs/types/curtainStatus.html +1 -0
- package/docs/types/curtainWebhookContext.html +1 -0
- package/docs/types/floorCleaningRobotS10.html +1 -0
- package/docs/types/floorCleaningRobotS10Status.html +1 -0
- package/docs/types/floorCleaningRobotS10WebhookContext.html +1 -0
- package/docs/types/hub2.html +1 -0
- package/docs/types/hub2ServiceData.html +1 -0
- package/docs/types/hub2Status.html +1 -0
- package/docs/types/hub2WebhookContext.html +1 -0
- package/docs/types/humidifier.html +1 -0
- package/docs/types/humidifierServiceData.html +1 -0
- package/docs/types/humidifierStatus.html +1 -0
- package/docs/types/humidifierWebhookContext.html +1 -0
- package/docs/types/indoorCam.html +1 -0
- package/docs/types/indoorCameraWebhookContext.html +1 -0
- package/docs/types/keypad.html +1 -0
- package/docs/types/keypadTouch.html +1 -0
- package/docs/types/keypadTouchWebhookContext.html +1 -0
- package/docs/types/keypadWebhookContext.html +1 -0
- package/docs/types/lock.html +1 -0
- package/docs/types/lockPro.html +1 -0
- package/docs/types/lockProServiceData.html +1 -0
- package/docs/types/lockProStatus.html +1 -0
- package/docs/types/lockProWebhookContext.html +1 -0
- package/docs/types/lockServiceData.html +1 -0
- package/docs/types/lockStatus.html +1 -0
- package/docs/types/lockWebhookContext.html +1 -0
- package/docs/types/meter.html +1 -0
- package/docs/types/meterPlus.html +1 -0
- package/docs/types/meterPlusServiceData.html +1 -0
- package/docs/types/meterPlusStatus.html +1 -0
- package/docs/types/meterPlusWebhookContext.html +1 -0
- package/docs/types/meterServiceData.html +1 -0
- package/docs/types/meterStatus.html +1 -0
- package/docs/types/meterWebhookContext.html +1 -0
- package/docs/types/motionSensor.html +1 -0
- package/docs/types/motionSensorServiceData.html +1 -0
- package/docs/types/motionSensorStatus.html +1 -0
- package/docs/types/motionSensorWebhookContext.html +1 -0
- package/docs/types/outdoorMeter.html +1 -0
- package/docs/types/outdoorMeterServiceData.html +1 -0
- package/docs/types/outdoorMeterStatus.html +1 -0
- package/docs/types/outdoorMeterWebhookContext.html +1 -0
- package/docs/types/panTiltCamWebhookContext.html +1 -0
- package/docs/types/pantiltCam.html +1 -0
- package/docs/types/pantiltCam2k.html +1 -0
- package/docs/types/plug.html +1 -0
- package/docs/types/plugMini.html +1 -0
- package/docs/types/plugMiniJPServiceData.html +1 -0
- package/docs/types/plugMiniJPWebhookContext.html +1 -0
- package/docs/types/plugMiniStatus.html +1 -0
- package/docs/types/plugMiniUSServiceData.html +1 -0
- package/docs/types/plugMiniUSWebhookContext.html +1 -0
- package/docs/types/plugStatus.html +1 -0
- package/docs/types/plugWebhookContext.html +1 -0
- package/docs/types/remote.html +1 -0
- package/docs/types/robotVacuumCleanerS1.html +1 -0
- package/docs/types/robotVacuumCleanerS1Plus.html +1 -0
- package/docs/types/robotVacuumCleanerS1PlusStatus.html +1 -0
- package/docs/types/robotVacuumCleanerS1PlusWebhookContext.html +1 -0
- package/docs/types/robotVacuumCleanerS1Status.html +1 -0
- package/docs/types/robotVacuumCleanerS1WebhookContext.html +1 -0
- package/docs/types/robotVacuumCleanerServiceData.html +1 -0
- package/docs/types/stripLight.html +1 -0
- package/docs/types/stripLightServiceData.html +1 -0
- package/docs/types/stripLightStatus.html +1 -0
- package/docs/types/stripLightWebhookContext.html +1 -0
- package/docs/types/waterLeakDetector.html +1 -0
- package/docs/types/waterLeakDetectorServiceData.html +1 -0
- package/docs/types/waterLeakDetectorStatus.html +1 -0
- package/docs/types/waterLeakDetectorWebhookContext.html +1 -0
- package/package.json +15 -13
- package/.github/npm-version-script.cjs +0 -81
- package/dist/switchbot.d.ts.map +0 -1
- package/dist/switchbot.js +0 -259
- package/dist/switchbot.js.map +0 -1
- package/dist/test/wohand2.test.d.ts +0 -2
- package/dist/test/wohand2.test.d.ts.map +0 -1
- package/dist/test/wohand2.test.js +0 -50
- package/dist/test/wohand2.test.js.map +0 -1
- package/dist/types/pushbody.d.ts +0 -6
- package/dist/types/pushbody.d.ts.map +0 -1
- package/dist/types/pushbody.js +0 -2
- package/dist/types/pushbody.js.map +0 -1
- package/docs/classes/SwitchBot.html +0 -16
- package/docs/types/SwitchBotBLEDevice.html +0 -1
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import * as Crypto from 'node:crypto';
|
|
3
|
-
/*
|
|
4
|
-
* wosmartlock.ts: Switchbot BLE API registration.
|
|
5
|
-
* adapted off the work done by [pySwitchbot](https://github.com/Danielhiversen/pySwitchbot)
|
|
6
|
-
*/
|
|
7
3
|
import { SwitchbotDevice } from '../device.js';
|
|
4
|
+
import { WoSmartLockCommands } from '../settings.js';
|
|
8
5
|
import { SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from '../types/types.js';
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Class representing a WoSmartLock device.
|
|
8
|
+
* @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/lock.md
|
|
9
|
+
*/
|
|
10
10
|
export class WoSmartLock extends SwitchbotDevice {
|
|
11
|
-
iv;
|
|
12
|
-
key_id;
|
|
13
|
-
encryption_key;
|
|
14
|
-
static COMMAND_GET_CKiv = '570f2103';
|
|
15
|
-
static COMMAND_LOCK_INFO = '570f4f8101';
|
|
16
|
-
static COMMAND_UNLOCK = '570f4e01011080';
|
|
17
|
-
static COMMAND_UNLOCK_NO_UNLATCH = '570f4e010110a0';
|
|
18
|
-
static COMMAND_LOCK = '570f4e01011000';
|
|
19
|
-
static COMMAND_ENABLE_NOTIFICATIONS = '570e01001e00008101';
|
|
20
|
-
static COMMAND_DISABLE_NOTIFICATIONS = '570e00';
|
|
11
|
+
iv = null;
|
|
12
|
+
key_id = '';
|
|
13
|
+
encryption_key = null;
|
|
21
14
|
static Result = {
|
|
22
15
|
ERROR: 0x00,
|
|
23
16
|
SUCCESS: 0x01,
|
|
@@ -25,227 +18,153 @@ export class WoSmartLock extends SwitchbotDevice {
|
|
|
25
18
|
};
|
|
26
19
|
static async validateResponse(res) {
|
|
27
20
|
if (res.length >= 3) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
case WoSmartLock.Result.SUCCESS_LOW_BATTERY:
|
|
32
|
-
return WoSmartLock.Result.SUCCESS_LOW_BATTERY;
|
|
21
|
+
const result = res.readUInt8(0);
|
|
22
|
+
if (result === WoSmartLock.Result.SUCCESS || result === WoSmartLock.Result.SUCCESS_LOW_BATTERY) {
|
|
23
|
+
return result;
|
|
33
24
|
}
|
|
34
25
|
}
|
|
35
26
|
return WoSmartLock.Result.ERROR;
|
|
36
27
|
}
|
|
37
28
|
static getLockStatus(code) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return 'LOCKING_STOP';
|
|
49
|
-
case 0b1010000:
|
|
50
|
-
return 'UNLOCKING_STOP';
|
|
51
|
-
case 0b1100000: // Only EU lock type
|
|
52
|
-
return 'NOT_FULLY_LOCKED';
|
|
53
|
-
default:
|
|
54
|
-
return 'UNKNOWN';
|
|
55
|
-
}
|
|
29
|
+
const statusMap = {
|
|
30
|
+
0b0000000: 'LOCKED',
|
|
31
|
+
0b0010000: 'UNLOCKED',
|
|
32
|
+
0b0100000: 'LOCKING',
|
|
33
|
+
0b0110000: 'UNLOCKING',
|
|
34
|
+
0b1000000: 'LOCKING_STOP',
|
|
35
|
+
0b1010000: 'UNLOCKING_STOP',
|
|
36
|
+
0b1100000: 'NOT_FULLY_LOCKED', // Only EU lock type
|
|
37
|
+
};
|
|
38
|
+
return statusMap[code] || 'UNKNOWN';
|
|
56
39
|
}
|
|
57
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Parses the service data from the SwitchBot Strip Light.
|
|
42
|
+
* @param {Buffer} serviceData - The service data buffer.
|
|
43
|
+
* @param {Buffer} manufacturerData - The manufacturer data buffer.
|
|
44
|
+
* @param {Function} emitLog - The function to emit log messages.
|
|
45
|
+
* @returns {Promise<lockServiceData | null>} - Parsed service data or null if invalid.
|
|
46
|
+
*/
|
|
47
|
+
static async parseServiceData(serviceData, manufacturerData, emitLog) {
|
|
58
48
|
if (manufacturerData.length < 11) {
|
|
59
|
-
|
|
60
|
-
onlog(`[parseServiceDataForWoSmartLock] Buffer length ${manufacturerData.length} is too short!`);
|
|
61
|
-
}
|
|
49
|
+
emitLog('debugerror', `[parseServiceDataForWoSmartLock] Buffer length ${manufacturerData.length} is too short!`);
|
|
62
50
|
return null;
|
|
63
51
|
}
|
|
64
|
-
// adv data needs both service data and manufacturer data
|
|
65
|
-
// byte var names based on documentation
|
|
66
52
|
const byte2 = serviceData.readUInt8(2);
|
|
67
53
|
const byte15 = manufacturerData.readUInt8(9);
|
|
68
54
|
const byte16 = manufacturerData.readUInt8(10);
|
|
69
|
-
const battery = byte2 & 0b01111111; // %
|
|
70
|
-
const calibration = !!(byte15 & 0b10000000);
|
|
71
|
-
const status = WoSmartLock.getLockStatus(byte15 & 0b01110000);
|
|
72
|
-
const update_from_secondary_lock = !!(byte15 & 0b00001000);
|
|
73
|
-
const door_open = !!(byte15 & 0b00000100);
|
|
74
|
-
const double_lock_mode = !!(byte16 & 0b10000000);
|
|
75
|
-
const unclosed_alarm = !!(byte16 & 0b00100000);
|
|
76
|
-
const unlocked_alarm = !!(byte16 & 0b00010000);
|
|
77
|
-
const auto_lock_paused = !!(byte16 & 0b00000010);
|
|
78
|
-
const night_latch = !!(manufacturerData.length > 11 && manufacturerData.readUInt8(11) & 0b00000001);
|
|
79
55
|
const data = {
|
|
80
56
|
model: SwitchBotBLEModel.Lock,
|
|
81
57
|
modelName: SwitchBotBLEModelName.Lock,
|
|
82
58
|
modelFriendlyName: SwitchBotBLEModelFriendlyName.Lock,
|
|
83
|
-
battery,
|
|
84
|
-
calibration,
|
|
85
|
-
status,
|
|
86
|
-
update_from_secondary_lock,
|
|
87
|
-
door_open,
|
|
88
|
-
double_lock_mode,
|
|
89
|
-
unclosed_alarm,
|
|
90
|
-
unlocked_alarm,
|
|
91
|
-
auto_lock_paused,
|
|
92
|
-
night_latch,
|
|
59
|
+
battery: byte2 & 0b01111111,
|
|
60
|
+
calibration: !!(byte15 & 0b10000000),
|
|
61
|
+
status: WoSmartLock.getLockStatus(byte15 & 0b01110000),
|
|
62
|
+
update_from_secondary_lock: !!(byte15 & 0b00001000),
|
|
63
|
+
door_open: !!(byte15 & 0b00000100),
|
|
64
|
+
double_lock_mode: !!(byte16 & 0b10000000),
|
|
65
|
+
unclosed_alarm: !!(byte16 & 0b00100000),
|
|
66
|
+
unlocked_alarm: !!(byte16 & 0b00010000),
|
|
67
|
+
auto_lock_paused: !!(byte16 & 0b00000010),
|
|
68
|
+
night_latch: !!(manufacturerData.length > 11 && manufacturerData.readUInt8(11) & 0b00000001),
|
|
93
69
|
};
|
|
94
70
|
return data;
|
|
95
71
|
}
|
|
96
72
|
constructor(peripheral, noble) {
|
|
97
73
|
super(peripheral, noble);
|
|
98
|
-
this.iv = null;
|
|
99
|
-
this.key_id = '';
|
|
100
|
-
this.encryption_key = null;
|
|
101
74
|
}
|
|
102
|
-
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
* - keyId, encryptionKey
|
|
108
|
-
*
|
|
109
|
-
* [Return value]
|
|
110
|
-
* - void
|
|
111
|
-
* ---------------------------------------------------------------- */
|
|
75
|
+
/**
|
|
76
|
+
* Initializes the encryption key info for valid lock communication.
|
|
77
|
+
* @param {string} keyId - The key ID.
|
|
78
|
+
* @param {string} encryptionKey - The encryption key.
|
|
79
|
+
*/
|
|
112
80
|
async setKey(keyId, encryptionKey) {
|
|
113
81
|
this.iv = null;
|
|
114
82
|
this.key_id = keyId;
|
|
115
83
|
this.encryption_key = Buffer.from(encryptionKey, 'hex');
|
|
116
84
|
}
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
* -
|
|
120
|
-
|
|
121
|
-
* [Arguments]
|
|
122
|
-
* - none
|
|
123
|
-
*
|
|
124
|
-
* [Return value]
|
|
125
|
-
* - Promise object
|
|
126
|
-
* WoSmartLock.LockResult will be passed to the `resolve()`.
|
|
127
|
-
* ---------------------------------------------------------------- */
|
|
85
|
+
/**
|
|
86
|
+
* Unlocks the Smart Lock.
|
|
87
|
+
* @returns {Promise<number>} - The result of the unlock operation.
|
|
88
|
+
*/
|
|
128
89
|
async unlock() {
|
|
129
|
-
await this.operateLock(
|
|
130
|
-
|
|
131
|
-
if (resBuf) {
|
|
132
|
-
return WoSmartLock.validateResponse(resBuf);
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
return WoSmartLockPro.Result.ERROR;
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
.catch((error) => {
|
|
139
|
-
return error;
|
|
140
|
-
});
|
|
90
|
+
const resBuf = await this.operateLock(WoSmartLockCommands.UNLOCK);
|
|
91
|
+
return resBuf ? WoSmartLock.validateResponse(resBuf) : WoSmartLock.Result.ERROR;
|
|
141
92
|
}
|
|
142
|
-
|
|
143
|
-
*
|
|
144
|
-
* -
|
|
145
|
-
|
|
146
|
-
* [Arguments]
|
|
147
|
-
* - none
|
|
148
|
-
*
|
|
149
|
-
* [Return value]
|
|
150
|
-
* - Promise object
|
|
151
|
-
* WoSmartLock.LockResult will be passed to the `resolve()`.
|
|
152
|
-
* ---------------------------------------------------------------- */
|
|
93
|
+
/**
|
|
94
|
+
* Unlocks the Smart Lock without unlatching the door.
|
|
95
|
+
* @returns {Promise<number>} - The result of the unlock operation.
|
|
96
|
+
*/
|
|
153
97
|
async unlockNoUnlatch() {
|
|
154
|
-
await this.operateLock(
|
|
155
|
-
|
|
156
|
-
if (resBuf) {
|
|
157
|
-
return WoSmartLock.validateResponse(resBuf);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
throw new Error('Failed to retrieve response buffer from the device.');
|
|
161
|
-
}
|
|
162
|
-
})
|
|
163
|
-
.catch((error) => {
|
|
164
|
-
return error;
|
|
165
|
-
});
|
|
98
|
+
const resBuf = await this.operateLock(WoSmartLockCommands.UNLOCK_NO_UNLATCH);
|
|
99
|
+
return resBuf ? WoSmartLock.validateResponse(resBuf) : WoSmartLock.Result.ERROR;
|
|
166
100
|
}
|
|
167
|
-
|
|
168
|
-
*
|
|
169
|
-
* -
|
|
170
|
-
|
|
171
|
-
* [Arguments]
|
|
172
|
-
* - none
|
|
173
|
-
*
|
|
174
|
-
* [Return value]
|
|
175
|
-
* - Promise object
|
|
176
|
-
* WoSmartLock.LockResult will be passed to the `resolve()`.
|
|
177
|
-
* ---------------------------------------------------------------- */
|
|
101
|
+
/**
|
|
102
|
+
* Locks the Smart Lock.
|
|
103
|
+
* @returns {Promise<number>} - The result of the lock operation.
|
|
104
|
+
*/
|
|
178
105
|
async lock() {
|
|
179
|
-
await this.operateLock(
|
|
180
|
-
|
|
181
|
-
if (resBuf) {
|
|
182
|
-
return WoSmartLock.validateResponse(resBuf);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
throw new Error('Failed to retrieve response buffer from the device.');
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
.catch((error) => {
|
|
189
|
-
return error;
|
|
190
|
-
});
|
|
106
|
+
const resBuf = await this.operateLock(WoSmartLockCommands.LOCK);
|
|
107
|
+
return resBuf ? WoSmartLock.validateResponse(resBuf) : WoSmartLock.Result.ERROR;
|
|
191
108
|
}
|
|
192
|
-
|
|
193
|
-
* info
|
|
194
|
-
* -
|
|
195
|
-
|
|
196
|
-
* [Arguments]
|
|
197
|
-
* - none
|
|
198
|
-
*
|
|
199
|
-
* [Return value]
|
|
200
|
-
* - Promise object
|
|
201
|
-
* state object will be passed to the `resolve()`
|
|
202
|
-
* ---------------------------------------------------------------- */
|
|
109
|
+
/**
|
|
110
|
+
* Gets general state info from the Smart Lock.
|
|
111
|
+
* @returns {Promise<object | null>} - The state object or null if an error occurred.
|
|
112
|
+
*/
|
|
203
113
|
async info() {
|
|
204
|
-
await this.operateLock(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
throw new Error('Failed to retrieve response buffer from the device.');
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
.catch((error) => {
|
|
221
|
-
return error;
|
|
222
|
-
});
|
|
114
|
+
const resBuf = await this.operateLock(WoSmartLockCommands.LOCK_INFO);
|
|
115
|
+
if (resBuf) {
|
|
116
|
+
return {
|
|
117
|
+
calibration: Boolean(resBuf[1] & 0b10000000),
|
|
118
|
+
status: WoSmartLock.getLockStatus((resBuf[1] & 0b01110000)),
|
|
119
|
+
door_open: Boolean(resBuf[1] & 0b00000100),
|
|
120
|
+
unclosed_alarm: Boolean(resBuf[2] & 0b00100000),
|
|
121
|
+
unlocked_alarm: Boolean(resBuf[2] & 0b00010000),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
223
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Encrypts a string using AES-128-CTR.
|
|
128
|
+
* @param {string} str - The string to encrypt.
|
|
129
|
+
* @returns {Promise<string>} - The encrypted string in hex format.
|
|
130
|
+
*/
|
|
224
131
|
async encrypt(str) {
|
|
225
132
|
const cipher = Crypto.createCipheriv('aes-128-ctr', this.encryption_key, this.iv);
|
|
226
133
|
return Buffer.concat([cipher.update(str, 'hex'), cipher.final()]).toString('hex');
|
|
227
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Decrypts a buffer using AES-128-CTR.
|
|
137
|
+
* @param {Buffer} data - The data to decrypt.
|
|
138
|
+
* @returns {Promise<Buffer>} - The decrypted data.
|
|
139
|
+
*/
|
|
228
140
|
async decrypt(data) {
|
|
229
141
|
const decipher = Crypto.createDecipheriv('aes-128-ctr', this.encryption_key, this.iv);
|
|
230
142
|
return Buffer.concat([decipher.update(data), decipher.final()]);
|
|
231
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Retrieves the IV from the device.
|
|
146
|
+
* @returns {Promise<Buffer>} - The IV buffer.
|
|
147
|
+
*/
|
|
232
148
|
async getIv() {
|
|
233
|
-
if (this.iv
|
|
234
|
-
const res = await this.operateLock(
|
|
149
|
+
if (!this.iv) {
|
|
150
|
+
const res = await this.operateLock(WoSmartLockCommands.GET_CKIV + this.key_id, false);
|
|
235
151
|
if (res) {
|
|
236
152
|
this.iv = res.subarray(4);
|
|
237
153
|
}
|
|
238
154
|
else {
|
|
239
|
-
// Handle the case when 'res' is undefined
|
|
240
|
-
// For example, you can throw an error or set a default value for 'this.iv'
|
|
241
155
|
throw new Error('Failed to retrieve IV from the device.');
|
|
242
156
|
}
|
|
243
157
|
}
|
|
244
158
|
return this.iv;
|
|
245
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Sends an encrypted command to the device.
|
|
162
|
+
* @param {string} key - The command key.
|
|
163
|
+
* @returns {Promise<Buffer>} - The response buffer.
|
|
164
|
+
*/
|
|
246
165
|
async encryptedCommand(key) {
|
|
247
166
|
const iv = await this.getIv();
|
|
248
|
-
const req = Buffer.from(key.substring(0, 2) + this.key_id + Buffer.from(iv.subarray(0, 2)).toString('hex') + this.encrypt(key.substring(2)), 'hex');
|
|
167
|
+
const req = Buffer.from(key.substring(0, 2) + this.key_id + Buffer.from(iv.subarray(0, 2)).toString('hex') + await this.encrypt(key.substring(2)), 'hex');
|
|
249
168
|
const bytes = await this.command(req);
|
|
250
169
|
const buf = Buffer.from(bytes);
|
|
251
170
|
const code = WoSmartLock.validateResponse(buf);
|
|
@@ -256,24 +175,24 @@ export class WoSmartLock extends SwitchbotDevice {
|
|
|
256
175
|
throw new Error(`The device returned an error: 0x${buf.toString('hex')}`);
|
|
257
176
|
}
|
|
258
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Operates the lock with the given command.
|
|
180
|
+
* @param {string} key - The command key.
|
|
181
|
+
* @param {boolean} [encrypt] - Whether to encrypt the command.
|
|
182
|
+
* @returns {Promise<Buffer>} - The response buffer.
|
|
183
|
+
*/
|
|
259
184
|
async operateLock(key, encrypt = true) {
|
|
260
|
-
// encrypted command
|
|
261
185
|
if (encrypt) {
|
|
262
|
-
return
|
|
186
|
+
return this.encryptedCommand(key);
|
|
263
187
|
}
|
|
264
188
|
const req = Buffer.from(`${key.substring(0, 2)}000000${key.substring(2)}`, 'hex');
|
|
265
|
-
await this.command(req)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return buf;
|
|
273
|
-
}
|
|
274
|
-
}).catch((error) => {
|
|
275
|
-
return error;
|
|
276
|
-
});
|
|
189
|
+
const bytes = await this.command(req);
|
|
190
|
+
const buf = Buffer.from(bytes);
|
|
191
|
+
const code = WoSmartLock.validateResponse(buf);
|
|
192
|
+
if (await code === WoSmartLock.Result.ERROR) {
|
|
193
|
+
throw new Error(`The device returned an error: 0x${buf.toString('hex')}`);
|
|
194
|
+
}
|
|
195
|
+
return buf;
|
|
277
196
|
}
|
|
278
197
|
}
|
|
279
198
|
//# sourceMappingURL=wosmartlock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wosmartlock.js","sourceRoot":"","sources":["../../src/device/wosmartlock.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wosmartlock.js","sourceRoot":"","sources":["../../src/device/wosmartlock.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,25 +2,29 @@ import type * as Noble from '@stoprocent/noble';
|
|
|
2
2
|
import type { lockProServiceData } from '../types/bledevicestatus.js';
|
|
3
3
|
import { Buffer } from 'node:buffer';
|
|
4
4
|
import { SwitchbotDevice } from '../device.js';
|
|
5
|
+
/**
|
|
6
|
+
* Class representing a WoSmartLockPro device.
|
|
7
|
+
* @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/lock.md
|
|
8
|
+
*/
|
|
5
9
|
export declare class WoSmartLockPro extends SwitchbotDevice {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
static COMMAND_GET_CK_IV: string;
|
|
10
|
-
static COMMAND_LOCK_INFO: string;
|
|
11
|
-
static COMMAND_UNLOCK: string;
|
|
12
|
-
static COMMAND_UNLOCK_NO_UNLATCH: string;
|
|
13
|
-
static COMMAND_LOCK: string;
|
|
14
|
-
static COMMAND_ENABLE_NOTIFICATIONS: string;
|
|
15
|
-
static COMMAND_DISABLE_NOTIFICATIONS: string;
|
|
10
|
+
iv: Buffer | null;
|
|
11
|
+
key_id: string;
|
|
12
|
+
encryption_key: Buffer | null;
|
|
16
13
|
static Result: {
|
|
17
14
|
ERROR: number;
|
|
18
15
|
SUCCESS: number;
|
|
19
16
|
SUCCESS_LOW_BATTERY: number;
|
|
20
17
|
};
|
|
21
|
-
static validateResponse(res: Buffer): number
|
|
18
|
+
static validateResponse(res: Buffer): Promise<number>;
|
|
22
19
|
static getLockStatus(code: number): string;
|
|
23
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Parses the service data from the SwitchBot Strip Light.
|
|
22
|
+
* @param {Buffer} serviceData - The service data buffer.
|
|
23
|
+
* @param {Buffer} manufacturerData - The manufacturer data buffer.
|
|
24
|
+
* @param {Function} emitLog - The function to emit log messages.
|
|
25
|
+
* @returns {Promise<lockProServiceData | null>} - Parsed service data or null if invalid.
|
|
26
|
+
*/
|
|
27
|
+
static parseServiceData(serviceData: Buffer, manufacturerData: Buffer, emitLog: (level: string, message: string) => void): Promise<lockProServiceData | null>;
|
|
24
28
|
constructor(peripheral: Noble.Peripheral, noble: typeof Noble);
|
|
25
29
|
/**
|
|
26
30
|
* Initializes the encryption key info for valid lock communication.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wosmartlockpro.d.ts","sourceRoot":"","sources":["../../src/device/wosmartlockpro.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,KAAK,MAAM,mBAAmB,CAAA;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAErE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"wosmartlockpro.d.ts","sourceRoot":"","sources":["../../src/device/wosmartlockpro.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,KAAK,MAAM,mBAAmB,CAAA;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAErE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAI9C;;;GAGG;AACH,qBAAa,cAAe,SAAQ,eAAe;IAC1C,EAAE,EAAE,MAAM,GAAG,IAAI,CAAO;IACxB,MAAM,EAAE,MAAM,CAAK;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAO;IAE3C,MAAM,CAAC,MAAM;;;;MAIZ;WAEY,gBAAgB,CAAC,GAAG,EAAE,MAAM;IAUzC,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM;IAajC;;;;;;OAMG;WACU,gBAAgB,CAC3B,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAChD,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;gBA+BzB,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK;IAI7D;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAMjD;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAK/B;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAKxC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAK7B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcpC;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK3C;;;;OAIG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK5C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAY9B;;;;OAIG;IACG,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBpD;;;;;OAKG;IACG,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAc,GAAG,OAAO,CAAC,MAAM,CAAC;CAc5E"}
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import * as Crypto from 'node:crypto';
|
|
3
3
|
import { SwitchbotDevice } from '../device.js';
|
|
4
|
+
import { WoSmartLockProCommands } from '../settings.js';
|
|
4
5
|
import { SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from '../types/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Class representing a WoSmartLockPro device.
|
|
8
|
+
* @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/lock.md
|
|
9
|
+
*/
|
|
5
10
|
export class WoSmartLockPro extends SwitchbotDevice {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
static COMMAND_GET_CK_IV = '570f2103';
|
|
10
|
-
static COMMAND_LOCK_INFO = '570f4f8102';
|
|
11
|
-
static COMMAND_UNLOCK = '570f4e0101000080';
|
|
12
|
-
static COMMAND_UNLOCK_NO_UNLATCH = '570f4e01010000a0';
|
|
13
|
-
static COMMAND_LOCK = '570f4e0101000000';
|
|
14
|
-
static COMMAND_ENABLE_NOTIFICATIONS = '570e01001e00008101';
|
|
15
|
-
static COMMAND_DISABLE_NOTIFICATIONS = '570e00';
|
|
11
|
+
iv = null;
|
|
12
|
+
key_id = '';
|
|
13
|
+
encryption_key = null;
|
|
16
14
|
static Result = {
|
|
17
15
|
ERROR: 0x00,
|
|
18
16
|
SUCCESS: 0x01,
|
|
19
17
|
SUCCESS_LOW_BATTERY: 0x06,
|
|
20
18
|
};
|
|
21
|
-
static validateResponse(res) {
|
|
19
|
+
static async validateResponse(res) {
|
|
22
20
|
if (res.length >= 3) {
|
|
23
21
|
const result = res.readUInt8(0);
|
|
24
22
|
if (result === WoSmartLockPro.Result.SUCCESS || result === WoSmartLockPro.Result.SUCCESS_LOW_BATTERY) {
|
|
@@ -39,9 +37,16 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
39
37
|
};
|
|
40
38
|
return statusMap[code] || 'UNKNOWN';
|
|
41
39
|
}
|
|
42
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Parses the service data from the SwitchBot Strip Light.
|
|
42
|
+
* @param {Buffer} serviceData - The service data buffer.
|
|
43
|
+
* @param {Buffer} manufacturerData - The manufacturer data buffer.
|
|
44
|
+
* @param {Function} emitLog - The function to emit log messages.
|
|
45
|
+
* @returns {Promise<lockProServiceData | null>} - Parsed service data or null if invalid.
|
|
46
|
+
*/
|
|
47
|
+
static async parseServiceData(serviceData, manufacturerData, emitLog) {
|
|
43
48
|
if (manufacturerData.length < 11) {
|
|
44
|
-
|
|
49
|
+
emitLog('debugerror', `[parseServiceDataForWoSmartLockPro] Buffer length ${manufacturerData.length} is too short!`);
|
|
45
50
|
return null;
|
|
46
51
|
}
|
|
47
52
|
const byte2 = serviceData.readUInt8(2);
|
|
@@ -75,16 +80,16 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
75
80
|
* @param {string} encryptionKey - The encryption key.
|
|
76
81
|
*/
|
|
77
82
|
async setKey(keyId, encryptionKey) {
|
|
78
|
-
this.
|
|
79
|
-
this.
|
|
80
|
-
this.
|
|
83
|
+
this.iv = null;
|
|
84
|
+
this.key_id = keyId;
|
|
85
|
+
this.encryption_key = Buffer.from(encryptionKey, 'hex');
|
|
81
86
|
}
|
|
82
87
|
/**
|
|
83
88
|
* Unlocks the Smart Lock.
|
|
84
89
|
* @returns {Promise<number>} - The result of the unlock operation.
|
|
85
90
|
*/
|
|
86
91
|
async unlock() {
|
|
87
|
-
const resBuf = await this.operateLockPro(
|
|
92
|
+
const resBuf = await this.operateLockPro(WoSmartLockProCommands.UNLOCK);
|
|
88
93
|
return resBuf ? WoSmartLockPro.validateResponse(resBuf) : WoSmartLockPro.Result.ERROR;
|
|
89
94
|
}
|
|
90
95
|
/**
|
|
@@ -92,7 +97,7 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
92
97
|
* @returns {Promise<number>} - The result of the unlock operation.
|
|
93
98
|
*/
|
|
94
99
|
async unlockNoUnlatch() {
|
|
95
|
-
const resBuf = await this.operateLockPro(
|
|
100
|
+
const resBuf = await this.operateLockPro(WoSmartLockProCommands.UNLOCK_NO_UNLATCH);
|
|
96
101
|
return resBuf ? WoSmartLockPro.validateResponse(resBuf) : WoSmartLockPro.Result.ERROR;
|
|
97
102
|
}
|
|
98
103
|
/**
|
|
@@ -100,7 +105,7 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
100
105
|
* @returns {Promise<number>} - The result of the lock operation.
|
|
101
106
|
*/
|
|
102
107
|
async lock() {
|
|
103
|
-
const resBuf = await this.operateLockPro(
|
|
108
|
+
const resBuf = await this.operateLockPro(WoSmartLockProCommands.LOCK);
|
|
104
109
|
return resBuf ? WoSmartLockPro.validateResponse(resBuf) : WoSmartLockPro.Result.ERROR;
|
|
105
110
|
}
|
|
106
111
|
/**
|
|
@@ -108,7 +113,7 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
108
113
|
* @returns {Promise<object | null>} - The state object or null if an error occurred.
|
|
109
114
|
*/
|
|
110
115
|
async info() {
|
|
111
|
-
const resBuf = await this.operateLockPro(
|
|
116
|
+
const resBuf = await this.operateLockPro(WoSmartLockProCommands.LOCK_INFO);
|
|
112
117
|
if (resBuf) {
|
|
113
118
|
return {
|
|
114
119
|
calibration: Boolean(resBuf[0] & 0b10000000),
|
|
@@ -126,7 +131,7 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
126
131
|
* @returns {Promise<string>} - The encrypted string in hex format.
|
|
127
132
|
*/
|
|
128
133
|
async encrypt(str) {
|
|
129
|
-
const cipher = Crypto.createCipheriv('aes-128-ctr', this.
|
|
134
|
+
const cipher = Crypto.createCipheriv('aes-128-ctr', this.encryption_key, this.iv);
|
|
130
135
|
return Buffer.concat([cipher.update(str, 'hex'), cipher.final()]).toString('hex');
|
|
131
136
|
}
|
|
132
137
|
/**
|
|
@@ -135,7 +140,7 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
135
140
|
* @returns {Promise<Buffer>} - The decrypted data.
|
|
136
141
|
*/
|
|
137
142
|
async decrypt(data) {
|
|
138
|
-
const decipher = Crypto.createDecipheriv('aes-128-ctr', this.
|
|
143
|
+
const decipher = Crypto.createDecipheriv('aes-128-ctr', this.encryption_key, this.iv);
|
|
139
144
|
return Buffer.concat([decipher.update(data), decipher.final()]);
|
|
140
145
|
}
|
|
141
146
|
/**
|
|
@@ -143,16 +148,16 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
143
148
|
* @returns {Promise<Buffer>} - The IV buffer.
|
|
144
149
|
*/
|
|
145
150
|
async getIv() {
|
|
146
|
-
if (!this.
|
|
147
|
-
const res = await this.operateLockPro(
|
|
151
|
+
if (!this.iv) {
|
|
152
|
+
const res = await this.operateLockPro(WoSmartLockProCommands.GET_CKIV + this.key_id, false);
|
|
148
153
|
if (res) {
|
|
149
|
-
this.
|
|
154
|
+
this.iv = res.subarray(4);
|
|
150
155
|
}
|
|
151
156
|
else {
|
|
152
157
|
throw new Error('Failed to retrieve IV from the device.');
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
|
-
return this.
|
|
160
|
+
return this.iv;
|
|
156
161
|
}
|
|
157
162
|
/**
|
|
158
163
|
* Sends an encrypted command to the device.
|
|
@@ -161,11 +166,11 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
161
166
|
*/
|
|
162
167
|
async encryptedCommand(key) {
|
|
163
168
|
const iv = await this.getIv();
|
|
164
|
-
const req = Buffer.from(key.substring(0, 2) + this.
|
|
169
|
+
const req = Buffer.from(key.substring(0, 2) + this.key_id + Buffer.from(iv.subarray(0, 2)).toString('hex') + await this.encrypt(key.substring(2)), 'hex');
|
|
165
170
|
const bytes = await this.command(req);
|
|
166
171
|
const buf = Buffer.from(bytes);
|
|
167
172
|
const code = WoSmartLockPro.validateResponse(buf);
|
|
168
|
-
if (code !== WoSmartLockPro.Result.ERROR) {
|
|
173
|
+
if (await code !== WoSmartLockPro.Result.ERROR) {
|
|
169
174
|
return Buffer.concat([buf.subarray(0, 1), await this.decrypt(buf.subarray(4))]);
|
|
170
175
|
}
|
|
171
176
|
else {
|
|
@@ -186,7 +191,7 @@ export class WoSmartLockPro extends SwitchbotDevice {
|
|
|
186
191
|
const bytes = await this.command(req);
|
|
187
192
|
const buf = Buffer.from(bytes);
|
|
188
193
|
const code = WoSmartLockPro.validateResponse(buf);
|
|
189
|
-
if (code === WoSmartLockPro.Result.ERROR) {
|
|
194
|
+
if (await code === WoSmartLockPro.Result.ERROR) {
|
|
190
195
|
throw new Error(`The device returned an error: 0x${buf.toString('hex')}`);
|
|
191
196
|
}
|
|
192
197
|
return buf;
|