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.cjs.js CHANGED
@@ -26,99 +26,190 @@ var dgram__namespace = /*#__PURE__*/_interopNamespaceDefault(dgram);
26
26
  var COMMANDS;
27
27
  (function (COMMANDS) {
28
28
  COMMANDS[COMMANDS["CMD_ACK_DATA"] = 2002] = "CMD_ACK_DATA";
29
+ /** There was an error when processing the request.*/
29
30
  COMMANDS[COMMANDS["CMD_ACK_ERROR"] = 2001] = "CMD_ACK_ERROR";
30
31
  COMMANDS[COMMANDS["CMD_ACK_ERROR_CMD"] = 65533] = "CMD_ACK_ERROR_CMD";
31
32
  COMMANDS[COMMANDS["CMD_ACK_ERROR_DATA"] = 65531] = "CMD_ACK_ERROR_DATA";
32
33
  COMMANDS[COMMANDS["CMD_ACK_ERROR_INIT"] = 65532] = "CMD_ACK_ERROR_INIT";
34
+ /** [0xD0, 0x07] The request was processed sucessfully. */
33
35
  COMMANDS[COMMANDS["CMD_ACK_OK"] = 2000] = "CMD_ACK_OK";
34
36
  COMMANDS[COMMANDS["CMD_ACK_REPEAT"] = 2004] = "CMD_ACK_REPEAT";
35
37
  COMMANDS[COMMANDS["CMD_ACK_RETRY"] = 2003] = "CMD_ACK_RETRY";
38
+ /** [0xD5, 0x07] Connection not authorized. */
36
39
  COMMANDS[COMMANDS["CMD_ACK_UNAUTH"] = 2005] = "CMD_ACK_UNAUTH";
40
+ /** Received unknown command. */
37
41
  COMMANDS[COMMANDS["CMD_ACK_UNKNOWN"] = 65535] = "CMD_ACK_UNKNOWN";
42
+ /** Request attendance log. */
38
43
  COMMANDS[COMMANDS["CMD_ATTLOG_RRQ"] = 13] = "CMD_ATTLOG_RRQ";
44
+ /** [0x4E, 0x04] Request to begin session using commkey. */
39
45
  COMMANDS[COMMANDS["CMD_AUTH"] = 1102] = "CMD_AUTH";
46
+ /** Disable normal authentication of users. */
40
47
  COMMANDS[COMMANDS["CMD_CANCELCAPTURE"] = 62] = "CMD_CANCELCAPTURE";
48
+ /** Capture fingerprint picture. */
41
49
  COMMANDS[COMMANDS["CMD_CAPTUREFINGER"] = 1009] = "CMD_CAPTUREFINGER";
50
+ /** Capture the entire image. */
42
51
  COMMANDS[COMMANDS["CMD_CAPTUREIMAGE"] = 1012] = "CMD_CAPTUREIMAGE";
52
+ /** Change transmission speed. */
43
53
  COMMANDS[COMMANDS["CMD_CHANGE_SPEED"] = 1101] = "CMD_CHANGE_SPEED";
54
+ /** [0x77, 0x00] Get checksum of machine's buffer. */
44
55
  COMMANDS[COMMANDS["CMD_CHECKSUM_BUFFER"] = 119] = "CMD_CHECKSUM_BUFFER";
56
+ /** Restore access control to default. */
45
57
  COMMANDS[COMMANDS["CMD_CLEAR_ACC"] = 32] = "CMD_CLEAR_ACC";
58
+ /** Clears admins privileges. */
46
59
  COMMANDS[COMMANDS["CMD_CLEAR_ADMIN"] = 20] = "CMD_CLEAR_ADMIN";
60
+ /** Delete attendance record. */
47
61
  COMMANDS[COMMANDS["CMD_CLEAR_ATTLOG"] = 15] = "CMD_CLEAR_ATTLOG";
62
+ /** Delete data. */
48
63
  COMMANDS[COMMANDS["CMD_CLEAR_DATA"] = 14] = "CMD_CLEAR_DATA";
64
+ /** Clear screen captions. */
49
65
  COMMANDS[COMMANDS["CMD_CLEAR_LCD"] = 67] = "CMD_CLEAR_LCD";
66
+ /** Delete operations log. */
50
67
  COMMANDS[COMMANDS["CMD_CLEAR_OPLOG"] = 33] = "CMD_CLEAR_OPLOG";
68
+ /** [0xE8, 0x03] Begin connection. */
51
69
  COMMANDS[COMMANDS["CMD_CONNECT"] = 1000] = "CMD_CONNECT";
70
+ /** [0xDD, 0x05] Data packet. */
52
71
  COMMANDS[COMMANDS["CMD_DATA"] = 1501] = "CMD_DATA";
72
+ /** Indicates that it is ready to receive data. */
53
73
  COMMANDS[COMMANDS["CMD_DATA_RDY"] = 1504] = "CMD_DATA_RDY";
74
+ /** Read/Write a large data set. */
54
75
  COMMANDS[COMMANDS["CMD_DATA_WRRQ"] = 1503] = "CMD_DATA_WRRQ";
76
+ /** Read saved data. */
55
77
  COMMANDS[COMMANDS["CMD_DB_RRQ"] = 7] = "CMD_DB_RRQ";
78
+ /** Deletes fingerprint template. */
56
79
  COMMANDS[COMMANDS["CMD_DEL_FPTMP"] = 134] = "CMD_DEL_FPTMP";
80
+ /** Delete short message. */
57
81
  COMMANDS[COMMANDS["CMD_DELETE_SMS"] = 72] = "CMD_DELETE_SMS";
82
+ /** Delete user short message. */
58
83
  COMMANDS[COMMANDS["CMD_DELETE_UDATA"] = 74] = "CMD_DELETE_UDATA";
84
+ /** Delete user. */
59
85
  COMMANDS[COMMANDS["CMD_DELETE_USER"] = 18] = "CMD_DELETE_USER";
86
+ /** Delete user fingerprint template. */
60
87
  COMMANDS[COMMANDS["CMD_DELETE_USERTEMP"] = 19] = "CMD_DELETE_USERTEMP";
88
+ /** Disables fingerprint, rfid reader and keyboard. */
61
89
  COMMANDS[COMMANDS["CMD_DISABLEDEVICE"] = 1003] = "CMD_DISABLEDEVICE";
90
+ /** Get door state. */
62
91
  COMMANDS[COMMANDS["CMD_DOORSTATE_RRQ"] = 75] = "CMD_DOORSTATE_RRQ";
92
+ /** Clear Mifare card. */
63
93
  COMMANDS[COMMANDS["CMD_EMPTY_MIFARE"] = 78] = "CMD_EMPTY_MIFARE";
94
+ /** Enables the ":" in screen clock. */
64
95
  COMMANDS[COMMANDS["CMD_ENABLE_CLOCK"] = 57] = "CMD_ENABLE_CLOCK";
96
+ /** Change machine state to "normal work". */
65
97
  COMMANDS[COMMANDS["CMD_ENABLEDEVICE"] = 1002] = "CMD_ENABLEDEVICE";
98
+ /** [0xE9, 0x03] Disconnect. */
66
99
  COMMANDS[COMMANDS["CMD_EXIT"] = 1001] = "CMD_EXIT";
100
+ /** [0xDE, 0x05] Release buffer used for data transmission. */
67
101
  COMMANDS[COMMANDS["CMD_FREE_DATA"] = 1502] = "CMD_FREE_DATA";
102
+ /** Request machine status (remaining space). */
68
103
  COMMANDS[COMMANDS["CMD_GET_FREE_SIZES"] = 50] = "CMD_GET_FREE_SIZES";
104
+ /** Request max size for users id. */
69
105
  COMMANDS[COMMANDS["CMD_GET_PINWIDTH"] = 69] = "CMD_GET_PINWIDTH";
106
+ /** Request machine time. */
70
107
  COMMANDS[COMMANDS["CMD_GET_TIME"] = 201] = "CMD_GET_TIME";
71
108
  COMMANDS[COMMANDS["CMD_GET_USERTEMP"] = 88] = "CMD_GET_USERTEMP";
109
+ /** Request the firmware edition. */
72
110
  COMMANDS[COMMANDS["CMD_GET_VERSION"] = 1100] = "CMD_GET_VERSION";
111
+ /** Get group timezone. */
73
112
  COMMANDS[COMMANDS["CMD_GRPTZ_RRQ"] = 25] = "CMD_GRPTZ_RRQ";
113
+ /** Set group timezone. */
74
114
  COMMANDS[COMMANDS["CMD_GRPTZ_WRQ"] = 26] = "CMD_GRPTZ_WRQ";
115
+ /** Read operations log. */
75
116
  COMMANDS[COMMANDS["CMD_OPLOG_RRQ"] = 34] = "CMD_OPLOG_RRQ";
117
+ /** Read configuration value of the machine. */
76
118
  COMMANDS[COMMANDS["CMD_OPTIONS_RRQ"] = 11] = "CMD_OPTIONS_RRQ";
119
+ /** Change configuration value of the machine. */
77
120
  COMMANDS[COMMANDS["CMD_OPTIONS_WRQ"] = 12] = "CMD_OPTIONS_WRQ";
121
+ /** Shut-down machine. */
78
122
  COMMANDS[COMMANDS["CMD_POWEROFF"] = 1005] = "CMD_POWEROFF";
123
+ /** [0xDC, 0x05] Prepare for data transmission. */
79
124
  COMMANDS[COMMANDS["CMD_PREPARE_DATA"] = 1500] = "CMD_PREPARE_DATA";
125
+ /** [0xF5, 0x03] Refresh the machine stored data. */
80
126
  COMMANDS[COMMANDS["CMD_REFRESHDATA"] = 1013] = "CMD_REFRESHDATA";
127
+ /** Refresh the configuration parameters. */
81
128
  COMMANDS[COMMANDS["CMD_REFRESHOPTION"] = 1014] = "CMD_REFRESHOPTION";
129
+ /** Realtime events. */
82
130
  COMMANDS[COMMANDS["CMD_REG_EVENT"] = 500] = "CMD_REG_EVENT";
131
+ /** Restart machine. */
83
132
  COMMANDS[COMMANDS["CMD_RESTART"] = 1004] = "CMD_RESTART";
133
+ /** Change machine state to "awaken". */
84
134
  COMMANDS[COMMANDS["CMD_RESUME"] = 1007] = "CMD_RESUME";
135
+ /** Set machine time. */
85
136
  COMMANDS[COMMANDS["CMD_SET_TIME"] = 202] = "CMD_SET_TIME";
137
+ /** Change machine state to "idle". */
86
138
  COMMANDS[COMMANDS["CMD_SLEEP"] = 1006] = "CMD_SLEEP";
139
+ /** Download short message. */
87
140
  COMMANDS[COMMANDS["CMD_SMS_RRQ"] = 71] = "CMD_SMS_RRQ";
141
+ /** Upload short message. */
88
142
  COMMANDS[COMMANDS["CMD_SMS_WRQ"] = 70] = "CMD_SMS_WRQ";
143
+ /** Start enroll procedure. */
89
144
  COMMANDS[COMMANDS["CMD_STARTENROLL"] = 61] = "CMD_STARTENROLL";
145
+ /** Set the machine to authentication state. */
90
146
  COMMANDS[COMMANDS["CMD_STARTVERIFY"] = 60] = "CMD_STARTVERIFY";
147
+ /** Query state. */
91
148
  COMMANDS[COMMANDS["CMD_STATE_RRQ"] = 64] = "CMD_STATE_RRQ";
149
+ /** Test if fingerprint exists. */
92
150
  COMMANDS[COMMANDS["CMD_TEST_TEMP"] = 1011] = "CMD_TEST_TEMP";
151
+ /** Test voice. */
93
152
  COMMANDS[COMMANDS["CMD_TESTVOICE"] = 1017] = "CMD_TESTVOICE";
153
+ /** [0x77, 0x00] Transfer fp template from buffer. */
94
154
  COMMANDS[COMMANDS["CMD_TMP_WRITE"] = 87] = "CMD_TMP_WRITE";
155
+ /** Get device timezones. */
95
156
  COMMANDS[COMMANDS["CMD_TZ_RRQ"] = 27] = "CMD_TZ_RRQ";
157
+ /** Set device timezones. */
96
158
  COMMANDS[COMMANDS["CMD_TZ_WRQ"] = 28] = "CMD_TZ_WRQ";
159
+ /** Set user short message. */
97
160
  COMMANDS[COMMANDS["CMD_UDATA_WRQ"] = 73] = "CMD_UDATA_WRQ";
161
+ /** Get group combination to unlock. */
98
162
  COMMANDS[COMMANDS["CMD_ULG_RRQ"] = 29] = "CMD_ULG_RRQ";
163
+ /** Set group combination to unlock. */
99
164
  COMMANDS[COMMANDS["CMD_ULG_WRQ"] = 30] = "CMD_ULG_WRQ";
165
+ /** Unlock door for a specified amount of time. */
100
166
  COMMANDS[COMMANDS["CMD_UNLOCK"] = 31] = "CMD_UNLOCK";
167
+ /** Upload user data. */
101
168
  COMMANDS[COMMANDS["CMD_USER_WRQ"] = 8] = "CMD_USER_WRQ";
169
+ /** Read user group. */
102
170
  COMMANDS[COMMANDS["CMD_USERGRP_RRQ"] = 21] = "CMD_USERGRP_RRQ";
171
+ /** Set user group. */
103
172
  COMMANDS[COMMANDS["CMD_USERGRP_WRQ"] = 22] = "CMD_USERGRP_WRQ";
173
+ /** [0x09, 0x00] Read user fingerprint template. */
104
174
  COMMANDS[COMMANDS["CMD_USERTEMP_RRQ"] = 9] = "CMD_USERTEMP_RRQ";
175
+ /** Upload user fingerprint template. */
105
176
  COMMANDS[COMMANDS["CMD_USERTEMP_WRQ"] = 10] = "CMD_USERTEMP_WRQ";
177
+ /** Get user timezones. */
106
178
  COMMANDS[COMMANDS["CMD_USERTZ_RRQ"] = 23] = "CMD_USERTZ_RRQ";
179
+ /** Set the user timezones. */
107
180
  COMMANDS[COMMANDS["CMD_USERTZ_WRQ"] = 24] = "CMD_USERTZ_WRQ";
181
+ /** Read verification style of a given user. */
108
182
  COMMANDS[COMMANDS["CMD_VERIFY_RRQ"] = 80] = "CMD_VERIFY_RRQ";
183
+ /** Change verification style of a given user. */
109
184
  COMMANDS[COMMANDS["CMD_VERIFY_WRQ"] = 79] = "CMD_VERIFY_WRQ";
185
+ /** Prints chars to the device screen. */
110
186
  COMMANDS[COMMANDS["CMD_WRITE_LCD"] = 66] = "CMD_WRITE_LCD";
187
+ /** Write data to Mifare card. */
111
188
  COMMANDS[COMMANDS["CMD_WRITE_MIFARE"] = 76] = "CMD_WRITE_MIFARE";
189
+ /** Triggered alarm. */
112
190
  COMMANDS[COMMANDS["EF_ALARM"] = 512] = "EF_ALARM";
191
+ /** Attendance entry. */
113
192
  COMMANDS[COMMANDS["EF_ATTLOG"] = 1] = "EF_ATTLOG";
193
+ /** Pressed keyboard key. */
114
194
  COMMANDS[COMMANDS["EF_BUTTON"] = 16] = "EF_BUTTON";
195
+ /** Upload user data. */
115
196
  COMMANDS[COMMANDS["EF_ENROLLFINGER"] = 8] = "EF_ENROLLFINGER";
197
+ /** Enrolled user. */
116
198
  COMMANDS[COMMANDS["EF_ENROLLUSER"] = 4] = "EF_ENROLLUSER";
199
+ /** Pressed finger. */
117
200
  COMMANDS[COMMANDS["EF_FINGER"] = 2] = "EF_FINGER";
201
+ /** Fingerprint score in enroll procedure. */
118
202
  COMMANDS[COMMANDS["EF_FPFTR"] = 256] = "EF_FPFTR";
203
+ /** Restore access control to default. */
119
204
  COMMANDS[COMMANDS["EF_UNLOCK"] = 32] = "EF_UNLOCK";
205
+ /** Registered user placed finger. */
120
206
  COMMANDS[COMMANDS["EF_VERIFY"] = 128] = "EF_VERIFY";
121
207
  })(COMMANDS || (COMMANDS = {}));
208
+ var DISCOVERED_CMD;
209
+ (function (DISCOVERED_CMD) {
210
+ /** Returned when the Finger id not exists in the user uid, when attempting to download single finger template */
211
+ DISCOVERED_CMD[DISCOVERED_CMD["FID_NOT_FOUND"] = 4993] = "FID_NOT_FOUND";
212
+ })(DISCOVERED_CMD || (DISCOVERED_CMD = {}));
122
213
  var Constants;
123
214
  (function (Constants) {
124
215
  Constants[Constants["USHRT_MAX"] = 65535] = "USHRT_MAX";
@@ -127,6 +218,7 @@ var Constants;
127
218
  Constants[Constants["MACHINE_PREPARE_DATA_2"] = 32130] = "MACHINE_PREPARE_DATA_2";
128
219
  })(Constants || (Constants = {}));
129
220
  const REQUEST_DATA = {
221
+ START_TAG: Buffer.from([0x50, 0x50, 0x82, 0x7d]),
130
222
  DISABLE_DEVICE: Buffer.from([0, 0, 0, 0]),
131
223
  GET_REAL_TIME_EVENT: Buffer.from([0x01, 0x00, 0x00, 0x00]),
132
224
  GET_ATTENDANCE_LOGS: Buffer.from([0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
@@ -274,18 +366,23 @@ class User {
274
366
  }
275
367
 
276
368
  class Attendance {
369
+ /** Internal serial number for the user */
277
370
  sn;
371
+ /** User ID/Pin stored as a string */
278
372
  user_id;
279
- record_time;
373
+ /** Verification type */
280
374
  type;
375
+ /** Time of the attendance event */
376
+ record_time;
377
+ /** Verify state */
281
378
  state;
282
379
  ip;
283
- constructor(sn, user_id, record_time, type, state) {
380
+ constructor(sn, user_id, type, record_time, state) {
284
381
  this.sn = sn;
285
382
  this.user_id = user_id;
383
+ this.type = type || undefined;
286
384
  this.record_time = record_time;
287
- this.type = type;
288
- this.state = state;
385
+ this.state = state || undefined;
289
386
  }
290
387
  }
291
388
 
@@ -329,17 +426,7 @@ const createUDPHeader = (command, sessionId, replyId, data) => {
329
426
  return buf;
330
427
  };
331
428
  const createTCPHeader = (command, sessionId, replyId, data) => {
332
- const dataBuffer = Buffer.from(data);
333
- const buf = Buffer.alloc(8 + dataBuffer.length);
334
- buf.writeUInt16LE(command, 0);
335
- buf.writeUInt16LE(0, 2);
336
- buf.writeUInt16LE(sessionId, 4);
337
- buf.writeUInt16LE(replyId, 6);
338
- dataBuffer.copy(buf, 8);
339
- const chksum2 = createChkSum(buf);
340
- buf.writeUInt16LE(chksum2, 2);
341
- replyId = (replyId + 1) % Constants.USHRT_MAX;
342
- buf.writeUInt16LE(replyId, 6);
429
+ const buf = createUDPHeader(command, sessionId, replyId, data);
343
430
  const prefixBuf = Buffer.from([0x50, 0x50, 0x82, 0x7d, 0x13, 0x00, 0x00, 0x00]);
344
431
  prefixBuf.writeUInt16LE(buf.length, 4);
345
432
  return Buffer.concat([prefixBuf, buf]);
@@ -399,7 +486,7 @@ const decodeRecordData40 = (recordData) => {
399
486
  .slice(2, 2 + 9)
400
487
  .toString('ascii')
401
488
  .split('\0')
402
- .shift() || '', parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(26, 1), recordData.readUIntLE(31, 1));
489
+ .shift() || '', recordData.readUIntLE(26, 1), parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(31, 1));
403
490
  };
404
491
  const decodeRecordData16 = (recordData) => {
405
492
  return {
@@ -515,6 +602,59 @@ const authKey = (comKey, sessionId) => {
515
602
  return Array.from(response);
516
603
  };
517
604
 
605
+ /**
606
+ * Error types for device communication
607
+ */
608
+ const ERROR_TYPES = {
609
+ ECONNRESET: 'ECONNRESET',
610
+ ECONNREFUSED: 'ECONNREFUSED'};
611
+ /**
612
+ * Custom error class for device communication errors
613
+ */
614
+ class ZkError {
615
+ err;
616
+ ip;
617
+ command;
618
+ /**
619
+ * Creates a new ZkError instance
620
+ * @param err The error object
621
+ * @param command The command that caused the error
622
+ * @param ip The IP address of the device
623
+ */
624
+ constructor(err, command, ip) {
625
+ this.err = err;
626
+ this.ip = ip;
627
+ this.command = command;
628
+ }
629
+ /**
630
+ * Gets a user-friendly error message
631
+ * @returns A formatted error message
632
+ */
633
+ toast() {
634
+ if (this.err.code === ERROR_TYPES.ECONNRESET) {
635
+ return 'Another device is connecting to the device so the connection is interrupted';
636
+ }
637
+ else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
638
+ return 'IP of the device is refused';
639
+ }
640
+ return this.err.message;
641
+ }
642
+ /**
643
+ * Gets detailed error information
644
+ * @returns An object containing error details
645
+ */
646
+ getError() {
647
+ return {
648
+ err: {
649
+ message: this.err.message,
650
+ code: this.err.code
651
+ },
652
+ ip: this.ip,
653
+ command: this.command
654
+ };
655
+ }
656
+ }
657
+
518
658
  /**
519
659
  * Represents a fingerprint template with associated metadata
520
660
  */
@@ -588,56 +728,547 @@ class Finger {
588
728
  }
589
729
  }
590
730
 
591
- /**
592
- * Error types for device communication
593
- */
594
- const ERROR_TYPES = {
595
- ECONNRESET: 'ECONNRESET',
596
- ECONNREFUSED: 'ECONNREFUSED'};
597
- /**
598
- * Custom error class for device communication errors
599
- */
600
- class ZkError {
601
- err;
602
- ip;
603
- command;
604
- /**
605
- * Creates a new ZkError instance
606
- * @param err The error object
607
- * @param command The command that caused the error
608
- * @param ip The IP address of the device
609
- */
610
- constructor(err, command, ip) {
611
- this.err = err;
612
- this.ip = ip;
613
- this.command = command;
731
+ class UserService {
732
+ _zkTcp;
733
+ _users;
734
+ constructor(zkTcp) {
735
+ this._zkTcp = zkTcp;
736
+ }
737
+ async getUserByUserId(user_id) {
738
+ if (!this._users) {
739
+ await this.getUsers();
740
+ }
741
+ if (this._users.has(String(user_id))) {
742
+ return this._users.get(String(user_id));
743
+ }
744
+ else
745
+ throw new Error("user_id not exists");
746
+ }
747
+ async getUsers() {
748
+ try {
749
+ // Free any existing buffer data to prepare for a new request
750
+ if (this._users) {
751
+ return { data: Array.from(this._users.values()) };
752
+ }
753
+ else {
754
+ this._users = new Map([]);
755
+ }
756
+ if (this._zkTcp.socket) {
757
+ await this._zkTcp.freeData();
758
+ }
759
+ // Request user data
760
+ const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_USERS);
761
+ // Free buffer data after receiving the data
762
+ if (this._zkTcp.socket) {
763
+ await this._zkTcp.freeData();
764
+ }
765
+ // Constants for user data processing
766
+ const USER_PACKET_SIZE = 72;
767
+ // Ensure data.data is a valid buffer
768
+ if (!data.data || !(data.data instanceof Buffer)) {
769
+ throw new Error('Invalid data received');
770
+ }
771
+ let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
772
+ const users = [];
773
+ // Process each user packet
774
+ while (userData.length >= USER_PACKET_SIZE) {
775
+ // Decode user data and add to the users array
776
+ const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
777
+ users.push(user);
778
+ this._users.set(user.user_id, user);
779
+ userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
780
+ }
781
+ // Return the list of users
782
+ return { data: users };
783
+ }
784
+ catch (err) {
785
+ // Log the error for debugging
786
+ console.error('Error getting users:', err);
787
+ // Re-throw the error to be handled by the caller
788
+ throw err;
789
+ }
790
+ }
791
+ async setUser(user_id, name, password, role = 0, cardno = 0) {
792
+ let user;
793
+ try {
794
+ user = await this.getUserByUserId(user_id);
795
+ }
796
+ catch (err) {
797
+ if (err.message.includes("user_id not exists")) {
798
+ user.uid = Math.max(...Array.from(this._users.values()).map(usr => usr.uid)) + 1;
799
+ this._users.set(user_id, user);
800
+ }
801
+ }
802
+ try {
803
+ // Validate input parameters
804
+ if (user_id.length > 9 ||
805
+ name.length > 24 ||
806
+ password.length > 8 ||
807
+ typeof role !== 'number' ||
808
+ cardno.toString().length > 10) {
809
+ throw new Error('Invalid input parameters');
810
+ }
811
+ // Allocate and initialize the buffer
812
+ const commandBuffer = Buffer.alloc(72);
813
+ // Fill the buffer with user data
814
+ commandBuffer.writeUInt16LE(user.uid, 0);
815
+ commandBuffer.writeUInt16LE(role, 2);
816
+ commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
817
+ commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
818
+ commandBuffer.writeUInt16LE(cardno, 35);
819
+ commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
820
+ commandBuffer.write(user_id.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
821
+ // Send the command and return the result
822
+ const created = await this._zkTcp.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
823
+ return !!created;
824
+ }
825
+ catch (err) {
826
+ // Log error details for debugging
827
+ console.error('Error setting user:', err);
828
+ // Re-throw error for upstream handling
829
+ throw err;
830
+ }
831
+ }
832
+ async DeleteUser(user_id) {
833
+ try {
834
+ const user = await this.getUserByUserId(user_id);
835
+ // Allocate and initialize the buffer
836
+ const commandBuffer = Buffer.alloc(72);
837
+ // Write UID to the buffer
838
+ commandBuffer.writeUInt16LE(user.uid, 0);
839
+ // Send the delete command and return the result
840
+ const deleted = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
841
+ return !!deleted;
842
+ }
843
+ catch (err) {
844
+ // Log error details for debugging
845
+ console.error('Error deleting user:', err);
846
+ // Re-throw error for upstream handling
847
+ throw err;
848
+ }
849
+ }
850
+ async getTemplates(callbackInProcess = () => { }) {
851
+ let templates = [];
852
+ try {
853
+ if (this._zkTcp.socket) {
854
+ await this._zkTcp.freeData();
855
+ }
856
+ await this._zkTcp.getSizes();
857
+ if (this._zkTcp.fp_count == 0)
858
+ return { data: [] };
859
+ await this._zkTcp.disableDevice();
860
+ const resp = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
861
+ let templateData = resp.data.subarray(4);
862
+ let totalSize = resp.data.readUIntLE(0, 4);
863
+ while (totalSize) {
864
+ const buf = templateData.subarray(0, 6);
865
+ const size = buf.readUIntLE(0, 2);
866
+ const uid = buf.readUIntLE(2, 2);
867
+ const fid = buf.readUIntLE(4, 1);
868
+ const valid = buf.readUIntLE(5, 1);
869
+ // Force-copy bytes so we don't retain the entire big backing buffer
870
+ const tplBytes = Buffer.from(templateData.subarray(6, size));
871
+ templates.push(new Finger(uid, fid, valid, tplBytes));
872
+ templateData = templateData.subarray(size);
873
+ totalSize -= size;
874
+ }
875
+ return { data: templates };
876
+ }
877
+ catch (err) {
878
+ this._zkTcp.verbose && console.log("Error getting templates", err);
879
+ return { data: templates };
880
+ }
881
+ finally {
882
+ await this._zkTcp.freeData();
883
+ await this._zkTcp.enableDevice();
884
+ }
885
+ }
886
+ async DownloadFp(user_id, fid) {
887
+ try {
888
+ const user = await this.getUserByUserId(user_id);
889
+ if (0 > fid || fid > 9)
890
+ throw new Error('fid must be between 0 and 9');
891
+ // Allocate and initialize the buffer
892
+ const data = Buffer.alloc(3);
893
+ // Fill the buffer with user data
894
+ data.writeUInt16LE(user.uid, 0);
895
+ data.writeUIntLE(fid, 2, 1);
896
+ this._zkTcp.replyId++;
897
+ const packet = createTCPHeader(COMMANDS.CMD_USERTEMP_RRQ, this._zkTcp.sessionId, this._zkTcp.replyId, data);
898
+ let fingerSize = 0;
899
+ let fingerTemplate = Buffer.from([]);
900
+ return await new Promise((resolve, reject) => {
901
+ let timeout;
902
+ const cleanup = () => {
903
+ if (this._zkTcp.socket) {
904
+ this._zkTcp.socket.removeListener('data', receiveData);
905
+ }
906
+ if (timeout)
907
+ clearTimeout(timeout);
908
+ };
909
+ let timer = () => setTimeout(() => {
910
+ cleanup();
911
+ reject(new Error('Time Out, Could not retrieve data'));
912
+ }, this._zkTcp.timeout);
913
+ const receiveData = (data) => {
914
+ timeout = timer();
915
+ if (data.length === 0)
916
+ return;
917
+ try {
918
+ if (data.length == 0)
919
+ return;
920
+ const headers = decodeTCPHeader(data);
921
+ switch (headers.commandId) {
922
+ case DISCOVERED_CMD.FID_NOT_FOUND:
923
+ throw new Error('Could not retrieve data. maybe finger id not exists?');
924
+ case COMMANDS.CMD_PREPARE_DATA:
925
+ fingerSize = data.readUIntLE(16, 2);
926
+ break;
927
+ case COMMANDS.CMD_DATA:
928
+ // A single 'data' event might contain multiple TCP packets combined by the OS
929
+ // in this method, is possible to get CMD_DATA and CMD_ACK_OK in the same event,
930
+ // so It's important to split data received for remove CMD_ACK_OK headers
931
+ fingerTemplate = Buffer.concat([fingerTemplate, data.subarray(16, fingerSize + 10)]);
932
+ // @ts-ignore
933
+ resolve(fingerTemplate);
934
+ break;
935
+ case COMMANDS.CMD_ACK_OK:
936
+ cleanup();
937
+ // @ts-ignore
938
+ resolve(fingerTemplate);
939
+ return;
940
+ default:
941
+ // If it's not a recognized command but has data, it might be raw template data
942
+ if (headers.commandId > 2000 && headers.commandId < 3000) {
943
+ // Likely another ACK or system msg
944
+ }
945
+ else {
946
+ fingerTemplate = Buffer.concat([fingerTemplate, data]);
947
+ }
948
+ break;
949
+ }
950
+ clearTimeout(timeout);
951
+ }
952
+ catch (e) {
953
+ cleanup();
954
+ reject(e);
955
+ }
956
+ };
957
+ if (this._zkTcp.socket) {
958
+ this._zkTcp.socket.on('data', receiveData);
959
+ this._zkTcp.socket.write(packet, (err) => {
960
+ if (err) {
961
+ cleanup();
962
+ reject(err);
963
+ }
964
+ });
965
+ }
966
+ else {
967
+ reject(new Error('Socket not initialized'));
968
+ }
969
+ });
970
+ }
971
+ catch (err) {
972
+ throw err;
973
+ }
974
+ finally {
975
+ await this._zkTcp.refreshData();
976
+ }
614
977
  }
615
978
  /**
616
- * Gets a user-friendly error message
617
- * @returns A formatted error message
618
- */
619
- toast() {
620
- if (this.err.code === ERROR_TYPES.ECONNRESET) {
621
- return 'Another device is connecting to the device so the connection is interrupted';
979
+ *
980
+ * @param user_id {string} user
981
+ * @param fingers {Finger[]} array of finger templates instances
982
+ * */
983
+ async saveTemplates(user_id, fingers = []) {
984
+ if (fingers.length > 9 || fingers.length == 0)
985
+ throw new Error("maximum finger length is 10 and can't be empty");
986
+ try {
987
+ await this._zkTcp.disableDevice();
988
+ // check users exists
989
+ const user = await this.getUserByUserId(user_id);
990
+ let fpack = Buffer.alloc(0);
991
+ let table = Buffer.alloc(0);
992
+ const fnum = 0x10;
993
+ let tstart = 0;
994
+ for (const finger of fingers) {
995
+ const tfp = finger.repackOnly();
996
+ 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
997
+ tableEntry.writeInt8(2, 0);
998
+ tableEntry.writeUInt16LE(user.uid, 1);
999
+ tableEntry.writeInt8(fnum + finger.fid, 3);
1000
+ tableEntry.writeUInt32LE(tstart, 4);
1001
+ table = Buffer.concat([table, tableEntry]);
1002
+ tstart += tfp.length;
1003
+ fpack = Buffer.concat([fpack, tfp]);
1004
+ }
1005
+ let upack;
1006
+ if (this._zkTcp.userPacketSize === 28) {
1007
+ upack = user.repack29();
1008
+ }
1009
+ else {
1010
+ upack = user.repack73();
1011
+ }
1012
+ const head = Buffer.alloc(12); // III = 3*4 bytes
1013
+ head.writeUInt32LE(upack.length, 0);
1014
+ head.writeUInt32LE(table.length, 4);
1015
+ head.writeUInt32LE(fpack.length, 8);
1016
+ const packet = Buffer.concat([head, upack, table, fpack]);
1017
+ const bufferResponse = await this._zkTcp.sendWithBuffer(packet);
1018
+ const command = 110;
1019
+ const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
1020
+ commandString.writeUInt32LE(12, 0);
1021
+ commandString.writeUInt16LE(0, 4);
1022
+ commandString.writeUInt16LE(8, 6);
1023
+ const cmdResponse = await this._zkTcp.executeCmd(command, commandString);
1024
+ if (this._zkTcp.verbose)
1025
+ console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
622
1026
  }
623
- else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
624
- return 'IP of the device is refused';
1027
+ catch (error) {
1028
+ throw error;
1029
+ }
1030
+ finally {
1031
+ await this._zkTcp.refreshData();
1032
+ await this._zkTcp.enableDevice();
1033
+ }
1034
+ }
1035
+ async deleteFinger(user_id, fid) {
1036
+ try {
1037
+ if (!this._users.has(user_id))
1038
+ throw new Error("user_id not exists");
1039
+ const user = await this.getUserByUserId(user_id);
1040
+ const buf = Buffer.alloc(4);
1041
+ buf.writeUInt16LE(user_id ? user.uid : 0, 0);
1042
+ buf.writeUint16LE(fid ? fid : 0, 2);
1043
+ const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
1044
+ return !!reply;
1045
+ }
1046
+ catch (error) {
1047
+ throw new Error("Can't save utemp");
1048
+ }
1049
+ finally {
1050
+ await this._zkTcp.refreshData();
1051
+ }
1052
+ }
1053
+ async enrollInfo(user_id, tempId) {
1054
+ let done = false;
1055
+ try {
1056
+ const userBuf = Buffer.alloc(24);
1057
+ userBuf.write(user_id, 0, 24, 'ascii');
1058
+ let commandString = Buffer.concat([
1059
+ userBuf,
1060
+ Buffer.from([tempId, 1])
1061
+ ]);
1062
+ const sendAckOk = async () => {
1063
+ try {
1064
+ const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this._zkTcp.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
1065
+ this._zkTcp.socket.write(buf);
1066
+ }
1067
+ catch (e) {
1068
+ throw new ZkError(e, COMMANDS.CMD_ACK_OK, this._zkTcp.ip);
1069
+ }
1070
+ };
1071
+ const cancel = await this._zkTcp.cancelCapture();
1072
+ const cmdResponse = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
1073
+ this._zkTcp.timeout = 60000; // 60 seconds timeout
1074
+ let attempts = 3;
1075
+ while (attempts > 0) {
1076
+ if (this._zkTcp.verbose)
1077
+ console.log(`A:${attempts} esperando primer regevent`);
1078
+ let dataRecv = await this._zkTcp.readSocket(17);
1079
+ await sendAckOk();
1080
+ if (dataRecv.length > 16) {
1081
+ const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1082
+ const res = padded.readUInt16LE(16);
1083
+ if (this._zkTcp.verbose)
1084
+ console.log(`res ${res}`);
1085
+ if (res === 0 || res === 6 || res === 4) {
1086
+ if (this._zkTcp.verbose)
1087
+ console.log("posible timeout o reg Fallido");
1088
+ break;
1089
+ }
1090
+ }
1091
+ if (this._zkTcp.verbose)
1092
+ console.log(`A:${attempts} esperando 2do regevent`);
1093
+ dataRecv = await this._zkTcp.readSocket(17);
1094
+ await sendAckOk();
1095
+ if (this._zkTcp.verbose)
1096
+ console.log(dataRecv);
1097
+ if (dataRecv.length > 8) {
1098
+ const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1099
+ const res = padded.readUInt16LE(16);
1100
+ if (this._zkTcp.verbose)
1101
+ console.log(`res ${res}`);
1102
+ if (res === 6 || res === 4) {
1103
+ if (this._zkTcp.verbose)
1104
+ console.log("posible timeout o reg Fallido");
1105
+ break;
1106
+ }
1107
+ else if (res === 0x64) {
1108
+ if (this._zkTcp.verbose)
1109
+ console.log("ok, continue?");
1110
+ attempts--;
1111
+ }
1112
+ }
1113
+ }
1114
+ if (attempts === 0) {
1115
+ const dataRecv = await this._zkTcp.readSocket(17);
1116
+ await sendAckOk();
1117
+ if (this._zkTcp.verbose)
1118
+ console.log(dataRecv.toString('hex'));
1119
+ const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1120
+ let res = padded.readUInt16LE(16);
1121
+ if (this._zkTcp.verbose)
1122
+ console.log(`res ${res}`);
1123
+ if (res === 5) {
1124
+ if (this._zkTcp.verbose)
1125
+ console.log("finger duplicate");
1126
+ }
1127
+ if (res === 6 || res === 4) {
1128
+ if (this._zkTcp.verbose)
1129
+ console.log("posible timeout");
1130
+ }
1131
+ if (res === 0) {
1132
+ const size = padded.readUInt16LE(10);
1133
+ const pos = padded.readUInt16LE(12);
1134
+ if (this._zkTcp.verbose)
1135
+ console.log(`enroll ok ${size} ${pos}`);
1136
+ done = true;
1137
+ }
1138
+ }
1139
+ //this.__sock.setTimeout(this.__timeout);
1140
+ await this._zkTcp.regEvent(0); // TODO: test
1141
+ return done;
1142
+ }
1143
+ catch (error) {
1144
+ throw error;
1145
+ }
1146
+ finally {
1147
+ await this._zkTcp.cancelCapture();
1148
+ await this.verify(user_id);
1149
+ }
1150
+ }
1151
+ async verify(user_id) {
1152
+ try {
1153
+ const user = await this.getUserByUserId(user_id);
1154
+ const command_string = Buffer.alloc(4);
1155
+ command_string.writeUInt32LE(user.uid, 0);
1156
+ const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
1157
+ if (this._zkTcp.verbose)
1158
+ console.log(reply.readUInt16LE(0));
1159
+ return !!reply;
1160
+ }
1161
+ catch (error) {
1162
+ console.error(error);
1163
+ throw error;
625
1164
  }
626
- return this.err.message;
627
1165
  }
628
1166
  /**
629
- * Gets detailed error information
630
- * @returns An object containing error details
1167
+ * Upload a single fingerprint for a given user id
1168
+ * @param user_id {string} user id for customer
1169
+ * @param fingerTemplate {string} finger template in base64 string
1170
+ * @param fid {number} finger id is a number between 0 and 9
1171
+ * @param fp_valid {number} finger flag. e.g., valid=1, duress=3
631
1172
  */
632
- getError() {
633
- return {
634
- err: {
635
- message: this.err.message,
636
- code: this.err.code
637
- },
638
- ip: this.ip,
639
- command: this.command
640
- };
1173
+ async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
1174
+ try {
1175
+ const check_ACK_OK = (buf) => {
1176
+ let resp_cmd = initPacket.readUInt16LE(0);
1177
+ if (resp_cmd === COMMANDS.CMD_ACK_OK)
1178
+ return true;
1179
+ else
1180
+ throw new Error(`received unexpected command: ${resp_cmd}`);
1181
+ };
1182
+ const user = this._users.get(user_id);
1183
+ await this._zkTcp.disableDevice();
1184
+ const prep_struct = Buffer.alloc(4);
1185
+ const fingerBuffer = Buffer.from(fingerTemplate, 'base64');
1186
+ const fp_size = fingerBuffer.length;
1187
+ prep_struct.writeUInt16LE(fp_size, 0);
1188
+ const initPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_PREPARE_DATA, prep_struct);
1189
+ check_ACK_OK(initPacket);
1190
+ const fpPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_DATA, fingerBuffer);
1191
+ check_ACK_OK(fpPacket);
1192
+ const cheksumPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_CHECKSUM_BUFFER, '');
1193
+ check_ACK_OK(cheksumPacket);
1194
+ const checksum = cheksumPacket.readUInt32LE(8);
1195
+ const tmp_wreq = Buffer.alloc(6);
1196
+ tmp_wreq.writeUInt16LE(user.uid, 0);
1197
+ tmp_wreq.writeUIntLE(fid, 2, 1);
1198
+ tmp_wreq.writeUIntLE(fp_valid, 3, 1);
1199
+ tmp_wreq.writeUInt16LE(fp_size, 4);
1200
+ const tmp_wreqPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_TMP_WRITE, tmp_wreq);
1201
+ check_ACK_OK(tmp_wreqPacket);
1202
+ const freeData = await this._zkTcp.executeCmd(COMMANDS.CMD_FREE_DATA, '');
1203
+ return check_ACK_OK(freeData);
1204
+ }
1205
+ catch (err) {
1206
+ throw err;
1207
+ }
1208
+ finally {
1209
+ await this._zkTcp.refreshData();
1210
+ await this._zkTcp.enableDevice();
1211
+ }
1212
+ }
1213
+ }
1214
+
1215
+ class TransactionService {
1216
+ _zkTcp;
1217
+ constructor(zkTcp) {
1218
+ this._zkTcp = zkTcp;
1219
+ }
1220
+ async getAttendances(callbackInProcess = () => { }) {
1221
+ try {
1222
+ // Free any existing buffer data to prepare for a new request
1223
+ if (this._zkTcp.socket) {
1224
+ await this._zkTcp.freeData();
1225
+ }
1226
+ // Request attendance logs and handle chunked data
1227
+ const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
1228
+ // Free buffer data after receiving the attendance logs
1229
+ if (this._zkTcp.socket) {
1230
+ await this._zkTcp.freeData();
1231
+ }
1232
+ // Constants for record processing
1233
+ const RECORD_PACKET_SIZE = 40;
1234
+ // Ensure data.data is a valid buffer
1235
+ if (!data.data || !(data.data instanceof Buffer)) {
1236
+ throw new Error('Invalid data received');
1237
+ }
1238
+ // Process the record data
1239
+ let recordData = data.data.subarray(4); // Skip header
1240
+ const records = [];
1241
+ // Process each attendance record
1242
+ while (recordData.length >= RECORD_PACKET_SIZE) {
1243
+ const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
1244
+ records.push({ ...record, ip: this._zkTcp.ip }); // Add IP address to each record
1245
+ recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
1246
+ }
1247
+ // Return the list of attendance records
1248
+ return { data: records };
1249
+ }
1250
+ catch (err) {
1251
+ // Log and re-throw the error
1252
+ console.error('Error getting attendance records:', err);
1253
+ throw err; // Re-throw the error for handling by the caller
1254
+ }
1255
+ }
1256
+ // Clears the attendance logs on the device
1257
+ async clearAttendanceLog() {
1258
+ try {
1259
+ // Execute the command to clear attendance logs
1260
+ await this._zkTcp.disableDevice();
1261
+ const buf = await this._zkTcp.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
1262
+ await this._zkTcp.refreshData();
1263
+ await this._zkTcp.enableDevice();
1264
+ return !!buf;
1265
+ }
1266
+ catch (err) {
1267
+ // Log the error for debugging purposes
1268
+ console.error('Error clearing attendance log:', err);
1269
+ // Re-throw the error to be handled by the caller
1270
+ throw err;
1271
+ }
641
1272
  }
642
1273
  }
643
1274
 
@@ -673,6 +1304,8 @@ class ZTCP {
673
1304
  verbose = false;
674
1305
  packetNumber = 0;
675
1306
  replyData = Buffer.from([]);
1307
+ _userService;
1308
+ _transactionService;
676
1309
  constructor(ip, port, timeout, comm_key, verbose) {
677
1310
  this.ip = ip;
678
1311
  this.port = port;
@@ -680,6 +1313,8 @@ class ZTCP {
680
1313
  this.replyId = 0;
681
1314
  this.comm_key = comm_key;
682
1315
  this.verbose = verbose;
1316
+ this._userService = new UserService(this);
1317
+ this._transactionService = new TransactionService(this);
683
1318
  }
684
1319
  createSocket(cbError, cbClose) {
685
1320
  return new Promise((resolve, reject) => {
@@ -901,6 +1536,10 @@ class ZTCP {
901
1536
  try {
902
1537
  // Write the message to the socket and wait for a response
903
1538
  const reply = await this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT);
1539
+ if (this.verbose) {
1540
+ let headers = decodeTCPHeader(reply);
1541
+ console.log('command: ', COMMANDS[headers.commandId], 'replyid: ', headers.replyId);
1542
+ }
904
1543
  // Remove TCP header from the response
905
1544
  const rReply = removeTcpHeader(reply);
906
1545
  // Update sessionId for connection command responses
@@ -1050,89 +1689,14 @@ class ZTCP {
1050
1689
  }
1051
1690
  });
1052
1691
  }
1053
- /**
1054
- * reject error when starting request data
1055
- * @return {Record<string, User[] | Error>} when receiving requested data
1056
- */
1057
- async getUsers() {
1058
- try {
1059
- // Free any existing buffer data to prepare for a new request
1060
- if (this.socket) {
1061
- await this.freeData();
1062
- }
1063
- // Request user data
1064
- const data = await this.readWithBuffer(REQUEST_DATA.GET_USERS);
1065
- // Free buffer data after receiving the data
1066
- if (this.socket) {
1067
- await this.freeData();
1068
- }
1069
- // Constants for user data processing
1070
- const USER_PACKET_SIZE = 72;
1071
- // Ensure data.data is a valid buffer
1072
- if (!data.data || !(data.data instanceof Buffer)) {
1073
- throw new Error('Invalid data received');
1074
- }
1075
- let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
1076
- const users = [];
1077
- // Process each user packet
1078
- while (userData.length >= USER_PACKET_SIZE) {
1079
- // Decode user data and add to the users array
1080
- const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
1081
- users.push(user);
1082
- userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
1083
- }
1084
- // Return the list of users
1085
- return { data: users };
1086
- }
1087
- catch (err) {
1088
- // Log the error for debugging
1089
- console.error('Error getting users:', err);
1090
- // Re-throw the error to be handled by the caller
1091
- throw err;
1092
- }
1093
- }
1094
1692
  /**
1095
1693
  *
1096
- * @param {*} ip
1097
- * @param {*} callbackInProcess
1098
- * reject error when starting request data
1099
- * return { data: records, err: Error } when receiving requested data
1100
- */
1101
- async getAttendances(callbackInProcess = () => { }) {
1102
- try {
1103
- // Free any existing buffer data to prepare for a new request
1104
- if (this.socket) {
1105
- await this.freeData();
1106
- }
1107
- // Request attendance logs and handle chunked data
1108
- const data = await this.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
1109
- // Free buffer data after receiving the attendance logs
1110
- if (this.socket) {
1111
- await this.freeData();
1112
- }
1113
- // Constants for record processing
1114
- const RECORD_PACKET_SIZE = 40;
1115
- // Ensure data.data is a valid buffer
1116
- if (!data.data || !(data.data instanceof Buffer)) {
1117
- throw new Error('Invalid data received');
1118
- }
1119
- // Process the record data
1120
- let recordData = data.data.subarray(4); // Skip header
1121
- const records = [];
1122
- // Process each attendance record
1123
- while (recordData.length >= RECORD_PACKET_SIZE) {
1124
- const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
1125
- records.push({ ...record, ip: this.ip }); // Add IP address to each record
1126
- recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
1127
- }
1128
- // Return the list of attendance records
1129
- return { data: records };
1130
- }
1131
- catch (err) {
1132
- // Log and re-throw the error
1133
- console.error('Error getting attendance records:', err);
1134
- throw err; // Re-throw the error for handling by the caller
1135
- }
1694
+ * @param {*} callbackInProcess
1695
+ * reject error when starting request data
1696
+ * return { data: records, err: Error } when receiving requested data
1697
+ */
1698
+ async getAttendances(callbackInProcess = () => { }) {
1699
+ return await this._transactionService.getAttendances(callbackInProcess);
1136
1700
  }
1137
1701
  async freeData() {
1138
1702
  try {
@@ -1328,17 +1892,26 @@ class ZTCP {
1328
1892
  }
1329
1893
  async getSerialNumber() {
1330
1894
  const keyword = '~SerialNumber';
1895
+ let serialNumber = '';
1896
+ let count = 10;
1331
1897
  try {
1332
1898
  // Execute the command to get serial number
1333
- const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1334
- // Extract and format the serial number from the response data
1335
- const serialNumber = data.slice(8) // Skip the first 8 bytes (header)
1336
- .toString('utf-8') // Convert buffer to string
1337
- .replace(`${keyword}=`, '') // Remove the keyword prefix
1338
- .replace('=', '') // Remove sometines last number is a character equal to = or unknow character
1339
- .replace(/\u0000/g, ''); // Remove null characters
1340
- if (serialNumber.length !== 13 && this.verbose) {
1341
- console.warn('Serial number length not equal to 13, check');
1899
+ /**
1900
+ * @dev implemented a counter and a while loop because sometimes serial number parses wrong for some reason
1901
+ * */
1902
+ while (serialNumber.length !== 13 && count > 0) {
1903
+ const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1904
+ // Extract and format the serial number from the response data
1905
+ const SN = data.slice(8) // Skip the first 8 bytes (header)
1906
+ .toString('utf-8') // Convert buffer to string
1907
+ .replace(`${keyword}=`, '') // Remove the keyword prefix
1908
+ .replace('=', '') // Remove sometines last number is a character equal to = or unknow character
1909
+ .replace(/\u0000/g, ''); // Remove null characters
1910
+ if (serialNumber.length !== 13 && this.verbose) {
1911
+ console.warn('Serial number length not equal to 13, check');
1912
+ }
1913
+ count--;
1914
+ serialNumber = SN;
1342
1915
  }
1343
1916
  return serialNumber;
1344
1917
  }
@@ -1450,11 +2023,10 @@ class ZTCP {
1450
2023
  // Execute the command to get the PIN information
1451
2024
  const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1452
2025
  // Extract and format the PIN information from the response data
1453
- // Remove null characters
1454
2026
  return data.slice(8) // Skip the first 8 bytes (header)
1455
2027
  .toString('ascii') // Convert buffer to ASCII string
1456
2028
  .replace(`${keyword}=`, '') // Remove the keyword prefix
1457
- .replace(/\u0000/g, '');
2029
+ .replace(/\u0000/g, ''); // Remove null characters 0x00
1458
2030
  }
1459
2031
  catch (err) {
1460
2032
  // Log the error for debugging
@@ -1506,7 +2078,9 @@ class ZTCP {
1506
2078
  // Execute the command to get firmware information
1507
2079
  const data = await this.executeCmd(1100, '');
1508
2080
  // Extract and return the firmware version from the response data
1509
- return data.slice(8).toString('ascii'); // Skip the first 8 bytes (header) and convert to ASCII string
2081
+ return data.slice(8) // Skip the first 8 bytes (header)
2082
+ .toString('ascii') // convert to ASCII string
2083
+ .replace(/\u0000/g, ''); // remove x00
1510
2084
  }
1511
2085
  catch (err) {
1512
2086
  // Log the error for debugging
@@ -1572,59 +2146,6 @@ class ZTCP {
1572
2146
  throw err;
1573
2147
  }
1574
2148
  }
1575
- async setUser(uid, userid, name, password, role = 0, cardno = 0) {
1576
- try {
1577
- // Validate input parameters
1578
- if (uid <= 0 || uid > 3000 ||
1579
- userid.length > 9 ||
1580
- name.length > 24 ||
1581
- password.length > 8 ||
1582
- typeof role !== 'number' ||
1583
- cardno.toString().length > 10) {
1584
- throw new Error('Invalid input parameters');
1585
- }
1586
- // Allocate and initialize the buffer
1587
- const commandBuffer = Buffer.alloc(72);
1588
- // Fill the buffer with user data
1589
- commandBuffer.writeUInt16LE(uid, 0);
1590
- commandBuffer.writeUInt16LE(role, 2);
1591
- commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
1592
- commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
1593
- commandBuffer.writeUInt16LE(cardno, 35);
1594
- commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
1595
- commandBuffer.write(userid.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
1596
- // Send the command and return the result
1597
- const created = await this.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
1598
- return !!created;
1599
- }
1600
- catch (err) {
1601
- // Log error details for debugging
1602
- console.error('Error setting user:', err);
1603
- // Re-throw error for upstream handling
1604
- throw err;
1605
- }
1606
- }
1607
- async deleteUser(uid) {
1608
- try {
1609
- // Validate input parameter
1610
- if (uid <= 0 || uid > 3000) {
1611
- throw new Error('Invalid UID: must be between 1 and 3000');
1612
- }
1613
- // Allocate and initialize the buffer
1614
- const commandBuffer = Buffer.alloc(72);
1615
- // Write UID to the buffer
1616
- commandBuffer.writeUInt16LE(uid, 0);
1617
- // Send the delete command and return the result
1618
- const deleted = await this.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
1619
- return !!deleted;
1620
- }
1621
- catch (err) {
1622
- // Log error details for debugging
1623
- console.error('Error deleting user:', err);
1624
- // Re-throw error for upstream handling
1625
- throw err;
1626
- }
1627
- }
1628
2149
  async getAttendanceSize() {
1629
2150
  try {
1630
2151
  // Execute command to get free sizes
@@ -1641,22 +2162,27 @@ class ZTCP {
1641
2162
  }
1642
2163
  // Clears the attendance logs on the device
1643
2164
  async clearAttendanceLog() {
1644
- try {
1645
- // Execute the command to clear attendance logs
1646
- return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
1647
- }
1648
- catch (err) {
1649
- // Log the error for debugging purposes
1650
- console.error('Error clearing attendance log:', err);
1651
- // Re-throw the error to be handled by the caller
1652
- throw err;
1653
- }
2165
+ return await this._transactionService.clearAttendanceLog();
1654
2166
  }
1655
- // Clears all data on the device
1656
- async clearData() {
2167
+ /**
2168
+ * Clears all data on the device
2169
+ * @value 1 Attendance records
2170
+ * @value 2 Fingerprint templates
2171
+ * @value 3 None
2172
+ * @value 4 Operation records
2173
+ * @value 5 User information
2174
+ * @default 0 Delete all
2175
+ */
2176
+ async clearData(value) {
1657
2177
  try {
1658
2178
  // Execute the command to clear all data
1659
- return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, '');
2179
+ await this.disableDevice();
2180
+ if (!value)
2181
+ value = 3;
2182
+ const buf = await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, value.toString());
2183
+ await this.refreshData();
2184
+ await this.enableDevice();
2185
+ return !!buf;
1660
2186
  }
1661
2187
  catch (err) {
1662
2188
  // Log the error for debugging purposes
@@ -1703,33 +2229,7 @@ class ZTCP {
1703
2229
  * @returns {Record<string, Finger[]>}
1704
2230
  */
1705
2231
  async getTemplates(callbackInProcess = () => { }) {
1706
- let templates = [];
1707
- try {
1708
- await this.getSizes();
1709
- if (this.fp_count == 0)
1710
- return { data: [] };
1711
- await this.freeData();
1712
- await this.disableDevice();
1713
- const Buffer = await this.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
1714
- let templateData = Buffer.data.subarray(4);
1715
- let totalSize = Buffer.data.readUIntLE(0, 4);
1716
- while (totalSize) {
1717
- const buf = templateData.subarray(0, 6);
1718
- const size = buf.readUIntLE(0, 2);
1719
- templates.push(new Finger(buf.readUIntLE(2, 2), buf.readUIntLE(4, 1), buf.readUIntLE(5, 1), templateData.subarray(6, size)));
1720
- templateData = templateData.subarray(size);
1721
- totalSize -= size;
1722
- }
1723
- return { data: templates };
1724
- }
1725
- catch (err) {
1726
- this.verbose && console.log("Error getting templates", err);
1727
- return { data: templates };
1728
- }
1729
- finally {
1730
- await this.enableDevice();
1731
- await this.freeData();
1732
- }
2232
+ return await this._userService.getTemplates(callbackInProcess);
1733
2233
  }
1734
2234
  /**
1735
2235
  * Return size
@@ -1805,197 +2305,6 @@ class ZTCP {
1805
2305
  throw new ZkError(e, COMMANDS.CMD_DATA, this.ip);
1806
2306
  }
1807
2307
  }
1808
- /**
1809
- * save user and template
1810
- *
1811
- * @param {User | number | string} user - User class object | uid | user_id
1812
- * @param {Finger[]} fingers - Array of finger class. `0 <= index <= 9`
1813
- */
1814
- async saveUserTemplate(user, fingers = []) {
1815
- if (fingers.length > 9 || fingers.length == 0)
1816
- throw new Error("maximum finger length is 10 and can't be empty");
1817
- try {
1818
- await this.disableDevice();
1819
- const users = await this.getUsers();
1820
- //check users exists
1821
- if (!users.data.some(u => u.uid == user.uid || +u.user_id == +user.user_id))
1822
- throw new Error("error validating user input");
1823
- if (!(user instanceof User)) {
1824
- let tusers = users.data.filter(x => x.uid === +user.uid);
1825
- if (tusers.length === 1) {
1826
- user = tusers[0];
1827
- }
1828
- else {
1829
- tusers = users.data.filter(x => x.user_id === String(user));
1830
- if (tusers.length === 1) {
1831
- user = tusers[0];
1832
- }
1833
- else {
1834
- throw new Error("Can't find user");
1835
- }
1836
- }
1837
- }
1838
- if (fingers instanceof Finger) {
1839
- fingers = [fingers];
1840
- }
1841
- let fpack = Buffer.alloc(0);
1842
- let table = Buffer.alloc(0);
1843
- const fnum = 0x10;
1844
- let tstart = 0;
1845
- for (const finger of fingers) {
1846
- const tfp = finger.repackOnly();
1847
- 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
1848
- tableEntry.writeInt8(2, 0);
1849
- tableEntry.writeUInt16LE(user.uid, 1);
1850
- tableEntry.writeInt8(fnum + finger.fid, 3);
1851
- tableEntry.writeUInt32LE(tstart, 4);
1852
- table = Buffer.concat([table, tableEntry]);
1853
- tstart += tfp.length;
1854
- fpack = Buffer.concat([fpack, tfp]);
1855
- }
1856
- let upack;
1857
- if (this.userPacketSize === 28) {
1858
- upack = user.repack29();
1859
- }
1860
- else {
1861
- upack = user.repack73();
1862
- }
1863
- const head = Buffer.alloc(12); // III = 3*4 bytes
1864
- head.writeUInt32LE(upack.length, 0);
1865
- head.writeUInt32LE(table.length, 4);
1866
- head.writeUInt32LE(fpack.length, 8);
1867
- const packet = Buffer.concat([head, upack, table, fpack]);
1868
- const bufferResponse = await this.sendWithBuffer(packet);
1869
- const command = 110;
1870
- const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
1871
- commandString.writeUInt32LE(12, 0);
1872
- commandString.writeUInt16LE(0, 4);
1873
- commandString.writeUInt16LE(8, 6);
1874
- const cmdResponse = await this.executeCmd(command, commandString);
1875
- if (this.verbose)
1876
- console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
1877
- }
1878
- catch (error) {
1879
- throw error;
1880
- }
1881
- finally {
1882
- await this.refreshData();
1883
- await this.enableDevice();
1884
- }
1885
- }
1886
- async deleteFinger(uid, fid) {
1887
- try {
1888
- const buf = Buffer.alloc(4);
1889
- buf.writeUInt16LE(uid, 0);
1890
- buf.writeUint16LE(fid, 2);
1891
- const reply = await this.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
1892
- return !!reply;
1893
- }
1894
- catch (error) {
1895
- throw new Error("Can't save utemp");
1896
- }
1897
- finally {
1898
- await this.refreshData();
1899
- }
1900
- }
1901
- async enrollUser(uid, tempId, userId = '') {
1902
- let done = false;
1903
- try {
1904
- //validate user exists
1905
- const users = await this.getUsers();
1906
- const filteredUsers = users.data.filter(x => x.uid === uid);
1907
- if (filteredUsers.length >= 1) {
1908
- userId = filteredUsers[0].user_id;
1909
- }
1910
- else {
1911
- throw new Error("user not found");
1912
- }
1913
- const userBuf = Buffer.alloc(24);
1914
- userBuf.write(userId.toString(), 0, 24, 'ascii');
1915
- let commandString = Buffer.concat([
1916
- userBuf,
1917
- Buffer.from([tempId, 1])
1918
- ]);
1919
- const cancel = await this.cancelCapture();
1920
- const cmdResponse = await this.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
1921
- this.timeout = 60000; // 60 seconds timeout
1922
- let attempts = 3;
1923
- while (attempts > 0) {
1924
- if (this.verbose)
1925
- console.log(`A:${attempts} esperando primer regevent`);
1926
- let dataRecv = await this.readSocket(17);
1927
- await this.ackOk();
1928
- if (dataRecv.length > 16) {
1929
- const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1930
- const res = padded.readUInt16LE(16);
1931
- if (this.verbose)
1932
- console.log(`res ${res}`);
1933
- if (res === 0 || res === 6 || res === 4) {
1934
- if (this.verbose)
1935
- console.log("posible timeout o reg Fallido");
1936
- break;
1937
- }
1938
- }
1939
- if (this.verbose)
1940
- console.log(`A:${attempts} esperando 2do regevent`);
1941
- dataRecv = await this.readSocket(17);
1942
- await this.ackOk();
1943
- if (this.verbose)
1944
- console.log(dataRecv);
1945
- if (dataRecv.length > 8) {
1946
- const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1947
- const res = padded.readUInt16LE(16);
1948
- if (this.verbose)
1949
- console.log(`res ${res}`);
1950
- if (res === 6 || res === 4) {
1951
- if (this.verbose)
1952
- console.log("posible timeout o reg Fallido");
1953
- break;
1954
- }
1955
- else if (res === 0x64) {
1956
- if (this.verbose)
1957
- console.log("ok, continue?");
1958
- attempts--;
1959
- }
1960
- }
1961
- }
1962
- if (attempts === 0) {
1963
- const dataRecv = await this.readSocket(17);
1964
- await this.ackOk();
1965
- if (this.verbose)
1966
- console.log(dataRecv.toString('hex'));
1967
- const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
1968
- let res = padded.readUInt16LE(16);
1969
- if (this.verbose)
1970
- console.log(`res ${res}`);
1971
- if (res === 5) {
1972
- if (this.verbose)
1973
- console.log("finger duplicate");
1974
- }
1975
- if (res === 6 || res === 4) {
1976
- if (this.verbose)
1977
- console.log("posible timeout");
1978
- }
1979
- if (res === 0) {
1980
- const size = padded.readUInt16LE(10);
1981
- const pos = padded.readUInt16LE(12);
1982
- if (this.verbose)
1983
- console.log(`enroll ok ${size} ${pos}`);
1984
- done = true;
1985
- }
1986
- }
1987
- //this.__sock.setTimeout(this.__timeout);
1988
- await this.regEvent(0); // TODO: test
1989
- return done;
1990
- }
1991
- catch (error) {
1992
- throw error;
1993
- }
1994
- finally {
1995
- await this.cancelCapture();
1996
- await this.verifyUser(undefined);
1997
- }
1998
- }
1999
2308
  async readSocket(length, cb = null) {
2000
2309
  let replyBufer = Buffer.from([]);
2001
2310
  let totalPackets = 0;
@@ -2045,15 +2354,6 @@ class ZTCP {
2045
2354
  throw new ZkError(e, COMMANDS.CMD_REG_EVENT, this.ip);
2046
2355
  }
2047
2356
  }
2048
- async ackOk() {
2049
- try {
2050
- const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
2051
- this.socket.write(buf);
2052
- }
2053
- catch (e) {
2054
- throw new ZkError(e, COMMANDS.CMD_ACK_OK, this.ip);
2055
- }
2056
- }
2057
2357
  async cancelCapture() {
2058
2358
  try {
2059
2359
  const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, '');
@@ -2063,22 +2363,6 @@ class ZTCP {
2063
2363
  throw new ZkError(e, COMMANDS.CMD_CANCELCAPTURE, this.ip);
2064
2364
  }
2065
2365
  }
2066
- async verifyUser(uid) {
2067
- try {
2068
- let command_string = '';
2069
- if (uid) {
2070
- command_string = Buffer.alloc(4);
2071
- command_string.writeUInt32LE(uid, 0);
2072
- }
2073
- const reply = await this.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
2074
- if (this.verbose)
2075
- console.log(reply.readUInt16LE(0));
2076
- return !!reply;
2077
- }
2078
- catch (error) {
2079
- console.error(error);
2080
- }
2081
- }
2082
2366
  async restartDevice() {
2083
2367
  try {
2084
2368
  await this.executeCmd(COMMANDS.CMD_RESTART, '');
@@ -2463,7 +2747,7 @@ class ZUDP {
2463
2747
  }
2464
2748
  async clearAttendanceLog() {
2465
2749
  try {
2466
- return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
2750
+ return !!await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
2467
2751
  }
2468
2752
  catch (err) {
2469
2753
  console.error('Error clearing attendance log:', err);
@@ -2472,7 +2756,7 @@ class ZUDP {
2472
2756
  }
2473
2757
  async clearData() {
2474
2758
  try {
2475
- return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
2759
+ return !!await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
2476
2760
  }
2477
2761
  catch (err) {
2478
2762
  console.error('Error clearing data:', err);
@@ -2663,7 +2947,7 @@ class Zklib {
2663
2947
  }
2664
2948
  }
2665
2949
  async getUsers() {
2666
- return this.functionWrapper(() => this.ztcp.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
2950
+ return this.functionWrapper(() => this.ztcp._userService.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
2667
2951
  }
2668
2952
  async getTime() {
2669
2953
  return this.functionWrapper(() => this.ztcp.getTime(), () => this.zudp.getTime(), 'GET_TIME');
@@ -2713,11 +2997,22 @@ class Zklib {
2713
2997
  async getFirmware() {
2714
2998
  return this.functionWrapper(() => this.ztcp.getFirmware(), async () => { throw new Error('UDP get firmware not supported'); }, 'GET_FIRMWARE');
2715
2999
  }
2716
- async setUser(uid, userid, name, password, role = 0, cardno = 0) {
2717
- return this.functionWrapper(() => this.ztcp.setUser(uid, userid, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
3000
+ /** Update or create a user if user id/pin not exists
3001
+ * @param user_id {string} user id/pin for customer
3002
+ * @param name {string} user name
3003
+ * @param password {string} user password
3004
+ * @param role {number} role/privilege id number
3005
+ * @param cardno {number} card number/id
3006
+ */
3007
+ async setUser(user_id, name, password, role = 0, cardno = 0) {
3008
+ return this.functionWrapper(() => this.ztcp._userService.setUser(user_id, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
2718
3009
  }
2719
- async deleteUser(uid) {
2720
- return this.functionWrapper(() => this.ztcp.deleteUser(uid), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
3010
+ /**
3011
+ * Delete user by a given user id/pin
3012
+ * @param user_id {string}
3013
+ */
3014
+ async deleteUser(user_id) {
3015
+ return this.functionWrapper(() => this.ztcp._userService.DeleteUser(user_id), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
2721
3016
  }
2722
3017
  async getAttendanceSize() {
2723
3018
  return this.functionWrapper(() => this.ztcp.getAttendanceSize(), async () => { throw new Error('UDP get attendance size not supported'); }, 'GET_ATTENDANCE_SIZE');
@@ -2731,25 +3026,55 @@ class Zklib {
2731
3026
  async getTemplates() {
2732
3027
  return this.functionWrapper(() => this.ztcp.getTemplates(), async () => { throw new Error('UDP get templates not supported'); }, 'GET_TEMPLATES');
2733
3028
  }
2734
- async saveUserTemplate(user, fingers = []) {
2735
- return await this.functionWrapper(async () => await this.ztcp.saveUserTemplate(user, fingers), async () => { throw new Error('UDP save user template not supported'); }, 'SAVE_USER_TEMPLATE');
3029
+ /**
3030
+ * Get a user template for a given user id/pin and finger id
3031
+ * @param user_id {string} user id/pin
3032
+ * @param fid {number} finger index
3033
+ */
3034
+ async getUserTemplate(user_id, fid) {
3035
+ 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');
3036
+ }
3037
+ /**
3038
+ * Upload a single fingerprint for a given user id
3039
+ * @param user_id {string} user id/pin for customer
3040
+ * @param fingerTemplate {string} finger template in base64 string
3041
+ * @param fid {number} finger id is a number between 0 and 9
3042
+ * @param fp_valid {number} finger flag. e.g., valid=1, duress=3
3043
+ */
3044
+ async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
3045
+ 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');
3046
+ }
3047
+ /**
3048
+ * save user and template
3049
+ *
3050
+ * @param {string} user_id - user id for customer
3051
+ * @param {Finger[]} fingers - Array of finger class
3052
+ */
3053
+ async saveUserTemplate(user_id, fingers = []) {
3054
+ 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');
2736
3055
  }
2737
- async deleteFinger(uid, fid) {
3056
+ /**
3057
+ * Delete a single finger template by user id and finger index
3058
+ * @param user_id {string} user id/pin for customer
3059
+ * @param fid {number} finger index
3060
+ */
3061
+ async deleteFinger(user_id, fid) {
2738
3062
  if (fid > 9 || 0 > fid)
2739
3063
  throw new Error("fid params out of index");
2740
- if (uid > 3000 || uid < 1)
2741
- throw new Error("fid params out of index");
2742
- return this.functionWrapper(() => this.ztcp.deleteFinger(uid, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
3064
+ return this.functionWrapper(() => this.ztcp._userService.deleteFinger(user_id, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
2743
3065
  }
2744
- async enrollUser(uid, temp_id, user_id) {
3066
+ /**
3067
+ * Start to enroll a finger template
3068
+ * @param user_id {string} user id/pin for customer
3069
+ * @param temp_id {number} finger index
3070
+ */
3071
+ async enrollUser(user_id, temp_id) {
2745
3072
  if (temp_id < 0 || temp_id > 9)
2746
3073
  throw new Error("temp_id out of range 0-9");
2747
- if (uid < 1 || uid > 3000)
2748
- throw new Error("uid out of range 1-3000");
2749
- return this.functionWrapper(() => this.ztcp.enrollUser(uid, temp_id, user_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
3074
+ return this.functionWrapper(() => this.ztcp._userService.enrollInfo(user_id, temp_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
2750
3075
  }
2751
- async verifyUser(uid) {
2752
- return this.functionWrapper(() => this.ztcp.verifyUser(uid), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
3076
+ async verifyUser(user_id) {
3077
+ return this.functionWrapper(() => this.ztcp._userService.verify(user_id), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
2753
3078
  }
2754
3079
  async restartDevice() {
2755
3080
  return this.functionWrapper(() => this.ztcp.restartDevice(), async () => { throw new Error('UDP restart device not supported'); }, 'RESTART_DEVICE');