rbzk 0.1.2 → 0.1.5
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.
- checksums.yaml +4 -4
- data/lib/rbzk/cli/commands.rb +300 -13
- data/lib/rbzk/finger.rb +12 -1
- data/lib/rbzk/user.rb +34 -0
- data/lib/rbzk/version.rb +1 -1
- data/lib/rbzk/zk.rb +211 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99e2a6812f00009e79f937e5b54639f676635c9a7f9b75702005a1582e796b44
|
4
|
+
data.tar.gz: 9cf5c9f5014e23a1795dd6d9d50698481ed380d446169cf13506316133da3db3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e571f268a2d311934bb19009b936c6154a88796d2d985eea483f076659e47e4fc2dd5f64de018fa01ce490e6d72d4285787dfae34a6e77262e3b93b2ec8289f
|
7
|
+
data.tar.gz: 134a4623eb8fa507a453fe8373ac3b7222a3e52cede1a7473e19f54f22cd062afe9fc1f009475e142ee48dbe0f8300c13343061e48ebe69da0c733abc06ebcc6
|
data/lib/rbzk/cli/commands.rb
CHANGED
@@ -75,6 +75,19 @@ module RBZK
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
desc "refresh [IP]", "Refresh device data"
|
79
|
+
|
80
|
+
def refresh(ip = nil)
|
81
|
+
# Use IP from options if not provided as argument
|
82
|
+
ip ||= options[:ip] || @config['ip']
|
83
|
+
|
84
|
+
with_connection(ip, options) do |conn|
|
85
|
+
puts "Refreshing device data..."
|
86
|
+
result = conn.refresh_data
|
87
|
+
puts "✓ Device data refreshed successfully!" if result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
78
91
|
desc "users [IP]", "Get users from the device"
|
79
92
|
|
80
93
|
def users(ip = nil)
|
@@ -327,6 +340,236 @@ module RBZK
|
|
327
340
|
end
|
328
341
|
end
|
329
342
|
|
343
|
+
desc "unlock [IP]", "Unlock the door"
|
344
|
+
method_option :time, type: :numeric, default: 3, desc: "Unlock time in seconds (default: 3)"
|
345
|
+
map "unlock" => :unlock_door
|
346
|
+
|
347
|
+
def unlock_door(ip = nil)
|
348
|
+
# Use IP from options if not provided as argument
|
349
|
+
ip ||= options[:ip] || @config['ip']
|
350
|
+
|
351
|
+
# Get the unlock time
|
352
|
+
time = options[:time] || 3
|
353
|
+
|
354
|
+
with_connection(ip, options) do |conn|
|
355
|
+
puts "Unlocking door for #{time} seconds..."
|
356
|
+
result = conn.unlock(time)
|
357
|
+
puts "✓ Door unlocked successfully!" if result
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
desc "door-state [IP]", "Get the door lock state"
|
362
|
+
map "door-state" => :door_state
|
363
|
+
|
364
|
+
def door_state(ip = nil)
|
365
|
+
# Use IP from options if not provided as argument
|
366
|
+
ip ||= options[:ip] || @config['ip']
|
367
|
+
|
368
|
+
with_connection(ip, options) do |conn|
|
369
|
+
state = conn.get_lock_state
|
370
|
+
puts "Door state: #{state ? 'Open' : 'Closed'}"
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
desc "write-lcd [IP] LINE_NUMBER TEXT", "Write text to LCD display"
|
375
|
+
map "write-lcd" => :write_lcd
|
376
|
+
|
377
|
+
def write_lcd(line_number, text, ip = nil)
|
378
|
+
# Use IP from options if not provided as argument
|
379
|
+
ip ||= options[:ip] || @config['ip']
|
380
|
+
|
381
|
+
# Convert line_number to integer
|
382
|
+
line_number = line_number.to_i
|
383
|
+
|
384
|
+
with_connection(ip, options) do |conn|
|
385
|
+
puts "Writing text to LCD line #{line_number}..."
|
386
|
+
result = conn.write_lcd(line_number, text)
|
387
|
+
puts "✓ Text written to LCD successfully!" if result
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
desc "clear-lcd [IP]", "Clear the LCD display"
|
392
|
+
map "clear-lcd" => :clear_lcd
|
393
|
+
|
394
|
+
def clear_lcd(ip = nil)
|
395
|
+
# Use IP from options if not provided as argument
|
396
|
+
ip ||= options[:ip] || @config['ip']
|
397
|
+
|
398
|
+
with_connection(ip, options) do |conn|
|
399
|
+
puts "Clearing LCD display..."
|
400
|
+
result = conn.clear_lcd
|
401
|
+
puts "✓ LCD cleared successfully!" if result
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
desc "add-user [IP]", "Add or update a user"
|
406
|
+
method_option :uid, type: :numeric, desc: "User ID (generated by device if not provided)"
|
407
|
+
method_option :name, type: :string, default: "", desc: "User name"
|
408
|
+
method_option :privilege, type: :numeric, default: 0, desc: "User privilege (0=User, 14=Admin)"
|
409
|
+
method_option :password, type: :string, default: "", desc: "User password"
|
410
|
+
method_option :group_id, type: :string, default: "", desc: "Group ID"
|
411
|
+
method_option :user_id, type: :string, default: "", desc: "Custom user ID"
|
412
|
+
method_option :card, type: :numeric, default: 0, desc: "Card number"
|
413
|
+
map "add-user" => :add_user
|
414
|
+
|
415
|
+
def add_user(ip = nil)
|
416
|
+
# Use IP from options if not provided as argument
|
417
|
+
ip ||= options[:ip] || @config['ip']
|
418
|
+
|
419
|
+
with_connection(ip, options) do |conn|
|
420
|
+
puts "Adding/updating user..."
|
421
|
+
|
422
|
+
result = conn.set_user(
|
423
|
+
uid: options[:uid],
|
424
|
+
name: options[:name] || "",
|
425
|
+
privilege: options[:privilege] || 0,
|
426
|
+
password: options[:password] || "",
|
427
|
+
group_id: options[:group_id] || "",
|
428
|
+
user_id: options[:user_id] || "",
|
429
|
+
card: options[:card] || 0
|
430
|
+
)
|
431
|
+
|
432
|
+
puts "✓ User added/updated successfully!" if result
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
desc "delete-user [IP]", "Delete a user"
|
437
|
+
method_option :uid, type: :numeric, desc: "User ID (generated by device)"
|
438
|
+
method_option :user_id, type: :string, desc: "Custom user ID"
|
439
|
+
map "delete-user" => :delete_user
|
440
|
+
|
441
|
+
def delete_user(ip = nil)
|
442
|
+
# Use IP from options if not provided as argument
|
443
|
+
ip ||= options[:ip] || @config['ip']
|
444
|
+
|
445
|
+
# Ensure at least one of uid or user_id is provided
|
446
|
+
if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
|
447
|
+
puts "Error: You must provide either --uid"
|
448
|
+
return
|
449
|
+
end
|
450
|
+
|
451
|
+
with_connection(ip, options) do |conn|
|
452
|
+
puts "Deleting user..."
|
453
|
+
|
454
|
+
# Use the User object with delete_user
|
455
|
+
result = conn.delete_user(uid: options[:uid])
|
456
|
+
|
457
|
+
if result
|
458
|
+
puts "✓ User deleted successfully!"
|
459
|
+
else
|
460
|
+
puts "✗ User not found or could not be deleted."
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
desc "get-templates [IP]", "Get all fingerprint templates"
|
466
|
+
map "get-templates" => :get_templates
|
467
|
+
|
468
|
+
def get_templates(ip = nil)
|
469
|
+
# Use IP from options if not provided as argument
|
470
|
+
ip ||= options[:ip] || @config['ip']
|
471
|
+
|
472
|
+
with_connection(ip, options) do |conn|
|
473
|
+
puts "Getting fingerprint templates..."
|
474
|
+
templates = conn.get_templates
|
475
|
+
|
476
|
+
if templates && !templates.empty?
|
477
|
+
puts "✓ Found #{templates.size} fingerprint templates:"
|
478
|
+
|
479
|
+
# Use Terminal::Table for pretty output
|
480
|
+
table = ::Terminal::Table.new do |t|
|
481
|
+
t.title = "Fingerprint Templates"
|
482
|
+
t.headings = [ 'UID', 'Finger ID', 'Valid', 'Size' ]
|
483
|
+
|
484
|
+
templates.each do |template|
|
485
|
+
t << [
|
486
|
+
template.uid,
|
487
|
+
template.fid,
|
488
|
+
template.valid == 1 ? 'Yes' : 'No',
|
489
|
+
template.size
|
490
|
+
]
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
puts table
|
495
|
+
else
|
496
|
+
puts "✓ No fingerprint templates found"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
desc "get-user-template [IP]", "Get a specific user's fingerprint template"
|
502
|
+
method_option :uid, type: :numeric, desc: "User ID (generated by device)"
|
503
|
+
method_option :user_id, type: :string, desc: "Custom user ID"
|
504
|
+
method_option :finger_id, type: :numeric, default: 0, desc: "Finger ID (0-9)"
|
505
|
+
map "get-user-template" => :get_user_template
|
506
|
+
|
507
|
+
def get_user_template(ip = nil)
|
508
|
+
# Use IP from options if not provided as argument
|
509
|
+
ip ||= options[:ip] || @config['ip']
|
510
|
+
|
511
|
+
# Ensure at least one of uid or user_id is provided
|
512
|
+
if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
|
513
|
+
puts "Error: You must provide either --uid or --user-id"
|
514
|
+
return
|
515
|
+
end
|
516
|
+
|
517
|
+
with_connection(ip, options) do |conn|
|
518
|
+
puts "Getting user fingerprint template..."
|
519
|
+
|
520
|
+
# Extract parameters from options
|
521
|
+
uid = options[:uid] || 0
|
522
|
+
user_id = options[:user_id] || ""
|
523
|
+
finger_id = options[:finger_id] || 0
|
524
|
+
|
525
|
+
template = conn.get_user_template(uid: uid, temp_id: finger_id, user_id: user_id)
|
526
|
+
|
527
|
+
if template
|
528
|
+
puts "✓ Found fingerprint template:"
|
529
|
+
puts " User ID: #{template.uid}"
|
530
|
+
puts " Finger ID: #{template.fid}"
|
531
|
+
puts " Valid: #{template.valid == 1 ? 'Yes' : 'No'}"
|
532
|
+
puts " Size: #{template.size} bytes"
|
533
|
+
else
|
534
|
+
puts "✗ Fingerprint template not found"
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
desc "delete-template [IP]", "Delete a specific fingerprint template"
|
540
|
+
method_option :uid, type: :numeric, desc: "User ID (generated by device)"
|
541
|
+
method_option :user_id, type: :string, desc: "Custom user ID"
|
542
|
+
method_option :finger_id, type: :numeric, default: 0, desc: "Finger ID (0-9)"
|
543
|
+
map "delete-template" => :delete_template
|
544
|
+
|
545
|
+
def delete_template(ip = nil)
|
546
|
+
# Use IP from options if not provided as argument
|
547
|
+
ip ||= options[:ip] || @config['ip']
|
548
|
+
|
549
|
+
# Ensure at least one of uid or user_id is provided
|
550
|
+
if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
|
551
|
+
puts "Error: You must provide either --uid or --user-id"
|
552
|
+
return
|
553
|
+
end
|
554
|
+
|
555
|
+
with_connection(ip, options) do |conn|
|
556
|
+
puts "Deleting fingerprint template..."
|
557
|
+
|
558
|
+
# Extract parameters from options
|
559
|
+
uid = options[:uid] || 0
|
560
|
+
user_id = options[:user_id] || ""
|
561
|
+
finger_id = options[:finger_id] || 0
|
562
|
+
|
563
|
+
result = conn.delete_user_template(uid: uid, temp_id: finger_id, user_id: user_id)
|
564
|
+
|
565
|
+
if result
|
566
|
+
puts "✓ Fingerprint template deleted successfully!"
|
567
|
+
else
|
568
|
+
puts "✗ Fingerprint template not found or could not be deleted"
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
330
573
|
desc "test-voice [IP]", "Test the device voice"
|
331
574
|
method_option :index, type: :numeric, desc: "Sound index to play (0-35, default: 0)"
|
332
575
|
map "test-voice" => :test_voice
|
@@ -376,6 +619,42 @@ module RBZK
|
|
376
619
|
end
|
377
620
|
end
|
378
621
|
|
622
|
+
desc "restart [IP]", "Restart the device"
|
623
|
+
|
624
|
+
def restart(ip = nil)
|
625
|
+
# Use IP from options if not provided as argument
|
626
|
+
ip ||= options[:ip] || @config['ip']
|
627
|
+
|
628
|
+
if yes?("Are you sure you want to restart the device? (y/N)")
|
629
|
+
with_connection(ip, options.merge(skip_disconnect_after_yield: true)) do |conn|
|
630
|
+
puts "Restarting device..."
|
631
|
+
result = conn.restart
|
632
|
+
puts "✓ Device restart command sent successfully!" if result
|
633
|
+
puts "The device will restart now. You may need to wait a few moments before reconnecting."
|
634
|
+
end
|
635
|
+
else
|
636
|
+
puts "Operation cancelled."
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
desc "poweroff [IP]", "Power off the device"
|
641
|
+
|
642
|
+
def poweroff(ip = nil)
|
643
|
+
# Use IP from options if not provided as argument
|
644
|
+
ip ||= options[:ip] || @config['ip']
|
645
|
+
|
646
|
+
if yes?("Are you sure you want to power off the device? (y/N)")
|
647
|
+
with_connection(ip, options.merge(skip_disconnect_after_yield: true)) do |conn|
|
648
|
+
puts "Powering off device..."
|
649
|
+
result = conn.poweroff
|
650
|
+
puts "✓ Device poweroff command sent successfully!" if result
|
651
|
+
puts "The device will power off now. You will need to manually power it back on."
|
652
|
+
end
|
653
|
+
else
|
654
|
+
puts "Operation cancelled."
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
379
658
|
desc "config", "Show current configuration"
|
380
659
|
|
381
660
|
def config
|
@@ -425,26 +704,30 @@ module RBZK
|
|
425
704
|
end
|
426
705
|
|
427
706
|
def with_connection(ip, options = {})
|
428
|
-
|
707
|
+
local_opts = options.dup # Use a copy for local modifications and access
|
708
|
+
skip_disconnect = local_opts.delete(:skip_disconnect_after_yield) || false
|
709
|
+
|
710
|
+
puts "Connecting to ZKTeco device at #{ip}:#{local_opts[:port] || @config['port'] || 4370}..." # Use local_opts
|
429
711
|
puts "Please ensure the device is powered on and connected to the network."
|
430
712
|
|
713
|
+
conn = nil # Initialize conn for safety in ensure block
|
431
714
|
begin
|
432
715
|
# Create ZK instance with options from config and command line
|
433
716
|
zk_options = {
|
434
|
-
port:
|
435
|
-
timeout:
|
436
|
-
password:
|
437
|
-
verbose:
|
438
|
-
force_udp:
|
439
|
-
omit_ping:
|
440
|
-
encoding:
|
717
|
+
port: local_opts[:port] || @config['port'] || 4370, # Use local_opts
|
718
|
+
timeout: local_opts[:timeout] || @config['timeout'] || 30, # Use local_opts
|
719
|
+
password: local_opts[:password] || @config['password'] || 0, # Use local_opts
|
720
|
+
verbose: local_opts[:verbose] || @config['verbose'] || false, # Use local_opts
|
721
|
+
force_udp: local_opts[:force_udp] || @config['force_udp'] || false, # Use local_opts
|
722
|
+
omit_ping: local_opts[:no_ping] || @config['no_ping'] || false, # Use local_opts
|
723
|
+
encoding: local_opts[:encoding] || @config['encoding'] || 'UTF-8' # Use local_opts
|
441
724
|
}
|
442
725
|
|
443
726
|
zk = RBZK::ZK.new(ip, **zk_options)
|
444
727
|
conn = zk.connect
|
445
728
|
|
446
729
|
if conn.connected?
|
447
|
-
puts "✓ Connected successfully!" unless
|
730
|
+
puts "✓ Connected successfully!" unless local_opts[:quiet] # Use local_opts
|
448
731
|
yield conn if block_given?
|
449
732
|
else
|
450
733
|
puts "✗ Failed to connect to device."
|
@@ -458,12 +741,16 @@ module RBZK
|
|
458
741
|
rescue => e
|
459
742
|
puts "✗ Unexpected Error: #{e.message}"
|
460
743
|
puts "An unexpected error occurred while communicating with the device."
|
461
|
-
puts e.backtrace.join("\n") if
|
744
|
+
puts e.backtrace.join("\n") if local_opts[:verbose] # Use local_opts
|
462
745
|
ensure
|
463
746
|
if conn && conn.connected?
|
464
|
-
|
465
|
-
|
466
|
-
|
747
|
+
if skip_disconnect
|
748
|
+
puts "Skipping disconnect as device is undergoing restart/poweroff." unless local_opts[:quiet] # Use local_opts
|
749
|
+
else
|
750
|
+
puts "Disconnecting from device..." unless local_opts[:quiet] # Use local_opts
|
751
|
+
conn.disconnect
|
752
|
+
puts "✓ Disconnected" unless local_opts[:quiet] # Use local_opts
|
753
|
+
end
|
467
754
|
end
|
468
755
|
end
|
469
756
|
end
|
data/lib/rbzk/finger.rb
CHANGED
@@ -2,13 +2,24 @@
|
|
2
2
|
|
3
3
|
module RBZK
|
4
4
|
class Finger
|
5
|
-
attr_accessor :uid, :fid, :valid, :template
|
5
|
+
attr_accessor :uid, :fid, :valid, :template, :size
|
6
6
|
|
7
7
|
def initialize(uid, fid, valid, template = "")
|
8
8
|
@uid = uid
|
9
9
|
@fid = fid
|
10
10
|
@valid = valid
|
11
11
|
@template = template
|
12
|
+
@size = template.length
|
13
|
+
end
|
14
|
+
|
15
|
+
# Pack the finger data into a binary string (full data)
|
16
|
+
def repack
|
17
|
+
[@size + 6, @uid, @fid, @valid].pack('S<S<CC') + @template
|
18
|
+
end
|
19
|
+
|
20
|
+
# Pack only the template data into a binary string
|
21
|
+
def repack_only
|
22
|
+
[@size].pack('S<') + @template
|
12
23
|
end
|
13
24
|
|
14
25
|
def to_s
|
data/lib/rbzk/user.rb
CHANGED
@@ -27,6 +27,40 @@ module RBZK
|
|
27
27
|
@card = card
|
28
28
|
end
|
29
29
|
|
30
|
+
# Pack the user data into a binary string for ZK6 devices (size 29)
|
31
|
+
def repack29
|
32
|
+
[2, @uid, @privilege].pack('CS<C') +
|
33
|
+
@password.encode(@@encoding, invalid: :replace, undef: :replace).ljust(5, "\x00")[0...5] +
|
34
|
+
@name.encode(@@encoding, invalid: :replace, undef: :replace).ljust(8, "\x00")[0...8] +
|
35
|
+
[@card, 0, @group_id.to_i, 0, @user_id.to_i].pack('L<CS<S<L<')
|
36
|
+
end
|
37
|
+
|
38
|
+
# Pack the user data into a binary string for ZK8 devices (size 73)
|
39
|
+
def repack73
|
40
|
+
[2, @uid, @privilege].pack('CS<C') +
|
41
|
+
@password.encode(@@encoding, invalid: :replace, undef: :replace).ljust(8, "\x00")[0...8] +
|
42
|
+
@name.encode(@@encoding, invalid: :replace, undef: :replace).ljust(24, "\x00")[0...24] +
|
43
|
+
[@card, 1].pack('L<C') +
|
44
|
+
@group_id.to_s.encode(@@encoding, invalid: :replace, undef: :replace).ljust(7, "\x00")[0...7] +
|
45
|
+
"\x00" +
|
46
|
+
@user_id.to_s.encode(@@encoding, invalid: :replace, undef: :replace).ljust(24, "\x00")[0...24]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Check if the user is disabled
|
50
|
+
def is_disabled?
|
51
|
+
(@privilege & 1) != 0
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check if the user is enabled
|
55
|
+
def is_enabled?
|
56
|
+
!is_disabled?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get the user type
|
60
|
+
def usertype
|
61
|
+
@privilege & 0xE
|
62
|
+
end
|
63
|
+
|
30
64
|
def to_s
|
31
65
|
"#{@uid} #{@user_id} #{@name} #{@privilege} #{@password} #{@group_id} #{@card}"
|
32
66
|
end
|
data/lib/rbzk/version.rb
CHANGED
data/lib/rbzk/zk.rb
CHANGED
@@ -15,8 +15,13 @@ module RBZK
|
|
15
15
|
|
16
16
|
def test_ping
|
17
17
|
begin
|
18
|
-
|
19
|
-
|
18
|
+
Timeout.timeout(5) do
|
19
|
+
s = TCPSocket.new(@ip, @port)
|
20
|
+
s.close
|
21
|
+
return true
|
22
|
+
end
|
23
|
+
rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError => e
|
24
|
+
return false
|
20
25
|
rescue => e
|
21
26
|
return false
|
22
27
|
end
|
@@ -343,6 +348,166 @@ module RBZK
|
|
343
348
|
end
|
344
349
|
end
|
345
350
|
|
351
|
+
# Unlock the door
|
352
|
+
# @param time [Integer] define delay in seconds
|
353
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
354
|
+
def unlock(time = 3)
|
355
|
+
command_string = [time * 10].pack('L<')
|
356
|
+
response = self.send_command(CMD_UNLOCK, command_string)
|
357
|
+
|
358
|
+
if response && response[:status]
|
359
|
+
true
|
360
|
+
else
|
361
|
+
raise RBZK::ZKErrorResponse, "Can't open door"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# Get the lock state
|
366
|
+
# @return [Boolean] true if door is open, false otherwise
|
367
|
+
def get_lock_state
|
368
|
+
response = self.send_command(CMD_DOORSTATE_RRQ)
|
369
|
+
|
370
|
+
if response && response[:status]
|
371
|
+
true
|
372
|
+
else
|
373
|
+
false
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
# Write text to LCD
|
378
|
+
# @param line_number [Integer] line number
|
379
|
+
# @param text [String] text to write
|
380
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
381
|
+
def write_lcd(line_number, text)
|
382
|
+
command_string = [line_number, 0].pack('s<c') + ' ' + text.encode(@encoding, invalid: :replace, undef: :replace)
|
383
|
+
response = self.send_command(CMD_WRITE_LCD, command_string)
|
384
|
+
|
385
|
+
if response && response[:status]
|
386
|
+
true
|
387
|
+
else
|
388
|
+
raise RBZK::ZKErrorResponse, "Can't write lcd"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# Clear LCD
|
393
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
394
|
+
def clear_lcd
|
395
|
+
response = self.send_command(CMD_CLEAR_LCD)
|
396
|
+
|
397
|
+
if response && response[:status]
|
398
|
+
true
|
399
|
+
else
|
400
|
+
raise RBZK::ZKErrorResponse, "Can't clear lcd"
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
# Refresh the device data
|
405
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
406
|
+
def refresh_data
|
407
|
+
response = self.send_command(CMD_REFRESHDATA)
|
408
|
+
|
409
|
+
if response && response[:status]
|
410
|
+
true
|
411
|
+
else
|
412
|
+
raise RBZK::ZKErrorResponse, "Can't refresh data"
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Create or update user by uid
|
417
|
+
# @param uid [Integer] user ID that are generated from device
|
418
|
+
# @param name [String] name of the user
|
419
|
+
# @param privilege [Integer] user privilege level (default or admin)
|
420
|
+
# @param password [String] user password
|
421
|
+
# @param group_id [String] group ID
|
422
|
+
# @param user_id [String] your own user ID
|
423
|
+
# @param card [Integer] card number
|
424
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
425
|
+
def set_user(uid: nil, name: '', privilege: 0, password: '', group_id: '', user_id: '', card: 0)
|
426
|
+
# If uid is not provided, use next_uid
|
427
|
+
if uid.nil?
|
428
|
+
uid = @next_uid
|
429
|
+
if user_id.empty?
|
430
|
+
user_id = @next_user_id
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# If user_id is not provided, use uid as string
|
435
|
+
if user_id.empty?
|
436
|
+
user_id = uid.to_s # ZK6 needs uid2 == uid
|
437
|
+
end
|
438
|
+
|
439
|
+
# Validate privilege
|
440
|
+
if privilege != USER_DEFAULT && privilege != USER_ADMIN
|
441
|
+
privilege = USER_DEFAULT
|
442
|
+
end
|
443
|
+
privilege = privilege.to_i
|
444
|
+
|
445
|
+
# Create command string based on user_packet_size
|
446
|
+
if @user_packet_size == 28 # firmware == 6
|
447
|
+
group_id = 0 if group_id.empty?
|
448
|
+
|
449
|
+
begin
|
450
|
+
command_string = [uid, privilege].pack('S<C') +
|
451
|
+
password.encode(@encoding, invalid: :replace, undef: :replace).ljust(5, "\x00")[0...5] +
|
452
|
+
name.encode(@encoding, invalid: :replace, undef: :replace).ljust(8, "\x00")[0...8] +
|
453
|
+
[card.to_i, 0, group_id.to_i, 0, user_id.to_i].pack('L<CS<S<L<')
|
454
|
+
rescue => e
|
455
|
+
if @verbose
|
456
|
+
puts "Error packing user: #{e.message}"
|
457
|
+
end
|
458
|
+
raise RBZK::ZKErrorResponse, "Can't pack user"
|
459
|
+
end
|
460
|
+
else
|
461
|
+
# For other firmware versions
|
462
|
+
name_pad = name.encode(@encoding, invalid: :replace, undef: :replace).ljust(24, "\x00")[0...24]
|
463
|
+
card_str = [card.to_i].pack('L<')[0...4]
|
464
|
+
command_string = [uid, privilege].pack('S<C') +
|
465
|
+
password.encode(@encoding, invalid: :replace, undef: :replace).ljust(8, "\x00")[0...8] +
|
466
|
+
name_pad +
|
467
|
+
card_str + "\x00" +
|
468
|
+
group_id.encode(@encoding, invalid: :replace, undef: :replace).ljust(7, "\x00")[0...7] +
|
469
|
+
"\x00" +
|
470
|
+
user_id.encode(@encoding, invalid: :replace, undef: :replace).ljust(24, "\x00")[0...24]
|
471
|
+
end
|
472
|
+
|
473
|
+
# Send command
|
474
|
+
response = self.send_command(CMD_USER_WRQ, command_string, 1024)
|
475
|
+
|
476
|
+
if response && response[:status]
|
477
|
+
# Update next_uid and next_user_id if necessary
|
478
|
+
self.refresh_data
|
479
|
+
if @next_uid == uid
|
480
|
+
@next_uid += 1
|
481
|
+
end
|
482
|
+
if @next_user_id == user_id
|
483
|
+
@next_user_id = @next_uid.to_s
|
484
|
+
end
|
485
|
+
true
|
486
|
+
else
|
487
|
+
raise RBZK::ZKErrorResponse, "Can't set user"
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
# Delete user by uid
|
492
|
+
# @param uid [Integer] user ID that are generated from device
|
493
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
494
|
+
def delete_user(uid: 0)
|
495
|
+
# Send command
|
496
|
+
command_string = [uid].pack('S<')
|
497
|
+
response = self.send_command(CMD_DELETE_USER, command_string)
|
498
|
+
|
499
|
+
if response && response[:status]
|
500
|
+
self.refresh_data
|
501
|
+
# Update next_uid if necessary
|
502
|
+
if uid == (@next_uid - 1)
|
503
|
+
@next_uid = uid
|
504
|
+
end
|
505
|
+
true
|
506
|
+
else
|
507
|
+
raise RBZK::ZKErrorResponse, "Can't delete user"
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
346
511
|
# Helper method to read data with buffer (ZK6: 1503)
|
347
512
|
def read_with_buffer(command, fct = 0, ext = 0)
|
348
513
|
|
@@ -712,6 +877,50 @@ module RBZK
|
|
712
877
|
end
|
713
878
|
end
|
714
879
|
|
880
|
+
# Send data with buffer
|
881
|
+
# @param buffer [String] data to send
|
882
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
883
|
+
def send_with_buffer(buffer)
|
884
|
+
max_chunk = 1024
|
885
|
+
size = buffer.size
|
886
|
+
self.free_data
|
887
|
+
|
888
|
+
command = CMD_PREPARE_DATA
|
889
|
+
command_string = [size].pack('L<')
|
890
|
+
response = self.send_command(command, command_string)
|
891
|
+
|
892
|
+
if !response || !response[:status]
|
893
|
+
raise RBZK::ZKErrorResponse, "Can't prepare data"
|
894
|
+
end
|
895
|
+
|
896
|
+
remain = size % max_chunk
|
897
|
+
packets = (size - remain) / max_chunk
|
898
|
+
start = 0
|
899
|
+
|
900
|
+
packets.times do
|
901
|
+
send_chunk(buffer[start, max_chunk])
|
902
|
+
start += max_chunk
|
903
|
+
end
|
904
|
+
|
905
|
+
send_chunk(buffer[start, remain]) if remain > 0
|
906
|
+
|
907
|
+
true
|
908
|
+
end
|
909
|
+
|
910
|
+
# Send a chunk of data
|
911
|
+
# @param command_string [String] data to send
|
912
|
+
# @return [Boolean] true if successful, raises exception otherwise
|
913
|
+
def send_chunk(command_string)
|
914
|
+
command = CMD_DATA
|
915
|
+
response = self.send_command(command, command_string)
|
916
|
+
|
917
|
+
if response && response[:status]
|
918
|
+
true
|
919
|
+
else
|
920
|
+
raise RBZK::ZKErrorResponse, "Can't send chunk"
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
715
924
|
# Helper method to read a chunk of data
|
716
925
|
def read_chunk(start, size)
|
717
926
|
if @verbose
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbzk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Khaled AbuShqear
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-28 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bytes
|