rbzk 0.1.6 → 0.1.8

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.
@@ -13,7 +13,6 @@ require 'terminal-table'
13
13
  module RBZK
14
14
  module CLI
15
15
  class Commands < Thor
16
-
17
16
  # Global options
18
17
  class_option :ip, type: :string, desc: 'IP address of the device'
19
18
  class_option :port, type: :numeric, desc: 'Port number (default: 4370)'
@@ -24,10 +23,13 @@ module RBZK
24
23
  class_option :no_ping, type: :boolean, desc: 'Skip ping check'
25
24
  class_option :encoding, type: :string, desc: 'Encoding for strings (default: UTF-8)'
26
25
 
27
- desc "info [IP]", "Get device information"
26
+ def self.exit_on_failure?
27
+ true
28
+ end
28
29
 
29
- def info(ip = nil)
30
+ desc 'info [IP]', 'Get device information'
30
31
 
32
+ def info(ip = nil)
31
33
  # Use IP from options if not provided as argument
32
34
  ip ||= options[:ip] || @config['ip']
33
35
 
@@ -59,16 +61,16 @@ module RBZK
59
61
  if defined?(::Terminal) && defined?(::Terminal::Table)
60
62
  # Pretty table output
61
63
  table = ::Terminal::Table.new do |t|
62
- t.title = "Device Information"
64
+ t.title = 'Device Information'
63
65
  device_info.each do |key, value|
64
- t << [ key, value ]
66
+ t << [key, value]
65
67
  end
66
68
  end
67
69
 
68
70
  puts table
69
71
  else
70
72
  # Fallback plain text output
71
- puts "Device Information:"
73
+ puts 'Device Information:'
72
74
  device_info.each do |key, value|
73
75
  puts "#{key}: #{value}"
74
76
  end
@@ -76,61 +78,61 @@ module RBZK
76
78
  end
77
79
  end
78
80
 
79
- desc "refresh [IP]", "Refresh device data"
81
+ desc 'refresh [IP]', 'Refresh device data'
80
82
 
81
83
  def refresh(ip = nil)
82
84
  # Use IP from options if not provided as argument
83
85
  ip ||= options[:ip] || @config['ip']
84
86
 
85
87
  with_connection(ip, options) do |conn|
86
- puts "Refreshing device data..."
88
+ puts 'Refreshing device data...'
87
89
  result = conn.refresh_data
88
- puts "✓ Device data refreshed successfully!" if result
90
+ puts '✓ Device data refreshed successfully!' if result
89
91
  end
90
92
  end
91
93
 
92
- desc "users [IP]", "Get users from the device"
94
+ desc 'users [IP]', 'Get users from the device'
93
95
 
94
96
  def users(ip = nil)
95
97
  # Use IP from options if not provided as argument
96
98
  ip ||= options[:ip] || @config['ip']
97
99
  with_connection(ip, options) do |conn|
98
- puts "Getting users..."
100
+ puts 'Getting users...'
99
101
  users = conn.get_users
100
102
  display_users(users)
101
103
  end
102
104
  end
103
105
 
104
106
  # Add aliases for common log commands
105
- desc "logs-today [IP]", "Get today's attendance logs"
106
- map "logs-today" => "logs"
107
+ desc 'logs-today [IP]', "Get today's attendance logs"
108
+ map 'logs-today' => 'logs'
107
109
 
108
110
  def logs_today(ip = nil)
109
- invoke :logs, [ ip ], { today: true }.merge(options)
111
+ invoke :logs, [ip], { today: true }.merge(options)
110
112
  end
111
113
 
112
- desc "logs-yesterday [IP]", "Get yesterday's attendance logs"
113
- map "logs-yesterday" => "logs"
114
+ desc 'logs-yesterday [IP]', "Get yesterday's attendance logs"
115
+ map 'logs-yesterday' => 'logs'
114
116
 
115
117
  def logs_yesterday(ip = nil)
116
- invoke :logs, [ ip ], { yesterday: true }.merge(options)
118
+ invoke :logs, [ip], { yesterday: true }.merge(options)
117
119
  end
118
120
 
119
- desc "logs-week [IP]", "Get this week's attendance logs"
120
- map "logs-week" => "logs"
121
+ desc 'logs-week [IP]', "Get this week's attendance logs"
122
+ map 'logs-week' => 'logs'
121
123
 
122
124
  def logs_week(ip = nil)
123
- invoke :logs, [ ip ], { week: true }.merge(options)
125
+ invoke :logs, [ip], { week: true }.merge(options)
124
126
  end
125
127
 
126
- desc "logs-month [IP]", "Get this month's attendance logs"
127
- map "logs-month" => "logs"
128
+ desc 'logs-month [IP]', "Get this month's attendance logs"
129
+ map 'logs-month' => 'logs'
128
130
 
129
131
  def logs_month(ip = nil)
130
- invoke :logs, [ ip ], { month: true }.merge(options)
132
+ invoke :logs, [ip], { month: true }.merge(options)
131
133
  end
132
134
 
133
- desc "logs-all [IP]", "Get all attendance logs without limit"
135
+ desc 'logs-all [IP]', 'Get all attendance logs without limit'
134
136
 
135
137
  def logs_all(ip = nil)
136
138
  # Use IP from options if not provided as argument
@@ -138,7 +140,7 @@ module RBZK
138
140
 
139
141
  with_connection(ip, options) do |conn|
140
142
  # Get attendance logs
141
- puts "Getting all attendance logs (this may take a while)..."
143
+ puts 'Getting all attendance logs (this may take a while)...'
142
144
  logs = conn.get_attendance_logs
143
145
  total_logs = logs.size
144
146
  puts "Total logs: #{total_logs}" if options[:verbose]
@@ -150,15 +152,17 @@ module RBZK
150
152
  if defined?(::Terminal) && defined?(::Terminal::Table)
151
153
  # Pretty table output
152
154
  table = ::Terminal::Table.new do |t|
153
- t.title = "All Attendance Logs (Showing All Records)"
154
- t.headings = [ 'User ID', 'Time', 'Status' ]
155
+ t.title = 'All Attendance Logs (Showing All Records)'
156
+ t.headings = ['UID', 'User ID', 'Time', 'Status', 'Punch Type']
155
157
 
156
158
  # Show all logs in the table
157
159
  logs.each do |log|
158
160
  t << [
161
+ log.uid,
159
162
  log.user_id,
160
163
  log.timestamp.strftime('%Y-%m-%d %H:%M:%S'),
161
- format_status(log.status)
164
+ log.status,
165
+ log.punch_name
162
166
  ]
163
167
  end
164
168
  end
@@ -167,7 +171,7 @@ module RBZK
167
171
  else
168
172
  # Fallback plain text output
169
173
  logs.each do |log|
170
- puts " User ID: #{log.user_id}, Time: #{log.timestamp.strftime('%Y-%m-%d %H:%M:%S')}, Status: #{format_status(log.status)}"
174
+ puts " UID: #{log.uid}, User ID: #{log.user_id}, Time: #{log.timestamp.strftime('%Y-%m-%d %H:%M:%S')}, Status: #{format_status(log.status)}, Punch: #{log.punch_name}"
171
175
  end
172
176
  end
173
177
  else
@@ -176,31 +180,32 @@ module RBZK
176
180
  end
177
181
  end
178
182
 
179
- desc "logs-custom START_DATE END_DATE [IP]", "Get logs for a custom date range (YYYY-MM-DD)"
183
+ desc 'logs-custom START_DATE END_DATE [IP]', 'Get logs for a custom date range (YYYY-MM-DD)'
180
184
 
181
185
  def logs_custom(start_date, end_date, ip = nil)
182
186
  # Use IP from options if not provided as argument
183
187
  ip ||= options[:ip] || @config['ip']
184
- invoke :logs, [ ip ], { start_date: start_date, end_date: end_date }.merge(options)
188
+ invoke :logs, [ip], { start_date: start_date, end_date: end_date }.merge(options)
185
189
  end
186
190
 
187
- desc "logs [IP]", "Get attendance logs"
191
+ desc 'logs [IP]', 'Get attendance logs'
188
192
  method_option :today, type: :boolean, desc: "Get only today's logs"
189
193
  method_option :yesterday, type: :boolean, desc: "Get only yesterday's logs"
190
194
  method_option :week, type: :boolean, desc: "Get this week's logs"
191
195
  method_option :month, type: :boolean, desc: "Get this month's logs"
192
- method_option :start_date, type: :string, desc: "Start date for custom range (YYYY-MM-DD)"
193
- method_option :end_date, type: :string, desc: "End date for custom range (YYYY-MM-DD)"
194
- method_option :start_time, type: :string, desc: "Start time for custom range (HH:MM)"
195
- method_option :end_time, type: :string, desc: "End time for custom range (HH:MM)"
196
- method_option :limit, type: :numeric, default: 25, desc: "Limit the number of logs displayed (default: 25, use 0 for all)"
196
+ method_option :start_date, type: :string, desc: 'Start date for custom range (YYYY-MM-DD)'
197
+ method_option :end_date, type: :string, desc: 'End date for custom range (YYYY-MM-DD)'
198
+ method_option :start_time, type: :string, desc: 'Start time for custom range (HH:MM)'
199
+ method_option :end_time, type: :string, desc: 'End time for custom range (HH:MM)'
200
+ method_option :limit, type: :numeric, default: 25,
201
+ desc: 'Limit the number of logs displayed (default: 25, use 0 for all)'
197
202
 
198
203
  def logs(ip = nil)
199
204
  # Use IP from options if not provided as argument
200
205
  ip ||= options[:ip] || @config['ip']
201
206
  with_connection(ip, options) do |conn|
202
207
  # Get attendance logs
203
- puts "Getting attendance logs..."
208
+ puts 'Getting attendance logs...'
204
209
  logs = conn.get_attendance_logs
205
210
  total_logs = logs.size
206
211
  puts "Total logs: #{total_logs}" if options[:verbose]
@@ -222,7 +227,8 @@ module RBZK
222
227
  elsif options[:month]
223
228
  today = Date.today
224
229
  start_of_month = Date.new(today.year, today.month, 1)
225
- logs = filter_logs_by_datetime(logs, start_of_month, today, options[:start_time], options[:end_time])
230
+ logs = filter_logs_by_datetime(logs, start_of_month, today, options[:start_time],
231
+ options[:end_time])
226
232
  "This Month's Attendance Logs (#{start_of_month} to #{today})"
227
233
  elsif options[:start_date] && options[:end_date]
228
234
  begin
@@ -233,11 +239,12 @@ module RBZK
233
239
  puts "Filtering logs from #{start_date} to #{end_date}..." if options[:verbose]
234
240
 
235
241
  # Use the filter_logs_by_datetime method
236
- logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time], options[:end_time])
242
+ logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time],
243
+ options[:end_time])
237
244
 
238
245
  "Attendance Logs (#{start_date} to #{end_date})"
239
246
  rescue ArgumentError
240
- puts "Error: Invalid date format. Please use YYYY-MM-DD format."
247
+ puts 'Error: Invalid date format. Please use YYYY-MM-DD format.'
241
248
  return
242
249
  end
243
250
  elsif options[:start_date]
@@ -249,11 +256,12 @@ module RBZK
249
256
  puts "Filtering logs from #{start_date} onwards..." if options[:verbose]
250
257
 
251
258
  # Use the filter_logs_by_datetime method
252
- logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time], options[:end_time])
259
+ logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time],
260
+ options[:end_time])
253
261
 
254
262
  "Attendance Logs (#{start_date} to #{end_date})"
255
263
  rescue ArgumentError
256
- puts "Error: Invalid date format. Please use YYYY-MM-DD format."
264
+ puts 'Error: Invalid date format. Please use YYYY-MM-DD format.'
257
265
  return
258
266
  end
259
267
  elsif options[:end_date]
@@ -266,15 +274,16 @@ module RBZK
266
274
  puts "Filtering logs from #{start_date} to #{end_date}..." if options[:verbose]
267
275
 
268
276
  # Use the filter_logs_by_datetime method
269
- logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time], options[:end_time])
277
+ logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time],
278
+ options[:end_time])
270
279
 
271
280
  "Attendance Logs (#{start_date} to #{end_date})"
272
281
  rescue ArgumentError
273
- puts "Error: Invalid date format. Please use YYYY-MM-DD format."
282
+ puts 'Error: Invalid date format. Please use YYYY-MM-DD format.'
274
283
  return
275
284
  end
276
285
  else
277
- "All Attendance Logs"
286
+ 'All Attendance Logs'
278
287
  end
279
288
 
280
289
  # Display logs
@@ -288,15 +297,17 @@ module RBZK
288
297
  if defined?(::Terminal) && defined?(::Terminal::Table)
289
298
  # Pretty table output
290
299
  table = ::Terminal::Table.new do |t|
291
- t.title = title || "Attendance Logs"
292
- t.headings = [ 'User ID', 'Time', 'Status' ]
300
+ t.title = title || 'Attendance Logs'
301
+ t.headings = ['UID', 'User ID', 'Time', 'Status', 'Punch Type']
293
302
 
294
303
  # Show logs in the table based on limit
295
304
  display_logs.each do |log|
296
305
  t << [
306
+ log.uid,
297
307
  log.user_id,
298
308
  log.timestamp.strftime('%Y-%m-%d %H:%M:%S'),
299
- format_status(log.status)
309
+ log.status.to_s,
310
+ log.punch_name
300
311
  ]
301
312
  end
302
313
  end
@@ -310,12 +321,10 @@ module RBZK
310
321
  else
311
322
  # Fallback plain text output
312
323
  display_logs.each do |log|
313
- puts " User ID: #{log.user_id}, Time: #{log.timestamp.strftime('%Y-%m-%d %H:%M:%S')}, Status: #{format_status(log.status)}"
324
+ puts " UID: #{log.uid}, User ID: #{log.user_id}, Time: #{log.timestamp.strftime('%Y-%m-%d %H:%M:%S')}, Status: #{format_status(log.status)}, Punch: #{log.punch_name}"
314
325
  end
315
326
 
316
- if logs.size > display_logs.size
317
- puts " ... and #{logs.size - display_logs.size} more records"
318
- end
327
+ puts " ... and #{logs.size - display_logs.size} more records" if logs.size > display_logs.size
319
328
  end
320
329
  else
321
330
  puts "\nNo attendance records found"
@@ -323,26 +332,25 @@ module RBZK
323
332
  end
324
333
  end
325
334
 
326
- desc "clear-logs [IP]", "Clear attendance logs"
327
- map "clear-logs" => :clear_logs
335
+ desc 'clear-logs [IP]', 'Clear attendance logs'
336
+ map 'clear-logs' => :clear_logs
328
337
 
329
338
  def clear_logs(ip = nil)
330
-
331
339
  # Use IP from options if not provided as argument
332
340
  ip ||= options[:ip] || @config['ip']
333
341
  with_connection(ip, options) do |conn|
334
- puts "WARNING: This will delete all attendance logs from the device."
335
- return unless yes?("Are you sure you want to continue? (y/N)")
342
+ puts 'WARNING: This will delete all attendance logs from the device.'
343
+ return unless yes?('Are you sure you want to continue? (y/N)')
336
344
 
337
- puts "Clearing attendance logs..."
345
+ puts 'Clearing attendance logs...'
338
346
  result = conn.clear_attendance
339
- puts "✓ Attendance logs cleared successfully!" if result
347
+ puts '✓ Attendance logs cleared successfully!' if result
340
348
  end
341
349
  end
342
350
 
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
351
+ desc 'unlock [IP]', 'Unlock the door'
352
+ method_option :time, type: :numeric, default: 3, desc: 'Unlock time in seconds (default: 3)'
353
+ map 'unlock' => :unlock_door
346
354
 
347
355
  def unlock_door(ip = nil)
348
356
  # Use IP from options if not provided as argument
@@ -354,12 +362,12 @@ module RBZK
354
362
  with_connection(ip, options) do |conn|
355
363
  puts "Unlocking door for #{time} seconds..."
356
364
  result = conn.unlock(time)
357
- puts "✓ Door unlocked successfully!" if result
365
+ puts '✓ Door unlocked successfully!' if result
358
366
  end
359
367
  end
360
368
 
361
- desc "door-state [IP]", "Get the door lock state"
362
- map "door-state" => :door_state
369
+ desc 'door-state [IP]', 'Get the door lock state'
370
+ map 'door-state' => :door_state
363
371
 
364
372
  def door_state(ip = nil)
365
373
  # Use IP from options if not provided as argument
@@ -371,8 +379,8 @@ module RBZK
371
379
  end
372
380
  end
373
381
 
374
- desc "write-lcd [IP] LINE_NUMBER TEXT", "Write text to LCD display"
375
- map "write-lcd" => :write_lcd
382
+ desc 'write-lcd [IP] LINE_NUMBER TEXT', 'Write text to LCD display'
383
+ map 'write-lcd' => :write_lcd
376
384
 
377
385
  def write_lcd(line_number, text, ip = nil)
378
386
  # Use IP from options if not provided as argument
@@ -384,59 +392,77 @@ module RBZK
384
392
  with_connection(ip, options) do |conn|
385
393
  puts "Writing text to LCD line #{line_number}..."
386
394
  result = conn.write_lcd(line_number, text)
387
- puts "✓ Text written to LCD successfully!" if result
395
+ puts '✓ Text written to LCD successfully!' if result
388
396
  end
389
397
  end
390
398
 
391
- desc "clear-lcd [IP]", "Clear the LCD display"
392
- map "clear-lcd" => :clear_lcd
399
+ desc 'clear-lcd [IP]', 'Clear the LCD display'
400
+ map 'clear-lcd' => :clear_lcd
393
401
 
394
402
  def clear_lcd(ip = nil)
395
403
  # Use IP from options if not provided as argument
396
404
  ip ||= options[:ip] || @config['ip']
397
405
 
398
406
  with_connection(ip, options) do |conn|
399
- puts "Clearing LCD display..."
407
+ puts 'Clearing LCD display...'
400
408
  result = conn.clear_lcd
401
- puts "✓ LCD cleared successfully!" if result
409
+ puts '✓ LCD cleared successfully!' if result
402
410
  end
403
411
  end
404
412
 
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
413
+ desc 'add-user [IP]', 'Add or update a user'
414
+ method_option :uid, type: :numeric, desc: 'User ID (generated by device if not provided)'
415
+ method_option :name, type: :string, default: '', desc: 'User name'
416
+ method_option :privilege, type: :numeric, default: 0, desc: 'User privilege (0=User, 14=Admin)'
417
+ method_option :password, type: :string, default: '', desc: 'User password'
418
+ method_option :group_id, type: :string, default: '', desc: 'Group ID'
419
+ method_option :user_id, type: :string, default: '', desc: 'Custom user ID'
420
+ method_option :card, type: :numeric, default: 0, desc: 'Card number'
421
+ map 'add-user' => :add_user
414
422
 
415
423
  def add_user(ip = nil)
416
424
  # Use IP from options if not provided as argument
417
425
  ip ||= options[:ip] || @config['ip']
418
426
 
419
427
  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
428
+ puts 'Adding/updating user...'
429
+
430
+ # Disable device for exclusive access during modification
431
+ begin
432
+ conn.disable_device
433
+ rescue StandardError
434
+ nil
435
+ end
436
+
437
+ begin
438
+ result = conn.set_user(
439
+ uid: options[:uid],
440
+ name: options[:name] || '',
441
+ privilege: options[:privilege] || 0,
442
+ password: options[:password] || '',
443
+ group_id: options[:group_id] || '',
444
+ user_id: options[:user_id] || '',
445
+ card: options[:card] || 0
446
+ )
447
+ puts '✓ User added/updated successfully!' if result
448
+ rescue RBZK::ZKError => e
449
+ # Reraise to let the with_connection handler print device messages
450
+ raise e
451
+ ensure
452
+ # Re-enable device after modification (always)
453
+ begin
454
+ conn.enable_device
455
+ rescue StandardError
456
+ nil
457
+ end
458
+ end
433
459
  end
434
460
  end
435
461
 
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
462
+ desc 'delete-user [IP]', 'Delete a user'
463
+ method_option :uid, type: :numeric, desc: 'User ID (generated by device)'
464
+ method_option :user_id, type: :string, desc: 'Custom user ID'
465
+ map 'delete-user' => :delete_user
440
466
 
441
467
  def delete_user(ip = nil)
442
468
  # Use IP from options if not provided as argument
@@ -444,33 +470,33 @@ module RBZK
444
470
 
445
471
  # Ensure at least one of uid or user_id is provided
446
472
  if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
447
- puts "Error: You must provide either --uid"
473
+ puts 'Error: You must provide either --uid'
448
474
  return
449
475
  end
450
476
 
451
477
  with_connection(ip, options) do |conn|
452
- puts "Deleting user..."
478
+ puts 'Deleting user...'
453
479
 
454
480
  # Use the User object with delete_user
455
481
  result = conn.delete_user(uid: options[:uid])
456
482
 
457
483
  if result
458
- puts "✓ User deleted successfully!"
484
+ puts '✓ User deleted successfully!'
459
485
  else
460
- puts "✗ User not found or could not be deleted."
486
+ puts '✗ User not found or could not be deleted.'
461
487
  end
462
488
  end
463
489
  end
464
490
 
465
- desc "get-templates [IP]", "Get all fingerprint templates"
466
- map "get-templates" => :get_templates
491
+ desc 'get-templates [IP]', 'Get all fingerprint templates'
492
+ map 'get-templates' => :get_templates
467
493
 
468
494
  def get_templates(ip = nil)
469
495
  # Use IP from options if not provided as argument
470
496
  ip ||= options[:ip] || @config['ip']
471
497
 
472
498
  with_connection(ip, options) do |conn|
473
- puts "Getting fingerprint templates..."
499
+ puts 'Getting fingerprint templates...'
474
500
  templates = conn.get_templates
475
501
 
476
502
  if templates && !templates.empty?
@@ -478,8 +504,8 @@ module RBZK
478
504
 
479
505
  # Use Terminal::Table for pretty output
480
506
  table = ::Terminal::Table.new do |t|
481
- t.title = "Fingerprint Templates"
482
- t.headings = [ 'UID', 'Finger ID', 'Valid', 'Size' ]
507
+ t.title = 'Fingerprint Templates'
508
+ t.headings = ['UID', 'Finger ID', 'Valid', 'Size']
483
509
 
484
510
  templates.each do |template|
485
511
  t << [
@@ -493,16 +519,16 @@ module RBZK
493
519
 
494
520
  puts table
495
521
  else
496
- puts "✓ No fingerprint templates found"
522
+ puts '✓ No fingerprint templates found'
497
523
  end
498
524
  end
499
525
  end
500
526
 
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
527
+ desc 'get-user-template [IP]', "Get a specific user's fingerprint template"
528
+ method_option :uid, type: :numeric, desc: 'User ID (generated by device)'
529
+ method_option :user_id, type: :string, desc: 'Custom user ID'
530
+ method_option :finger_id, type: :numeric, default: 0, desc: 'Finger ID (0-9)'
531
+ map 'get-user-template' => :get_user_template
506
532
 
507
533
  def get_user_template(ip = nil)
508
534
  # Use IP from options if not provided as argument
@@ -510,37 +536,37 @@ module RBZK
510
536
 
511
537
  # Ensure at least one of uid or user_id is provided
512
538
  if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
513
- puts "Error: You must provide either --uid or --user-id"
539
+ puts 'Error: You must provide either --uid or --user-id'
514
540
  return
515
541
  end
516
542
 
517
543
  with_connection(ip, options) do |conn|
518
- puts "Getting user fingerprint template..."
544
+ puts 'Getting user fingerprint template...'
519
545
 
520
546
  # Extract parameters from options
521
547
  uid = options[:uid] || 0
522
- user_id = options[:user_id] || ""
548
+ user_id = options[:user_id] || ''
523
549
  finger_id = options[:finger_id] || 0
524
550
 
525
551
  template = conn.get_user_template(uid: uid, temp_id: finger_id, user_id: user_id)
526
552
 
527
553
  if template
528
- puts "✓ Found fingerprint template:"
554
+ puts '✓ Found fingerprint template:'
529
555
  puts " User ID: #{template.uid}"
530
556
  puts " Finger ID: #{template.fid}"
531
557
  puts " Valid: #{template.valid == 1 ? 'Yes' : 'No'}"
532
558
  puts " Size: #{template.size} bytes"
533
559
  else
534
- puts "✗ Fingerprint template not found"
560
+ puts '✗ Fingerprint template not found'
535
561
  end
536
562
  end
537
563
  end
538
564
 
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
565
+ desc 'delete-template [IP]', 'Delete a specific fingerprint template'
566
+ method_option :uid, type: :numeric, desc: 'User ID (generated by device)'
567
+ method_option :user_id, type: :string, desc: 'Custom user ID'
568
+ method_option :finger_id, type: :numeric, default: 0, desc: 'Finger ID (0-9)'
569
+ map 'delete-template' => :delete_template
544
570
 
545
571
  def delete_template(ip = nil)
546
572
  # Use IP from options if not provided as argument
@@ -548,31 +574,31 @@ module RBZK
548
574
 
549
575
  # Ensure at least one of uid or user_id is provided
550
576
  if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
551
- puts "Error: You must provide either --uid or --user-id"
577
+ puts 'Error: You must provide either --uid or --user-id'
552
578
  return
553
579
  end
554
580
 
555
581
  with_connection(ip, options) do |conn|
556
- puts "Deleting fingerprint template..."
582
+ puts 'Deleting fingerprint template...'
557
583
 
558
584
  # Extract parameters from options
559
585
  uid = options[:uid] || 0
560
- user_id = options[:user_id] || ""
586
+ user_id = options[:user_id] || ''
561
587
  finger_id = options[:finger_id] || 0
562
588
 
563
589
  result = conn.delete_user_template(uid: uid, temp_id: finger_id, user_id: user_id)
564
590
 
565
591
  if result
566
- puts "✓ Fingerprint template deleted successfully!"
592
+ puts '✓ Fingerprint template deleted successfully!'
567
593
  else
568
- puts "✗ Fingerprint template not found or could not be deleted"
594
+ puts '✗ Fingerprint template not found or could not be deleted'
569
595
  end
570
596
  end
571
597
  end
572
598
 
573
- desc "test-voice [IP]", "Test the device voice"
574
- method_option :index, type: :numeric, desc: "Sound index to play (0-35, default: 0)"
575
- map "test-voice" => :test_voice
599
+ desc 'test-voice [IP]', 'Test the device voice'
600
+ method_option :index, type: :numeric, desc: 'Sound index to play (0-35, default: 0)'
601
+ map 'test-voice' => :test_voice
576
602
 
577
603
  def test_voice(ip = nil)
578
604
  # Use IP from options if not provided as argument
@@ -583,89 +609,89 @@ module RBZK
583
609
 
584
610
  # Print available sound indices if verbose
585
611
  if options[:verbose]
586
- puts "Available sound indices:"
587
- puts " 0: Thank You"
588
- puts " 1: Incorrect Password"
589
- puts " 2: Access Denied"
590
- puts " 3: Invalid ID"
591
- puts " 4: Please try again"
592
- puts " 5: Duplicate ID"
593
- puts " 6: The clock is flow"
594
- puts " 7: The clock is full"
595
- puts " 8: Duplicate finger"
596
- puts " 9: Duplicated punch"
597
- puts "10: Beep kuko"
598
- puts "11: Beep siren"
599
- puts "13: Beep bell"
600
- puts "18: Windows(R) opening sound"
601
- puts "20: Fingerprint not emolt"
602
- puts "21: Password not emolt"
603
- puts "22: Badges not emolt"
604
- puts "23: Face not emolt"
605
- puts "24: Beep standard"
606
- puts "30: Invalid user"
607
- puts "31: Invalid time period"
608
- puts "32: Invalid combination"
609
- puts "33: Illegal Access"
610
- puts "34: Disk space full"
611
- puts "35: Duplicate fingerprint"
612
- puts "51: Focus eyes on the green box"
612
+ puts 'Available sound indices:'
613
+ puts ' 0: Thank You'
614
+ puts ' 1: Incorrect Password'
615
+ puts ' 2: Access Denied'
616
+ puts ' 3: Invalid ID'
617
+ puts ' 4: Please try again'
618
+ puts ' 5: Duplicate ID'
619
+ puts ' 6: The clock is flow'
620
+ puts ' 7: The clock is full'
621
+ puts ' 8: Duplicate finger'
622
+ puts ' 9: Duplicated punch'
623
+ puts '10: Beep kuko'
624
+ puts '11: Beep siren'
625
+ puts '13: Beep bell'
626
+ puts '18: Windows(R) opening sound'
627
+ puts '20: Fingerprint not emolt'
628
+ puts '21: Password not emolt'
629
+ puts '22: Badges not emolt'
630
+ puts '23: Face not emolt'
631
+ puts '24: Beep standard'
632
+ puts '30: Invalid user'
633
+ puts '31: Invalid time period'
634
+ puts '32: Invalid combination'
635
+ puts '33: Illegal Access'
636
+ puts '34: Disk space full'
637
+ puts '35: Duplicate fingerprint'
638
+ puts '51: Focus eyes on the green box'
613
639
  end
614
640
 
615
641
  with_connection(ip, options) do |conn|
616
642
  puts "Testing device voice with index #{index}..."
617
643
  result = conn.test_voice(index)
618
- puts "✓ Voice test successful!" if result
644
+ puts '✓ Voice test successful!' if result
619
645
  end
620
646
  end
621
647
 
622
- desc "restart [IP]", "Restart the device"
648
+ desc 'restart [IP]', 'Restart the device'
623
649
 
624
650
  def restart(ip = nil)
625
651
  # Use IP from options if not provided as argument
626
652
  ip ||= options[:ip] || @config['ip']
627
653
 
628
- if yes?("Are you sure you want to restart the device? (y/N)")
654
+ if yes?('Are you sure you want to restart the device? (y/N)')
629
655
  with_connection(ip, options.merge(skip_disconnect_after_yield: true)) do |conn|
630
- puts "Restarting device..."
656
+ puts 'Restarting device...'
631
657
  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."
658
+ puts '✓ Device restart command sent successfully!' if result
659
+ puts 'The device will restart now. You may need to wait a few moments before reconnecting.'
634
660
  end
635
661
  else
636
- puts "Operation cancelled."
662
+ puts 'Operation cancelled.'
637
663
  end
638
664
  end
639
665
 
640
- desc "poweroff [IP]", "Power off the device"
666
+ desc 'poweroff [IP]', 'Power off the device'
641
667
 
642
668
  def poweroff(ip = nil)
643
669
  # Use IP from options if not provided as argument
644
670
  ip ||= options[:ip] || @config['ip']
645
671
 
646
- if yes?("Are you sure you want to power off the device? (y/N)")
672
+ if yes?('Are you sure you want to power off the device? (y/N)')
647
673
  with_connection(ip, options.merge(skip_disconnect_after_yield: true)) do |conn|
648
- puts "Powering off device..."
674
+ puts 'Powering off device...'
649
675
  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."
676
+ puts '✓ Device poweroff command sent successfully!' if result
677
+ puts 'The device will power off now. You will need to manually power it back on.'
652
678
  end
653
679
  else
654
- puts "Operation cancelled."
680
+ puts 'Operation cancelled.'
655
681
  end
656
682
  end
657
683
 
658
- desc "config", "Show current configuration"
684
+ desc 'config', 'Show current configuration'
659
685
 
660
686
  def config
661
- puts "RBZK Configuration"
662
- puts "=================="
687
+ puts 'RBZK Configuration'
688
+ puts '=================='
663
689
  @config.to_h.each do |key, value|
664
690
  puts "#{key}: #{value}"
665
691
  end
666
692
  end
667
693
 
668
- desc "config-set KEY VALUE", "Set a configuration value"
694
+ desc 'config-set KEY VALUE', 'Set a configuration value'
669
695
 
670
696
  def config_set(key, value)
671
697
  # Convert value to appropriate type
@@ -683,16 +709,16 @@ module RBZK
683
709
  puts "Configuration updated: #{key} = #{typed_value}"
684
710
  end
685
711
 
686
- desc "config-reset", "Reset configuration to defaults"
712
+ desc 'config-reset', 'Reset configuration to defaults'
687
713
 
688
714
  def config_reset
689
- if yes?("Are you sure you want to reset all configuration to defaults? (y/N)")
715
+ if yes?('Are you sure you want to reset all configuration to defaults? (y/N)')
690
716
  FileUtils.rm_f(@config.config_file)
691
717
  @config = RBZK::CLI::Config.new
692
- puts "Configuration reset to defaults."
718
+ puts 'Configuration reset to defaults.'
693
719
  invoke :config
694
720
  else
695
- puts "Operation cancelled."
721
+ puts 'Operation cancelled.'
696
722
  end
697
723
  end
698
724
 
@@ -708,7 +734,7 @@ module RBZK
708
734
  skip_disconnect = local_opts.delete(:skip_disconnect_after_yield) || false
709
735
 
710
736
  puts "Connecting to ZKTeco device at #{ip}:#{local_opts[:port] || @config['port'] || 4370}..." # Use local_opts
711
- puts "Please ensure the device is powered on and connected to the network."
737
+ puts 'Please ensure the device is powered on and connected to the network.'
712
738
 
713
739
  conn = nil # Initialize conn for safety in ensure block
714
740
  begin
@@ -727,29 +753,32 @@ module RBZK
727
753
  conn = zk.connect
728
754
 
729
755
  if conn.connected?
730
- puts "✓ Connected successfully!" unless local_opts[:quiet] # Use local_opts
756
+ puts '✓ Connected successfully!' unless local_opts[:quiet] # Use local_opts
731
757
  yield conn if block_given?
732
758
  else
733
- puts "✗ Failed to connect to device."
759
+ puts '✗ Failed to connect to device.'
734
760
  end
735
761
  rescue RBZK::ZKNetworkError => e
736
762
  puts "✗ Network Error: #{e.message}"
737
- puts "Please check the IP address and ensure the device is reachable."
763
+ puts 'Please check the IP address and ensure the device is reachable.'
764
+ rescue RBZK::ZKErrorExists => e
765
+ puts "✗ Device Error: #{e.message}"
738
766
  rescue RBZK::ZKErrorResponse => e
739
767
  puts "✗ Device Error: #{e.message}"
740
- puts "The device returned an error response."
741
- rescue => e
768
+ puts 'The device returned an error response.'
769
+ rescue StandardError => e
742
770
  puts "✗ Unexpected Error: #{e.message}"
743
- puts "An unexpected error occurred while communicating with the device."
771
+ puts 'An unexpected error occurred while communicating with the device.'
744
772
  puts e.backtrace.join("\n") if local_opts[:verbose] # Use local_opts
745
773
  ensure
746
774
  if conn && conn.connected?
747
775
  if skip_disconnect
748
- puts "Skipping disconnect as device is undergoing restart/poweroff." unless local_opts[:quiet] # Use local_opts
776
+ # Use local_opts
777
+ puts 'Skipping disconnect as device is undergoing restart/poweroff.' unless local_opts[:quiet]
749
778
  else
750
- puts "Disconnecting from device..." unless local_opts[:quiet] # Use local_opts
779
+ puts 'Disconnecting from device...' unless local_opts[:quiet] # Use local_opts
751
780
  conn.disconnect
752
- puts "✓ Disconnected" unless local_opts[:quiet] # Use local_opts
781
+ puts '✓ Disconnected' unless local_opts[:quiet] # Use local_opts
753
782
  end
754
783
  end
755
784
  end
@@ -761,8 +790,8 @@ module RBZK
761
790
 
762
791
  # Use Terminal::Table for pretty output
763
792
  table = ::Terminal::Table.new do |t|
764
- t.title = "Users"
765
- t.headings = [ 'UID', 'User ID', 'Name', 'Privilege', 'Password', 'Group ID', 'Card' ]
793
+ t.title = 'Users'
794
+ t.headings = ['UID', 'User ID', 'Name', 'Privilege', 'Password', 'Group ID', 'Card']
766
795
 
767
796
  users.each do |user|
768
797
  t << [
@@ -770,7 +799,7 @@ module RBZK
770
799
  user.user_id,
771
800
  user.name,
772
801
  format_privilege(user.privilege),
773
- (user.password.nil? || user.password.empty?) ? '(none)' : '(set)',
802
+ user.password.nil? || user.password.empty? ? '(none)' : '(set)',
774
803
  user.group_id,
775
804
  user.card
776
805
  ]
@@ -779,7 +808,7 @@ module RBZK
779
808
 
780
809
  puts table
781
810
  else
782
- puts "✓ No users found"
811
+ puts '✓ No users found'
783
812
  end
784
813
  end
785
814
 
@@ -799,32 +828,17 @@ module RBZK
799
828
  log.timestamp >= start_datetime && log.timestamp <= end_datetime
800
829
  end
801
830
 
802
- if options[:verbose]
803
- puts "Filtered logs: #{filtered_logs.size} of #{logs.size}"
804
- end
831
+ puts "Filtered logs: #{filtered_logs.size} of #{logs.size}" if options[:verbose]
805
832
 
806
833
  filtered_logs
807
834
  end
808
835
 
809
- def format_status(status)
810
- case status
811
- when 0 then "Check In"
812
- when 1 then "Check Out"
813
- when 2 then "Break Out"
814
- when 3 then "Break In"
815
- when 4 then "Overtime In"
816
- when 5 then "Overtime Out"
817
- else "Unknown (#{status})"
818
- end
819
- end
820
-
821
836
  def format_privilege(privilege)
822
837
  case privilege
823
- when 0 then "User"
824
- when 1 then "Enroller"
825
- when 2 then "Manager"
826
- when 3 then "Administrator"
827
- when 14 then "Super Admin"
838
+ when RBZK::Constants::USER_DEFAULT then 'User'
839
+ when RBZK::Constants::USER_ENROLLER then 'Enroller'
840
+ when RBZK::Constants::USER_MANAGER then 'Manager'
841
+ when RBZK::Constants::USER_ADMIN then 'Admin'
828
842
  else "Unknown (#{privilege})"
829
843
  end
830
844
  end