zklib-ts
Version:
Unofficial zkteco library allows Node.js developers to easily interface with ZK BioMetric Fingerprint Attendance Devices
803 lines (799 loc) • 34.8 kB
JavaScript
'use strict';
var net = require('net');
var command = require('./helper/command');
var handler = require('./exceptions/handler');
var utils = require('./helper/utils');
var user_service = require('./services/user.service');
var transaction_service = require('./services/transaction.service');
var options_service = require('./services/options.service');
class ZTCP {
/**
* @param_ip ip address of device
* @param_port port number of device
* @param_timeout connection timout
* @param_comm_key communication key of device (if the case)
* @return Zkteco TCP socket connection instance
*/
ip;
port;
timeout;
sessionId = 0;
replyId = 0;
socket;
comm_key;
user_count = 0;
fp_count = 0;
pwd_count = 0;
oplog_count = 0;
attlog_count = 0;
fp_cap = 0;
user_cap = 0;
attlog_cap = 0;
fp_av = 0;
user_av = 0;
attlog_av = 0;
face_count = 0;
face_cap = 0;
userPacketSize = 72;
verbose = false;
packetNumber = 0;
replyData = Buffer.from([]);
_optionsService;
_transactionService;
_userService;
constructor(ip, port, timeout, comm_key, verbose) {
this.ip = ip;
this.port = port;
this.timeout = timeout ? timeout : 10000;
this.replyId = 0;
this.comm_key = comm_key;
this.verbose = verbose;
this._optionsService = new options_service.OptionsService(this);
this._userService = new user_service.UserService(this);
this._transactionService = new transaction_service.TransactionService(this);
}
createSocket(cbError, cbClose) {
return new Promise((resolve, reject) => {
this.socket = new net.Socket();
// Handle socket error
this.socket.once("error", (err) => {
this.socket = undefined; // Ensure socket reference is cleared
reject(err);
if (typeof cbError === "function")
cbError(err);
});
// Handle successful connection
this.socket.once("connect", () => {
resolve(this.socket);
});
// Handle socket closure
this.socket.once("close", () => {
this.socket = undefined; // Ensure socket reference is cleared
if (typeof cbClose === "function")
cbClose("tcp");
});
// Set socket timeout if provided
if (this.timeout) {
this.socket.setTimeout(this.timeout);
}
// Initiate connection
this.socket.connect(this.port, this.ip);
});
}
async connect() {
try {
let reply = await this.executeCmd(command.COMMANDS.CMD_CONNECT, "");
if (reply.readUInt16LE(0) === command.COMMANDS.CMD_ACK_OK) {
return true;
}
if (reply.readUInt16LE(0) === command.COMMANDS.CMD_ACK_UNAUTH) {
const hashedCommkey = utils.authKey(this.comm_key, this.sessionId);
reply = await this.executeCmd(command.COMMANDS.CMD_AUTH, hashedCommkey);
if (reply.readUInt16LE(0) === command.COMMANDS.CMD_ACK_OK) {
return true;
}
else {
throw new Error("error de authenticacion");
}
}
else {
// No reply received; throw an error
throw new Error("NO_REPLY_ON_CMD_CONNECT");
}
}
catch (err) {
// Log the error for debugging, if necessary
console.error("Failed to connect:", err);
// Re-throw the error for handling by the caller
throw err;
}
}
async closeSocket() {
return new Promise((resolve, reject) => {
// If no socket is present, resolve immediately
if (!this.socket) {
return resolve(true);
}
// Clean up listeners to avoid potential memory leaks or duplicate handling
this.socket.removeAllListeners("data");
// Set a timeout to handle cases where socket.end might not resolve
const timer = setTimeout(() => {
this.socket?.destroy(); // Forcibly close the socket if not closed properly
resolve(true); // Resolve even if the socket was not closed properly
}, 2000);
// Close the socket and clear the timeout upon successful completion
this.socket.end(() => {
clearTimeout(timer);
resolve(true); // Resolve once the socket has ended
});
// Handle socket errors during closing
this.socket.once("error", (err) => {
clearTimeout(timer);
reject(err); // Reject the promise with the error
});
});
}
writeMessage(msg, connect, cb) {
return new Promise((resolve, reject) => {
// Check if the socket is initialized
if (!this.socket) {
return reject(new Error("Socket is not initialized"));
}
// Define a variable for the timeout reference
const timer = setTimeout(() => {
// Check if the socket is still valid before trying to remove the listener
cleanUp();
reject(new Error("TIMEOUT_ON_WRITING_MESSAGE")); // Reject the promise on timeout
}, connect ? 3000 : this.timeout);
// Handle incoming data
const onData = (data) => {
// Check if the socket is still valid before trying to remove the listener
cleanUp(); // Clear the timeout once data is received
resolve(cb(data)); // Resolve the promise with the received data
};
const cleanUp = () => {
if (timer)
clearTimeout(timer);
if (this.socket) {
this.socket.removeListener("data", onData); // Remove the data event listener
}
};
// Attach the data event listener
this.socket.on("data", onData);
// Attempt to write the message to the socket
this.socket.write(msg, undefined, (err) => {
if (err) {
cleanUp();
reject(err); // Reject the promise with the write error
}
});
});
}
async requestData(msg) {
try {
return await new Promise((resolve, reject) => {
let timer = null;
let replyBuffer = Buffer.from([]);
// Internal callback to handle data reception
const internalCallback = (data_1) => {
if (this.socket) {
this.socket.removeListener("data", handleOnData); // Clean up listener
}
if (timer)
clearTimeout(timer); // Clear the timeout
resolve(data_1); // Resolve the promise with the data
};
const onTimeOut = () => setTimeout(() => {
if (this.socket) {
this.socket.removeListener("data", handleOnData); // Clean up listener on timeout
}
reject(new Error("TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA")); // Reject on timeout
}, this.timeout);
// Handle incoming data
const handleOnData = (data_3) => {
replyBuffer = Buffer.concat([replyBuffer, data_3]); // Accumulate data
// Check if the data is a valid TCP event
if (utils.checkNotEventTCP(data_3))
return;
// Decode the TCP header
const header = utils.decodeTCPHeader(replyBuffer.subarray(0, 16));
if (this.verbose) {
console.log("response command: ", header.commandId, command.COMMANDS[header.commandId], "replyid: ", header.replyId);
}
// Handle based on command ID
if (header.commandId === command.COMMANDS.CMD_DATA) {
// Set a timeout to handle delayed responses
timer = setTimeout(() => {
internalCallback(replyBuffer); // Resolve with accumulated buffer
}, 1000);
}
else {
// Set a timeout to handle errors
timer = onTimeOut();
// Extract packet length and handle accordingly
const packetLength = data_3.readUIntLE(4, 2);
if (packetLength > 8) {
internalCallback(data_3); // Resolve immediately if sufficient data
}
}
};
// Ensure the socket is valid before attaching the listener
if (this.socket) {
this.socket.on("data", handleOnData);
// Write the message to the socket
this.socket.write(msg, undefined, (err) => {
if (err) {
if (this.socket) {
this.socket.removeListener("data", handleOnData); // Clean up listener on error
}
return reject(err); // Reject the promise with the error
}
// Set a timeout to handle cases where no response is received
timer = onTimeOut();
});
}
else {
reject(new Error("SOCKET_NOT_INITIALIZED")); // Reject if socket is not initialized
}
});
}
catch (err_1) {
console.error("Promise Rejected:", err_1); // Log the rejection reason
throw err_1; // Re-throw the error to be handled by the caller
}
}
/**
*
* @param {*} command
* @param {*} data
*
*
* reject error when command fail and resolve data when success
*/
async executeCmd(command$1, data) {
// Reset sessionId and replyId for connection commands
if (command$1 === command.COMMANDS.CMD_CONNECT) {
this.sessionId = 0;
this.replyId = 0;
}
else {
this.replyId++;
}
const currentReply = this.replyId;
const buf = utils.createTCPHeader(command$1, this.sessionId, this.replyId, data);
const callback = (responseData) => {
const packets = utils.splitTcpPackets(responseData);
for (const packet of packets) {
const headers = utils.decodeTCPHeader(packet);
if (this.verbose) {
const JOIN_CMD = { ...command.COMMANDS, ...command.DISCOVERED_CMD };
console.debug("request command:", command.COMMANDS[command$1], "\nresponse command: ", JOIN_CMD[headers.commandId], "replyid: ", headers.replyId);
}
if (+headers.replyId === currentReply + 1) {
return packet;
}
continue;
}
};
return new Promise((Resolve, Reject) => {
// Write the message to the socket and wait for a response
this.writeMessage(buf, command$1 === command.COMMANDS.CMD_CONNECT || command$1 === command.COMMANDS.CMD_EXIT, callback)
.then((reply) => {
// Remove TCP header from the response
let rReply;
try {
rReply = utils.removeTcpHeader(reply);
}
catch (e) {
console.log("reply", reply);
}
// Update sessionId for connection command responses
if (command$1 === command.COMMANDS.CMD_CONNECT &&
rReply &&
rReply.length >= 6) {
// Assuming sessionId is located at offset 4 and is 2 bytes long
this.sessionId = rReply.readUInt16LE(4);
}
Resolve(rReply);
})
.catch((err) => {
// Log or handle the error if necessary
console.error("Error executing command:", err);
Reject(err); // Re-throw the error for handling by the caller
});
});
}
async sendChunkRequest(start, size) {
this.replyId++;
const reqData = Buffer.alloc(8);
reqData.writeUInt32LE(start, 0);
reqData.writeUInt32LE(size, 4);
const buf = utils.createTCPHeader(command.COMMANDS.CMD_DATA_RDY, this.sessionId, this.replyId, reqData);
try {
const promise = new Promise((resolve, reject) => {
this.socket?.write(buf, undefined, (err) => {
if (err) {
console.error(`[TCP][SEND_CHUNK_REQUEST] Error sending chunk request: ${err.message}`);
reject(err); // Reject the promise if there is an error
}
else {
resolve(true); // Resolve the promise if the write operation succeeds
}
});
});
await promise;
}
catch (err) {
// Handle or log the error as needed
console.error(`[TCP][SEND_CHUNK_REQUEST] Exception: ${err.message}`);
throw err; // Re-throw the error for handling by the caller
}
}
/**
*
* @param {Buffer} reqData - indicate the type of data that need to receive ( user or attLog)
* @param {Function} cb - callback is triggered when receiving packets
*
* readWithBuffer will reject error if it'wrong when starting request data
* readWithBuffer will return { data: replyData , err: Error } when receiving requested data
*/
readWithBuffer(reqData, cb) {
return new Promise(async (resolve, reject) => {
this.replyId++;
const buf = utils.createTCPHeader(command.COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData);
let reply;
try {
reply = await this.requestData(buf);
}
catch (err) {
reject(err);
}
const header = utils.decodeTCPHeader(reply?.subarray(0, 16));
switch (header.commandId) {
case command.COMMANDS.CMD_DATA: {
resolve({ data: reply.subarray(16), mode: 8 });
break;
}
case command.COMMANDS.CMD_ACK_OK:
case command.COMMANDS.CMD_PREPARE_DATA: {
// this case show that data is prepared => send command to get these data
// reply variable includes information about the size of following data
const recvData = reply.subarray(16);
const size = recvData.readUIntLE(1, 4);
// We need to split the data to many chunks to receive , because it's to large
// After receiving all chunk data , we concat it to TotalBuffer variable , that 's the data we want
const remain = size % command.Constants.MAX_CHUNK;
const numberChunks = Math.round(size - remain) / command.Constants.MAX_CHUNK;
this.packetNumber = numberChunks + (remain > 0 ? 1 : 0);
//let replyData = Buffer.from([])
let totalBuffer = Buffer.from([]);
let realTotalBuffer = Buffer.from([]);
let timer = setTimeout(() => {
internalCallback(this.replyData, new Error("TIMEOUT WHEN RECEIVING PACKET"));
}, this.timeout);
const internalCallback = (replyData, err = null) => {
this.socket && this.socket.removeAllListeners("data");
timer && clearTimeout(timer);
resolve({ data: replyData, err });
};
this.socket?.once("close", () => {
internalCallback(this.replyData, new Error("Socket is disconnected unexpectedly"));
});
for (let i = 0; i <= numberChunks; i++) {
const data = await new Promise((resolve2, reject2) => {
try {
this.sendChunkRequest(i * command.Constants.MAX_CHUNK, i === numberChunks ? remain : command.Constants.MAX_CHUNK);
this.socket?.on("data", (reply) => {
clearTimeout(timer);
timer = setTimeout(() => {
internalCallback(this.replyData, new Error(`TIME OUT !! ${this.packetNumber} PACKETS REMAIN !`));
}, this.timeout);
if (this.verbose && reply.length >= 8) {
const headers = utils.decodeTCPHeader(reply);
if (command.COMMANDS[headers.commandId]) {
switch (headers.commandId) {
case command.COMMANDS.CMD_ACK_OK:
case command.COMMANDS.CMD_DATA:
this.verbose &&
console.log("CMD received: ", command.COMMANDS[headers.commandId]);
break;
case command.COMMANDS.CMD_PREPARE_DATA:
this.verbose &&
console.log("CMD received: ", command.COMMANDS[headers.commandId]);
this.verbose &&
console.log(`recieve chunk: prepare data size is ${headers.payloadSize}`);
break;
default:
break;
}
}
}
totalBuffer = Buffer.concat([totalBuffer, reply]);
const packetLength = totalBuffer.readUIntLE(4, 2);
if (totalBuffer.length >= 8 + packetLength) {
realTotalBuffer = Buffer.concat([
realTotalBuffer,
totalBuffer.subarray(16, 8 + packetLength),
]);
totalBuffer = totalBuffer.subarray(8 + packetLength);
if ((this.packetNumber > 1 &&
realTotalBuffer.length === command.Constants.MAX_CHUNK + 8) ||
(this.packetNumber === 1 &&
realTotalBuffer.length === remain + 8)) {
this.packetNumber--;
cb && cb(realTotalBuffer.length, size);
resolve2(realTotalBuffer.subarray(8));
totalBuffer = Buffer.from([]);
realTotalBuffer = Buffer.from([]);
}
}
});
}
catch (e) {
reject2(e);
}
});
this.replyData = Buffer.concat([
this.replyData,
data,
]);
this.socket?.removeAllListeners("data");
if (this.packetNumber <= 0) {
resolve({ data: this.replyData });
}
}
break;
}
default: {
reject(new Error("ERROR_IN_UNHANDLE_CMD " + utils.exportErrorMessage(header.commandId)));
}
}
});
}
/**
*
* @param {*} callbackInProcess
* reject error when starting request data
* return { data: records, err: Error } when receiving requested data
*/
async freeData() {
try {
const resp = await this.executeCmd(command.COMMANDS.CMD_FREE_DATA, "");
return !!resp;
}
catch (err) {
console.error("Error freeing data:", err);
throw err; // Optionally, re-throw the error if you need to handle it upstream
}
}
async disableDevice() {
try {
const resp = await this.executeCmd(command.COMMANDS.CMD_DISABLEDEVICE, command.REQUEST_DATA.DISABLE_DEVICE);
return !!resp;
}
catch (err) {
console.error("Error disabling device:", err);
throw err; // Optionally, re-throw the error if you need to handle it upstream
}
}
async enableDevice() {
try {
const resp = await this.executeCmd(command.COMMANDS.CMD_ENABLEDEVICE, "");
return !!resp;
}
catch (err) {
console.error("Error enabling device:", err);
throw err; // Optionally, re-throw the error if you need to handle it upstream
}
}
async disconnect() {
try {
// Attempt to execute the disconnect command
await this.executeCmd(command.COMMANDS.CMD_EXIT, "");
}
catch (err) {
// Log any errors encountered during command execution
console.error("Error during disconnection:", err);
// Optionally, add more handling or recovery logic here
}
// Attempt to close the socket and return the result
try {
await this.closeSocket();
}
catch (err) {
// Log any errors encountered while closing the socket
console.error("Error during socket closure:", err);
// Optionally, rethrow or handle the error if necessary
throw err; // Re-throwing to propagate the error
}
}
async getInfo() {
try {
// Execute the command to retrieve free sizes from the device
const data = await this.executeCmd(command.COMMANDS.CMD_GET_FREE_SIZES, "");
// Parse the response data to extract and return relevant information
return {
userCounts: data.readUIntLE(24, 4), // Number of users
logCounts: data.readUIntLE(40, 4), // Number of logs
logCapacity: data.readUIntLE(72, 4), // Capacity of logs in bytes
};
}
catch (err) {
// Log the error for debugging purposes
console.error("Error getting device info:", err);
// Re-throw the error to allow upstream error handling
throw err;
}
}
async getSizes() {
try {
// Execute the command to retrieve free sizes from the device
const data = await this.executeCmd(command.COMMANDS.CMD_GET_FREE_SIZES, "");
// Parse the response data to extract and return relevant information
const buf = data.slice(8); // remove header
this.user_count = buf.readUIntLE(16, 4);
this.fp_count = buf.readUIntLE(24, 4);
this.pwd_count = buf.readUIntLE(52, 4);
this.oplog_count = buf.readUIntLE(40, 4);
this.attlog_count = buf.readUIntLE(32, 4);
this.fp_cap = buf.readUIntLE(56, 4);
this.user_cap = buf.readUIntLE(60, 4);
this.attlog_cap = buf.readUIntLE(64, 4);
this.fp_av = buf.readUIntLE(68, 4);
this.user_av = buf.readUIntLE(72, 4);
this.attlog_av = buf.readUIntLE(76, 4);
this.face_count = buf.readUIntLE(80, 4);
this.face_cap = buf.readUIntLE(88, 4);
return {
userCounts: this.user_count, // Number of users
logCounts: this.attlog_count, // Number of logs
fingerCount: this.fp_count,
adminCount: this.pwd_count,
opLogCount: this.oplog_count,
logCapacity: this.attlog_cap, // Capacity of logs in bytes
fingerCapacity: this.fp_cap,
userCapacity: this.user_cap,
attLogCapacity: this.attlog_cap,
fingerAvailable: this.fp_av,
userAvailable: this.user_av,
attLogAvailable: this.attlog_av,
faceCount: this.face_count,
faceCapacity: this.face_cap,
};
}
catch (err) {
// Log the error for debugging purposes
console.error("Error getting device info:", err);
// Re-throw the error to allow upstream error handling
throw err;
}
}
#listeners = new Map();
async getAttendanceSize() {
try {
// Execute command to get free sizes
const data = await this.executeCmd(command.COMMANDS.CMD_GET_FREE_SIZES, "");
// Parse and return the attendance size
return data.readUIntLE(40, 4); // Assuming data at offset 40 represents the attendance size
}
catch (err) {
// Log error details for debugging
console.error("Error getting attendance size:", err);
// Re-throw the error to be handled by the caller
throw err;
}
}
// Clears the attendance logs on the device
async clearAttendanceLog() {
return await this._transactionService.clearAttendanceLog();
}
/**
* Clears all data on the device
* @value 1 Attendance records
* @value 2 Fingerprint templates
* @value 3 None
* @value 4 Operation records
* @value 5 User information
* @default 0 Delete all
*/
async clearData(value) {
try {
// Execute the command to clear all data
await this.disableDevice();
if (!value)
value = 3;
const buf = await this.executeCmd(command.COMMANDS.CMD_CLEAR_DATA, value.toString());
await this.refreshData();
await this.enableDevice();
return !!buf;
}
catch (err) {
// Log the error for debugging purposes
console.error("Error clearing data:", err);
// Re-throw the error to be handled by the caller
throw err;
}
}
async getRealTimeLogs(cb = (realTimeLog) => { }) {
this.replyId++; // Increment the reply ID for this request
try {
// Create a buffer with the command header to request real-time logs
const buf = utils.createTCPHeader(command.COMMANDS.CMD_REG_EVENT, this.sessionId, this.replyId, Buffer.from([0x01, 0x00, 0x00, 0x00]));
// Send the request to the device
this.socket?.write(buf, undefined, (err) => {
if (err) {
// Log and reject the promise if there is an error writing to the socket
console.error("Error sending real-time logs request:", err);
throw err;
}
});
// Ensure data listeners are added only once
if (this.socket?.listenerCount("data") === 0) {
console.log("entraaa");
this.socket.on("data", (data) => {
// Check if the data is an event and not just a regular response
if (utils.checkNotEventTCP(data)) {
// Process the data if it is of the expected length
if (data.length > 16) {
// Decode and pass the log to the callback
cb(utils.decodeRTEvent(data));
}
}
});
}
}
catch (err) {
// Handle errors and reject the promise
console.error("Error getting real-time logs:", err);
throw err;
}
}
/**
* Get all Finger objects
* @returns {Record<string, Finger[]>}
*/
async getTemplates(callbackInProcess = () => { }) {
return await this._userService.getTemplates(callbackInProcess);
}
/**
* Return size
* @param packet
*/
testTcpTop(packet) {
// Check if packet is too small
if (packet.length <= 8)
return 0;
// Extract header values using little-endian format
const headerValue1 = packet.readUInt16LE(0);
const headerValue2 = packet.readUInt16LE(2);
const size = packet.readUInt32LE(4);
// Check if magic numbers match
if (headerValue1 === command.Constants.MACHINE_PREPARE_DATA_1 &&
headerValue2 === command.Constants.MACHINE_PREPARE_DATA_2) {
return size;
}
return 0;
}
async refreshData() {
try {
const reply = await this.executeCmd(command.COMMANDS.CMD_REFRESHDATA, "");
return !!reply;
}
catch (err) {
console.error("Error getting user templates: ", err);
throw err;
}
}
async sendWithBuffer(buffer) {
const MAX_CHUNK = 1024;
const size = buffer.length;
await this.freeData();
const commandString = Buffer.alloc(4); // 'I' is 4 bytes
commandString.writeUInt32LE(size, 0);
try {
const cmdResponse = await this.executeCmd(command.COMMANDS.CMD_PREPARE_DATA, commandString);
// responds with 2000 = CMD_ACK_OK
if (!cmdResponse) {
throw new Error("Can't prepare data");
}
}
catch (e) {
console.error(e);
}
const remain = size % MAX_CHUNK;
const packets = Math.floor((size - remain) / MAX_CHUNK);
let start = 0;
try {
for (let i = 0; i < packets; i++) {
const resp = await this.sendChunk(buffer.slice(start, start + MAX_CHUNK));
if (resp) {
start += MAX_CHUNK;
if (i == packets - 1 && remain) {
const lastPacket = await this.sendChunk(buffer.slice(start, start + remain));
return lastPacket;
}
}
}
}
catch (e) {
console.error(e);
}
}
async sendChunk(commandString) {
try {
return await new Promise((resolve, reject) => {
resolve(this.executeCmd(command.COMMANDS.CMD_DATA, commandString));
});
}
catch (e) {
throw new handler.ZkError(e, command.COMMANDS.CMD_DATA, this.ip);
}
}
async readSocket(length, cb = null) {
let replyBufer = Buffer.from([]);
const totalPackets = 0;
return new Promise((resolve, reject) => {
let timer = setTimeout(() => {
internalCallback(replyBufer, new Error("TIMEOUT WHEN RECEIVING PACKET"));
}, this.timeout);
const internalCallback = (replyData, err = null) => {
this.socket && this.socket.removeListener("data", onDataEnroll);
timer && clearTimeout(timer);
resolve({ data: replyData, err: err });
};
function onDataEnroll(data) {
clearTimeout(timer);
timer = setTimeout(() => {
internalCallback(replyBufer, new Error(`TIME OUT !! ${totalPackets} PACKETS REMAIN !`));
}, this.timeout);
replyBufer = Buffer.concat([replyBufer, data], replyBufer.length + data.length);
if (data.length == length) {
internalCallback(data);
}
}
this.socket.once("close", () => {
internalCallback(replyBufer, new Error("Socket is disconnected unexpectedly"));
});
this.socket.on("data", onDataEnroll);
}).catch((err) => {
console.error("Promise Rejected:", err); // Log the rejection reason
throw err; // Re-throw the error to be handled by the caller
});
}
/**
* Register events
* @param {number} flags - Event flags
* @returns {Promise<void>}
* @throws {ZKErrorResponse} If registration fails
*/
async regEvent(flags) {
try {
const commandString = Buffer.alloc(4); // 'I' format is 4 bytes
commandString.writeUInt32LE(flags, 0); // Little-endian unsigned int
const cmdResponse = await this.executeCmd(command.COMMANDS.CMD_REG_EVENT, commandString);
if (this.verbose)
console.log("regEvent: ", cmdResponse.readUInt16LE(0));
}
catch (e) {
throw new handler.ZkError(e, command.COMMANDS.CMD_REG_EVENT, this.ip);
}
}
async cancelCapture() {
try {
const reply = await this.executeCmd(command.COMMANDS.CMD_CANCELCAPTURE, "");
return !!reply;
}
catch (e) {
throw new handler.ZkError(e, command.COMMANDS.CMD_CANCELCAPTURE, this.ip);
}
}
async restartDevice() {
try {
await this.executeCmd(command.COMMANDS.CMD_RESTART, "");
}
catch (e) {
throw new handler.ZkError(e, command.COMMANDS.CMD_RESTART, this.ip);
}
}
}
exports.ZTCP = ZTCP;