zklib-ts 1.0.4 → 1.0.6

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/dist/index.es.js CHANGED
@@ -5,99 +5,190 @@ import * as dgram from 'node:dgram';
5
5
  var COMMANDS;
6
6
  (function (COMMANDS) {
7
7
  COMMANDS[COMMANDS["CMD_ACK_DATA"] = 2002] = "CMD_ACK_DATA";
8
+ /** There was an error when processing the request.*/
8
9
  COMMANDS[COMMANDS["CMD_ACK_ERROR"] = 2001] = "CMD_ACK_ERROR";
9
10
  COMMANDS[COMMANDS["CMD_ACK_ERROR_CMD"] = 65533] = "CMD_ACK_ERROR_CMD";
10
11
  COMMANDS[COMMANDS["CMD_ACK_ERROR_DATA"] = 65531] = "CMD_ACK_ERROR_DATA";
11
12
  COMMANDS[COMMANDS["CMD_ACK_ERROR_INIT"] = 65532] = "CMD_ACK_ERROR_INIT";
13
+ /** [0xD0, 0x07] The request was processed sucessfully. */
12
14
  COMMANDS[COMMANDS["CMD_ACK_OK"] = 2000] = "CMD_ACK_OK";
13
15
  COMMANDS[COMMANDS["CMD_ACK_REPEAT"] = 2004] = "CMD_ACK_REPEAT";
14
16
  COMMANDS[COMMANDS["CMD_ACK_RETRY"] = 2003] = "CMD_ACK_RETRY";
17
+ /** [0xD5, 0x07] Connection not authorized. */
15
18
  COMMANDS[COMMANDS["CMD_ACK_UNAUTH"] = 2005] = "CMD_ACK_UNAUTH";
19
+ /** Received unknown command. */
16
20
  COMMANDS[COMMANDS["CMD_ACK_UNKNOWN"] = 65535] = "CMD_ACK_UNKNOWN";
21
+ /** Request attendance log. */
17
22
  COMMANDS[COMMANDS["CMD_ATTLOG_RRQ"] = 13] = "CMD_ATTLOG_RRQ";
23
+ /** [0x4E, 0x04] Request to begin session using commkey. */
18
24
  COMMANDS[COMMANDS["CMD_AUTH"] = 1102] = "CMD_AUTH";
25
+ /** Disable normal authentication of users. */
19
26
  COMMANDS[COMMANDS["CMD_CANCELCAPTURE"] = 62] = "CMD_CANCELCAPTURE";
27
+ /** Capture fingerprint picture. */
20
28
  COMMANDS[COMMANDS["CMD_CAPTUREFINGER"] = 1009] = "CMD_CAPTUREFINGER";
29
+ /** Capture the entire image. */
21
30
  COMMANDS[COMMANDS["CMD_CAPTUREIMAGE"] = 1012] = "CMD_CAPTUREIMAGE";
31
+ /** Change transmission speed. */
22
32
  COMMANDS[COMMANDS["CMD_CHANGE_SPEED"] = 1101] = "CMD_CHANGE_SPEED";
33
+ /** [0x77, 0x00] Get checksum of machine's buffer. */
23
34
  COMMANDS[COMMANDS["CMD_CHECKSUM_BUFFER"] = 119] = "CMD_CHECKSUM_BUFFER";
35
+ /** Restore access control to default. */
24
36
  COMMANDS[COMMANDS["CMD_CLEAR_ACC"] = 32] = "CMD_CLEAR_ACC";
37
+ /** Clears admins privileges. */
25
38
  COMMANDS[COMMANDS["CMD_CLEAR_ADMIN"] = 20] = "CMD_CLEAR_ADMIN";
39
+ /** Delete attendance record. */
26
40
  COMMANDS[COMMANDS["CMD_CLEAR_ATTLOG"] = 15] = "CMD_CLEAR_ATTLOG";
41
+ /** Delete data. */
27
42
  COMMANDS[COMMANDS["CMD_CLEAR_DATA"] = 14] = "CMD_CLEAR_DATA";
43
+ /** Clear screen captions. */
28
44
  COMMANDS[COMMANDS["CMD_CLEAR_LCD"] = 67] = "CMD_CLEAR_LCD";
45
+ /** Delete operations log. */
29
46
  COMMANDS[COMMANDS["CMD_CLEAR_OPLOG"] = 33] = "CMD_CLEAR_OPLOG";
47
+ /** [0xE8, 0x03] Begin connection. */
30
48
  COMMANDS[COMMANDS["CMD_CONNECT"] = 1000] = "CMD_CONNECT";
49
+ /** [0xDD, 0x05] Data packet. */
31
50
  COMMANDS[COMMANDS["CMD_DATA"] = 1501] = "CMD_DATA";
51
+ /** Indicates that it is ready to receive data. */
32
52
  COMMANDS[COMMANDS["CMD_DATA_RDY"] = 1504] = "CMD_DATA_RDY";
53
+ /** Read/Write a large data set. */
33
54
  COMMANDS[COMMANDS["CMD_DATA_WRRQ"] = 1503] = "CMD_DATA_WRRQ";
55
+ /** Read saved data. */
34
56
  COMMANDS[COMMANDS["CMD_DB_RRQ"] = 7] = "CMD_DB_RRQ";
57
+ /** Deletes fingerprint template. */
35
58
  COMMANDS[COMMANDS["CMD_DEL_FPTMP"] = 134] = "CMD_DEL_FPTMP";
59
+ /** Delete short message. */
36
60
  COMMANDS[COMMANDS["CMD_DELETE_SMS"] = 72] = "CMD_DELETE_SMS";
61
+ /** Delete user short message. */
37
62
  COMMANDS[COMMANDS["CMD_DELETE_UDATA"] = 74] = "CMD_DELETE_UDATA";
63
+ /** Delete user. */
38
64
  COMMANDS[COMMANDS["CMD_DELETE_USER"] = 18] = "CMD_DELETE_USER";
65
+ /** Delete user fingerprint template. */
39
66
  COMMANDS[COMMANDS["CMD_DELETE_USERTEMP"] = 19] = "CMD_DELETE_USERTEMP";
67
+ /** Disables fingerprint, rfid reader and keyboard. */
40
68
  COMMANDS[COMMANDS["CMD_DISABLEDEVICE"] = 1003] = "CMD_DISABLEDEVICE";
69
+ /** Get door state. */
41
70
  COMMANDS[COMMANDS["CMD_DOORSTATE_RRQ"] = 75] = "CMD_DOORSTATE_RRQ";
71
+ /** Clear Mifare card. */
42
72
  COMMANDS[COMMANDS["CMD_EMPTY_MIFARE"] = 78] = "CMD_EMPTY_MIFARE";
73
+ /** Enables the ":" in screen clock. */
43
74
  COMMANDS[COMMANDS["CMD_ENABLE_CLOCK"] = 57] = "CMD_ENABLE_CLOCK";
75
+ /** Change machine state to "normal work". */
44
76
  COMMANDS[COMMANDS["CMD_ENABLEDEVICE"] = 1002] = "CMD_ENABLEDEVICE";
77
+ /** [0xE9, 0x03] Disconnect. */
45
78
  COMMANDS[COMMANDS["CMD_EXIT"] = 1001] = "CMD_EXIT";
79
+ /** [0xDE, 0x05] Release buffer used for data transmission. */
46
80
  COMMANDS[COMMANDS["CMD_FREE_DATA"] = 1502] = "CMD_FREE_DATA";
81
+ /** Request machine status (remaining space). */
47
82
  COMMANDS[COMMANDS["CMD_GET_FREE_SIZES"] = 50] = "CMD_GET_FREE_SIZES";
83
+ /** Request max size for users id. */
48
84
  COMMANDS[COMMANDS["CMD_GET_PINWIDTH"] = 69] = "CMD_GET_PINWIDTH";
85
+ /** Request machine time. */
49
86
  COMMANDS[COMMANDS["CMD_GET_TIME"] = 201] = "CMD_GET_TIME";
50
87
  COMMANDS[COMMANDS["CMD_GET_USERTEMP"] = 88] = "CMD_GET_USERTEMP";
88
+ /** Request the firmware edition. */
51
89
  COMMANDS[COMMANDS["CMD_GET_VERSION"] = 1100] = "CMD_GET_VERSION";
90
+ /** Get group timezone. */
52
91
  COMMANDS[COMMANDS["CMD_GRPTZ_RRQ"] = 25] = "CMD_GRPTZ_RRQ";
92
+ /** Set group timezone. */
53
93
  COMMANDS[COMMANDS["CMD_GRPTZ_WRQ"] = 26] = "CMD_GRPTZ_WRQ";
94
+ /** Read operations log. */
54
95
  COMMANDS[COMMANDS["CMD_OPLOG_RRQ"] = 34] = "CMD_OPLOG_RRQ";
96
+ /** Read configuration value of the machine. */
55
97
  COMMANDS[COMMANDS["CMD_OPTIONS_RRQ"] = 11] = "CMD_OPTIONS_RRQ";
98
+ /** Change configuration value of the machine. */
56
99
  COMMANDS[COMMANDS["CMD_OPTIONS_WRQ"] = 12] = "CMD_OPTIONS_WRQ";
100
+ /** Shut-down machine. */
57
101
  COMMANDS[COMMANDS["CMD_POWEROFF"] = 1005] = "CMD_POWEROFF";
102
+ /** [0xDC, 0x05] Prepare for data transmission. */
58
103
  COMMANDS[COMMANDS["CMD_PREPARE_DATA"] = 1500] = "CMD_PREPARE_DATA";
104
+ /** [0xF5, 0x03] Refresh the machine stored data. */
59
105
  COMMANDS[COMMANDS["CMD_REFRESHDATA"] = 1013] = "CMD_REFRESHDATA";
106
+ /** Refresh the configuration parameters. */
60
107
  COMMANDS[COMMANDS["CMD_REFRESHOPTION"] = 1014] = "CMD_REFRESHOPTION";
108
+ /** Realtime events. */
61
109
  COMMANDS[COMMANDS["CMD_REG_EVENT"] = 500] = "CMD_REG_EVENT";
110
+ /** Restart machine. */
62
111
  COMMANDS[COMMANDS["CMD_RESTART"] = 1004] = "CMD_RESTART";
112
+ /** Change machine state to "awaken". */
63
113
  COMMANDS[COMMANDS["CMD_RESUME"] = 1007] = "CMD_RESUME";
114
+ /** Set machine time. */
64
115
  COMMANDS[COMMANDS["CMD_SET_TIME"] = 202] = "CMD_SET_TIME";
116
+ /** Change machine state to "idle". */
65
117
  COMMANDS[COMMANDS["CMD_SLEEP"] = 1006] = "CMD_SLEEP";
118
+ /** Download short message. */
66
119
  COMMANDS[COMMANDS["CMD_SMS_RRQ"] = 71] = "CMD_SMS_RRQ";
120
+ /** Upload short message. */
67
121
  COMMANDS[COMMANDS["CMD_SMS_WRQ"] = 70] = "CMD_SMS_WRQ";
122
+ /** Start enroll procedure. */
68
123
  COMMANDS[COMMANDS["CMD_STARTENROLL"] = 61] = "CMD_STARTENROLL";
124
+ /** Set the machine to authentication state. */
69
125
  COMMANDS[COMMANDS["CMD_STARTVERIFY"] = 60] = "CMD_STARTVERIFY";
126
+ /** Query state. */
70
127
  COMMANDS[COMMANDS["CMD_STATE_RRQ"] = 64] = "CMD_STATE_RRQ";
128
+ /** Test if fingerprint exists. */
71
129
  COMMANDS[COMMANDS["CMD_TEST_TEMP"] = 1011] = "CMD_TEST_TEMP";
130
+ /** Test voice. */
72
131
  COMMANDS[COMMANDS["CMD_TESTVOICE"] = 1017] = "CMD_TESTVOICE";
132
+ /** [0x77, 0x00] Transfer fp template from buffer. */
73
133
  COMMANDS[COMMANDS["CMD_TMP_WRITE"] = 87] = "CMD_TMP_WRITE";
134
+ /** Get device timezones. */
74
135
  COMMANDS[COMMANDS["CMD_TZ_RRQ"] = 27] = "CMD_TZ_RRQ";
136
+ /** Set device timezones. */
75
137
  COMMANDS[COMMANDS["CMD_TZ_WRQ"] = 28] = "CMD_TZ_WRQ";
138
+ /** Set user short message. */
76
139
  COMMANDS[COMMANDS["CMD_UDATA_WRQ"] = 73] = "CMD_UDATA_WRQ";
140
+ /** Get group combination to unlock. */
77
141
  COMMANDS[COMMANDS["CMD_ULG_RRQ"] = 29] = "CMD_ULG_RRQ";
142
+ /** Set group combination to unlock. */
78
143
  COMMANDS[COMMANDS["CMD_ULG_WRQ"] = 30] = "CMD_ULG_WRQ";
144
+ /** Unlock door for a specified amount of time. */
79
145
  COMMANDS[COMMANDS["CMD_UNLOCK"] = 31] = "CMD_UNLOCK";
146
+ /** Upload user data. */
80
147
  COMMANDS[COMMANDS["CMD_USER_WRQ"] = 8] = "CMD_USER_WRQ";
148
+ /** Read user group. */
81
149
  COMMANDS[COMMANDS["CMD_USERGRP_RRQ"] = 21] = "CMD_USERGRP_RRQ";
150
+ /** Set user group. */
82
151
  COMMANDS[COMMANDS["CMD_USERGRP_WRQ"] = 22] = "CMD_USERGRP_WRQ";
152
+ /** [0x09, 0x00] Read user fingerprint template. */
83
153
  COMMANDS[COMMANDS["CMD_USERTEMP_RRQ"] = 9] = "CMD_USERTEMP_RRQ";
154
+ /** Upload user fingerprint template. */
84
155
  COMMANDS[COMMANDS["CMD_USERTEMP_WRQ"] = 10] = "CMD_USERTEMP_WRQ";
156
+ /** Get user timezones. */
85
157
  COMMANDS[COMMANDS["CMD_USERTZ_RRQ"] = 23] = "CMD_USERTZ_RRQ";
158
+ /** Set the user timezones. */
86
159
  COMMANDS[COMMANDS["CMD_USERTZ_WRQ"] = 24] = "CMD_USERTZ_WRQ";
160
+ /** Read verification style of a given user. */
87
161
  COMMANDS[COMMANDS["CMD_VERIFY_RRQ"] = 80] = "CMD_VERIFY_RRQ";
162
+ /** Change verification style of a given user. */
88
163
  COMMANDS[COMMANDS["CMD_VERIFY_WRQ"] = 79] = "CMD_VERIFY_WRQ";
164
+ /** Prints chars to the device screen. */
89
165
  COMMANDS[COMMANDS["CMD_WRITE_LCD"] = 66] = "CMD_WRITE_LCD";
166
+ /** Write data to Mifare card. */
90
167
  COMMANDS[COMMANDS["CMD_WRITE_MIFARE"] = 76] = "CMD_WRITE_MIFARE";
168
+ /** Triggered alarm. */
91
169
  COMMANDS[COMMANDS["EF_ALARM"] = 512] = "EF_ALARM";
170
+ /** Attendance entry. */
92
171
  COMMANDS[COMMANDS["EF_ATTLOG"] = 1] = "EF_ATTLOG";
172
+ /** Pressed keyboard key. */
93
173
  COMMANDS[COMMANDS["EF_BUTTON"] = 16] = "EF_BUTTON";
174
+ /** Upload user data. */
94
175
  COMMANDS[COMMANDS["EF_ENROLLFINGER"] = 8] = "EF_ENROLLFINGER";
176
+ /** Enrolled user. */
95
177
  COMMANDS[COMMANDS["EF_ENROLLUSER"] = 4] = "EF_ENROLLUSER";
178
+ /** Pressed finger. */
96
179
  COMMANDS[COMMANDS["EF_FINGER"] = 2] = "EF_FINGER";
180
+ /** Fingerprint score in enroll procedure. */
97
181
  COMMANDS[COMMANDS["EF_FPFTR"] = 256] = "EF_FPFTR";
182
+ /** Restore access control to default. */
98
183
  COMMANDS[COMMANDS["EF_UNLOCK"] = 32] = "EF_UNLOCK";
184
+ /** Registered user placed finger. */
99
185
  COMMANDS[COMMANDS["EF_VERIFY"] = 128] = "EF_VERIFY";
100
186
  })(COMMANDS || (COMMANDS = {}));
187
+ var DISCOVERED_CMD;
188
+ (function (DISCOVERED_CMD) {
189
+ /** Returned when the Finger id not exists in the user uid, when attempting to download single finger template */
190
+ DISCOVERED_CMD[DISCOVERED_CMD["FID_NOT_FOUND"] = 4993] = "FID_NOT_FOUND";
191
+ })(DISCOVERED_CMD || (DISCOVERED_CMD = {}));
101
192
  var Constants;
102
193
  (function (Constants) {
103
194
  Constants[Constants["USHRT_MAX"] = 65535] = "USHRT_MAX";
@@ -106,6 +197,7 @@ var Constants;
106
197
  Constants[Constants["MACHINE_PREPARE_DATA_2"] = 32130] = "MACHINE_PREPARE_DATA_2";
107
198
  })(Constants || (Constants = {}));
108
199
  const REQUEST_DATA = {
200
+ START_TAG: Buffer.from([0x50, 0x50, 0x82, 0x7d]),
109
201
  DISABLE_DEVICE: Buffer.from([0, 0, 0, 0]),
110
202
  GET_REAL_TIME_EVENT: Buffer.from([0x01, 0x00, 0x00, 0x00]),
111
203
  GET_ATTENDANCE_LOGS: Buffer.from([0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
@@ -253,18 +345,23 @@ class User {
253
345
  }
254
346
 
255
347
  class Attendance {
348
+ /** Internal serial number for the user */
256
349
  sn;
350
+ /** User ID/Pin stored as a string */
257
351
  user_id;
258
- record_time;
352
+ /** Verification type */
259
353
  type;
354
+ /** Time of the attendance event */
355
+ record_time;
356
+ /** Verify state */
260
357
  state;
261
358
  ip;
262
- constructor(sn, user_id, record_time, type, state) {
359
+ constructor(sn, user_id, type, record_time, state) {
263
360
  this.sn = sn;
264
361
  this.user_id = user_id;
362
+ this.type = type || undefined;
265
363
  this.record_time = record_time;
266
- this.type = type;
267
- this.state = state;
364
+ this.state = state || undefined;
268
365
  }
269
366
  }
270
367
 
@@ -308,17 +405,7 @@ const createUDPHeader = (command, sessionId, replyId, data) => {
308
405
  return buf;
309
406
  };
310
407
  const createTCPHeader = (command, sessionId, replyId, data) => {
311
- const dataBuffer = Buffer.from(data);
312
- const buf = Buffer.alloc(8 + dataBuffer.length);
313
- buf.writeUInt16LE(command, 0);
314
- buf.writeUInt16LE(0, 2);
315
- buf.writeUInt16LE(sessionId, 4);
316
- buf.writeUInt16LE(replyId, 6);
317
- dataBuffer.copy(buf, 8);
318
- const chksum2 = createChkSum(buf);
319
- buf.writeUInt16LE(chksum2, 2);
320
- replyId = (replyId + 1) % Constants.USHRT_MAX;
321
- buf.writeUInt16LE(replyId, 6);
408
+ const buf = createUDPHeader(command, sessionId, replyId, data);
322
409
  const prefixBuf = Buffer.from([0x50, 0x50, 0x82, 0x7d, 0x13, 0x00, 0x00, 0x00]);
323
410
  prefixBuf.writeUInt16LE(buf.length, 4);
324
411
  return Buffer.concat([prefixBuf, buf]);
@@ -378,7 +465,7 @@ const decodeRecordData40 = (recordData) => {
378
465
  .slice(2, 2 + 9)
379
466
  .toString('ascii')
380
467
  .split('\0')
381
- .shift() || '', parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(26, 1), recordData.readUIntLE(31, 1));
468
+ .shift() || '', recordData.readUIntLE(26, 1), parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(31, 1));
382
469
  };
383
470
  const decodeRecordData16 = (recordData) => {
384
471
  return {
@@ -494,6 +581,59 @@ const authKey = (comKey, sessionId) => {
494
581
  return Array.from(response);
495
582
  };
496
583
 
584
+ /**
585
+ * Error types for device communication
586
+ */
587
+ const ERROR_TYPES = {
588
+ ECONNRESET: 'ECONNRESET',
589
+ ECONNREFUSED: 'ECONNREFUSED'};
590
+ /**
591
+ * Custom error class for device communication errors
592
+ */
593
+ class ZkError {
594
+ err;
595
+ ip;
596
+ command;
597
+ /**
598
+ * Creates a new ZkError instance
599
+ * @param err The error object
600
+ * @param command The command that caused the error
601
+ * @param ip The IP address of the device
602
+ */
603
+ constructor(err, command, ip) {
604
+ this.err = err;
605
+ this.ip = ip;
606
+ this.command = command;
607
+ }
608
+ /**
609
+ * Gets a user-friendly error message
610
+ * @returns A formatted error message
611
+ */
612
+ toast() {
613
+ if (this.err.code === ERROR_TYPES.ECONNRESET) {
614
+ return 'Another device is connecting to the device so the connection is interrupted';
615
+ }
616
+ else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
617
+ return 'IP of the device is refused';
618
+ }
619
+ return this.err.message;
620
+ }
621
+ /**
622
+ * Gets detailed error information
623
+ * @returns An object containing error details
624
+ */
625
+ getError() {
626
+ return {
627
+ err: {
628
+ message: this.err.message,
629
+ code: this.err.code
630
+ },
631
+ ip: this.ip,
632
+ command: this.command
633
+ };
634
+ }
635
+ }
636
+
497
637
  /**
498
638
  * Represents a fingerprint template with associated metadata
499
639
  */
@@ -567,56 +707,547 @@ class Finger {
567
707
  }
568
708
  }
569
709
 
570
- /**
571
- * Error types for device communication
572
- */
573
- const ERROR_TYPES = {
574
- ECONNRESET: 'ECONNRESET',
575
- ECONNREFUSED: 'ECONNREFUSED'};
576
- /**
577
- * Custom error class for device communication errors
578
- */
579
- class ZkError {
580
- err;
581
- ip;
582
- command;
583
- /**
584
- * Creates a new ZkError instance
585
- * @param err The error object
586
- * @param command The command that caused the error
587
- * @param ip The IP address of the device
588
- */
589
- constructor(err, command, ip) {
590
- this.err = err;
591
- this.ip = ip;
592
- this.command = command;
710
+ class UserService {
711
+ _zkTcp;
712
+ _users;
713
+ constructor(zkTcp) {
714
+ this._zkTcp = zkTcp;
715
+ }
716
+ async getUserByUserId(user_id) {
717
+ if (!this._users) {
718
+ await this.getUsers();
719
+ }
720
+ if (this._users.has(String(user_id))) {
721
+ return this._users.get(String(user_id));
722
+ }
723
+ else
724
+ throw new Error("user_id not exists");
725
+ }
726
+ async getUsers() {
727
+ try {
728
+ // Free any existing buffer data to prepare for a new request
729
+ if (this._users) {
730
+ return { data: Array.from(this._users.values()) };
731
+ }
732
+ else {
733
+ this._users = new Map([]);
734
+ }
735
+ if (this._zkTcp.socket) {
736
+ await this._zkTcp.freeData();
737
+ }
738
+ // Request user data
739
+ const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_USERS);
740
+ // Free buffer data after receiving the data
741
+ if (this._zkTcp.socket) {
742
+ await this._zkTcp.freeData();
743
+ }
744
+ // Constants for user data processing
745
+ const USER_PACKET_SIZE = 72;
746
+ // Ensure data.data is a valid buffer
747
+ if (!data.data || !(data.data instanceof Buffer)) {
748
+ throw new Error('Invalid data received');
749
+ }
750
+ let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
751
+ const users = [];
752
+ // Process each user packet
753
+ while (userData.length >= USER_PACKET_SIZE) {
754
+ // Decode user data and add to the users array
755
+ const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
756
+ users.push(user);
757
+ this._users.set(user.user_id, user);
758
+ userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
759
+ }
760
+ // Return the list of users
761
+ return { data: users };
762
+ }
763
+ catch (err) {
764
+ // Log the error for debugging
765
+ console.error('Error getting users:', err);
766
+ // Re-throw the error to be handled by the caller
767
+ throw err;
768
+ }
769
+ }
770
+ async setUser(user_id, name, password, role = 0, cardno = 0) {
771
+ let user;
772
+ try {
773
+ user = await this.getUserByUserId(user_id);
774
+ }
775
+ catch (err) {
776
+ if (err.message.includes("user_id not exists")) {
777
+ user.uid = Math.max(...Array.from(this._users.values()).map(usr => usr.uid)) + 1;
778
+ this._users.set(user_id, user);
779
+ }
780
+ }
781
+ try {
782
+ // Validate input parameters
783
+ if (user_id.length > 9 ||
784
+ name.length > 24 ||
785
+ password.length > 8 ||
786
+ typeof role !== 'number' ||
787
+ cardno.toString().length > 10) {
788
+ throw new Error('Invalid input parameters');
789
+ }
790
+ // Allocate and initialize the buffer
791
+ const commandBuffer = Buffer.alloc(72);
792
+ // Fill the buffer with user data
793
+ commandBuffer.writeUInt16LE(user.uid, 0);
794
+ commandBuffer.writeUInt16LE(role, 2);
795
+ commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
796
+ commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
797
+ commandBuffer.writeUInt16LE(cardno, 35);
798
+ commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
799
+ commandBuffer.write(user_id.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
800
+ // Send the command and return the result
801
+ const created = await this._zkTcp.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
802
+ return !!created;
803
+ }
804
+ catch (err) {
805
+ // Log error details for debugging
806
+ console.error('Error setting user:', err);
807
+ // Re-throw error for upstream handling
808
+ throw err;
809
+ }
810
+ }
811
+ async DeleteUser(user_id) {
812
+ try {
813
+ const user = await this.getUserByUserId(user_id);
814
+ // Allocate and initialize the buffer
815
+ const commandBuffer = Buffer.alloc(72);
816
+ // Write UID to the buffer
817
+ commandBuffer.writeUInt16LE(user.uid, 0);
818
+ // Send the delete command and return the result
819
+ const deleted = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
820
+ return !!deleted;
821
+ }
822
+ catch (err) {
823
+ // Log error details for debugging
824
+ console.error('Error deleting user:', err);
825
+ // Re-throw error for upstream handling
826
+ throw err;
827
+ }
828
+ }
829
+ async getTemplates(callbackInProcess = () => { }) {
830
+ let templates = [];
831
+ try {
832
+ if (this._zkTcp.socket) {
833
+ await this._zkTcp.freeData();
834
+ }
835
+ await this._zkTcp.getSizes();
836
+ if (this._zkTcp.fp_count == 0)
837
+ return { data: [] };
838
+ await this._zkTcp.disableDevice();
839
+ const resp = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
840
+ let templateData = resp.data.subarray(4);
841
+ let totalSize = resp.data.readUIntLE(0, 4);
842
+ while (totalSize) {
843
+ const buf = templateData.subarray(0, 6);
844
+ const size = buf.readUIntLE(0, 2);
845
+ const uid = buf.readUIntLE(2, 2);
846
+ const fid = buf.readUIntLE(4, 1);
847
+ const valid = buf.readUIntLE(5, 1);
848
+ // Force-copy bytes so we don't retain the entire big backing buffer
849
+ const tplBytes = Buffer.from(templateData.subarray(6, size));
850
+ templates.push(new Finger(uid, fid, valid, tplBytes));
851
+ templateData = templateData.subarray(size);
852
+ totalSize -= size;
853
+ }
854
+ return { data: templates };
855
+ }
856
+ catch (err) {
857
+ this._zkTcp.verbose && console.log("Error getting templates", err);
858
+ return { data: templates };
859
+ }
860
+ finally {
861
+ await this._zkTcp.freeData();
862
+ await this._zkTcp.enableDevice();
863
+ }
864
+ }
865
+ async DownloadFp(user_id, fid) {
866
+ try {
867
+ const user = await this.getUserByUserId(user_id);
868
+ if (0 > fid || fid > 9)
869
+ throw new Error('fid must be between 0 and 9');
870
+ // Allocate and initialize the buffer
871
+ const data = Buffer.alloc(3);
872
+ // Fill the buffer with user data
873
+ data.writeUInt16LE(user.uid, 0);
874
+ data.writeUIntLE(fid, 2, 1);
875
+ this._zkTcp.replyId++;
876
+ const packet = createTCPHeader(COMMANDS.CMD_USERTEMP_RRQ, this._zkTcp.sessionId, this._zkTcp.replyId, data);
877
+ let fingerSize = 0;
878
+ let fingerTemplate = Buffer.from([]);
879
+ return await new Promise((resolve, reject) => {
880
+ let timeout;
881
+ const cleanup = () => {
882
+ if (this._zkTcp.socket) {
883
+ this._zkTcp.socket.removeListener('data', receiveData);
884
+ }
885
+ if (timeout)
886
+ clearTimeout(timeout);
887
+ };
888
+ let timer = () => setTimeout(() => {
889
+ cleanup();
890
+ reject(new Error('Time Out, Could not retrieve data'));
891
+ }, this._zkTcp.timeout);
892
+ const receiveData = (data) => {
893
+ timeout = timer();
894
+ if (data.length === 0)
895
+ return;
896
+ try {
897
+ if (data.length == 0)
898
+ return;
899
+ const headers = decodeTCPHeader(data);
900
+ switch (headers.commandId) {
901
+ case DISCOVERED_CMD.FID_NOT_FOUND:
902
+ throw new Error('Could not retrieve data. maybe finger id not exists?');
903
+ case COMMANDS.CMD_PREPARE_DATA:
904
+ fingerSize = data.readUIntLE(16, 2);
905
+ break;
906
+ case COMMANDS.CMD_DATA:
907
+ // A single 'data' event might contain multiple TCP packets combined by the OS
908
+ // in this method, is possible to get CMD_DATA and CMD_ACK_OK in the same event,
909
+ // so It's important to split data received for remove CMD_ACK_OK headers
910
+ fingerTemplate = Buffer.concat([fingerTemplate, data.subarray(16, fingerSize + 10)]);
911
+ // @ts-ignore
912
+ resolve(fingerTemplate);
913
+ break;
914
+ case COMMANDS.CMD_ACK_OK:
915
+ cleanup();
916
+ // @ts-ignore
917
+ resolve(fingerTemplate);
918
+ return;
919
+ default:
920
+ // If it's not a recognized command but has data, it might be raw template data
921
+ if (headers.commandId > 2000 && headers.commandId < 3000) {
922
+ // Likely another ACK or system msg
923
+ }
924
+ else {
925
+ fingerTemplate = Buffer.concat([fingerTemplate, data]);
926
+ }
927
+ break;
928
+ }
929
+ clearTimeout(timeout);
930
+ }
931
+ catch (e) {
932
+ cleanup();
933
+ reject(e);
934
+ }
935
+ };
936
+ if (this._zkTcp.socket) {
937
+ this._zkTcp.socket.on('data', receiveData);
938
+ this._zkTcp.socket.write(packet, (err) => {
939
+ if (err) {
940
+ cleanup();
941
+ reject(err);
942
+ }
943
+ });
944
+ }
945
+ else {
946
+ reject(new Error('Socket not initialized'));
947
+ }
948
+ });
949
+ }
950
+ catch (err) {
951
+ throw err;
952
+ }
953
+ finally {
954
+ await this._zkTcp.refreshData();
955
+ }
593
956
  }
594
957
  /**
595
- * Gets a user-friendly error message
596
- * @returns A formatted error message
597
- */
598
- toast() {
599
- if (this.err.code === ERROR_TYPES.ECONNRESET) {
600
- return 'Another device is connecting to the device so the connection is interrupted';
958
+ *
959
+ * @param user_id {string} user
960
+ * @param fingers {Finger[]} array of finger templates instances
961
+ * */
962
+ async saveTemplates(user_id, fingers = []) {
963
+ if (fingers.length > 9 || fingers.length == 0)
964
+ throw new Error("maximum finger length is 10 and can't be empty");
965
+ try {
966
+ await this._zkTcp.disableDevice();
967
+ // check users exists
968
+ const user = await this.getUserByUserId(user_id);
969
+ let fpack = Buffer.alloc(0);
970
+ let table = Buffer.alloc(0);
971
+ const fnum = 0x10;
972
+ let tstart = 0;
973
+ for (const finger of fingers) {
974
+ const tfp = finger.repackOnly();
975
+ const tableEntry = Buffer.alloc(11); // b=1, H=2, b=1, I=4 => 1+2+1+4=8? Wait, bHbI is 1+2+1+4=8 bytes
976
+ tableEntry.writeInt8(2, 0);
977
+ tableEntry.writeUInt16LE(user.uid, 1);
978
+ tableEntry.writeInt8(fnum + finger.fid, 3);
979
+ tableEntry.writeUInt32LE(tstart, 4);
980
+ table = Buffer.concat([table, tableEntry]);
981
+ tstart += tfp.length;
982
+ fpack = Buffer.concat([fpack, tfp]);
983
+ }
984
+ let upack;
985
+ if (this._zkTcp.userPacketSize === 28) {
986
+ upack = user.repack29();
987
+ }
988
+ else {
989
+ upack = user.repack73();
990
+ }
991
+ const head = Buffer.alloc(12); // III = 3*4 bytes
992
+ head.writeUInt32LE(upack.length, 0);
993
+ head.writeUInt32LE(table.length, 4);
994
+ head.writeUInt32LE(fpack.length, 8);
995
+ const packet = Buffer.concat([head, upack, table, fpack]);
996
+ const bufferResponse = await this._zkTcp.sendWithBuffer(packet);
997
+ const command = 110;
998
+ const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
999
+ commandString.writeUInt32LE(12, 0);
1000
+ commandString.writeUInt16LE(0, 4);
1001
+ commandString.writeUInt16LE(8, 6);
1002
+ const cmdResponse = await this._zkTcp.executeCmd(command, commandString);
1003
+ if (this._zkTcp.verbose)
1004
+ console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
601
1005
  }
602
- else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
603
- return 'IP of the device is refused';
1006
+ catch (error) {
1007
+ throw error;
1008
+ }
1009
+ finally {
1010
+ await this._zkTcp.refreshData();
1011
+ await this._zkTcp.enableDevice();
1012
+ }
1013
+ }
1014
+ async deleteFinger(user_id, fid) {
1015
+ try {
1016
+ if (!this._users.has(user_id))
1017
+ throw new Error("user_id not exists");
1018
+ const user = await this.getUserByUserId(user_id);
1019
+ const buf = Buffer.alloc(4);
1020
+ buf.writeUInt16LE(user_id ? user.uid : 0, 0);
1021
+ buf.writeUint16LE(fid ? fid : 0, 2);
1022
+ const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
1023
+ return !!reply;
1024
+ }
1025
+ catch (error) {
1026
+ throw new Error("Can't save utemp");
1027
+ }
1028
+ finally {
1029
+ await this._zkTcp.refreshData();
1030
+ }
1031
+ }
1032
+ async enrollInfo(user_id, tempId) {
1033
+ let done = false;
1034
+ try {
1035
+ const userBuf = Buffer.alloc(24);
1036
+ userBuf.write(user_id, 0, 24, 'ascii');
1037
+ let commandString = Buffer.concat([
1038
+ userBuf,
1039
+ Buffer.from([tempId, 1])
1040
+ ]);
1041
+ const sendAckOk = async () => {
1042
+ try {
1043
+ const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this._zkTcp.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
1044
+ this._zkTcp.socket.write(buf);
1045
+ }
1046
+ catch (e) {
1047
+ throw new ZkError(e, COMMANDS.CMD_ACK_OK, this._zkTcp.ip);
1048
+ }
1049
+ };
1050
+ const cancel = await this._zkTcp.cancelCapture();
1051
+ const cmdResponse = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
1052
+ this._zkTcp.timeout = 60000; // 60 seconds timeout
1053
+ let attempts = 3;
1054
+ while (attempts > 0) {
1055
+ if (this._zkTcp.verbose)
1056
+ console.log(`A:${attempts} esperando primer regevent`);
1057
+ let dataRecv = await this._zkTcp.readSocket(17);
1058
+ await sendAckOk();
1059
+ if (dataRecv.length > 16) {
1060
+ const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1061
+ const res = padded.readUInt16LE(16);
1062
+ if (this._zkTcp.verbose)
1063
+ console.log(`res ${res}`);
1064
+ if (res === 0 || res === 6 || res === 4) {
1065
+ if (this._zkTcp.verbose)
1066
+ console.log("posible timeout o reg Fallido");
1067
+ break;
1068
+ }
1069
+ }
1070
+ if (this._zkTcp.verbose)
1071
+ console.log(`A:${attempts} esperando 2do regevent`);
1072
+ dataRecv = await this._zkTcp.readSocket(17);
1073
+ await sendAckOk();
1074
+ if (this._zkTcp.verbose)
1075
+ console.log(dataRecv);
1076
+ if (dataRecv.length > 8) {
1077
+ const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1078
+ const res = padded.readUInt16LE(16);
1079
+ if (this._zkTcp.verbose)
1080
+ console.log(`res ${res}`);
1081
+ if (res === 6 || res === 4) {
1082
+ if (this._zkTcp.verbose)
1083
+ console.log("posible timeout o reg Fallido");
1084
+ break;
1085
+ }
1086
+ else if (res === 0x64) {
1087
+ if (this._zkTcp.verbose)
1088
+ console.log("ok, continue?");
1089
+ attempts--;
1090
+ }
1091
+ }
1092
+ }
1093
+ if (attempts === 0) {
1094
+ const dataRecv = await this._zkTcp.readSocket(17);
1095
+ await sendAckOk();
1096
+ if (this._zkTcp.verbose)
1097
+ console.log(dataRecv.toString('hex'));
1098
+ const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1099
+ let res = padded.readUInt16LE(16);
1100
+ if (this._zkTcp.verbose)
1101
+ console.log(`res ${res}`);
1102
+ if (res === 5) {
1103
+ if (this._zkTcp.verbose)
1104
+ console.log("finger duplicate");
1105
+ }
1106
+ if (res === 6 || res === 4) {
1107
+ if (this._zkTcp.verbose)
1108
+ console.log("posible timeout");
1109
+ }
1110
+ if (res === 0) {
1111
+ const size = padded.readUInt16LE(10);
1112
+ const pos = padded.readUInt16LE(12);
1113
+ if (this._zkTcp.verbose)
1114
+ console.log(`enroll ok ${size} ${pos}`);
1115
+ done = true;
1116
+ }
1117
+ }
1118
+ //this.__sock.setTimeout(this.__timeout);
1119
+ await this._zkTcp.regEvent(0); // TODO: test
1120
+ return done;
1121
+ }
1122
+ catch (error) {
1123
+ throw error;
1124
+ }
1125
+ finally {
1126
+ await this._zkTcp.cancelCapture();
1127
+ await this.verify(user_id);
1128
+ }
1129
+ }
1130
+ async verify(user_id) {
1131
+ try {
1132
+ const user = await this.getUserByUserId(user_id);
1133
+ const command_string = Buffer.alloc(4);
1134
+ command_string.writeUInt32LE(user.uid, 0);
1135
+ const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
1136
+ if (this._zkTcp.verbose)
1137
+ console.log(reply.readUInt16LE(0));
1138
+ return !!reply;
1139
+ }
1140
+ catch (error) {
1141
+ console.error(error);
1142
+ throw error;
604
1143
  }
605
- return this.err.message;
606
1144
  }
607
1145
  /**
608
- * Gets detailed error information
609
- * @returns An object containing error details
1146
+ * Upload a single fingerprint for a given user id
1147
+ * @param user_id {string} user id for customer
1148
+ * @param fingerTemplate {string} finger template in base64 string
1149
+ * @param fid {number} finger id is a number between 0 and 9
1150
+ * @param fp_valid {number} finger flag. e.g., valid=1, duress=3
610
1151
  */
611
- getError() {
612
- return {
613
- err: {
614
- message: this.err.message,
615
- code: this.err.code
616
- },
617
- ip: this.ip,
618
- command: this.command
619
- };
1152
+ async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
1153
+ try {
1154
+ const check_ACK_OK = (buf) => {
1155
+ let resp_cmd = initPacket.readUInt16LE(0);
1156
+ if (resp_cmd === COMMANDS.CMD_ACK_OK)
1157
+ return true;
1158
+ else
1159
+ throw new Error(`received unexpected command: ${resp_cmd}`);
1160
+ };
1161
+ const user = this._users.get(user_id);
1162
+ await this._zkTcp.disableDevice();
1163
+ const prep_struct = Buffer.alloc(4);
1164
+ const fingerBuffer = Buffer.from(fingerTemplate, 'base64');
1165
+ const fp_size = fingerBuffer.length;
1166
+ prep_struct.writeUInt16LE(fp_size, 0);
1167
+ const initPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_PREPARE_DATA, prep_struct);
1168
+ check_ACK_OK(initPacket);
1169
+ const fpPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_DATA, fingerBuffer);
1170
+ check_ACK_OK(fpPacket);
1171
+ const cheksumPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_CHECKSUM_BUFFER, '');
1172
+ check_ACK_OK(cheksumPacket);
1173
+ const checksum = cheksumPacket.readUInt32LE(8);
1174
+ const tmp_wreq = Buffer.alloc(6);
1175
+ tmp_wreq.writeUInt16LE(user.uid, 0);
1176
+ tmp_wreq.writeUIntLE(fid, 2, 1);
1177
+ tmp_wreq.writeUIntLE(fp_valid, 3, 1);
1178
+ tmp_wreq.writeUInt16LE(fp_size, 4);
1179
+ const tmp_wreqPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_TMP_WRITE, tmp_wreq);
1180
+ check_ACK_OK(tmp_wreqPacket);
1181
+ const freeData = await this._zkTcp.executeCmd(COMMANDS.CMD_FREE_DATA, '');
1182
+ return check_ACK_OK(freeData);
1183
+ }
1184
+ catch (err) {
1185
+ throw err;
1186
+ }
1187
+ finally {
1188
+ await this._zkTcp.refreshData();
1189
+ await this._zkTcp.enableDevice();
1190
+ }
1191
+ }
1192
+ }
1193
+
1194
+ class TransactionService {
1195
+ _zkTcp;
1196
+ constructor(zkTcp) {
1197
+ this._zkTcp = zkTcp;
1198
+ }
1199
+ async getAttendances(callbackInProcess = () => { }) {
1200
+ try {
1201
+ // Free any existing buffer data to prepare for a new request
1202
+ if (this._zkTcp.socket) {
1203
+ await this._zkTcp.freeData();
1204
+ }
1205
+ // Request attendance logs and handle chunked data
1206
+ const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
1207
+ // Free buffer data after receiving the attendance logs
1208
+ if (this._zkTcp.socket) {
1209
+ await this._zkTcp.freeData();
1210
+ }
1211
+ // Constants for record processing
1212
+ const RECORD_PACKET_SIZE = 40;
1213
+ // Ensure data.data is a valid buffer
1214
+ if (!data.data || !(data.data instanceof Buffer)) {
1215
+ throw new Error('Invalid data received');
1216
+ }
1217
+ // Process the record data
1218
+ let recordData = data.data.subarray(4); // Skip header
1219
+ const records = [];
1220
+ // Process each attendance record
1221
+ while (recordData.length >= RECORD_PACKET_SIZE) {
1222
+ const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
1223
+ records.push({ ...record, ip: this._zkTcp.ip }); // Add IP address to each record
1224
+ recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
1225
+ }
1226
+ // Return the list of attendance records
1227
+ return { data: records };
1228
+ }
1229
+ catch (err) {
1230
+ // Log and re-throw the error
1231
+ console.error('Error getting attendance records:', err);
1232
+ throw err; // Re-throw the error for handling by the caller
1233
+ }
1234
+ }
1235
+ // Clears the attendance logs on the device
1236
+ async clearAttendanceLog() {
1237
+ try {
1238
+ // Execute the command to clear attendance logs
1239
+ await this._zkTcp.disableDevice();
1240
+ const buf = await this._zkTcp.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
1241
+ await this._zkTcp.refreshData();
1242
+ await this._zkTcp.enableDevice();
1243
+ return !!buf;
1244
+ }
1245
+ catch (err) {
1246
+ // Log the error for debugging purposes
1247
+ console.error('Error clearing attendance log:', err);
1248
+ // Re-throw the error to be handled by the caller
1249
+ throw err;
1250
+ }
620
1251
  }
621
1252
  }
622
1253
 
@@ -652,6 +1283,8 @@ class ZTCP {
652
1283
  verbose = false;
653
1284
  packetNumber = 0;
654
1285
  replyData = Buffer.from([]);
1286
+ _userService;
1287
+ _transactionService;
655
1288
  constructor(ip, port, timeout, comm_key, verbose) {
656
1289
  this.ip = ip;
657
1290
  this.port = port;
@@ -659,6 +1292,8 @@ class ZTCP {
659
1292
  this.replyId = 0;
660
1293
  this.comm_key = comm_key;
661
1294
  this.verbose = verbose;
1295
+ this._userService = new UserService(this);
1296
+ this._transactionService = new TransactionService(this);
662
1297
  }
663
1298
  createSocket(cbError, cbClose) {
664
1299
  return new Promise((resolve, reject) => {
@@ -880,6 +1515,10 @@ class ZTCP {
880
1515
  try {
881
1516
  // Write the message to the socket and wait for a response
882
1517
  const reply = await this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT);
1518
+ if (this.verbose) {
1519
+ let headers = decodeTCPHeader(reply);
1520
+ console.log('command: ', COMMANDS[headers.commandId], 'replyid: ', headers.replyId);
1521
+ }
883
1522
  // Remove TCP header from the response
884
1523
  const rReply = removeTcpHeader(reply);
885
1524
  // Update sessionId for connection command responses
@@ -1029,89 +1668,14 @@ class ZTCP {
1029
1668
  }
1030
1669
  });
1031
1670
  }
1032
- /**
1033
- * reject error when starting request data
1034
- * @return {Record<string, User[] | Error>} when receiving requested data
1035
- */
1036
- async getUsers() {
1037
- try {
1038
- // Free any existing buffer data to prepare for a new request
1039
- if (this.socket) {
1040
- await this.freeData();
1041
- }
1042
- // Request user data
1043
- const data = await this.readWithBuffer(REQUEST_DATA.GET_USERS);
1044
- // Free buffer data after receiving the data
1045
- if (this.socket) {
1046
- await this.freeData();
1047
- }
1048
- // Constants for user data processing
1049
- const USER_PACKET_SIZE = 72;
1050
- // Ensure data.data is a valid buffer
1051
- if (!data.data || !(data.data instanceof Buffer)) {
1052
- throw new Error('Invalid data received');
1053
- }
1054
- let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
1055
- const users = [];
1056
- // Process each user packet
1057
- while (userData.length >= USER_PACKET_SIZE) {
1058
- // Decode user data and add to the users array
1059
- const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
1060
- users.push(user);
1061
- userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
1062
- }
1063
- // Return the list of users
1064
- return { data: users };
1065
- }
1066
- catch (err) {
1067
- // Log the error for debugging
1068
- console.error('Error getting users:', err);
1069
- // Re-throw the error to be handled by the caller
1070
- throw err;
1071
- }
1072
- }
1073
1671
  /**
1074
1672
  *
1075
- * @param {*} ip
1076
- * @param {*} callbackInProcess
1077
- * reject error when starting request data
1078
- * return { data: records, err: Error } when receiving requested data
1079
- */
1080
- async getAttendances(callbackInProcess = () => { }) {
1081
- try {
1082
- // Free any existing buffer data to prepare for a new request
1083
- if (this.socket) {
1084
- await this.freeData();
1085
- }
1086
- // Request attendance logs and handle chunked data
1087
- const data = await this.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
1088
- // Free buffer data after receiving the attendance logs
1089
- if (this.socket) {
1090
- await this.freeData();
1091
- }
1092
- // Constants for record processing
1093
- const RECORD_PACKET_SIZE = 40;
1094
- // Ensure data.data is a valid buffer
1095
- if (!data.data || !(data.data instanceof Buffer)) {
1096
- throw new Error('Invalid data received');
1097
- }
1098
- // Process the record data
1099
- let recordData = data.data.subarray(4); // Skip header
1100
- const records = [];
1101
- // Process each attendance record
1102
- while (recordData.length >= RECORD_PACKET_SIZE) {
1103
- const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
1104
- records.push({ ...record, ip: this.ip }); // Add IP address to each record
1105
- recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
1106
- }
1107
- // Return the list of attendance records
1108
- return { data: records };
1109
- }
1110
- catch (err) {
1111
- // Log and re-throw the error
1112
- console.error('Error getting attendance records:', err);
1113
- throw err; // Re-throw the error for handling by the caller
1114
- }
1673
+ * @param {*} callbackInProcess
1674
+ * reject error when starting request data
1675
+ * return { data: records, err: Error } when receiving requested data
1676
+ */
1677
+ async getAttendances(callbackInProcess = () => { }) {
1678
+ return await this._transactionService.getAttendances(callbackInProcess);
1115
1679
  }
1116
1680
  async freeData() {
1117
1681
  try {
@@ -1307,17 +1871,26 @@ class ZTCP {
1307
1871
  }
1308
1872
  async getSerialNumber() {
1309
1873
  const keyword = '~SerialNumber';
1874
+ let serialNumber = '';
1875
+ let count = 10;
1310
1876
  try {
1311
1877
  // Execute the command to get serial number
1312
- const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1313
- // Extract and format the serial number from the response data
1314
- const serialNumber = data.slice(8) // Skip the first 8 bytes (header)
1315
- .toString('utf-8') // Convert buffer to string
1316
- .replace(`${keyword}=`, '') // Remove the keyword prefix
1317
- .replace('=', '') // Remove sometines last number is a character equal to = or unknow character
1318
- .replace(/\u0000/g, ''); // Remove null characters
1319
- if (serialNumber.length !== 13 && this.verbose) {
1320
- console.warn('Serial number length not equal to 13, check');
1878
+ /**
1879
+ * @dev implemented a counter and a while loop because sometimes serial number parses wrong for some reason
1880
+ * */
1881
+ while (serialNumber.length !== 13 && count > 0) {
1882
+ const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1883
+ // Extract and format the serial number from the response data
1884
+ const SN = data.slice(8) // Skip the first 8 bytes (header)
1885
+ .toString('utf-8') // Convert buffer to string
1886
+ .replace(`${keyword}=`, '') // Remove the keyword prefix
1887
+ .replace('=', '') // Remove sometines last number is a character equal to = or unknow character
1888
+ .replace(/\u0000/g, ''); // Remove null characters
1889
+ if (serialNumber.length !== 13 && this.verbose) {
1890
+ console.warn('Serial number length not equal to 13, check');
1891
+ }
1892
+ count--;
1893
+ serialNumber = SN;
1321
1894
  }
1322
1895
  return serialNumber;
1323
1896
  }
@@ -1429,11 +2002,10 @@ class ZTCP {
1429
2002
  // Execute the command to get the PIN information
1430
2003
  const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1431
2004
  // Extract and format the PIN information from the response data
1432
- // Remove null characters
1433
2005
  return data.slice(8) // Skip the first 8 bytes (header)
1434
2006
  .toString('ascii') // Convert buffer to ASCII string
1435
2007
  .replace(`${keyword}=`, '') // Remove the keyword prefix
1436
- .replace(/\u0000/g, '');
2008
+ .replace(/\u0000/g, ''); // Remove null characters 0x00
1437
2009
  }
1438
2010
  catch (err) {
1439
2011
  // Log the error for debugging
@@ -1485,7 +2057,9 @@ class ZTCP {
1485
2057
  // Execute the command to get firmware information
1486
2058
  const data = await this.executeCmd(1100, '');
1487
2059
  // Extract and return the firmware version from the response data
1488
- return data.slice(8).toString('ascii'); // Skip the first 8 bytes (header) and convert to ASCII string
2060
+ return data.slice(8) // Skip the first 8 bytes (header)
2061
+ .toString('ascii') // convert to ASCII string
2062
+ .replace(/\u0000/g, ''); // remove x00
1489
2063
  }
1490
2064
  catch (err) {
1491
2065
  // Log the error for debugging
@@ -1551,59 +2125,6 @@ class ZTCP {
1551
2125
  throw err;
1552
2126
  }
1553
2127
  }
1554
- async setUser(uid, userid, name, password, role = 0, cardno = 0) {
1555
- try {
1556
- // Validate input parameters
1557
- if (uid <= 0 || uid > 3000 ||
1558
- userid.length > 9 ||
1559
- name.length > 24 ||
1560
- password.length > 8 ||
1561
- typeof role !== 'number' ||
1562
- cardno.toString().length > 10) {
1563
- throw new Error('Invalid input parameters');
1564
- }
1565
- // Allocate and initialize the buffer
1566
- const commandBuffer = Buffer.alloc(72);
1567
- // Fill the buffer with user data
1568
- commandBuffer.writeUInt16LE(uid, 0);
1569
- commandBuffer.writeUInt16LE(role, 2);
1570
- commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
1571
- commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
1572
- commandBuffer.writeUInt16LE(cardno, 35);
1573
- commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
1574
- commandBuffer.write(userid.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
1575
- // Send the command and return the result
1576
- const created = await this.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
1577
- return !!created;
1578
- }
1579
- catch (err) {
1580
- // Log error details for debugging
1581
- console.error('Error setting user:', err);
1582
- // Re-throw error for upstream handling
1583
- throw err;
1584
- }
1585
- }
1586
- async deleteUser(uid) {
1587
- try {
1588
- // Validate input parameter
1589
- if (uid <= 0 || uid > 3000) {
1590
- throw new Error('Invalid UID: must be between 1 and 3000');
1591
- }
1592
- // Allocate and initialize the buffer
1593
- const commandBuffer = Buffer.alloc(72);
1594
- // Write UID to the buffer
1595
- commandBuffer.writeUInt16LE(uid, 0);
1596
- // Send the delete command and return the result
1597
- const deleted = await this.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
1598
- return !!deleted;
1599
- }
1600
- catch (err) {
1601
- // Log error details for debugging
1602
- console.error('Error deleting user:', err);
1603
- // Re-throw error for upstream handling
1604
- throw err;
1605
- }
1606
- }
1607
2128
  async getAttendanceSize() {
1608
2129
  try {
1609
2130
  // Execute command to get free sizes
@@ -1620,22 +2141,27 @@ class ZTCP {
1620
2141
  }
1621
2142
  // Clears the attendance logs on the device
1622
2143
  async clearAttendanceLog() {
1623
- try {
1624
- // Execute the command to clear attendance logs
1625
- return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
1626
- }
1627
- catch (err) {
1628
- // Log the error for debugging purposes
1629
- console.error('Error clearing attendance log:', err);
1630
- // Re-throw the error to be handled by the caller
1631
- throw err;
1632
- }
2144
+ return await this._transactionService.clearAttendanceLog();
1633
2145
  }
1634
- // Clears all data on the device
1635
- async clearData() {
2146
+ /**
2147
+ * Clears all data on the device
2148
+ * @value 1 Attendance records
2149
+ * @value 2 Fingerprint templates
2150
+ * @value 3 None
2151
+ * @value 4 Operation records
2152
+ * @value 5 User information
2153
+ * @default 0 Delete all
2154
+ */
2155
+ async clearData(value) {
1636
2156
  try {
1637
2157
  // Execute the command to clear all data
1638
- return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, '');
2158
+ await this.disableDevice();
2159
+ if (!value)
2160
+ value = 3;
2161
+ const buf = await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, value.toString());
2162
+ await this.refreshData();
2163
+ await this.enableDevice();
2164
+ return !!buf;
1639
2165
  }
1640
2166
  catch (err) {
1641
2167
  // Log the error for debugging purposes
@@ -1682,33 +2208,7 @@ class ZTCP {
1682
2208
  * @returns {Record<string, Finger[]>}
1683
2209
  */
1684
2210
  async getTemplates(callbackInProcess = () => { }) {
1685
- let templates = [];
1686
- try {
1687
- await this.getSizes();
1688
- if (this.fp_count == 0)
1689
- return { data: [] };
1690
- await this.freeData();
1691
- await this.disableDevice();
1692
- const Buffer = await this.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
1693
- let templateData = Buffer.data.subarray(4);
1694
- let totalSize = Buffer.data.readUIntLE(0, 4);
1695
- while (totalSize) {
1696
- const buf = templateData.subarray(0, 6);
1697
- const size = buf.readUIntLE(0, 2);
1698
- templates.push(new Finger(buf.readUIntLE(2, 2), buf.readUIntLE(4, 1), buf.readUIntLE(5, 1), templateData.subarray(6, size)));
1699
- templateData = templateData.subarray(size);
1700
- totalSize -= size;
1701
- }
1702
- return { data: templates };
1703
- }
1704
- catch (err) {
1705
- this.verbose && console.log("Error getting templates", err);
1706
- return { data: templates };
1707
- }
1708
- finally {
1709
- await this.enableDevice();
1710
- await this.freeData();
1711
- }
2211
+ return await this._userService.getTemplates(callbackInProcess);
1712
2212
  }
1713
2213
  /**
1714
2214
  * Return size
@@ -1784,197 +2284,6 @@ class ZTCP {
1784
2284
  throw new ZkError(e, COMMANDS.CMD_DATA, this.ip);
1785
2285
  }
1786
2286
  }
1787
- /**
1788
- * save user and template
1789
- *
1790
- * @param {User | number | string} user - User class object | uid | user_id
1791
- * @param {Finger[]} fingers - Array of finger class. `0 <= index <= 9`
1792
- */
1793
- async saveUserTemplate(user, fingers = []) {
1794
- if (fingers.length > 9 || fingers.length == 0)
1795
- throw new Error("maximum finger length is 10 and can't be empty");
1796
- try {
1797
- await this.disableDevice();
1798
- const users = await this.getUsers();
1799
- //check users exists
1800
- if (!users.data.some(u => u.uid == user.uid || +u.user_id == +user.user_id))
1801
- throw new Error("error validating user input");
1802
- if (!(user instanceof User)) {
1803
- let tusers = users.data.filter(x => x.uid === +user.uid);
1804
- if (tusers.length === 1) {
1805
- user = tusers[0];
1806
- }
1807
- else {
1808
- tusers = users.data.filter(x => x.user_id === String(user));
1809
- if (tusers.length === 1) {
1810
- user = tusers[0];
1811
- }
1812
- else {
1813
- throw new Error("Can't find user");
1814
- }
1815
- }
1816
- }
1817
- if (fingers instanceof Finger) {
1818
- fingers = [fingers];
1819
- }
1820
- let fpack = Buffer.alloc(0);
1821
- let table = Buffer.alloc(0);
1822
- const fnum = 0x10;
1823
- let tstart = 0;
1824
- for (const finger of fingers) {
1825
- const tfp = finger.repackOnly();
1826
- const tableEntry = Buffer.alloc(11); // b=1, H=2, b=1, I=4 => 1+2+1+4=8? Wait, bHbI is 1+2+1+4=8 bytes
1827
- tableEntry.writeInt8(2, 0);
1828
- tableEntry.writeUInt16LE(user.uid, 1);
1829
- tableEntry.writeInt8(fnum + finger.fid, 3);
1830
- tableEntry.writeUInt32LE(tstart, 4);
1831
- table = Buffer.concat([table, tableEntry]);
1832
- tstart += tfp.length;
1833
- fpack = Buffer.concat([fpack, tfp]);
1834
- }
1835
- let upack;
1836
- if (this.userPacketSize === 28) {
1837
- upack = user.repack29();
1838
- }
1839
- else {
1840
- upack = user.repack73();
1841
- }
1842
- const head = Buffer.alloc(12); // III = 3*4 bytes
1843
- head.writeUInt32LE(upack.length, 0);
1844
- head.writeUInt32LE(table.length, 4);
1845
- head.writeUInt32LE(fpack.length, 8);
1846
- const packet = Buffer.concat([head, upack, table, fpack]);
1847
- const bufferResponse = await this.sendWithBuffer(packet);
1848
- const command = 110;
1849
- const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
1850
- commandString.writeUInt32LE(12, 0);
1851
- commandString.writeUInt16LE(0, 4);
1852
- commandString.writeUInt16LE(8, 6);
1853
- const cmdResponse = await this.executeCmd(command, commandString);
1854
- if (this.verbose)
1855
- console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
1856
- }
1857
- catch (error) {
1858
- throw error;
1859
- }
1860
- finally {
1861
- await this.refreshData();
1862
- await this.enableDevice();
1863
- }
1864
- }
1865
- async deleteFinger(uid, fid) {
1866
- try {
1867
- const buf = Buffer.alloc(4);
1868
- buf.writeUInt16LE(uid, 0);
1869
- buf.writeUint16LE(fid, 2);
1870
- const reply = await this.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
1871
- return !!reply;
1872
- }
1873
- catch (error) {
1874
- throw new Error("Can't save utemp");
1875
- }
1876
- finally {
1877
- await this.refreshData();
1878
- }
1879
- }
1880
- async enrollUser(uid, tempId, userId = '') {
1881
- let done = false;
1882
- try {
1883
- //validate user exists
1884
- const users = await this.getUsers();
1885
- const filteredUsers = users.data.filter(x => x.uid === uid);
1886
- if (filteredUsers.length >= 1) {
1887
- userId = filteredUsers[0].user_id;
1888
- }
1889
- else {
1890
- throw new Error("user not found");
1891
- }
1892
- const userBuf = Buffer.alloc(24);
1893
- userBuf.write(userId.toString(), 0, 24, 'ascii');
1894
- let commandString = Buffer.concat([
1895
- userBuf,
1896
- Buffer.from([tempId, 1])
1897
- ]);
1898
- const cancel = await this.cancelCapture();
1899
- const cmdResponse = await this.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
1900
- this.timeout = 60000; // 60 seconds timeout
1901
- let attempts = 3;
1902
- while (attempts > 0) {
1903
- if (this.verbose)
1904
- console.log(`A:${attempts} esperando primer regevent`);
1905
- let dataRecv = await this.readSocket(17);
1906
- await this.ackOk();
1907
- if (dataRecv.length > 16) {
1908
- const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1909
- const res = padded.readUInt16LE(16);
1910
- if (this.verbose)
1911
- console.log(`res ${res}`);
1912
- if (res === 0 || res === 6 || res === 4) {
1913
- if (this.verbose)
1914
- console.log("posible timeout o reg Fallido");
1915
- break;
1916
- }
1917
- }
1918
- if (this.verbose)
1919
- console.log(`A:${attempts} esperando 2do regevent`);
1920
- dataRecv = await this.readSocket(17);
1921
- await this.ackOk();
1922
- if (this.verbose)
1923
- console.log(dataRecv);
1924
- if (dataRecv.length > 8) {
1925
- const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1926
- const res = padded.readUInt16LE(16);
1927
- if (this.verbose)
1928
- console.log(`res ${res}`);
1929
- if (res === 6 || res === 4) {
1930
- if (this.verbose)
1931
- console.log("posible timeout o reg Fallido");
1932
- break;
1933
- }
1934
- else if (res === 0x64) {
1935
- if (this.verbose)
1936
- console.log("ok, continue?");
1937
- attempts--;
1938
- }
1939
- }
1940
- }
1941
- if (attempts === 0) {
1942
- const dataRecv = await this.readSocket(17);
1943
- await this.ackOk();
1944
- if (this.verbose)
1945
- console.log(dataRecv.toString('hex'));
1946
- const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1947
- let res = padded.readUInt16LE(16);
1948
- if (this.verbose)
1949
- console.log(`res ${res}`);
1950
- if (res === 5) {
1951
- if (this.verbose)
1952
- console.log("finger duplicate");
1953
- }
1954
- if (res === 6 || res === 4) {
1955
- if (this.verbose)
1956
- console.log("posible timeout");
1957
- }
1958
- if (res === 0) {
1959
- const size = padded.readUInt16LE(10);
1960
- const pos = padded.readUInt16LE(12);
1961
- if (this.verbose)
1962
- console.log(`enroll ok ${size} ${pos}`);
1963
- done = true;
1964
- }
1965
- }
1966
- //this.__sock.setTimeout(this.__timeout);
1967
- await this.regEvent(0); // TODO: test
1968
- return done;
1969
- }
1970
- catch (error) {
1971
- throw error;
1972
- }
1973
- finally {
1974
- await this.cancelCapture();
1975
- await this.verifyUser(undefined);
1976
- }
1977
- }
1978
2287
  async readSocket(length, cb = null) {
1979
2288
  let replyBufer = Buffer.from([]);
1980
2289
  let totalPackets = 0;
@@ -2024,15 +2333,6 @@ class ZTCP {
2024
2333
  throw new ZkError(e, COMMANDS.CMD_REG_EVENT, this.ip);
2025
2334
  }
2026
2335
  }
2027
- async ackOk() {
2028
- try {
2029
- const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
2030
- this.socket.write(buf);
2031
- }
2032
- catch (e) {
2033
- throw new ZkError(e, COMMANDS.CMD_ACK_OK, this.ip);
2034
- }
2035
- }
2036
2336
  async cancelCapture() {
2037
2337
  try {
2038
2338
  const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, '');
@@ -2042,22 +2342,6 @@ class ZTCP {
2042
2342
  throw new ZkError(e, COMMANDS.CMD_CANCELCAPTURE, this.ip);
2043
2343
  }
2044
2344
  }
2045
- async verifyUser(uid) {
2046
- try {
2047
- let command_string = '';
2048
- if (uid) {
2049
- command_string = Buffer.alloc(4);
2050
- command_string.writeUInt32LE(uid, 0);
2051
- }
2052
- const reply = await this.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
2053
- if (this.verbose)
2054
- console.log(reply.readUInt16LE(0));
2055
- return !!reply;
2056
- }
2057
- catch (error) {
2058
- console.error(error);
2059
- }
2060
- }
2061
2345
  async restartDevice() {
2062
2346
  try {
2063
2347
  await this.executeCmd(COMMANDS.CMD_RESTART, '');
@@ -2442,7 +2726,7 @@ class ZUDP {
2442
2726
  }
2443
2727
  async clearAttendanceLog() {
2444
2728
  try {
2445
- return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
2729
+ return !!await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
2446
2730
  }
2447
2731
  catch (err) {
2448
2732
  console.error('Error clearing attendance log:', err);
@@ -2451,7 +2735,7 @@ class ZUDP {
2451
2735
  }
2452
2736
  async clearData() {
2453
2737
  try {
2454
- return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
2738
+ return !!await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
2455
2739
  }
2456
2740
  catch (err) {
2457
2741
  console.error('Error clearing data:', err);
@@ -2642,7 +2926,7 @@ class Zklib {
2642
2926
  }
2643
2927
  }
2644
2928
  async getUsers() {
2645
- return this.functionWrapper(() => this.ztcp.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
2929
+ return this.functionWrapper(() => this.ztcp._userService.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
2646
2930
  }
2647
2931
  async getTime() {
2648
2932
  return this.functionWrapper(() => this.ztcp.getTime(), () => this.zudp.getTime(), 'GET_TIME');
@@ -2692,11 +2976,22 @@ class Zklib {
2692
2976
  async getFirmware() {
2693
2977
  return this.functionWrapper(() => this.ztcp.getFirmware(), async () => { throw new Error('UDP get firmware not supported'); }, 'GET_FIRMWARE');
2694
2978
  }
2695
- async setUser(uid, userid, name, password, role = 0, cardno = 0) {
2696
- return this.functionWrapper(() => this.ztcp.setUser(uid, userid, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
2979
+ /** Update or create a user if user id/pin not exists
2980
+ * @param user_id {string} user id/pin for customer
2981
+ * @param name {string} user name
2982
+ * @param password {string} user password
2983
+ * @param role {number} role/privilege id number
2984
+ * @param cardno {number} card number/id
2985
+ */
2986
+ async setUser(user_id, name, password, role = 0, cardno = 0) {
2987
+ return this.functionWrapper(() => this.ztcp._userService.setUser(user_id, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
2697
2988
  }
2698
- async deleteUser(uid) {
2699
- return this.functionWrapper(() => this.ztcp.deleteUser(uid), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
2989
+ /**
2990
+ * Delete user by a given user id/pin
2991
+ * @param user_id {string}
2992
+ */
2993
+ async deleteUser(user_id) {
2994
+ return this.functionWrapper(() => this.ztcp._userService.DeleteUser(user_id), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
2700
2995
  }
2701
2996
  async getAttendanceSize() {
2702
2997
  return this.functionWrapper(() => this.ztcp.getAttendanceSize(), async () => { throw new Error('UDP get attendance size not supported'); }, 'GET_ATTENDANCE_SIZE');
@@ -2710,25 +3005,55 @@ class Zklib {
2710
3005
  async getTemplates() {
2711
3006
  return this.functionWrapper(() => this.ztcp.getTemplates(), async () => { throw new Error('UDP get templates not supported'); }, 'GET_TEMPLATES');
2712
3007
  }
2713
- async saveUserTemplate(user, fingers = []) {
2714
- return await this.functionWrapper(async () => await this.ztcp.saveUserTemplate(user, fingers), async () => { throw new Error('UDP save user template not supported'); }, 'SAVE_USER_TEMPLATE');
3008
+ /**
3009
+ * Get a user template for a given user id/pin and finger id
3010
+ * @param user_id {string} user id/pin
3011
+ * @param fid {number} finger index
3012
+ */
3013
+ async getUserTemplate(user_id, fid) {
3014
+ return await this.functionWrapper(async () => await this.ztcp._userService.DownloadFp(user_id, fid), async () => { throw new Error('UDP get user template not implemented'); }, 'GET_USER_TEMPLATE');
3015
+ }
3016
+ /**
3017
+ * Upload a single fingerprint for a given user id
3018
+ * @param user_id {string} user id/pin for customer
3019
+ * @param fingerTemplate {string} finger template in base64 string
3020
+ * @param fid {number} finger id is a number between 0 and 9
3021
+ * @param fp_valid {number} finger flag. e.g., valid=1, duress=3
3022
+ */
3023
+ async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
3024
+ return await this.functionWrapper(async () => await this.ztcp._userService.uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid), async () => { throw new Error('UDP get user template not implemented'); }, 'UPLOAD_USER_TEMPLATE');
3025
+ }
3026
+ /**
3027
+ * save user and template
3028
+ *
3029
+ * @param {string} user_id - user id for customer
3030
+ * @param {Finger[]} fingers - Array of finger class
3031
+ */
3032
+ async saveUserTemplate(user_id, fingers = []) {
3033
+ return await this.functionWrapper(async () => await this.ztcp._userService.saveTemplates(user_id, fingers), async () => { throw new Error('UDP save user template not supported'); }, 'SAVE_USER_TEMPLATE');
2715
3034
  }
2716
- async deleteFinger(uid, fid) {
3035
+ /**
3036
+ * Delete a single finger template by user id and finger index
3037
+ * @param user_id {string} user id/pin for customer
3038
+ * @param fid {number} finger index
3039
+ */
3040
+ async deleteFinger(user_id, fid) {
2717
3041
  if (fid > 9 || 0 > fid)
2718
3042
  throw new Error("fid params out of index");
2719
- if (uid > 3000 || uid < 1)
2720
- throw new Error("fid params out of index");
2721
- return this.functionWrapper(() => this.ztcp.deleteFinger(uid, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
3043
+ return this.functionWrapper(() => this.ztcp._userService.deleteFinger(user_id, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
2722
3044
  }
2723
- async enrollUser(uid, temp_id, user_id) {
3045
+ /**
3046
+ * Start to enroll a finger template
3047
+ * @param user_id {string} user id/pin for customer
3048
+ * @param temp_id {number} finger index
3049
+ */
3050
+ async enrollUser(user_id, temp_id) {
2724
3051
  if (temp_id < 0 || temp_id > 9)
2725
3052
  throw new Error("temp_id out of range 0-9");
2726
- if (uid < 1 || uid > 3000)
2727
- throw new Error("uid out of range 1-3000");
2728
- return this.functionWrapper(() => this.ztcp.enrollUser(uid, temp_id, user_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
3053
+ return this.functionWrapper(() => this.ztcp._userService.enrollInfo(user_id, temp_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
2729
3054
  }
2730
- async verifyUser(uid) {
2731
- return this.functionWrapper(() => this.ztcp.verifyUser(uid), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
3055
+ async verifyUser(user_id) {
3056
+ return this.functionWrapper(() => this.ztcp._userService.verify(user_id), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
2732
3057
  }
2733
3058
  async restartDevice() {
2734
3059
  return this.functionWrapper(() => this.ztcp.restartDevice(), async () => { throw new Error('UDP restart device not supported'); }, 'RESTART_DEVICE');