ttlock-sdk-js
Version:
JavaScript port of the TTLock Android SDK
1,331 lines • 50.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.TTLock = void 0;
const AudioManage_1 = require("../constant/AudioManage");
const FeatureValue_1 = require("../constant/FeatureValue");
const Lock_1 = require("../constant/Lock");
const LockedStatus_1 = require("../constant/LockedStatus");
const PassageModeOperate_1 = require("../constant/PassageModeOperate");
const timingUtil_1 = require("../util/timingUtil");
const TTLockApi_1 = require("./TTLockApi");
class TTLock extends TTLockApi_1.TTLockApi {
constructor(device, data) {
super(device, data);
this.skipDataRead = false;
this.connecting = false;
this.connected = false;
this.device.on("connected", this.onConnected.bind(this));
this.device.on("disconnected", this.onDisconnected.bind(this));
this.device.on("updated", this.onTTDeviceUpdated.bind(this));
this.device.on("dataReceived", this.onDataReceived.bind(this));
}
getAddress() {
return this.device.address;
}
getName() {
return this.device.name;
}
getManufacturer() {
return this.device.manufacturer;
}
getModel() {
return this.device.model;
}
getFirmware() {
return this.device.firmware;
}
getBattery() {
return this.batteryCapacity;
}
getRssi() {
return this.rssi;
}
async connect(skipDataRead = false, timeout = 15) {
if (this.connecting) {
console.log("Connect allready in progress");
return false;
}
if (this.connected) {
return true;
}
this.connecting = true;
this.skipDataRead = skipDataRead;
const connected = await this.device.connect();
let timeoutCycles = timeout * 10;
if (connected) {
console.log("Lock waiting for connection to be completed");
do {
await timingUtil_1.sleep(100);
timeoutCycles--;
} while (!this.connected && timeoutCycles > 0 && this.connecting);
}
else {
console.log("Lock connect failed");
}
this.skipDataRead = false;
this.connecting = false;
// it is possible that even tho device initially connected, reading initial data will disconnect
return this.connected;
}
isConnected() {
return this.connected;
}
async disconnect() {
await this.device.disconnect();
}
isInitialized() {
return this.initialized;
}
isPaired() {
const privateData = this.privateData;
if (privateData.aesKey && privateData.admin && privateData.admin.adminPs && privateData.admin.unlockKey) {
return true;
}
else {
return false;
}
}
hasLockSound() {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.AUDIO_MANAGEMENT)) {
return true;
}
return false;
}
hasPassCode() {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.PASSCODE)) {
return true;
}
return false;
}
hasICCard() {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.IC)) {
return true;
}
return false;
}
hasFingerprint() {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.FINGER_PRINT)) {
return true;
}
return false;
}
hasAutolock() {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.AUTO_LOCK)) {
return true;
}
return false;
}
hasNewEvents() {
return this.newEvents;
}
/**
* Initialize and pair with a new lock
*/
async initLock() {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (this.initialized) {
throw new Error("Lock is not in pairing mode");
}
// TODO: also check if lock is already inited (has AES key)
try {
// Init
console.log("========= init");
await this.initCommand();
console.log("========= init");
// Get AES key
console.log("========= AES key");
const aesKey = await this.getAESKeyCommand();
console.log("========= AES key:", aesKey.toString("hex"));
// Add admin
console.log("========= admin");
const admin = await this.addAdminCommand(aesKey);
console.log("========= admin:", admin);
// Calibrate time
// this seems to fail on some locks
// see https://github.com/kind3r/hass-addons/issues/11
try {
console.log("========= time");
await this.calibrateTimeCommand(aesKey);
console.log("========= time");
}
catch (error) {
console.error(error);
}
// Search device features
console.log("========= feature list");
const featureList = await this.searchDeviceFeatureCommand(aesKey);
console.log("========= feature list", featureList);
let switchState, lockSound, displayPasscode, autoLockTime, lightingTime, adminPasscode, pwdInfo, remoteUnlock;
// Feature depended queries
// if (featureList.has(FeatureValue.RESET_BUTTON)
// || featureList.has(FeatureValue.TAMPER_ALERT)
// || featureList.has(FeatureValue.PRIVACK_LOCK)) {
// console.log("========= switchState");
// switchState = await this.getSwitchStateCommand(undefined, aesKey);
// console.log("========= switchState:", switchState);
// }
if (featureList.has(FeatureValue_1.FeatureValue.AUDIO_MANAGEMENT)) {
console.log("========= lockSound");
try {
lockSound = await this.audioManageCommand(undefined, aesKey);
}
catch (error) {
// this sometimes fails
console.error(error);
}
console.log("========= lockSound:", lockSound);
}
if (featureList.has(FeatureValue_1.FeatureValue.PASSWORD_DISPLAY_OR_HIDE)) {
console.log("========= displayPasscode");
displayPasscode = await this.screenPasscodeManageCommand(undefined, aesKey);
console.log("========= displayPasscode:", displayPasscode);
}
if (featureList.has(FeatureValue_1.FeatureValue.AUTO_LOCK)) {
console.log("========= autoLockTime");
autoLockTime = await this.searchAutoLockTimeCommand(undefined, aesKey);
console.log("========= autoLockTime:", autoLockTime);
}
// if (featureList.has(FeatureValue.LAMP)) {
// console.log("========= lightingTime");
// lightingTime = await this.controlLampCommand(undefined, aesKey);
// console.log("========= lightingTime:", lightingTime);
// }
if (featureList.has(FeatureValue_1.FeatureValue.GET_ADMIN_CODE)) {
// Command.COMM_GET_ADMIN_CODE
console.log("========= getAdminCode");
adminPasscode = await this.getAdminCodeCommand(aesKey);
console.log("========= getAdminCode", adminPasscode);
if (adminPasscode == "") {
console.log("========= set adminPasscode");
adminPasscode = await this.setAdminKeyboardPwdCommand(undefined, aesKey);
console.log("========= set adminPasscode:", adminPasscode);
}
}
else if (this.device.lockType == Lock_1.LockType.LOCK_TYPE_V3_CAR) {
// Command.COMM_GET_ALARM_ERRCORD_OR_OPERATION_FINISHED
}
else if (this.device.lockType == Lock_1.LockType.LOCK_TYPE_V3) {
console.log("========= set adminPasscode");
adminPasscode = await this.setAdminKeyboardPwdCommand(undefined, aesKey);
console.log("========= set adminPasscode:", adminPasscode);
}
// console.log("========= init passwords");
// pwdInfo = await this.initPasswordsCommand(aesKey);
// console.log("========= init passwords:", pwdInfo);
if (featureList.has(FeatureValue_1.FeatureValue.CONFIG_GATEWAY_UNLOCK)) {
console.log("========= remoteUnlock");
remoteUnlock = await this.controlRemoteUnlockCommand(undefined, aesKey);
console.log("========= remoteUnlock:", remoteUnlock);
}
console.log("========= finished");
await this.operateFinishedCommand(aesKey);
console.log("========= finished");
// save all the data we gathered during init sequence
if (aesKey)
this.privateData.aesKey = Buffer.from(aesKey);
if (admin)
this.privateData.admin = admin;
if (featureList)
this.featureList = featureList;
if (switchState)
this.switchState = switchState;
if (lockSound)
this.lockSound = lockSound;
if (displayPasscode)
this.displayPasscode = displayPasscode;
if (autoLockTime)
this.autoLockTime = autoLockTime;
if (lightingTime)
this.lightingTime = lightingTime;
if (adminPasscode)
this.privateData.adminPasscode = adminPasscode;
if (pwdInfo)
this.privateData.pwdInfo = pwdInfo;
if (remoteUnlock)
this.remoteUnlock = remoteUnlock;
this.lockedStatus = LockedStatus_1.LockedStatus.LOCKED; // always locked by default
// read device information
console.log("========= device info");
try {
this.deviceInfo = await this.macro_readAllDeviceInfo(aesKey);
}
catch (error) {
// this sometimes fails
console.error(error);
}
console.log("========= device info:", this.deviceInfo);
}
catch (error) {
console.error("Error while initialising lock", error);
return false;
}
// TODO: we should now refresh the device's data (disconnect and reconnect maybe ?)
this.initialized = true;
this.emit("dataUpdated", this);
return true;
}
/**
* Lock the lock
*/
async lock() {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
try {
console.log("========= check user time");
const psFromLock = await this.checkUserTime();
console.log("========= check user time", psFromLock);
console.log("========= lock");
const lockData = await this.lockCommand(psFromLock);
console.log("========= lock", lockData);
this.lockedStatus = LockedStatus_1.LockedStatus.LOCKED;
this.emit("locked", this);
}
catch (error) {
console.error("Error locking the lock", error);
return false;
}
return true;
}
/**
* Unlock the lock
*/
async unlock() {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
try {
console.log("========= check user time");
const psFromLock = await this.checkUserTime();
console.log("========= check user time", psFromLock);
console.log("========= unlock");
const unlockData = await this.unlockCommand(psFromLock);
console.log("========= unlock", unlockData);
this.lockedStatus = LockedStatus_1.LockedStatus.UNLOCKED;
this.emit("unlocked", this);
// if autolock is on, then emit locked event after the timeout has passed
if (this.autoLockTime > 0) {
setTimeout(() => {
this.lockedStatus = LockedStatus_1.LockedStatus.LOCKED;
this.emit("locked", this);
}, this.autoLockTime * 1000);
}
}
catch (error) {
console.error("Error unlocking the lock", error);
return false;
}
return true;
}
/**
* Get the status of the lock (locked or unlocked)
*/
async getLockStatus(noCache = false) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
const oldStatus = this.lockedStatus;
if (noCache || this.lockedStatus == LockedStatus_1.LockedStatus.UNKNOWN) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
console.log("========= check lock status");
this.lockedStatus = await this.searchBycicleStatusCommand();
console.log("========= check lock status", this.lockedStatus);
}
catch (error) {
console.error("Error getting lock status", error);
}
}
if (oldStatus != this.lockedStatus) {
if (this.lockedStatus == LockedStatus_1.LockedStatus.LOCKED) {
this.emit("locked", this);
}
else {
this.emit("unlocked", this);
}
}
return this.lockedStatus;
}
async getAutolockTime(noCache = false) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
const oldAutoLockTime = this.autoLockTime;
if (noCache || this.autoLockTime == -1) {
if (typeof this.featureList != "undefined") {
if (this.featureList.has(FeatureValue_1.FeatureValue.AUTO_LOCK)) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= autoLockTime");
this.autoLockTime = await this.searchAutoLockTimeCommand();
console.log("========= autoLockTime:", this.autoLockTime);
}
}
catch (error) {
console.error(error);
}
}
}
}
if (oldAutoLockTime != this.autoLockTime) {
this.emit("dataUpdated", this);
}
return this.autoLockTime;
}
async setAutoLockTime(autoLockTime) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (this.autoLockTime != autoLockTime) {
if (typeof this.featureList != "undefined") {
if (this.featureList.has(FeatureValue_1.FeatureValue.AUTO_LOCK)) {
try {
if (await this.macro_adminLogin()) {
console.log("========= autoLockTime");
await this.searchAutoLockTimeCommand(autoLockTime);
console.log("========= autoLockTime");
this.autoLockTime = autoLockTime;
this.emit("dataUpdated", this);
return true;
}
}
catch (error) {
console.error(error);
}
}
}
}
return false;
}
async getLockSound(noCache = false) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
const oldSound = this.lockSound;
if (noCache || this.lockSound == AudioManage_1.AudioManage.UNKNOWN) {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.AUDIO_MANAGEMENT)) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
console.log("========= lockSound");
this.lockSound = await this.audioManageCommand();
console.log("========= lockSound:", this.lockSound);
}
catch (error) {
console.error("Error getting lock sound status", error);
}
}
}
if (oldSound != this.lockSound) {
this.emit("dataUpdated", this);
}
return this.lockSound;
}
async setLockSound(lockSound) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (this.lockSound != lockSound) {
if (typeof this.featureList != "undefined" && this.featureList.has(FeatureValue_1.FeatureValue.AUDIO_MANAGEMENT)) {
try {
if (await this.macro_adminLogin()) {
console.log("========= lockSound");
this.lockSound = await this.audioManageCommand(lockSound);
console.log("========= lockSound:", this.lockSound);
this.emit("dataUpdated", this);
return true;
}
}
catch (error) {
console.error(error);
}
}
}
return false;
}
async resetLock() {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= reset");
await this.resetLockCommand();
console.log("========= reset");
}
else {
return false;
}
}
catch (error) {
console.error("Error while reseting the lock", error);
return false;
}
await this.disconnect();
this.emit("lockReset", this.device.address, this.device.id);
return true;
}
async getPassageMode() {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
let data = [];
try {
if (await this.macro_adminLogin()) {
let sequence = 0;
do {
console.log("========= get passage mode");
const response = await this.getPassageModeCommand(sequence);
console.log("========= get passage mode", response);
sequence = response.sequence;
response.data.forEach((passageData) => {
data.push(passageData);
});
} while (sequence != -1);
}
}
catch (error) {
console.error("Error while getting passage mode", error);
}
return data;
}
async setPassageMode(data) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= set passage mode");
await this.setPassageModeCommand(data);
console.log("========= set passage mode");
}
else {
return false;
}
}
catch (error) {
console.error("Error while getting passage mode", error);
return false;
}
return true;
}
async deletePassageMode(data) {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= delete passage mode");
await this.setPassageModeCommand(data, PassageModeOperate_1.PassageModeOperate.DELETE);
console.log("========= delete passage mode");
}
}
catch (error) {
console.error("Error while deleting passage mode", error);
return false;
}
return true;
}
async clearPassageMode() {
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= clear passage mode");
await this.clearPassageModeCommand();
console.log("========= clear passage mode");
}
else {
return false;
}
}
catch (error) {
console.error("Error while deleting passage mode", error);
return false;
}
return true;
}
/**
* Add a new passcode to unlock
* @param type PassCode type: 1 - permanent, 2 - one time, 3 - limited time
* @param passCode 4-9 digits code
* @param startDate Valid from YYYYMMDDHHmm
* @param endDate Valid to YYYYMMDDHHmm
*/
async addPassCode(type, passCode, startDate, endDate) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasPassCode()) {
throw new Error("No PassCode support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= add passCode");
const result = await this.createCustomPasscodeCommand(type, passCode, startDate, endDate);
console.log("========= add passCode", result);
return result;
}
else {
return false;
}
}
catch (error) {
console.error("Error while adding passcode", error);
return false;
}
}
/**
* Update a passcode to unlock
* @param type PassCode type: 1 - permanent, 2 - one time, 3 - limited time
* @param oldPassCode 4-9 digits code - old code
* @param newPassCode 4-9 digits code - new code
* @param startDate Valid from YYYYMMDDHHmm
* @param endDate Valid to YYYYMMDDHHmm
*/
async updatePassCode(type, oldPassCode, newPassCode, startDate, endDate) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasPassCode()) {
throw new Error("No PassCode support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= update passCode");
const result = await this.updateCustomPasscodeCommand(type, oldPassCode, newPassCode, startDate, endDate);
console.log("========= update passCode", result);
return result;
}
else {
return false;
}
}
catch (error) {
console.error("Error while updating passcode", error);
return false;
}
}
/**
* Delete a set passcode
* @param type PassCode type: 1 - permanent, 2 - one time, 3 - limited time
* @param passCode 4-9 digits code
*/
async deletePassCode(type, passCode) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasPassCode()) {
throw new Error("No PassCode support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= delete passCode");
const result = await this.deleteCustomPasscodeCommand(type, passCode);
console.log("========= delete passCode", result);
return result;
}
else {
return false;
}
}
catch (error) {
console.error("Error while deleting passcode", error);
return false;
}
}
/**
* Remove all stored passcodes
*/
async clearPassCodes() {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasPassCode()) {
throw new Error("No PassCode support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= clear passCodes");
const result = await this.clearCustomPasscodesCommand();
console.log("========= clear passCodes", result);
return result;
}
else {
return false;
}
}
catch (error) {
console.error("Error while clearing passcodes", error);
return false;
}
}
/**
* Get all valid passcodes
*/
async getPassCodes() {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasPassCode()) {
throw new Error("No PassCode support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = [];
try {
if (await this.macro_adminLogin()) {
let sequence = 0;
do {
console.log("========= get passCodes", sequence);
const response = await this.getCustomPasscodesCommand(sequence);
console.log("========= get passCodes", response);
sequence = response.sequence;
response.data.forEach((passageData) => {
data.push(passageData);
});
} while (sequence != -1);
}
}
catch (error) {
console.error("Error while getting passCodes", error);
}
return data;
}
/**
* Add an IC Card
* @param startDate Valid from YYYYMMDDHHmm
* @param endDate Valid to YYYYMMDDHHmm
* @param cardNumber serial number of an already known card
* @returns serial number of the card that was added
*/
async addICCard(startDate, endDate, cardNumber) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasICCard()) {
throw new Error("No IC Card support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = "";
try {
if (await this.macro_adminLogin()) {
console.log("========= add IC Card");
if (typeof cardNumber != "undefined") {
const addedCardNumber = await this.addICCommand(cardNumber, startDate, endDate);
console.log("========= add IC Card", addedCardNumber);
}
else {
const addedCardNumber = await this.addICCommand();
console.log("========= updating IC Card", addedCardNumber);
const response = await this.updateICCommand(addedCardNumber, startDate, endDate);
console.log("========= updating IC Card", response);
data = addedCardNumber;
}
}
}
catch (error) {
console.error("Error while adding IC Card", error);
}
return data;
}
/**
* Update an IC Card
* @param cardNumber Serial number of the card
* @param startDate Valid from YYYYMMDDHHmm
* @param endDate Valid to YYYYMMDDHHmm
*/
async updateICCard(cardNumber, startDate, endDate) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasICCard()) {
throw new Error("No IC Card support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = false;
try {
if (await this.macro_adminLogin()) {
console.log("========= updating IC Card", cardNumber);
const response = await this.updateICCommand(cardNumber, startDate, endDate);
console.log("========= updating IC Card", response);
data = response;
}
}
catch (error) {
console.error("Error while updating IC Card", error);
}
return data;
}
/**
* Delete an IC Card
* @param cardNumber Serial number of the card
*/
async deleteICCard(cardNumber) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasICCard()) {
throw new Error("No IC Card support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = false;
try {
if (await this.macro_adminLogin()) {
console.log("========= updating IC Card", cardNumber);
const response = await this.deleteICCommand(cardNumber);
console.log("========= updating IC Card", response);
data = response;
}
}
catch (error) {
console.error("Error while adding IC Card", error);
}
return data;
}
/**
* Clear all IC Card data
*/
async clearICCards() {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasICCard()) {
throw new Error("No IC Card support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = false;
try {
if (await this.macro_adminLogin()) {
console.log("========= clearing IC Cards");
const response = await this.clearICCommand();
console.log("========= clearing IC Cards", response);
data = response;
}
}
catch (error) {
console.error("Error while clearing IC Cards", error);
}
return data;
}
/**
* Get all valid IC cards and their validity interval
*/
async getICCards() {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasICCard()) {
throw new Error("No IC Card support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = [];
try {
if (await this.macro_adminLogin()) {
let sequence = 0;
do {
console.log("========= get IC Cards", sequence);
const response = await this.getICCommand(sequence);
console.log("========= get IC Cards", response);
sequence = response.sequence;
response.data.forEach((card) => {
data.push(card);
});
} while (sequence != -1);
}
}
catch (error) {
console.error("Error while getting IC Cards", error);
}
return data;
}
/**
* Add a Fingerprint
* @param startDate Valid from YYYYMMDDHHmm
* @param endDate Valid to YYYYMMDDHHmm
* @returns serial number of the firngerprint that was added
*/
async addFingerprint(startDate, endDate) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasFingerprint()) {
throw new Error("No fingerprint support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = "";
try {
if (await this.macro_adminLogin()) {
console.log("========= add Fingerprint");
const fpNumber = await this.addFRCommand();
console.log("========= updating Fingerprint", fpNumber);
const response = await this.updateFRCommand(fpNumber, startDate, endDate);
console.log("========= updating Fingerprint", response);
data = fpNumber;
}
}
catch (error) {
console.error("Error while adding Fingerprint", error);
}
return data;
}
/**
* Update a fingerprint
* @param fpNumber Serial number of the fingerprint
* @param startDate Valid from YYYYMMDDHHmm
* @param endDate Valid to YYYYMMDDHHmm
*/
async updateFingerprint(fpNumber, startDate, endDate) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasFingerprint()) {
throw new Error("No fingerprint support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = false;
try {
if (await this.macro_adminLogin()) {
console.log("========= updating Fingerprint", fpNumber);
const response = await this.updateFRCommand(fpNumber, startDate, endDate);
console.log("========= updating Fingerprint", response);
data = response;
}
}
catch (error) {
console.error("Error while updating Fingerprint", error);
}
return data;
}
/**
* Delete a fingerprint
* @param fpNumber Serial number of the fingerprint
*/
async deleteFingerprint(fpNumber) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasFingerprint()) {
throw new Error("No fingerprint support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = false;
try {
if (await this.macro_adminLogin()) {
console.log("========= updating Fingerprint", fpNumber);
const response = await this.deleteFRCommand(fpNumber);
console.log("========= updating Fingerprint", response);
data = response;
}
}
catch (error) {
console.error("Error while adding Fingerprint", error);
}
return data;
}
/**
* Clear all fingerprint data
*/
async clearFingerprints() {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasFingerprint()) {
throw new Error("No fingerprint support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = false;
try {
if (await this.macro_adminLogin()) {
console.log("========= clearing Fingerprints");
const response = await this.clearFRCommand();
console.log("========= clearing Fingerprints", response);
data = response;
}
}
catch (error) {
console.error("Error while clearing Fingerprints", error);
}
return data;
}
/**
* Get all valid IC cards and their validity interval
*/
async getFingerprints() {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.hasFingerprint()) {
throw new Error("No fingerprint support");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let data = [];
try {
if (await this.macro_adminLogin()) {
let sequence = 0;
do {
console.log("========= get Fingerprints", sequence);
const response = await this.getFRCommand(sequence);
console.log("========= get Fingerprints", response);
sequence = response.sequence;
response.data.forEach((fingerprint) => {
data.push(fingerprint);
});
} while (sequence != -1);
}
}
catch (error) {
console.error("Error while getting Fingerprints", error);
}
return data;
}
/**
* No ideea what this does ...
* @param type
*/
async setRemoteUnlock(type) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (typeof this.featureList == "undefined") {
throw new Error("Lock features missing");
}
if (!this.featureList.has(FeatureValue_1.FeatureValue.CONFIG_GATEWAY_UNLOCK)) {
throw new Error("Lock does not support remote unlock");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
try {
if (await this.macro_adminLogin()) {
console.log("========= remoteUnlock");
if (typeof type != "undefined") {
this.remoteUnlock = await this.controlRemoteUnlockCommand(type);
}
else {
this.remoteUnlock = await this.controlRemoteUnlockCommand();
}
console.log("========= remoteUnlock:", this.remoteUnlock);
}
}
catch (error) {
console.error("Error on remote unlock", error);
}
return this.remoteUnlock;
}
async getOperationLog(all = false, noCache = false) {
if (!this.initialized) {
throw new Error("Lock is in pairing mode");
}
if (!this.isConnected()) {
throw new Error("Lock is not connected");
}
let newOperations = [];
// in all mode do the following
// - get new operations
// - sort operation log by recordNumber
// - create list of missing/invalid recordNumber
// - fetch those records
const maxRetry = 3;
// first, always get new operations
if (this.hasNewEvents()) {
let sequence = 0xffff;
let retry = 0;
do {
console.log("========= get OperationLog", sequence);
try {
const response = await this.getOperationLogCommand(sequence);
sequence = response.sequence;
for (let log of response.data) {
if (log) {
newOperations.push(log);
this.operationLog[log.recordNumber] = log;
}
}
retry = 0;
}
catch (error) {
retry++;
}
} while (sequence > 0 && retry < maxRetry);
}
// if all operations were requested
if (all) {
let operations = [];
let maxRecordNumber = 0;
if (noCache) {
// if cache will not be used start with only the new operations
for (let log of newOperations) {
if (log) {
operations[log.recordNumber] = log;
if (log.recordNumber > maxRecordNumber) {
maxRecordNumber = log.recordNumber;
}
}
}
}
else {
// otherwise copy current operation log
for (let log of this.operationLog) {
if (log) {
operations[log.recordNumber] = log;
if (log.recordNumber > maxRecordNumber) {
maxRecordNumber = log.recordNumber;
}
}
}
}
if (operations.length == 0) {
// if no operations, start with 0 and keep going
let sequence = 0;
let failedSequences = 0;
let retry = 0;
do {
console.log("========= get OperationLog", sequence);
try {
const response = await this.getOperationLogCommand(sequence);
sequence = response.sequence;
console.log("========= get OperationLog next seq", sequence);
for (let log of response.data) {
operations[log.recordNumber] = log;
}
retry = 0;
}
catch (error) {
retry++;
// some operations just can't be read
if (retry == maxRetry) {
console.log("========= get OperationLog skip seq", sequence);
sequence++;
failedSequences++;
retry = 0;
}
}
} while (sequence > 0 && retry < maxRetry);
}
else {
// if we have operations, check for missing
let missing = [];
for (let i = 0; i < maxRecordNumber; i++) {
if (typeof operations[i] == "undefined" || operations[i] == null) {
missing.push(i);
}
}
for (let sequence of missing) {
let retry = 0;
let success = false;
do {
console.log("========= get OperationLog", sequence);
try {
const response = await this.getOperationLogCommand(sequence);
for (let log of response.data) {
operations[log.recordNumber] = log;
}
retry = 0;
success = true;
}
catch (error) {
retry++;
}
} while (!success && retry < maxRetry);
}
}
this.operationLog = operations;
this.emit("dataUpdated", this);
return this.operationLog;
}
else {
if (newOperations.length > 0) {
this.emit("dataUpdated", this);
}
return newOperations;
}
}
onDataReceived(command) {
// is this just a notification (like the lock was locked/unlocked etc.)
if (this.privateData.aesKey) {
command.setAesKey(this.privateData.aesKey);
const data = command.getCommand().getRawData();
console.log("Received:", command);
if (data) {
console.log("Data", data.toString("hex"));
}
}
else {
console.error("Unable to decrypt notification, no AES key");
}
}
async onConnected() {
if (this.isPaired() && !this.skipDataRead) {
// read general data
console.log("Connected to known lock, reading general data");
try {
if (typeof this.featureList == "undefined") {
// Search device features
console.log("========= feature list");
this.featureList = await this.searchDeviceFeatureCommand();
console.log("========= feature list", this.featureList);
}
// Auto lock time
if (this.featureList.has(FeatureValue_1.FeatureValue.AUTO_LOCK) && this.autoLockTime == -1 && await this.macro_adminLogin()) {
console.log("========= autoLockTime");
this.autoLockTime = await this.searchAutoLockTimeCommand();
console.log("========= autoLockTime:", this.autoLockTime);
}
if (this.lockedStatus == LockedStatus_1.LockedStatus.UNKNOWN) {
// Locked/unlocked status
console.log("========= check lock status");
this.lockedStatus = await this.searchBycicleStatusCommand();
console.log("========= check lock status", this.lockedStatus);
}
if (this.featureList.has(FeatureValue_1.FeatureValue.AUDIO_MANAGEMENT) && this.lockSound == AudioManage_1.AudioManage.UNKNOWN) {
console.log("========= lockSound");
this.lockSound = await this.audioManageCommand();
console.log("========= lockSound:", this.lockSound);
}
}
catch (error) {
console.error("Failed reading all general data from lock", error);
// TODO: judge the error and fail connect
}
}
else {
if (this.device.isUnlock) {
this.lockedStatus = LockedStatus_1.LockedStatus.UNLOCKED;
}
else {
this.lockedStatus = LockedStatus_1.LockedStatus.LOCKED;
}
}
// are we still connected ? It is possible the lock will disconnect while reading general data
if (this.device.connected) {
this.connected = true;
this.emit("connected", this);
}
}
async onDisconnected() {
this.connected = false;
this.adminAuth = false;
this.connecting = false;
this.emit("disconnected", this);
}
async onTTDeviceUpdated() {
this.updateFromTTDevice();
}
getLockData() {
var _a;
if (this.isPaired()) {
const privateData = {
aesKey: (_a = this.privateData.aesKey) === null || _a === void 0 ? void 0 : _a.toString("hex"),
admin: this.privateData.admin,
adminPasscode: this.privateData.adminPasscode,
pwdInfo: this.privateData.pwdInfo
};
const data = {
address: this.device.address,
battery: this.batteryCapacity,
rssi: this.rssi,
autoLockTime: this.autoLockTime ? this.autoLockTime : -1,
lockedStatus: this.lockedStatus,
privateData: privateData,
operationLog: this.operationLog
};
return data;
}
}
/** Just for debugging */
toJSON(asObject = false) {
let json = this.device.toJSON(true);
if (this.featureList)
Reflect.set(json, 'featureList', this.featureList);
if (this.switchState)
Reflect.set(json, 'switchState', this.switchState);
if (this.lockSound)
Reflect.set(json, 'lockSound', this.lockSound);
if (this.displayPasscode)
Reflect.set(json, 'displayPasscode', this.displayPasscode);
if (this.autoLockTime)
Reflect.set(json, 'autoLockTime', this.autoLockTime);
if (this.lightingTime)
Reflect.set(json, 'lightingTime', this.lightingTime);
if (this.remoteUnlock)
Reflect.set(json, 'remoteUnlock', this.remoteUnlock);
if (this.deviceInfo)
Reflect.set(json, 'deviceInfo', this.deviceInfo);
const privateData = {};
if (this.privateData.aesKey)
Reflect.set(privateData, 'aesKey', this.privateData.aesKey.toString("hex"));
if (this.privateData.admin)
Reflect.set(privateData, 'admin', this.privateData.admin);
if (this.privateData.adminPasscode)
Reflect.set(privateData, 'adminPasscode', this.privateData.adminPasscode);
if (this.privateData