Class: Radbeacon::BluetoothLeDevice

Inherits:
Object
  • Object
show all
Defined in:
lib/radbeacon/bluetooth_le_device.rb

Direct Known Subclasses

Usb

Constant Summary collapse

TIMEOUT =
0.5

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mac_address, name) ⇒ BluetoothLeDevice

Returns a new instance of BluetoothLeDevice.



11
12
13
14
15
16
17
18
# File 'lib/radbeacon/bluetooth_le_device.rb', line 11

def initialize(mac_address, name)
  @errors = []
  @mac_address = mac_address
  @name = name
  @is_connectable = false
  @characteristics = Array.new
  @values = Hash.new
end

Instance Attribute Details

#characteristicsObject

Returns the value of attribute characteristics.



7
8
9
# File 'lib/radbeacon/bluetooth_le_device.rb', line 7

def characteristics
  @characteristics
end

#errorsObject

Returns the value of attribute errors.



7
8
9
# File 'lib/radbeacon/bluetooth_le_device.rb', line 7

def errors
  @errors
end

#is_connectableObject

Returns the value of attribute is_connectable.



7
8
9
# File 'lib/radbeacon/bluetooth_le_device.rb', line 7

def is_connectable
  @is_connectable
end

#mac_addressObject

Returns the value of attribute mac_address.



7
8
9
# File 'lib/radbeacon/bluetooth_le_device.rb', line 7

def mac_address
  @mac_address
end

#nameObject

Returns the value of attribute name.



7
8
9
# File 'lib/radbeacon/bluetooth_le_device.rb', line 7

def name
  @name
end

#valuesObject

Returns the value of attribute values.



7
8
9
# File 'lib/radbeacon/bluetooth_le_device.rb', line 7

def values
  @values
end

Instance Method Details

#can_connect?Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/radbeacon/bluetooth_le_device.rb', line 37

def can_connect?
  @errors = []
  result = false
  cmd = "gatttool -b #{@mac_address} --interactive"
  PTY.spawn(cmd) do |output, input, pid|
    output.expect(/\[LE\]>/)
    input.puts "connect"
    if output.expect(/Connection successful/, TIMEOUT)
      @is_connectable = true
      result = true
    else
      @errors << "Connection failed"
    end
    input.puts "quit"
  end
  result
end

#char_valuesObject



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/radbeacon/bluetooth_le_device.rb', line 102

def char_values
  @errors = []
  result = false
  cmd = "gatttool -b #{@mac_address} --interactive"
  if @characteristics != []
    PTY.spawn(cmd) do |output, input, pid|
      output.expect(/\[LE\]>/)
      input.puts "connect"
      if output.expect(/Connection successful/, TIMEOUT)
        @characteristics.each do |char|
          input.puts "char-read-hnd #{char['value_handle']}"
          if output.expect(/Characteristic value\/descriptor: /, TIMEOUT)
            if value = output.expect(/^[0-9a-f\s]+\n/, TIMEOUT)
              @values[char['value_handle']] = value.first.strip
            end
          end
        end
        result = true
      else
        @errors << "Fetch characteristic values failed"
      end
      input.puts "quit"
    end
  else
    @errors << "No characteristics present"
  end
  result
end

#characteristics_commandObject



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
80
# File 'lib/radbeacon/bluetooth_le_device.rb', line 55

def characteristics_command
  success = true
  output = nil
  rout, wout = IO.pipe
  rerr, werr = IO.pipe
  characteristics_command_str = "gatttool -b #{@mac_address} --characteristics"
  pid = Process.spawn(characteristics_command_str, :out => wout, :err => werr)
  begin
    Timeout.timeout(5) do
      Process.wait(pid)
    end
  rescue Timeout::Error
    Process.kill('TERM', pid)
    success = false
  end
  wout.close
  werr.close
  stdout = rout.readlines.join("")
  stderr = rerr.readlines.join("")
  rout.close
  rerr.close
  if success
    output = [stdout, stderr].join("")
  end
  output
end

#discover_characteristicsObject



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/radbeacon/bluetooth_le_device.rb', line 82

def discover_characteristics
  @errors = []
  result = false
  output = characteristics_command
  if output && output.strip != "Discover all characteristics failed: Internal application error: I/O"
    @characteristics = []
    output.each_line do |line|
      result = line.scan(/^handle = (0x[a-f0-9]{4}), char properties = (0x[a-f0-9]{2}), char value handle = (0x[a-f0-9]{4}), uuid = ([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/)
      if !result.empty?
        characteristic = {"handle" => result[0][0], "properties" => result[0][1], "value_handle" => result[0][2], "uuid" => result[0][3]}
        @characteristics << characteristic
      end
    end
    result = true
  else
    @errors << "Discover characteristics failed"
  end
  result
end

#displayObject



20
21
22
# File 'lib/radbeacon/bluetooth_le_device.rb', line 20

def display
  puts "MAC Address: " + @mac_address + " Name: " + @name + " Can connect: " + @is_connectable.to_s
end

#fetch_characteristicsObject



24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/radbeacon/bluetooth_le_device.rb', line 24

def fetch_characteristics
  result = false
  if self.can_connect?
    @is_connectable = true
    if self.discover_characteristics
      if self.char_values
        result = true
      end
    end
  end
  result
end