class Radbeacon::Usb

Constants

ADVERTISING_RATE_VALUES
BEACON_TYPES
C_APPEARANCE
C_DEVICE_NAME
Define GATT characteristic constants

Generic Access Profile

C_FIRMWARE_STRING
C_MANUFACTURER_NAME

Device Information

C_MODEL_NUMBER
C_SERIAL_STRING
DEFAULT_MEASURED_POWER_VALUES
GATT_ACTION
GATT_ACTION_CONNECTABLE_TIME
GATT_ACTION_DFU
GATT_ACTION_DONOTHING
Define GATT action/result constants

Actions

GATT_ACTION_FACTORY_RESET
GATT_ACTION_LOCK
GATT_ACTION_UPDATE_ADV
GATT_ACTION_UPDATE_PIN
GATT_BCTYPE
GATT_BEACON_SWITCH
GATT_CONN_TIMEOUT
GATT_DEV_ID
GATT_DEV_MODEL

Configuration

GATT_DEV_NAME
GATT_ERROR
GATT_FWVERSION
GATT_INTERVAL
GATT_INVALID_PIN
GATT_MAJOR
GATT_MINOR
GATT_NEW_PIN
GATT_PIN
GATT_POWER
GATT_RESULT
GATT_SUCCES

Results

GATT_TXPOWER
GATT_UUID
TIMEOUT

Timeout length for GATT commands

TRANSMIT_POWER_VALUES

Transmit power and advertisement frequency values

VALID_UUID

Valid UUID pattern

Attributes

adv_rate[RW]
beacon_type[RW]
dev_id[RW]
dev_model[RW]
dev_name[RW]
dev_version[RW]
errors[RW]
mac_address[RW]
major[RW]
minor[RW]
power[RW]
tx_power[RW]
uuid[RW]

Public Class Methods

create_if_valid(device) click to toggle source
# File lib/radbeacon/usb.rb, line 64
def self.create_if_valid(device)
  # Check everything
  required_attrs = [GATT_DEV_MODEL, GATT_DEV_ID, GATT_FWVERSION, GATT_DEV_NAME,
    GATT_UUID, GATT_MAJOR, GATT_MINOR, GATT_POWER, GATT_TXPOWER, GATT_INTERVAL, GATT_BCTYPE]
  if required_attrs.all? { |key| device.values[key] }
    self.new(device)
  end
end
new(device) click to toggle source
# File lib/radbeacon/usb.rb, line 73
def initialize(device)
  @errors = []
  @mac_address = device.mac_address
  @dev_model = bytes_to_text(device.values[GATT_DEV_MODEL])
  @dev_id =  bytes_to_text(device.values[GATT_DEV_ID])
  @dev_version =  bytes_to_text(device.values[GATT_FWVERSION])
  @dev_name =  bytes_to_text(device.values[GATT_DEV_NAME])
  @uuid =  bytes_to_uuid(device.values[GATT_UUID])
  @major =  bytes_to_major_minor(device.values[GATT_MAJOR])
  @minor = bytes_to_major_minor(device.values[GATT_MINOR])
  @power = bytes_to_power(device.values[GATT_POWER])
  @tx_power = TRANSMIT_POWER_VALUES.key(device.values[GATT_TXPOWER])
  @adv_rate = ADVERTISING_RATE_VALUES.key(device.values[GATT_INTERVAL].delete(' '))
  @beacon_type = BEACON_TYPES.key(device.values[GATT_BCTYPE])
end

Public Instance Methods

boot_to_dfu(pin) click to toggle source
# File lib/radbeacon/usb.rb, line 130
def boot_to_dfu(pin)
  dfu_commands = ["#{GATT_ACTION} #{GATT_ACTION_DFU}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
  con(dfu_commands)
end
change_pin(new_pin, old_pin) click to toggle source
# File lib/radbeacon/usb.rb, line 119
def change_pin(new_pin, old_pin)
  update_pin_commands = ["#{GATT_NEW_PIN} #{pin_to_bytes(new_pin)}", "#{GATT_ACTION} #{GATT_ACTION_UPDATE_PIN}",
    "#{GATT_PIN} #{pin_to_bytes(old_pin)}"]
  con(update_pin_commands)
end
factory_reset(pin) click to toggle source
# File lib/radbeacon/usb.rb, line 125
def factory_reset(pin)
  reset_commands = ["#{GATT_ACTION} #{GATT_ACTION_FACTORY_RESET}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
  con(reset_commands) && defaults
end
lock(pin) click to toggle source
# File lib/radbeacon/usb.rb, line 135
def lock(pin)
  lock_commands = ["#{GATT_ACTION} #{GATT_ACTION_LOCK}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
  con(lock_commands)
end
save(pin) click to toggle source
# File lib/radbeacon/usb.rb, line 106
def save(pin)
  if self.valid?
    update_params_commands = ["#{GATT_DEV_NAME} #{text_to_bytes(@dev_name)}",
      "#{GATT_UUID} #{uuid_to_bytes(@uuid)}", "#{GATT_MAJOR} #{major_minor_to_bytes(@major)}",
      "#{GATT_MINOR} #{major_minor_to_bytes(@minor)}", "#{GATT_POWER} #{power_to_bytes(@power)}",
      "#{GATT_TXPOWER} #{TRANSMIT_POWER_VALUES[@tx_power]}", "#{GATT_INTERVAL} #{ADVERTISING_RATE_VALUES[@adv_rate]}",
      "#{GATT_BCTYPE} #{BEACON_TYPES[@beacon_type]}", "#{GATT_ACTION} #{GATT_ACTION_UPDATE_ADV}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
    con(update_params_commands)
  else
    false
  end
end
valid?() click to toggle source
# File lib/radbeacon/usb.rb, line 89
def valid?
  @errors = []
  @errors << "Invalid device name" unless @dev_name.length <= 20
  @errors << "Invalid UUID" unless @uuid.match(VALID_UUID)
  @errors << "Invalid major value" unless @major.to_i.between?(0, 65535)
  @errors << "Invalid minor value" unless @minor.to_i.between?(0, 65535)
  @errors << "Invalid measured power value" unless @power.to_i.between?(-127, -1)
  @errors << "Invalid transmit power" unless TRANSMIT_POWER_VALUES.has_key?(@tx_power)
  @errors << "Invalid advertising rate" unless ADVERTISING_RATE_VALUES.has_key?(@adv_rate)
  @errors << "Invalid beacon type" unless BEACON_TYPES.has_key?(@beacon_type)
  if @errors.empty?
    true
  else
    false
  end
end

Private Instance Methods

con(commands) click to toggle source
# File lib/radbeacon/usb.rb, line 157
def con(commands)
  @errors = []
  result = false
  cmd_line = "gatttool -b #{@mac_address} --interactive"
  PTY.spawn(cmd_line) do |output, input, pid|
    output.expect(/\[LE\]>/)
    input.puts "connect"
    if output.expect(/Connection successful/, TIMEOUT)
      commands.each do |cmd|
        cmd = "char-write-req #{cmd}"
        input.puts cmd
        if output.expect(/Characteristic value was written successfully/, TIMEOUT)
          result = true
        else
          @errors << "Action failed: #{cmd}"
          result = false
          break
        end
      end
      if result
        input.puts "char-read-hnd #{GATT_RESULT}"
        if output.expect(/Characteristic value\/descriptor: 00 00 00 00/, TIMEOUT)
          result = true
        else
          @errors << "Invalid PIN"
          result = false
        end
      end
    else
      @errors << "Connection failed"
    end
    input.puts "quit"
    _, status = Process.waitpid2(pid)
    if !status.success?
      result = false
      @errors << "Process failed to exit properly"
    end
  end
  result
end
defaults() click to toggle source
# File lib/radbeacon/usb.rb, line 146
def defaults
  @dev_name = "RadBeacon USB"
  @uuid = "2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6"
  @major = 1
  @minor = 1
  @power = -66
  @tx_power = 3
  @adv_rate = 10
  @beacon_type = "dual"
end