Class: RBZK::CLI::Commands

Inherits:
Thor
  • Object
show all
Defined in:
lib/rbzk/cli/commands.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.exit_on_failure?Boolean

Returns:

  • (Boolean)


26
27
28
# File 'lib/rbzk/cli/commands.rb', line 26

def self.exit_on_failure?
  true
end

Instance Method Details

#add_user(ip = nil) ⇒ Object



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/rbzk/cli/commands.rb', line 423

def add_user(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    puts 'Adding/updating user...'

    # Disable device for exclusive access during modification
    begin
      puts '✓ Disabling device...'
      conn.disable_device
    rescue StandardError
      nil
    end

    begin
      result = conn.set_user(
        uid: options[:uid],
        name: options[:name] || '',
        privilege: options[:privilege] || 0,
        password: options[:password] || '',
        group_id: options[:group_id] || '',
        user_id: options[:user_id] || '',
        card: options[:card] || 0
      )
      puts '✓ User added/updated successfully!' if result
    rescue RBZK::ZKError => e
      # Reraise to let the with_connection handler print device messages
      raise e
    ensure
      # Re-enable device after modification (always)
      begin
        puts '✓ Re-enabling device...'
        conn.enable_device
      rescue StandardError => e
        # Do not raise - just log a warning if verbose
        puts "Warning: failed to re-enable device: #{e.message}" if options[:verbose]
      end
    end
  end
end

#clear_lcd(ip = nil) ⇒ Object



402
403
404
405
406
407
408
409
410
411
# File 'lib/rbzk/cli/commands.rb', line 402

def clear_lcd(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    puts 'Clearing LCD display...'
    result = conn.clear_lcd
    puts '✓ LCD cleared successfully!' if result
  end
end

#clear_logs(ip = nil) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/rbzk/cli/commands.rb', line 338

def clear_logs(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']
  with_connection(ip, options) do |conn|
    puts 'WARNING: This will delete all attendance logs from the device.'
    return unless yes?('Are you sure you want to continue? (y/N)')

    puts 'Clearing attendance logs...'
    result = conn.clear_attendance
    puts '✓ Attendance logs cleared successfully!' if result
  end
end

#configObject



716
717
718
719
720
721
722
# File 'lib/rbzk/cli/commands.rb', line 716

def config
  puts 'RBZK Configuration'
  puts '=================='
  @config.to_h.each do |key, value|
    puts "#{key}: #{value}"
  end
end

#config_resetObject



744
745
746
747
748
749
750
751
752
753
# File 'lib/rbzk/cli/commands.rb', line 744

def config_reset
  if yes?('Are you sure you want to reset all configuration to defaults? (y/N)')
    FileUtils.rm_f(@config.config_file)
    @config = RBZK::CLI::Config.new
    puts 'Configuration reset to defaults.'
    invoke :config
  else
    puts 'Operation cancelled.'
  end
end

#config_set(key, value) ⇒ Object



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
# File 'lib/rbzk/cli/commands.rb', line 726

def config_set(key, value)
  # Convert value to appropriate type
  typed_value = case key
                when 'port', 'timeout', 'password'
                  value.to_i
                when 'verbose', 'force_udp', 'no_ping'
                  value.downcase == 'true'
                else
                  value
                end

  @config[key] = typed_value
  @config.save
  puts "Configuration updated: #{key} = #{typed_value}"
end

#delete_template(ip = nil) ⇒ Object



573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
# File 'lib/rbzk/cli/commands.rb', line 573

def delete_template(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  # Ensure at least one of uid or user_id is provided
  if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
    puts 'Error: You must provide either --uid or --user-id'
    return
  end

  with_connection(ip, options) do |conn|
    puts 'Deleting fingerprint template...'

    # Extract parameters from options
    uid = options[:uid] || 0
    user_id = options[:user_id] || ''
    finger_id = options[:finger_id] || 0

    result = conn.delete_user_template(uid: uid, temp_id: finger_id, user_id: user_id)

    if result
      puts '✓ Fingerprint template deleted successfully!'
    else
      puts '✗ Fingerprint template not found or could not be deleted'
    end
  end
end

#delete_user(ip = nil) ⇒ Object



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/rbzk/cli/commands.rb', line 470

def delete_user(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  # Ensure at least one of uid or user_id is provided
  if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
    puts 'Error: You must provide either --uid'
    return
  end

  with_connection(ip, options) do |conn|
    puts 'Deleting user...'

    # Use the User object with delete_user
    result = conn.delete_user(uid: options[:uid])

    if result
      puts '✓ User deleted successfully!'
    else
      puts '✗ User not found or could not be deleted.'
    end
  end
end

#disable_device(ip = nil) ⇒ Object



685
686
687
688
689
690
691
692
693
694
# File 'lib/rbzk/cli/commands.rb', line 685

def disable_device(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    puts 'Disabling device...'
    result = conn.disable_device
    puts '✓ Device disabled successfully!' if result
  end
end

#door_state(ip = nil) ⇒ Object



372
373
374
375
376
377
378
379
380
# File 'lib/rbzk/cli/commands.rb', line 372

def door_state(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    state = conn.get_lock_state
    puts "Door state: #{state ? 'Open' : 'Closed'}"
  end
end

#enable_device(ip = nil) ⇒ Object



671
672
673
674
675
676
677
678
679
680
# File 'lib/rbzk/cli/commands.rb', line 671

def enable_device(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    puts 'Enabling device...'
    result = conn.enable_device
    puts '✓ Device enabled successfully!' if result
  end
end

#get_templates(ip = nil) ⇒ Object



497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
# File 'lib/rbzk/cli/commands.rb', line 497

def get_templates(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    puts 'Getting fingerprint templates...'
    templates = conn.get_templates

    if templates && !templates.empty?
      puts "✓ Found #{templates.size} fingerprint templates:"

      # Use Terminal::Table for pretty output
      table = ::Terminal::Table.new do |t|
        t.title = 'Fingerprint Templates'
        t.headings = [ 'UID', 'Finger ID', 'Valid', 'Size' ]

        templates.each do |template|
          t << [
            template.uid,
            template.fid,
            template.valid == 1 ? 'Yes' : 'No',
            template.size
          ]
        end
      end

      puts table
    else
      puts '✓ No fingerprint templates found'
    end
  end
end

#get_user_template(ip = nil) ⇒ Object



536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'lib/rbzk/cli/commands.rb', line 536

def get_user_template(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  # Ensure at least one of uid or user_id is provided
  if options[:uid].nil? && (options[:user_id].nil? || options[:user_id].empty?)
    puts 'Error: You must provide either --uid or --user-id'
    return
  end

  with_connection(ip, options) do |conn|
    puts 'Getting user fingerprint template...'

    # Extract parameters from options
    uid = options[:uid] || 0
    finger_id = options[:finger_id] || 0

    template = conn.get_user_template(uid, finger_id)

    if template
      puts '✓ Found fingerprint template:'
      puts "  User ID: #{template.uid}"
      puts "  Finger ID: #{template.fid}"
      puts "  Valid: #{template.valid == 1 ? 'Yes' : 'No'}"
      puts "  Size: #{template.size} bytes"
    else
      puts '✗ Fingerprint template not found'
    end
  end
end

#info(ip = nil) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rbzk/cli/commands.rb', line 32

def info(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    # Get device information
    # First read sizes to get user counts and capacities
    conn.read_sizes

    device_info = {
      'Serial Number' => conn.get_serialnumber,
      'MAC Address' => conn.get_mac,
      'Device Name' => conn.get_device_name,
      'Firmware Version' => conn.get_firmware_version,
      'Platform' => conn.get_platform,
      'Face Version' => conn.get_face_version,
      'Fingerprint Version' => conn.get_fp_version,
      'Device Time' => conn.get_time,
      'Users' => conn.instance_variable_get(:@users),
      'Fingerprints' => conn.instance_variable_get(:@fingers),
      'Attendance Records' => conn.instance_variable_get(:@records),
      'User Capacity' => conn.instance_variable_get(:@users_cap),
      'Fingerprint Capacity' => conn.instance_variable_get(:@fingers_cap),
      'Record Capacity' => conn.instance_variable_get(:@rec_cap),
      'Face Capacity' => conn.instance_variable_get(:@faces_cap),
      'Faces' => conn.instance_variable_get(:@faces)
    }

    # Display information
    if defined?(::Terminal) && defined?(::Terminal::Table)
      # Pretty table output
      table = ::Terminal::Table.new do |t|
        t.title = 'Device Information'
        device_info.each do |key, value|
          t << [ key, value ]
        end
      end

      puts table
    else
      # Fallback plain text output
      puts 'Device Information:'
      device_info.each do |key, value|
        puts "#{key}: #{value}"
      end
    end
  end
end

#logs(ip = nil) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/rbzk/cli/commands.rb', line 203

def logs(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']
  with_connection(ip, options) do |conn|
    # Get attendance logs
    puts 'Getting attendance logs...'
    logs = conn.get_attendance_logs
    total_logs = logs.size
    puts "Total logs: #{total_logs}" if options[:verbose]

    # Filter logs based on options
    title = if options[:today]
              today = Date.today
              logs = filter_logs_by_datetime(logs, today, today, options[:start_time], options[:end_time])
              "Today's Attendance Logs (#{today})"
            elsif options[:yesterday]
              yesterday = Date.today - 1
              logs = filter_logs_by_datetime(logs, yesterday, yesterday, options[:start_time], options[:end_time])
              "Yesterday's Attendance Logs (#{yesterday})"
            elsif options[:week]
              today = Date.today
              start_of_week = today - today.wday
              logs = filter_logs_by_datetime(logs, start_of_week, today, options[:start_time], options[:end_time])
              "This Week's Attendance Logs (#{start_of_week} to #{today})"
            elsif options[:month]
              today = Date.today
              start_of_month = Date.new(today.year, today.month, 1)
              logs = filter_logs_by_datetime(logs, start_of_month, today, options[:start_time],
                                             options[:end_time])
              "This Month's Attendance Logs (#{start_of_month} to #{today})"
            elsif options[:start_date] && options[:end_date]
              begin
                start_date = Date.parse(options[:start_date])
                end_date = Date.parse(options[:end_date])

                # Print debug info
                puts "Filtering logs from #{start_date} to #{end_date}..." if options[:verbose]

                # Use the filter_logs_by_datetime method
                logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time],
                                               options[:end_time])

                "Attendance Logs (#{start_date} to #{end_date})"
              rescue ArgumentError
                puts 'Error: Invalid date format. Please use YYYY-MM-DD format.'
                return
              end
            elsif options[:start_date]
              begin
                start_date = Date.parse(options[:start_date])
                end_date = Date.today

                # Print debug info
                puts "Filtering logs from #{start_date} onwards..." if options[:verbose]

                # Use the filter_logs_by_datetime method
                logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time],
                                               options[:end_time])

                "Attendance Logs (#{start_date} to #{end_date})"
              rescue ArgumentError
                puts 'Error: Invalid date format. Please use YYYY-MM-DD format.'
                return
              end
            elsif options[:end_date]
              begin
                end_date = Date.parse(options[:end_date])
                # Default start date to 30 days before end date
                start_date = end_date - 30

                # Print debug info
                puts "Filtering logs from #{start_date} to #{end_date}..." if options[:verbose]

                # Use the filter_logs_by_datetime method
                logs = filter_logs_by_datetime(logs, start_date, end_date, options[:start_time],
                                               options[:end_time])

                "Attendance Logs (#{start_date} to #{end_date})"
              rescue ArgumentError
                puts 'Error: Invalid date format. Please use YYYY-MM-DD format.'
                return
              end
            else
              'All Attendance Logs'
            end

    # Display logs
    if logs && !logs.empty?
      puts "\nFound #{logs.size} attendance records:"

      # Determine how many logs to display
      limit = options[:limit] || 25
      display_logs = limit > 0 ? logs.first(limit) : logs

      if defined?(::Terminal) && defined?(::Terminal::Table)
        # Pretty table output
        table = ::Terminal::Table.new do |t|
          t.title = title || 'Attendance Logs'
          t.headings = [ 'UID', 'User ID', 'Time', 'Status', 'Punch Type' ]

          # Show logs in the table based on limit
          display_logs.each do |log|
            t << [
              log.uid,
              log.user_id,
              log.timestamp.strftime('%Y-%m-%d %H:%M:%S'),
              log.status.to_s,
              log.punch_name
            ]
          end
        end

        puts table

        # Show summary if logs were limited
        if limit > 0 && logs.size > limit
          puts "Showing #{display_logs.size} of #{logs.size} records. Use --limit option to change the number of records displayed."
        end
      else
        # Fallback plain text output
        display_logs.each do |log|
          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}"
        end

        puts "  ... and #{logs.size - display_logs.size} more records" if logs.size > display_logs.size
      end
    else
      puts "\nNo attendance records found"
    end
  end
end

#logs_all(ip = nil) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/rbzk/cli/commands.rb', line 137

def logs_all(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    # Get attendance logs
    puts 'Getting all attendance logs (this may take a while)...'
    logs = conn.get_attendance_logs
    total_logs = logs.size
    puts "Total logs: #{total_logs}" if options[:verbose]

    # Display logs
    if logs && !logs.empty?
      puts "\nFound #{logs.size} attendance records:"

      if defined?(::Terminal) && defined?(::Terminal::Table)
        # Pretty table output
        table = ::Terminal::Table.new do |t|
          t.title = 'All Attendance Logs (Showing All Records)'
          t.headings = [ 'UID', 'User ID', 'Time', 'Status', 'Punch Type' ]

          # Show all logs in the table
          logs.each do |log|
            t << [
              log.uid,
              log.user_id,
              log.timestamp.strftime('%Y-%m-%d %H:%M:%S'),
              log.status,
              log.punch_name
            ]
          end
        end

        puts table
      else
        # Fallback plain text output
        logs.each do |log|
          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}"
        end
      end
    else
      puts "\nNo attendance records found"
    end
  end
end

#logs_custom(start_date, end_date, ip = nil) ⇒ Object



185
186
187
188
189
# File 'lib/rbzk/cli/commands.rb', line 185

def logs_custom(start_date, end_date, ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']
  invoke :logs, [ ip ], { start_date: start_date, end_date: end_date }.merge(options)
end

#logs_month(ip = nil) ⇒ Object



131
132
133
# File 'lib/rbzk/cli/commands.rb', line 131

def logs_month(ip = nil)
  invoke :logs, [ ip ], { month: true }.merge(options)
end

#logs_today(ip = nil) ⇒ Object



110
111
112
# File 'lib/rbzk/cli/commands.rb', line 110

def logs_today(ip = nil)
  invoke :logs, [ ip ], { today: true }.merge(options)
end

#logs_week(ip = nil) ⇒ Object



124
125
126
# File 'lib/rbzk/cli/commands.rb', line 124

def logs_week(ip = nil)
  invoke :logs, [ ip ], { week: true }.merge(options)
end

#logs_yesterday(ip = nil) ⇒ Object



117
118
119
# File 'lib/rbzk/cli/commands.rb', line 117

def logs_yesterday(ip = nil)
  invoke :logs, [ ip ], { yesterday: true }.merge(options)
end

#poweroff(ip = nil) ⇒ Object



698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
# File 'lib/rbzk/cli/commands.rb', line 698

def poweroff(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  if yes?('Are you sure you want to power off the device? (y/N)')
    with_connection(ip, options.merge(skip_disconnect_after_yield: true)) do |conn|
      puts 'Powering off device...'
      result = conn.poweroff
      puts '✓ Device poweroff command sent successfully!' if result
      puts 'The device will power off now. You will need to manually power it back on.'
    end
  else
    puts 'Operation cancelled.'
  end
end

#refresh(ip = nil) ⇒ Object



83
84
85
86
87
88
89
90
91
92
# File 'lib/rbzk/cli/commands.rb', line 83

def refresh(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  with_connection(ip, options) do |conn|
    puts 'Refreshing device data...'
    result = conn.refresh_data
    puts '✓ Device data refreshed successfully!' if result
  end
end

#restart(ip = nil) ⇒ Object



652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'lib/rbzk/cli/commands.rb', line 652

def restart(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  if yes?('Are you sure you want to restart the device? (y/N)')
    with_connection(ip, options.merge(skip_disconnect_after_yield: true)) do |conn|
      puts 'Restarting device...'
      result = conn.restart
      puts '✓ Device restart command sent successfully!' if result
      puts 'The device will restart now. You may need to wait a few moments before reconnecting.'
    end
  else
    puts 'Operation cancelled.'
  end
end

#test_voice(ip = nil) ⇒ Object



605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
# File 'lib/rbzk/cli/commands.rb', line 605

def test_voice(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  # Get the sound index
  index = options[:index] || 0

  # Print available sound indices if verbose
  if options[:verbose]
    puts 'Available sound indices:'
    puts ' 0: Thank You'
    puts ' 1: Incorrect Password'
    puts ' 2: Access Denied'
    puts ' 3: Invalid ID'
    puts ' 4: Please try again'
    puts ' 5: Duplicate ID'
    puts ' 6: The clock is flow'
    puts ' 7: The clock is full'
    puts ' 8: Duplicate finger'
    puts ' 9: Duplicated punch'
    puts '10: Beep kuko'
    puts '11: Beep siren'
    puts '13: Beep bell'
    puts '18: Windows(R) opening sound'
    puts '20: Fingerprint not emolt'
    puts '21: Password not emolt'
    puts '22: Badges not emolt'
    puts '23: Face not emolt'
    puts '24: Beep standard'
    puts '30: Invalid user'
    puts '31: Invalid time period'
    puts '32: Invalid combination'
    puts '33: Illegal Access'
    puts '34: Disk space full'
    puts '35: Duplicate fingerprint'
    puts '51: Focus eyes on the green box'
  end

  with_connection(ip, options) do |conn|
    puts "Testing device voice with index #{index}..."
    result = conn.test_voice(index)
    puts '✓ Voice test successful!' if result
  end
end

#unlock_door(ip = nil) ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/rbzk/cli/commands.rb', line 355

def unlock_door(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  # Get the unlock time
  time = options[:time] || 3

  with_connection(ip, options) do |conn|
    puts "Unlocking door for #{time} seconds..."
    result = conn.unlock(time)
    puts '✓ Door unlocked successfully!' if result
  end
end

#users(ip = nil) ⇒ Object



96
97
98
99
100
101
102
103
104
# File 'lib/rbzk/cli/commands.rb', line 96

def users(ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']
  with_connection(ip, options) do |conn|
    puts 'Getting users...'
    users = conn.get_users
    display_users(users)
  end
end

#write_lcd(line_number, text, ip = nil) ⇒ Object



385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/rbzk/cli/commands.rb', line 385

def write_lcd(line_number, text, ip = nil)
  # Use IP from options if not provided as argument
  ip ||= options[:ip] || @config['ip']

  # Convert line_number to integer
  line_number = line_number.to_i

  with_connection(ip, options) do |conn|
    puts "Writing text to LCD line #{line_number}..."
    result = conn.write_lcd(line_number, text)
    puts '✓ Text written to LCD successfully!' if result
  end
end