Class: HIDAPI::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/hidapi/engine.rb

Overview

A wrapper around the USB context that makes it easy to locate HID devices.

Constant Summary collapse

HID_CLASS =

Contains the class code for HID devices from LIBUSB.

LIBUSB::CLASS_HID

Instance Method Summary collapse

Constructor Details

#initializeEngine

Creates a new engine.


14
15
16
# File 'lib/hidapi/engine.rb', line 14

def initialize
  @context = LIBUSB::Context.new
end

Instance Method Details

#enumerate(vendor_id = 0, product_id = 0, options = {}) ⇒ Object

Enumerates the HID devices matching the vendor and product IDs.

Both vendor_id and product_id are optional. They will act as a wild card if set to 0 (the default).


23
24
25
26
27
28
29
30
31
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
# File 'lib/hidapi/engine.rb', line 23

def enumerate(vendor_id = 0, product_id = 0, options = {})
  raise HIDAPI::HidApiError, 'not initialized' unless @context

  if vendor_id.is_a?(Hash) || (vendor_id.is_a?(String) && options.empty?)
    options = vendor_id
    vendor_id = 0
    product_id = 0
  end

  if product_id.is_a?(Hash) || (product_id.is_a?(String) && options.empty?)
    options = product_id
    product_id = 0
  end

  if options.is_a?(String) || options.is_a?(Symbol)
    options = { as: options }
  end

  unless options.nil? || options.is_a?(Hash)
    raise ArgumentError, 'options hash is invalid'
  end

  klass = (options || {}).delete(:as) || 'HIDAPI::Device'
  klass = Object.const_get(klass) unless klass == :no_mapping

  filters = { bClass: HID_CLASS }

  unless vendor_id.nil? || vendor_id.to_i == 0
    filters[:idVendor] = vendor_id.to_i
  end
  unless product_id.nil? || product_id.to_i == 0
    filters[:idProduct] = product_id.to_i
  end

  list = @context.devices(filters)

  if klass != :no_mapping
    list.to_a.map{ |dev| klass.new(dev) }
  else
    list.to_a
  end
end

#get_device(vendor_id, product_id, serial_number = nil, options = {}) ⇒ Object

Gets the first device with the specified vendor_id, product_id, and optionally serial_number.

Raises:

  • (ArgumentError)

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/hidapi/engine.rb', line 68

def get_device(vendor_id, product_id, serial_number = nil, options = {})
  raise ArgumentError, 'vendor_id must be provided' if vendor_id.to_i == 0
  raise ArgumentError, 'product_id must be provided' if product_id.to_i == 0

  if serial_number.is_a?(Hash)
    options = serial_number
    serial_number = nil
  end

  klass = (options || {}).delete(:as) || 'HIDAPI::Device'
  klass = Object.const_get(klass) unless klass == :no_mapping

  list = enumerate(vendor_id, product_id, as: :no_mapping)
  return nil unless list && list.count > 0
  if serial_number.to_s == ''
    if klass != :no_mapping
      return klass.new(list.first)
    else
      return list.first
    end
  end
  list.each do |dev|
    if dev.serial_number == serial_number
      if klass != :no_mapping
        return klass.new(dev)
      else
        return dev
      end
    end
  end
  nil
end

#get_device_by_path(path, options = {}) ⇒ Object

Gets the device with the specified path.


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/hidapi/engine.rb', line 110

def get_device_by_path(path, options = {})

  # Our linux setup routine creates convenient /dev/hidapi/* links.
  # If the user wants to open one of those, we can simple parse the link to generate
  # the path that the library expects.
  if File.exist?(path)

    hidapi_regex = /^\/dev\/hidapi\//
    usb_bus_regex = /^\/dev\/bus\/usb\/(?<BUS>\d+)\/(?<ADDR>\d+)$/

    if hidapi_regex.match(path)
      path = File.expand_path(File.readlink(path), File.dirname(path))
    elsif !usb_bus_regex.match(path)
      raise HIDAPI::DevicePathInvalid, 'Cannot open file paths other than /dev/hidapi/XXX or /dev/bus/usb/XXX/XXX paths.'
    end

    # path should now be in the form /dev/bus/usb/AAA/BBB
    match = usb_bus_regex.match(path)

    raise HIDAPI::DevicePathInvalid, "Link target does not appear valid (#{path})." unless match

    interface = (options.delete(:interface) || 0).to_s(16)

    path = HIDAPI::Device.validate_path("#{match['BUS']}:#{match['ADDR']}:#{interface}")
  end

  valid_path = HIDAPI::Device.validate_path(path)
  raise HIDAPI::DevicePathInvalid, "Path should be in BUS:ADDRESS:INTERFACE format with each value being in hexadecimal (ie - 0001:01A:00), not #{path}." unless valid_path
  path = valid_path

  klass = (options || {}).delete(:as) || 'HIDAPI::Device'
  klass = Object.const_get(klass) unless klass == :no_mapping

  enumerate(as: :no_mapping).each do |usb_dev|
    usb_dev.settings.each do |intf_desc|
      if intf_desc.bInterfaceClass == HID_CLASS
        dev_path = HIDAPI::Device.make_path(usb_dev, intf_desc.bInterfaceNumber)
        if dev_path == path
          if klass != :no_mapping
            return klass.new(usb_dev, intf_desc.bInterfaceNumber)
          else
            return usb_dev
          end
        end
      end
    end
  end
end

#inspectObject

:nodoc:


187
188
189
# File 'lib/hidapi/engine.rb', line 187

def inspect   # :nodoc:
  "#<#{self.class.name}:#{self.object_id.to_hex(16)} context=0x#{@context.object_id.to_hex(16)}>"
end

#open(vendor_id, product_id, serial_number = nil, options = {}) ⇒ Object

Opens the first device with the specified vendor_id, product_id, and optionally serial_number.


103
104
105
106
# File 'lib/hidapi/engine.rb', line 103

def open(vendor_id, product_id, serial_number = nil, options = {})
  dev = get_device(vendor_id, product_id, serial_number, options)
  dev.open if dev
end

#open_path(path, options = {}) ⇒ Object

Opens the device with the specified path.


161
162
163
164
# File 'lib/hidapi/engine.rb', line 161

def open_path(path, options = {})
  dev = get_device_by_path(path, options)
  dev.open if dev
end

#to_sObject

:nodoc:


191
192
193
# File 'lib/hidapi/engine.rb', line 191

def to_s      # :nodoc:
  inspect
end

#usb_code_for_current_localeObject

Gets the USB code for the current locale.


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/hidapi/engine.rb', line 168

def usb_code_for_current_locale
  @usb_code_for_current_locale ||=
      begin
        locale = I18n.locale
        if locale
          locale = locale.to_s.partition('.')[0]  # remove encoding
          result = HIDAPI::Language.get_by_code(locale)
          unless result
            locale = locale.partition('_')[0]     # chop off extra specification
            result = HIDAPI::Language.get_by_code(locale)
          end
          result ? result[:usb_code] : 0
        else
          0
        end
      end
end