radbeacon 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 25442163473a2a7ce504430cce4a4ecb803abbd2
4
+ data.tar.gz: 423b8b43f661d3927f48cab858bea9c6cf86272c
5
+ SHA512:
6
+ metadata.gz: 9b4504f24ec9ae91fff3e6de568d9603ce377e3b25c26e5bd92f4aa45789c182d6149be3a69b84688e2515db76285b91183b27e15612a77d970ed66c66a5fbfb
7
+ data.tar.gz: e3be66fd90ef611358c15e6fd879d4b8caaa43e50887173e7bd18b5afb4585166ad161b2575b2123136ff5b9b72c900720b5998ce682b558146059d56f4e7282
@@ -0,0 +1,5 @@
1
+ cprun.sh
2
+ yell.rb
3
+ yeller.rb
4
+ .DS_Store
5
+ pkg/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in radbeacon.gemspec
4
+ gemspec
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ radbeacon (0.1.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rake (10.1.0)
11
+ rspec (3.2.0)
12
+ rspec-core (~> 3.2.0)
13
+ rspec-expectations (~> 3.2.0)
14
+ rspec-mocks (~> 3.2.0)
15
+ rspec-core (3.2.2)
16
+ rspec-support (~> 3.2.0)
17
+ rspec-expectations (3.2.0)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.2.0)
20
+ rspec-mocks (3.2.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.2.0)
23
+ rspec-support (3.2.2)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 1.9)
30
+ radbeacon!
31
+ rake (~> 10.0)
32
+ rspec (~> 3.2)
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,97 @@
1
+ <img src="http://i.imgur.com/gI6paLj.jpg" style="float:left" height="200">
2
+
3
+ # What's all this then?
4
+
5
+ A ruby gem that provides RadBeacon scanning and configuring capabilities on a linux machine. Currently the only supported beacon type is RadBeacon USB.
6
+
7
+ # Scanning
8
+
9
+ The `Radbeacon::Scanner` class has a `scan` method that returns an array of configurable RadBeacons (in the form of `Radbeacon::Usb` objects). The duration of a scan (default = 5 seconds) is an attribute that can be set during initialization.
10
+
11
+ ```
12
+ scanner = Radbeacon::Scanner.new(10)
13
+ radbeacons = scanner.scan
14
+ ```
15
+
16
+ There is also a `fetch` method that returns a `Radbeacon::Usb` object for a given MAC address.
17
+
18
+ ```
19
+ radbeacon = scanner.fetch("11:22:33:44:55:66")
20
+ ```
21
+
22
+ #### Scan Options
23
+
24
+ An (optional) `options` hash is available as an attribute on the `Scanner` class. Below are the following options that can be used:
25
+
26
+ - `:filter_mac` - Specify an array of MAC addresses that the scanner will filter for when doing a scan
27
+
28
+ ```
29
+ options[:filter_mac] = ["11:22:33:44:55:66", "55:66:77:88:99:00"]
30
+ ```
31
+
32
+ - `:enable_hcitool_duration` - Use the custom hcitool-duration binary that adds a duration option to the `hcitool lescan` command. To use this you must copy the new hcitool binary over the standard one (located at `/usr/bin/hcitool` on the ZBOX for example).
33
+
34
+ ```
35
+ options[:enable_hcitool_duration] = true
36
+ ```
37
+
38
+ #Configuring
39
+
40
+ All identifiers and other parameters are attributes on the `Radbeacon::Usb` class:
41
+
42
+ - `dev_name`
43
+ - `uuid`
44
+ - `major`
45
+ - `minor`
46
+ - `power`
47
+ - `tx_power`
48
+ - `adv_rate`
49
+ - `beacon_type`
50
+
51
+ To make a config change, simply assign one of these attributes to the desired value and call the `save()` method with the beacon's PIN (as a string).
52
+
53
+ ```
54
+ radbeacon.save(pin)
55
+ ```
56
+
57
+ For example:
58
+
59
+ ```
60
+ radbeacon.dev_name = "Test Beacon"
61
+ radbeacon.uuid = "2F234454-CF6D-4A0F-ADF2-F4911BA9ABCD"
62
+ radbeacon.major = 1
63
+ radbeacon.minor = 1
64
+ radbeacon.power = -66
65
+ radbeacon.tx_power = 3
66
+ radbeacon.adv_rate = 10
67
+ radbeacon.beacon_type = "dual"
68
+ radbeacon.save('0000')
69
+ ```
70
+
71
+ # Other Actions
72
+
73
+ All other RadBeacon actions are available as well
74
+
75
+ ##### Change PIN
76
+ ```
77
+ radbeacon.change_pin(new_pin, old_pin)
78
+ ```
79
+
80
+ ##### Factory Reset
81
+ ```
82
+ radbeacon.factory_reset(pin)
83
+ ```
84
+
85
+ ##### Boot to DFU
86
+ ```
87
+ radbeacon.boot_to_dfu(pin)
88
+ ```
89
+
90
+ ##### Lock
91
+ ```
92
+ radbeacon.lock(pin)
93
+ ```
94
+
95
+ # Dependencies
96
+
97
+ BlueZ (Linux Bluetooth stack) is required to scan for and communicate with RadBeacons via Bluetooth. Specifically, the `hcitool` and `gatttool` commands.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,9 @@
1
+ require "radbeacon/version"
2
+ require "radbeacon/le_scanner"
3
+ require "radbeacon/bluetooth_le_device"
4
+ require "radbeacon/utils"
5
+ require "radbeacon/scanner"
6
+ require "radbeacon/usb"
7
+ module Radbeacon
8
+
9
+ end
@@ -0,0 +1,135 @@
1
+ require 'pty'
2
+ require 'expect'
3
+
4
+ module Radbeacon
5
+ class BluetoothLeDevice
6
+ attr_reader :mac_address, :name, :is_connectable, :characteristics, :values, :errors
7
+
8
+ TIMEOUT = 0.5
9
+
10
+ def initialize(mac_address, name)
11
+ @errors = []
12
+ @mac_address = mac_address
13
+ @name = name
14
+ @is_connectable = false
15
+ @characteristics = Array.new
16
+ @values = Hash.new
17
+ end
18
+
19
+ def display
20
+ puts "MAC Address: " + @mac_address + " Name: " + @name + " Can connect: " + @is_connectable.to_s
21
+ end
22
+
23
+ def fetch_characteristics
24
+ result = false
25
+ if self.can_connect?
26
+ @is_connectable = true
27
+ if self.discover_characteristics
28
+ if self.char_values
29
+ result = true
30
+ end
31
+ end
32
+ end
33
+ result
34
+ end
35
+
36
+ def can_connect?
37
+ @errors = []
38
+ result = false
39
+ cmd = "gatttool -b #{@mac_address} --interactive"
40
+ PTY.spawn(cmd) do |output, input, pid|
41
+ output.expect(/\[LE\]>/)
42
+ input.puts "connect"
43
+ if output.expect(/Connection successful/, TIMEOUT)
44
+ @is_connectable = true
45
+ result = true
46
+ else
47
+ @errors << "Connection failed"
48
+ end
49
+ input.puts "quit"
50
+ end
51
+ result
52
+ end
53
+
54
+ def characteristics_command
55
+ success = true
56
+ output = nil
57
+ rout, wout = IO.pipe
58
+ rerr, werr = IO.pipe
59
+ characteristics_command_str = "gatttool -b #{@mac_address} --characteristics"
60
+ pid = Process.spawn(characteristics_command_str, :out => wout, :err => werr)
61
+ begin
62
+ Timeout.timeout(5) do
63
+ Process.wait(pid)
64
+ end
65
+ rescue Timeout::Error
66
+ Process.kill('TERM', pid)
67
+ success = false
68
+ end
69
+ wout.close
70
+ werr.close
71
+ stdout = rout.readlines.join("")
72
+ stderr = rerr.readlines.join("")
73
+ rout.close
74
+ rerr.close
75
+ if success
76
+ output = [stdout, stderr].join("")
77
+ end
78
+ output
79
+ end
80
+
81
+ def discover_characteristics
82
+ @errors = []
83
+ result = false
84
+ output = characteristics_command
85
+ if output && output.strip != "Discover all characteristics failed: Internal application error: I/O"
86
+ @characteristics = []
87
+ output.each_line do |line|
88
+ 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})$/)
89
+ if !result.empty?
90
+ characteristic = {"handle" => result[0][0], "properties" => result[0][1], "value_handle" => result[0][2], "uuid" => result[0][3]}
91
+ @characteristics << characteristic
92
+ end
93
+ end
94
+ result = true
95
+ else
96
+ @errors << "Discover characteristics failed"
97
+ end
98
+ result
99
+ end
100
+
101
+ def char_values
102
+ @errors = []
103
+ result = false
104
+ cmd = "gatttool -b #{@mac_address} --interactive"
105
+ if @characteristics != []
106
+ PTY.spawn(cmd) do |output, input, pid|
107
+ output.expect(/\[LE\]>/)
108
+ input.puts "connect"
109
+ if output.expect(/Connection successful/, TIMEOUT)
110
+ @characteristics.each do |char|
111
+ input.puts "char-read-hnd #{char['value_handle']}"
112
+ if output.expect(/Characteristic value\/descriptor: /, TIMEOUT)
113
+ if value = output.expect(/^[0-9a-f\s]+\n/, TIMEOUT)
114
+ @values[char['value_handle']] = value.first.strip
115
+ end
116
+ end
117
+ end
118
+ result = true
119
+ else
120
+ @errors << "Fetch characteristic values failed"
121
+ end
122
+ input.puts "quit"
123
+ end
124
+ else
125
+ @errors << "No characteristics present"
126
+ end
127
+ result
128
+ end
129
+
130
+ private
131
+
132
+ attr_writer :mac_address, :name, :is_connectable, :characteristics, :values, :errors
133
+
134
+ end
135
+ end
@@ -0,0 +1,65 @@
1
+ module Radbeacon
2
+
3
+ class LeScanner
4
+ attr_accessor :duration, :options
5
+
6
+ def initialize(duration = 5)
7
+ @duration = duration
8
+ @options = {}
9
+ end
10
+
11
+ def scan_command
12
+ rout, wout = IO.pipe
13
+ scan_command_str = "sudo hcitool lescan"
14
+ pid = Process.spawn(scan_command_str, :out => wout)
15
+ begin
16
+ Timeout.timeout(@duration) do
17
+ Process.wait(pid)
18
+ end
19
+ rescue Timeout::Error
20
+ Process.kill('TERM', pid)
21
+ end
22
+ wout.close
23
+ scan_output = rout.readlines.join("")
24
+ rout.close
25
+ scan_output
26
+ end
27
+
28
+ def scan_command_duration
29
+ `sudo hcitool lescan --duration #{@duration}`
30
+ end
31
+
32
+ def passive_scan
33
+ devices = Array.new
34
+ if @options[:enable_hcitool_duration] == true
35
+ scan_output = self.scan_command_duration
36
+ else
37
+ scan_output = self.scan_command
38
+ end
39
+ scan_output.each_line do |line|
40
+ result = line.scan(/^([A-F0-9:]{15}[A-F0-9]{2}) (.*)$/)
41
+ if !result.empty?
42
+ mac_address = result[0][0]
43
+ name = result[0][1]
44
+ if !devices.find {|s| s.mac_address == mac_address}
45
+ filter_mac = @options[:filter_mac]
46
+ if !filter_mac or (filter_mac.include?(mac_address) if filter_mac.is_a?(Array))
47
+ device = BluetoothLeDevice.new(mac_address, name)
48
+ devices << device
49
+ end
50
+ end
51
+ end
52
+ end
53
+ devices
54
+ end
55
+
56
+ def scan
57
+ devices = self.passive_scan
58
+ devices.each do |dev|
59
+ dev.fetch_characteristics
60
+ end
61
+ devices
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ module Radbeacon
2
+ class Scanner < LeScanner
3
+
4
+ C_DEVICE_NAME = "0x0003"
5
+ RADBEACON_USB = "52 61 64 42 65 61 63 6f 6e 20 55 53 42"
6
+
7
+ def scan
8
+ devices = super
9
+ radbeacons = devices.map { |dev| radbeacon_check(dev) }
10
+ radbeacons.compact
11
+ end
12
+
13
+ def fetch(mac_address)
14
+ dev = BluetoothLeDevice.new(mac_address, nil)
15
+ radbeacon_check(dev) if dev.fetch_characteristics
16
+ end
17
+
18
+ def radbeacon_check(device)
19
+ radbeacon = nil
20
+ case device.values[C_DEVICE_NAME]
21
+ when RADBEACON_USB
22
+ radbeacon = Usb.create_if_valid(device)
23
+ end
24
+ radbeacon
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,196 @@
1
+ module Radbeacon
2
+ class Usb < BluetoothLeDevice
3
+ include Utils
4
+
5
+ ## Define GATT characteristic constants
6
+ # Generic Access Profile
7
+ C_DEVICE_NAME = "0x0003"
8
+ C_APPEARANCE = "0x0006"
9
+ # Device Information
10
+ C_MANUFACTURER_NAME = "0x000a"
11
+ C_MODEL_NUMBER = "0x000d"
12
+ C_SERIAL_STRING = "0x0010"
13
+ C_FIRMWARE_STRING = "0x0013"
14
+ # Configuration
15
+ GATT_DEV_MODEL = "0x0017"
16
+ GATT_DEV_ID = "0x001a"
17
+ GATT_DEV_NAME = "0x001d"
18
+ GATT_UUID = "0x0020"
19
+ GATT_MAJOR = "0x0023"
20
+ GATT_MINOR = "0x0026"
21
+ GATT_POWER = "0x0029"
22
+ GATT_TXPOWER = "0x002c"
23
+ GATT_INTERVAL = "0x002f"
24
+ GATT_RESULT = "0x0032"
25
+ GATT_NEW_PIN = "0x0035"
26
+ GATT_ACTION = "0x0038"
27
+ GATT_PIN = "0x003b"
28
+ GATT_BCTYPE = "0x003e"
29
+ GATT_FWVERSION = "0x0041"
30
+ GATT_CONN_TIMEOUT = "0x0044"
31
+ GATT_BEACON_SWITCH = "0x0047"
32
+
33
+ ## Define GATT action/result constants
34
+ # Actions
35
+ GATT_ACTION_DONOTHING = "00000000"
36
+ GATT_ACTION_UPDATE_ADV = "00000001"
37
+ GATT_ACTION_UPDATE_PIN = "00000002"
38
+ GATT_ACTION_FACTORY_RESET = "00000003"
39
+ GATT_ACTION_DFU = "00000004"
40
+ GATT_ACTION_LOCK = "00000005"
41
+ GATT_ACTION_CONNECTABLE_TIME = "00000006"
42
+ # Results
43
+ GATT_SUCCES = "00000000"
44
+ GATT_INVALID_PIN = "00000001"
45
+ GATT_ERROR = "00000002" #not used
46
+
47
+ ## Transmit power and advertisement frequency values
48
+ TRANSMIT_POWER_VALUES = {-23 => "00", -21 => "01", -18 => "03", -14 => "05",
49
+ -11 => "07", -7 => "09", -4 => "0b", 0 => "0d", 3 => "0f"}
50
+ DEFAULT_MEASURED_POWER_VALUES = [-94, -92, -90, -86, -84, -79, -74, -72, -66]
51
+ ADVERTISING_RATE_VALUES = {0 => "0000", 1 => "2006", 2 => "0003", 3 => "f501", 4 => "7001",
52
+ 5 => "2001", 6 => "ea00", 7 => "c400", 8 => "a800", 9 => "9100", 10 => "8000"}
53
+ BEACON_TYPES = {"ibeacon" => "01", "altbeacon" => "02", "dual" => "03"}
54
+
55
+ ## Timeout length for GATT commands
56
+ TIMEOUT = 0.5
57
+
58
+ ## Valid UUID pattern
59
+ VALID_UUID = /^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$/
60
+
61
+ attr_reader :mac_address, :errors, :dev_model, :dev_id, :dev_version
62
+ attr_accessor :dev_name, :uuid, :major, :minor, :power, :tx_power, :adv_rate, :beacon_type
63
+
64
+ def self.create_if_valid(device)
65
+ # Check everything
66
+ required_attrs = [GATT_DEV_MODEL, GATT_DEV_ID, GATT_FWVERSION, GATT_DEV_NAME,
67
+ GATT_UUID, GATT_MAJOR, GATT_MINOR, GATT_POWER, GATT_TXPOWER, GATT_INTERVAL, GATT_BCTYPE]
68
+ if required_attrs.all? { |key| device.values[key] }
69
+ self.new(device)
70
+ end
71
+ end
72
+
73
+ def initialize(device)
74
+ @errors = []
75
+ @mac_address = device.mac_address
76
+ @dev_model = bytes_to_text(device.values[GATT_DEV_MODEL])
77
+ @dev_id = bytes_to_text(device.values[GATT_DEV_ID])
78
+ @dev_version = bytes_to_text(device.values[GATT_FWVERSION])
79
+ @dev_name = bytes_to_text(device.values[GATT_DEV_NAME])
80
+ @uuid = bytes_to_uuid(device.values[GATT_UUID])
81
+ @major = bytes_to_major_minor(device.values[GATT_MAJOR])
82
+ @minor = bytes_to_major_minor(device.values[GATT_MINOR])
83
+ @power = bytes_to_power(device.values[GATT_POWER])
84
+ @tx_power = TRANSMIT_POWER_VALUES.key(device.values[GATT_TXPOWER])
85
+ @adv_rate = ADVERTISING_RATE_VALUES.key(device.values[GATT_INTERVAL].delete(' '))
86
+ @beacon_type = BEACON_TYPES.key(device.values[GATT_BCTYPE])
87
+ end
88
+
89
+ def valid?
90
+ @errors = []
91
+ @errors << "Invalid device name" unless @dev_name.length <= 20
92
+ @errors << "Invalid UUID" unless @uuid.match(VALID_UUID)
93
+ @errors << "Invalid major value" unless @major.to_i.between?(0, 65535)
94
+ @errors << "Invalid minor value" unless @minor.to_i.between?(0, 65535)
95
+ @errors << "Invalid measured power value" unless @power.to_i.between?(-127, -1)
96
+ @errors << "Invalid transmit power" unless TRANSMIT_POWER_VALUES.has_key?(@tx_power)
97
+ @errors << "Invalid advertising rate" unless ADVERTISING_RATE_VALUES.has_key?(@adv_rate)
98
+ @errors << "Invalid beacon type" unless BEACON_TYPES.has_key?(@beacon_type)
99
+ if @errors.empty?
100
+ true
101
+ else
102
+ false
103
+ end
104
+ end
105
+
106
+ def save(pin)
107
+ if self.valid?
108
+ update_params_commands = ["#{GATT_DEV_NAME} #{text_to_bytes(@dev_name)}",
109
+ "#{GATT_UUID} #{uuid_to_bytes(@uuid)}", "#{GATT_MAJOR} #{major_minor_to_bytes(@major)}",
110
+ "#{GATT_MINOR} #{major_minor_to_bytes(@minor)}", "#{GATT_POWER} #{power_to_bytes(@power)}",
111
+ "#{GATT_TXPOWER} #{TRANSMIT_POWER_VALUES[@tx_power]}", "#{GATT_INTERVAL} #{ADVERTISING_RATE_VALUES[@adv_rate]}",
112
+ "#{GATT_BCTYPE} #{BEACON_TYPES[@beacon_type]}", "#{GATT_ACTION} #{GATT_ACTION_UPDATE_ADV}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
113
+ con(update_params_commands)
114
+ else
115
+ false
116
+ end
117
+ end
118
+
119
+ def change_pin(new_pin, old_pin)
120
+ update_pin_commands = ["#{GATT_NEW_PIN} #{pin_to_bytes(new_pin)}", "#{GATT_ACTION} #{GATT_ACTION_UPDATE_PIN}",
121
+ "#{GATT_PIN} #{pin_to_bytes(old_pin)}"]
122
+ con(update_pin_commands)
123
+ end
124
+
125
+ def factory_reset(pin)
126
+ reset_commands = ["#{GATT_ACTION} #{GATT_ACTION_FACTORY_RESET}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
127
+ con(reset_commands) && defaults
128
+ end
129
+
130
+ def boot_to_dfu(pin)
131
+ dfu_commands = ["#{GATT_ACTION} #{GATT_ACTION_DFU}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
132
+ con(dfu_commands)
133
+ end
134
+
135
+ def lock(pin)
136
+ lock_commands = ["#{GATT_ACTION} #{GATT_ACTION_LOCK}", "#{GATT_PIN} #{pin_to_bytes(pin)}"]
137
+ con(lock_commands)
138
+ end
139
+
140
+ private
141
+ attr_writer :mac_address, :errors, :dev_model, :dev_id, :dev_version
142
+
143
+ def defaults
144
+ @dev_name = "RadBeacon USB"
145
+ @uuid = "2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6"
146
+ @major = 1
147
+ @minor = 1
148
+ @power = -66
149
+ @tx_power = 3
150
+ @adv_rate = 10
151
+ @beacon_type = "dual"
152
+ end
153
+
154
+ def con(commands)
155
+ @errors = []
156
+ result = false
157
+ cmd = "gatttool -b #{@mac_address} --interactive"
158
+ PTY.spawn(cmd) do |output, input, pid|
159
+ output.expect(/\[LE\]>/)
160
+ input.puts "connect"
161
+ if output.expect(/Connection successful/, TIMEOUT)
162
+ commands.each do |cmd|
163
+ cmd = "char-write-req #{cmd}"
164
+ input.puts cmd
165
+ if output.expect(/Characteristic value was written successfully/, TIMEOUT)
166
+ result = true
167
+ else
168
+ @errors << "Action failed: #{cmd}"
169
+ result = false
170
+ break
171
+ end
172
+ end
173
+ if result
174
+ input.puts "char-read-hnd #{GATT_RESULT}"
175
+ if output.expect(/Characteristic value\/descriptor: 00 00 00 00/, TIMEOUT)
176
+ result = true
177
+ else
178
+ @errors << "Invalid PIN"
179
+ result = false
180
+ end
181
+ end
182
+ else
183
+ @errors << "Connection failed"
184
+ end
185
+ input.puts "quit"
186
+ _, status = Process.waitpid2(pid)
187
+ if !status.success?
188
+ result = false
189
+ @errors << "Process failed to exit properly"
190
+ end
191
+ end
192
+ result
193
+ end
194
+
195
+ end
196
+ end
@@ -0,0 +1,41 @@
1
+ module Radbeacon
2
+ module Utils
3
+
4
+ def text_to_bytes(text)
5
+ text.unpack('H*')[0]
6
+ end
7
+
8
+ def bytes_to_text(bytes)
9
+ [bytes.delete(' ')].pack('H*').gsub(/\x00/,'')
10
+ end
11
+
12
+ def uuid_to_bytes(uuid)
13
+ uuid.gsub(/-/, '')
14
+ end
15
+
16
+ def bytes_to_uuid(bytes)
17
+ bytes.delete(' ').sub(/([a-fA-F0-9]{8})([a-fA-F0-9]{4})([a-fA-F0-9]{4})([a-fA-F0-9]{4})([a-fA-F0-9]{12})/, '\1-\2-\3-\4-\5').upcase
18
+ end
19
+
20
+ def major_minor_to_bytes(value)
21
+ sprintf("%04x", value.to_i)
22
+ end
23
+
24
+ def bytes_to_major_minor(bytes)
25
+ bytes.delete(' ').to_i(16)
26
+ end
27
+
28
+ def power_to_bytes(power)
29
+ sprintf("%x", power.to_i + 256)
30
+ end
31
+
32
+ def bytes_to_power(bytes)
33
+ bytes.to_i(16) - 256
34
+ end
35
+
36
+ def pin_to_bytes(pin)
37
+ pin.unpack('H*')[0]
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Radbeacon
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'radbeacon/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "radbeacon"
8
+ spec.version = Radbeacon::VERSION
9
+ spec.authors = ["Radius Networks"]
10
+ spec.email = ["support@radiusnetworks.com"]
11
+
12
+ spec.summary = %q{Provides RadBeacon (BLE Proximity Beacon) scanning and configuring capabilities on a linux machine.}
13
+ spec.homepage = "http://www.radiusnetworks.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.9"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.2"
24
+
25
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radbeacon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Radius Networks
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ description:
56
+ email:
57
+ - support@radiusnetworks.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - Gemfile
65
+ - Gemfile.lock
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - lib/radbeacon.rb
70
+ - lib/radbeacon/bluetooth_le_device.rb
71
+ - lib/radbeacon/le_scanner.rb
72
+ - lib/radbeacon/scanner.rb
73
+ - lib/radbeacon/usb.rb
74
+ - lib/radbeacon/utils.rb
75
+ - lib/radbeacon/version.rb
76
+ - radbeacon.gemspec
77
+ homepage: http://www.radiusnetworks.com
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.4.3
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Provides RadBeacon (BLE Proximity Beacon) scanning and configuring capabilities
101
+ on a linux machine.
102
+ test_files: []