ble 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05bba72a755368615142178e182af8ade6c5f97d
4
- data.tar.gz: 0c0e6f32a4abd668bce46c26f845492e440a72a5
3
+ metadata.gz: d0d63de7a39417e54dd34e2fc70a326061d36cc6
4
+ data.tar.gz: ac360bfb08b81186e16840c3c7dac76f09788d6f
5
5
  SHA512:
6
- metadata.gz: 223434f164e5c8adad7c0bfd043b4cf4955329d939c88210fc22d6abf422110ce8713dede74bec3641ce8b1b7f2dd1e81a03458496159793ae3832642c377438
7
- data.tar.gz: 29dbc315196f93c8c5e2f2ddd8ba2dcc0788561a19061637938cafc6541f5978d3d9d2162437c74e4746d7d12c0ac4d3349c4d2ed2f499930ee10bf5d0442874
6
+ metadata.gz: d4a84b8634507629242d89e81542d03e5036ca73faba52e1a4db5d128ea8140e8ac46925de9b45948e12fa6de894865a9e2835500b5ef28265aa2c597bae989a
7
+ data.tar.gz: ff96bdeadd3582a120bcd68b8b48864a736d611653c8754508daf08fe68f7fa5808cc7395397277c64be0c434a3a14f642f4bb2d445968f98927007e997caf4b
data/lib/ble.rb CHANGED
@@ -61,9 +61,11 @@ module BLE
61
61
  # on this device
62
62
  class NotFound < Error ; end
63
63
  class AccessUnavailable < Error ; end
64
-
64
+ class NotSupported < Error ; end
65
65
 
66
- GATT_BASE_UUID="00000000-0000-1000-8000-00805F9B34FB"
66
+
67
+ # Base UUID for GATT services defined with 16bit or 32bit UUID
68
+ GATT_BASE_UUID="00000000-0000-1000-8000-00805f9b34fb"
67
69
 
68
70
  #"DisplayOnly", "DisplayYesNo", "KeyboardOnly",
69
71
  # "NoInputNoOutput" and "KeyboardDisplay" which
@@ -73,9 +75,9 @@ module BLE
73
75
  raise NotYetImplemented
74
76
  bus = DBus.session_bus
75
77
  service = bus.request_service("org.ruby.service")
76
-
78
+
77
79
  service.export(BLE::Agent.new(agent_path))
78
-
80
+
79
81
  o_bluez = BLUEZ.object('/org/bluez')
80
82
  o_bluez.introspect
81
83
  o_bluez[I_AGENT_MANAGER].RegisterAgent(agent_path, "NoInputNoOutput")
@@ -83,30 +85,25 @@ module BLE
83
85
 
84
86
 
85
87
 
86
- def self.UUID(val)
87
- val.downcase
88
- end
89
-
90
- class UUID
91
- REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
92
- end
93
-
94
88
 
95
89
  # Check if Bluetooth underlying API is accessible
96
90
  def self.ok?
97
91
  BLUEZ.exists?
98
92
  end
99
-
93
+
100
94
  end
101
95
 
102
96
  require_relative 'ble/version'
97
+ require_relative 'ble/uuid'
103
98
  require_relative 'ble/adapter'
104
99
  require_relative 'ble/device'
105
100
  require_relative 'ble/service'
106
101
  require_relative 'ble/characteristic'
107
102
  require_relative 'ble/agent'
108
103
 
109
- require_relative 'ble/db_service'
110
- require_relative 'ble/db_characteristic'
104
+ require_relative 'ble/db_sig_service'
105
+ require_relative 'ble/db_sig_characteristic'
106
+ require_relative 'ble/db_eddystone'
107
+ require_relative 'ble/db_nordic'
111
108
 
112
109
 
@@ -76,6 +76,7 @@ module Characteristic
76
76
  when Symbol then DB_NICKNAME[id]
77
77
  when UUID::REGEX then DB_UUID[id]
78
78
  when String then DB_TYPE[id]
79
+ when Integer then DB_UUID[BLE::UUID(id)]
79
80
  else raise ArgumentError, "invalid type for characteristic id"
80
81
  end
81
82
  end
@@ -104,56 +105,49 @@ module Characteristic
104
105
  #
105
106
  # @param uuid [Integer, String] 16-bit, 32-bit or 128-bit uuid
106
107
  # @param name [String]
107
- # @param type [String]
108
- # @option opts :in [Proc] convert to ruby
109
- # @option opts :out [Proc] convert to bluetooth data
110
- # @option opts :vry [Proc] verify
108
+ # @option opts :type [String] type
109
+ # @option opts :nick [Symbol] nickname
110
+ # @option opts :in [Proc] convert to ruby
111
+ # @option opts :out [Proc] convert to bluetooth data
112
+ # @option opts :vry [Proc] verify
111
113
  # @return [Hash] characteristic description
112
- def self.add(uuid, name:, type:, **opts)
113
- _in = opts.delete :in
114
- _out = opts.delete :out
115
- vrfy = opts.delete :vrfy
114
+ def self.add(uuid, name:, **opts)
115
+ uuid = BLE::UUID(uuid)
116
+ type = opts.delete :type
117
+ nick = opts.delete :nick
118
+ _in = opts.delete :in
119
+ _out = opts.delete :out
120
+ vrfy = opts.delete :vrfy
116
121
  if opts.first
117
122
  raise ArgumentError, "unknown keyword: #{opts.first[0]}"
118
123
  end
119
124
 
120
- uuid = case uuid
121
- when Integer
122
- if !(0..4294967296).include?(uuid)
123
- raise ArgumentError, "not a 16bit or 32bit uuid"
124
- end
125
- ([uuid].pack("L>").unpack('H*').first +
126
- "-0000-1000-8000-00805F9B34FB")
127
-
128
- when String
129
- if uuid !~ UUID::REGEX
130
- raise ArgumentError, "not a 128bit uuid string"
131
- end
132
- uuid
133
- else raise ArgumentError, "invalid uuid type"
134
- end
135
- uuid = uuid.downcase
136
- type = type.downcase
137
125
 
138
- DB_TYPE[type] = DB_UUID[uuid] = {
139
- name: name,
140
- type: type,
126
+ desc = DB_UUID[uuid] = {
141
127
  uuid: uuid,
142
- in: _in,
143
- out: _out,
128
+ name: name,
129
+ in: _in,
130
+ out: _out,
144
131
  vrfy: vrfy
145
132
  }
146
133
 
147
- stype = type.split('.')
148
- key = stype.pop.to_sym
149
- prefix = stype.join('.')
150
- case prefix
151
- when 'org.bluetooth.characteristic'
152
- if DB_NICKNAME.include?(key)
134
+ # Add type if specified
135
+ if type
136
+ type = type.downcase
137
+ desc.merge!(type: type)
138
+ DB_TYPE[type] = desc
139
+ end
140
+
141
+ # Add nickname if specified or can be derived from type
142
+ if nick.nil? && type && type =~ /\.(?<key>[^.]+)$/
143
+ nick = $1.to_sym if type.start_with? 'org.bluetooth.characteristic'
144
+ end
145
+ if nick
146
+ if DB_NICKNAME.include?(nick)
153
147
  raise ArgumentError,
154
- "nickname '#{key}' already registered (type: #{type})"
148
+ "nickname '#{nick}' already registered (uuid: #{uuid})"
155
149
  end
156
- DB_NICKNAME[key] = DB_UUID[uuid]
150
+ DB_NICKNAME[nick] = desc
157
151
  end
158
152
  end
159
153
  end
@@ -0,0 +1,50 @@
1
+ module BLE
2
+ module Service
3
+ add 'ee0c2080-8786-40ba-ab96-99b91ac981d8',
4
+ name: 'Eddystone-URL Beacon Configuration'
5
+ end
6
+ end
7
+
8
+ module BLE
9
+ module Characteristic
10
+ add 'ee0c2081-8786-40ba-ab96-99b91ac981d8',
11
+ name: 'Eddystone Lock State',
12
+ nick: :esurl_lockstate
13
+
14
+ add 'ee0c2082-8786-40ba-ab96-99b91ac981d8',
15
+ name: 'Eddystone Lock',
16
+ nick: :esurl_lock
17
+
18
+ add 'ee0c2083-8786-40ba-ab96-99b91ac981d8',
19
+ name: 'Eddystone Unlock',
20
+ nick: :esurl_unlock
21
+
22
+ add 'ee0c2084-8786-40ba-ab96-99b91ac981d8',
23
+ name: 'Eddystone URL Data',
24
+ nick: :esurl_data
25
+
26
+ add 'ee0c2085-8786-40ba-ab96-99b91ac981d8',
27
+ name: 'Eddystone Flags',
28
+ nick: :esurl_flags
29
+
30
+ add 'ee0c2086-8786-40ba-ab96-99b91ac981d8',
31
+ name: 'Eddystone Adv Tx Power Levels',
32
+ nick: :esurl_adv_txpower_levels
33
+
34
+ add 'ee0c2087-8786-40ba-ab96-99b91ac981d8',
35
+ name: 'Eddystone TX power mode',
36
+ nick: :esurl_txpower_mode
37
+
38
+ add 'ee0c2088-8786-40ba-ab96-99b91ac981d8',
39
+ name: 'Eddystone Beacon period',
40
+ nick: :esurl_beacon_period
41
+
42
+ add 'ee0c2089-8786-40ba-ab96-99b91ac981d8',
43
+ name: 'Eddystone Reset',
44
+ nick: :esurl_reset
45
+
46
+ add 'ee0c208a-8786-40ba-ab96-99b91ac981d8',
47
+ name: 'Eddystone Radio Tx Power Levels',
48
+ nick: :esurl_radio_txpower_levels
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ module BLE
2
+ module Service
3
+ add '00001530-1212-efde-1523-785feabcd123',
4
+ name: 'Nordic Device Firmware Update Service'
5
+
6
+ add '6e400001-b5a3-f393-e0a9-e50e24dcca9e',
7
+ name: 'Nordic UART Service'
8
+ end
9
+ end
10
+
11
+
12
+ module BLE
13
+ module Characteristic
14
+ add '00001531-1212-efde-1523-785feabcd123', # WriteWithoutResponse
15
+ name: 'DFU Packet'
16
+
17
+ add '00001532-1212-efde-1523-785feabcd123', # Write,Notify
18
+ name: 'DFU Control Point'
19
+
20
+ add '6e400002-b5a3-f393-e0a9-e50e24dcca9e',
21
+ name: 'UART TX'
22
+
23
+ add '6e400003-b5a3-f393-e0a9-e50e24dcca9e',
24
+ name: 'UART RX'
25
+ end
26
+ end
@@ -0,0 +1,142 @@
1
+ module BLE
2
+ module Characteristic
3
+ # C | Integer | 8-bit unsigned (unsigned char)
4
+ # S | Integer | 16-bit unsigned, native endian (uint16_t)
5
+ # L | Integer | 32-bit unsigned, native endian (uint32_t)
6
+ # Q | Integer | 64-bit unsigned, native endian (uint64_t)
7
+ # | |
8
+ # c | Integer | 8-bit signed (signed char)
9
+ # s | Integer | 16-bit signed, native endian (int16_t)
10
+ # l | Integer | 32-bit signed, native endian (int32_t)
11
+ # q | Integer | 64-bit signed, native endian (int64_t)
12
+ #
13
+ # < | Little endian
14
+ # > | Big endian
15
+
16
+ add 0x2A07,
17
+ name: 'Tx Power Level',
18
+ type: 'org.bluetooth.characteristic.tx_power_level',
19
+ vrfy: ->(x) { (-100..20).include?(c) },
20
+ in: ->(s) { s.unpack('c').first },
21
+ out: ->(v) { [ v ].pack('c') }
22
+
23
+ add 0x2A23,
24
+ name: 'System ID',
25
+ type: 'org.bluetooth.characteristic.system_id',
26
+ vrfy: ->(x) { (0..1099511627775).include?(x[0]) &&
27
+ (0..16777215 ).include?(x[1])},
28
+ in: ->(s) { raise NotYetImplemented },
29
+ out: ->(v) { raise NotYetImplemented }
30
+
31
+ add 0x2A24,
32
+ name: 'Model Number String',
33
+ type: 'org.bluetooth.characteristic.model_number_string',
34
+ in: ->(s) { s.force_encoding('UTF-8') },
35
+ out: ->(v) { v.encode('UTF-8') }
36
+
37
+ add 0x2A25,
38
+ name: 'Serial Number String',
39
+ type: 'org.bluetooth.characteristic.serial_number_string',
40
+ in: ->(s) { s.force_encoding('UTF-8') },
41
+ out: ->(v) { v.encode('UTF-8') }
42
+
43
+ add 0x2A26,
44
+ name: 'Firmware Revision String',
45
+ type: 'org.bluetooth.characteristic.firmware_revision_string',
46
+ in: ->(s) { s.force_encoding('UTF-8') },
47
+ out: ->(v) { v.encode('UTF-8') }
48
+
49
+ add 0x2A27,
50
+ name: 'Hardware Revision String',
51
+ type: 'org.bluetooth.characteristic.hardware_revision_string',
52
+ in: ->(s) { s.force_encoding('UTF-8') },
53
+ out: ->(v) { v.encode('UTF-8') }
54
+
55
+ add 0x2A28,
56
+ name: 'Software Revision String',
57
+ type: 'org.bluetooth.characteristic.software_revision_string',
58
+ in: ->(s) { s.force_encoding('UTF-8') },
59
+ out: ->(v) { v.encode('UTF-8') }
60
+
61
+ add 0x2A29,
62
+ name: 'Manufacturer Name String',
63
+ type: 'org.bluetooth.characteristic.manufacturer_name_string',
64
+ in: ->(s) { s.force_encoding('UTF-8') },
65
+ out: ->(v) { v.encode('UTF-8') }
66
+
67
+ add 0x2A2A,
68
+ name: 'IEEE 11073-20601 Regulatory Certification Data List',
69
+ type: 'org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list',
70
+ in: ->(s) { raise NotYetImplemented },
71
+ out: ->(v) { raise NotYetImplemented }
72
+
73
+ add 0x2A50,
74
+ name: 'PnP ID',
75
+ type: 'org.bluetooth.characteristic.pnp_id',
76
+ in: ->(s) { vendor_src, vendor_id,
77
+ product_id, product_version = s.unpack('CS<S<S<')
78
+ vendor_src = case vendor_src
79
+ when 1 then :bluetooth_sig
80
+ when 2 then :usb_forum
81
+ else :reserved
82
+ end
83
+ [ vendor_src, vendor_id,
84
+ product_id, product_version ] },
85
+ out: ->(v) { raise NotYetImplemented }
86
+
87
+
88
+ add 0x2A6E,
89
+ name: 'Temperature',
90
+ type: 'org.bluetooth.characteristic.temperature',
91
+ vrfy: ->(x) { (0..100).include?(x) },
92
+ in: ->(s) { s.unpack('s<').first.to_f / 100 },
93
+ out: ->(v) { [ v*100 ].pack('s<') }
94
+
95
+ add 0x2A76,
96
+ name: 'UV Index',
97
+ type: 'org.bluetooth.characteristic.uv_index',
98
+ in: ->(s) { s.unpack('C').first },
99
+ out: ->(v) { [ v ].pack('C') }
100
+
101
+ add 0x2A77,
102
+ name: 'Irradiance',
103
+ type: 'org.bluetooth.characteristic.irradiance',
104
+ in: ->(s) { s.unpack('S<').first.to_f / 10 },
105
+ out: ->(v) { [ v*10 ].pack('S<') }
106
+
107
+ add 0x2A7A,
108
+ name: 'Heat Index',
109
+ type: 'org.bluetooth.characteristic.heat_index',
110
+ in: ->(s) { s.unpack('c').first },
111
+ out: ->(v) { [ v ].pack('c') }
112
+
113
+ add 0x2A19,
114
+ name: 'Battery Level',
115
+ type: 'org.bluetooth.characteristic.battery_level',
116
+ vrfy: ->(x) { (0..100).include?(x) },
117
+ in: ->(s) { s.unpack('C').first },
118
+ out: ->(v) { [ v ].pack('C') }
119
+
120
+ add 0x2A6F,
121
+ name: 'Humidity',
122
+ type: 'org.bluetooth.characteristic.humidity',
123
+ vrfy: ->(x) { (0..100).include?(x) },
124
+ in: ->(s) { s.unpack('S<').first.to_f / 100 },
125
+ out: ->(v) { [ v*100 ].pack('S<') }
126
+
127
+ add 0x2A6D,
128
+ name: 'Pressure',
129
+ type: 'org.bluetooth.characteristic.pressure',
130
+ vrfy: ->(x) { x >= 0 },
131
+ in: ->(s) { s.unpack('L<').first.to_f / 10 },
132
+ out: ->(v) { [ v*10 ].pack('L<') }
133
+
134
+ add 0x2AB3,
135
+ name: 'Altitude',
136
+ type: 'org.bluetooth.characteristic.altitude',
137
+ vrfy: ->(x) { x >= 0 },
138
+ in: ->(s) { s.unpack('S<').first },
139
+ out: ->(v) { [ v ].pack('S<') }
140
+
141
+ end
142
+ end
@@ -55,6 +55,5 @@ module Service
55
55
  add 0x181D,
56
56
  name: 'Weight Scale',
57
57
  type: 'org.bluetooth.service.weight_scale'
58
-
59
58
  end
60
59
  end
@@ -19,19 +19,19 @@ class Device
19
19
  @adapter, @dev = adapter, dev
20
20
  @auto_refresh = auto_refresh
21
21
  @services = {}
22
-
22
+
23
23
  @n_adapter = adapter
24
24
  @p_adapter = "/org/bluez/#{@n_adapter}"
25
25
  @o_adapter = BLUEZ.object(@p_adapter)
26
26
  @o_adapter.introspect
27
-
27
+
28
28
  @n_dev = 'dev_' + dev.tr(':', '_')
29
29
  @p_dev = "/org/bluez/#{@n_adapter}/#{@n_dev}"
30
30
  @o_dev = BLUEZ.object(@p_dev)
31
31
  @o_dev.introspect
32
-
32
+
33
33
  self.refresh if @auto_refresh
34
-
34
+
35
35
  @o_dev[I_PROPERTIES]
36
36
  .on_signal('PropertiesChanged') do |intf, props|
37
37
  case intf
@@ -43,7 +43,7 @@ class Device
43
43
  end
44
44
  end
45
45
  end
46
-
46
+
47
47
  # This removes the remote device object.
48
48
  # It will remove also the pairing information.
49
49
  # @return [Boolean]
@@ -58,8 +58,8 @@ class Device
58
58
  else raise ScriptError
59
59
  end
60
60
  end
61
-
62
-
61
+
62
+
63
63
  # This method will connect to the remote device,
64
64
  # initiate pairing and then retrieve all SDP records
65
65
  # (or GATT primary services).
@@ -71,6 +71,7 @@ class Device
71
71
  # all other cases the default agent will handle this just fine.
72
72
  # In case there is no application agent and also
73
73
  # no default agent present, this method will fail.
74
+ #
74
75
  # @return [Boolean]
75
76
  def pair
76
77
  @o_dev[I_DEVICE].Pair
@@ -88,7 +89,7 @@ class Device
88
89
  else raise ScriptError
89
90
  end
90
91
  end
91
-
92
+
92
93
  # This method can be used to cancel a pairing
93
94
  # operation initiated by the Pair method.
94
95
  # @return [Boolean]
@@ -102,7 +103,7 @@ class Device
102
103
  else raise ScriptError
103
104
  end
104
105
  end
105
-
106
+
106
107
  # This connect to the specified profile UUID or to any (:all)
107
108
  # profiles the remote device supports that can be connected to
108
109
  # and have been flagged as auto-connectable on our side. If
@@ -133,7 +134,7 @@ class Device
133
134
  else raise ScriptError
134
135
  end
135
136
  end
136
-
137
+
137
138
  # This method gracefully disconnects :all connected profiles
138
139
  # and then terminates low-level ACL connection.
139
140
  # ACL connection will be terminated even if some profiles
@@ -161,7 +162,7 @@ class Device
161
162
  when E_INVALID_ARGUMENTS
162
163
  raise ArgumentError, "unsupported profile (#{profile})"
163
164
  when E_NOT_SUPPORTED
164
- raise NotSuppported
165
+ raise NotSupported
165
166
  when E_NOT_CONNECTED
166
167
  true
167
168
  when E_UNKNOWN_OBJECT
@@ -169,7 +170,7 @@ class Device
169
170
  else raise ScriptError
170
171
  end
171
172
  end
172
-
173
+
173
174
  # Indicates if the remote device is paired
174
175
  def is_paired?
175
176
  @o_dev[I_DEVICE]['Paired']
@@ -180,7 +181,7 @@ class Device
180
181
  else raise ScriptError
181
182
  end
182
183
  end
183
-
184
+
184
185
  # Indicates if the remote device is currently connected.
185
186
  def is_connected?
186
187
  @o_dev[I_DEVICE]['Connected']
@@ -191,7 +192,7 @@ class Device
191
192
  else raise ScriptError
192
193
  end
193
194
  end
194
-
195
+
195
196
  # List of available services as UUID.
196
197
  #
197
198
  # @raise [NotConnected] if device is not in a connected state
@@ -201,18 +202,25 @@ class Device
201
202
  # @note This is the list of UUIDs for which we have an entry
202
203
  # in the underlying api (bluez-dbus), which can be less
203
204
  # that the list of advertised UUIDs.
205
+ # @example list available services
206
+ # $d.services.each {|uuid|
207
+ # info = BLE::Service[uuid]
208
+ # name = info.nil? ? uuid : info[:name]
209
+ # puts name
210
+ # }
211
+ #
204
212
  # @return [Array<String>] List of service UUID
205
213
  def services
206
214
  raise NotConnected unless is_connected?
207
215
  @services.keys
208
216
  end
209
-
217
+
210
218
  # Check if service is available on the device
211
219
  # @return [Boolean]
212
220
  def has_service?(service)
213
221
  @service.key?(_uuid_service(service))
214
222
  end
215
-
223
+
216
224
  # List of available characteristics UUID for a service.
217
225
  #
218
226
  # @param service service can be a UUID, a service type or
@@ -229,21 +237,21 @@ class Device
229
237
  chars.keys
230
238
  end
231
239
  end
232
-
240
+
233
241
  # The Bluetooth device address of the remote device.
234
242
  # @return [String] MAC address
235
243
  def address
236
244
  @o_dev[I_DEVICE]['Address']
237
245
  end
238
-
246
+
239
247
  # The Bluetooth remote name.
240
248
  # It is better to always use the {#alias} when displaying the
241
- # devices name.
249
+ # devices name.
242
250
  # @return [String] name
243
251
  def name # optional
244
252
  @o_dev[I_DEVICE]['Name']
245
253
  end
246
-
254
+
247
255
  # The name alias for the remote device.
248
256
  # The alias can be used to have a different friendly name for the
249
257
  # remote device.
@@ -259,13 +267,13 @@ class Device
259
267
  def alias=(val)
260
268
  @o_dev[I_DEVICE]['Alias'] = val.nil? ? "" : val.to_str
261
269
  end
262
-
270
+
263
271
  # Is the device trusted?
264
272
  # @return [Boolean]
265
273
  def is_trusted?
266
274
  @o_dev[I_DEVICE]['Trusted']
267
275
  end
268
-
276
+
269
277
  # Indicates if the remote is seen as trusted. This
270
278
  # setting can be changed by the application.
271
279
  # @param val [Boolean]
@@ -276,13 +284,13 @@ class Device
276
284
  end
277
285
  @o_dev[I_DEVICE]['Trusted'] = val
278
286
  end
279
-
287
+
280
288
  # Is the device blocked?
281
289
  # @return [Boolean]
282
290
  def is_blocked?
283
291
  @o_dev[I_DEVICE]['Blocked']
284
292
  end
285
-
293
+
286
294
  # If set to true any incoming connections from the
287
295
  # device will be immediately rejected. Any device
288
296
  # drivers will also be removed and no new ones will
@@ -295,7 +303,7 @@ class Device
295
303
  end
296
304
  @o_dev[I_DEVICE]['Blocked'] = val
297
305
  end
298
-
306
+
299
307
  # Received Signal Strength Indicator of the remote
300
308
  # device (inquiry or advertising).
301
309
  # @return [Integer]
@@ -307,7 +315,7 @@ class Device
307
315
  else raise ScriptError
308
316
  end
309
317
  end
310
-
318
+
311
319
  # Advertised transmitted power level (inquiry or advertising).
312
320
  # @return [Integer]
313
321
  def tx_power # optional
@@ -318,8 +326,8 @@ class Device
318
326
  else raise ScriptError
319
327
  end
320
328
  end
321
-
322
-
329
+
330
+
323
331
  # Refresh list of services and characteristics
324
332
  # @return [Boolean]
325
333
  def refresh
@@ -328,7 +336,7 @@ class Device
328
336
  rescue NotConnected, StalledObject
329
337
  false
330
338
  end
331
-
339
+
332
340
  # Refresh list of services and characteristics
333
341
  # @raise [NotConnected] if device is not in a connected state
334
342
  # @return [self]
@@ -363,7 +371,7 @@ class Device
363
371
  sleep(0.25) ; max_wait -= 0.25 ; retry
364
372
  end
365
373
  raise NotReady
366
-
374
+
367
375
  else raise ScriptError
368
376
  end
369
377
  end
@@ -422,7 +430,7 @@ class Device
422
430
  flags = char[:flags]
423
431
  obj = char[:obj]
424
432
  info = Characteristic[uuid]
425
-
433
+
426
434
  if flags.include?('write') ||
427
435
  flags.include?('write-without-response')
428
436
  if !raw && info
@@ -442,9 +450,9 @@ class Device
442
450
  end
443
451
  nil
444
452
  end
445
-
453
+
446
454
  private
447
-
455
+
448
456
  def _characteristics(service)
449
457
  if srv = @services[_uuid_service(service)]
450
458
  srv[:characteristics]
@@ -462,7 +470,7 @@ class Device
462
470
  if uuid.nil?
463
471
  raise ArgumentError, "unable to get UUID for service"
464
472
  end
465
-
473
+
466
474
  uuid
467
475
  end
468
476
  def _uuid_characteristic(characteristic)
@@ -477,7 +485,7 @@ class Device
477
485
  if uuid.nil?
478
486
  raise ArgumentError, "unable to get UUID for service"
479
487
  end
480
-
488
+
481
489
  uuid
482
490
  end
483
491
  end
@@ -34,6 +34,7 @@ module Service
34
34
  when Symbol then DB_NICKNAME[id]
35
35
  when UUID::REGEX then DB_UUID[id]
36
36
  when String then DB_TYPE[id]
37
+ when Integer then DB_UUID[BLE::UUID(id)]
37
38
  else raise ArgumentError, "invalid type for service id"
38
39
  end
39
40
  end
@@ -79,49 +80,41 @@ module Service
79
80
  #
80
81
  # @param uuid [Integer, String] 16-bit, 32-bit or 128-bit uuid
81
82
  # @param name [String]
82
- # @param type [String]
83
+ # @option opts :type [String] type
84
+ # @option opts :nick [Symbol] nickname
83
85
  # @return [Hash] service description
84
- def self.add(uuid, name:, type:, **opts)
86
+ def self.add(uuid, name:, **opts)
87
+ uuid = BLE::UUID(uuid)
88
+ type = opts.delete :type
89
+ nick = opts.delete :nick
85
90
  if opts.first
86
91
  raise ArgumentError, "unknown keyword: #{opts.first[0]}"
87
92
  end
88
93
 
89
- uuid = case uuid
90
- when Integer
91
- if !(0..4294967296).include?(uuid)
92
- raise ArgumentError, "not a 16bit or 32bit uuid"
93
- end
94
- ([uuid].pack("L>").unpack('H*').first +
95
- "-0000-1000-8000-00805F9B34FB")
96
-
97
- when String
98
- if uuid !~ UUID::REGEX
99
- raise ArgumentError, "not a 128bit uuid string"
100
- end
101
- uuid
102
- else raise ArgumentError, "invalid uuid type"
103
- end
104
- uuid = uuid.downcase
105
- type = type.downcase
106
-
107
- desc = DB_TYPE[type] = DB_UUID[uuid] = {
108
- name: name,
109
- type: type,
94
+ desc = DB_UUID[uuid] = {
110
95
  uuid: uuid,
96
+ name: name,
111
97
  }
112
-
113
- stype = type.split('.')
114
- key = stype.pop.to_sym
115
- prefix = stype.join('.')
116
- case prefix
117
- when 'org.bluetooth.service'
118
- if DB_NICKNAME.include?(key)
98
+
99
+ # Add type if specified
100
+ if type
101
+ type = type.downcase
102
+ desc.merge!(type: type)
103
+ DB_TYPE[type] = desc
104
+ end
105
+
106
+ # Add nickname if specified or can be derived from type
107
+ if nick.nil? && type && type =~ /\.(?<key>[^.]+)$/
108
+ nick = $1.to_sym if type.start_with? 'org.bluetooth.service'
109
+ end
110
+ if nick
111
+ if DB_NICKNAME.include?(nick)
119
112
  raise ArgumentError,
120
- "nickname '#{key}' already registered (type: #{type})"
113
+ "nickname '#{nick}' already registered (uuid: #{uuid})"
121
114
  end
122
- DB_NICKNAME[key] = desc
115
+ DB_NICKNAME[nick] = desc
123
116
  end
124
-
117
+
125
118
  desc
126
119
  end
127
120
  end
@@ -0,0 +1,25 @@
1
+ module BLE
2
+ # Cast value to a well-formatted 128bit UUID string
3
+ # @param [String, Integer] val uuid
4
+ # @return [String] well formated 128bit UUID
5
+ def self.UUID(val)
6
+ case val
7
+ when Integer
8
+ if !(0..4294967295).include?(val) # 2**32-1
9
+ raise ArgumentError, "not a 16-bit or 32-bit UUID"
10
+ end
11
+ ([val].pack("L>").unpack('H*').first + GATT_BASE_UUID[8..-1])
12
+ when String
13
+ if val !~ UUID::REGEX
14
+ raise ArgumentError, "not a 128bit uuid string"
15
+ end
16
+ val.downcase
17
+ else raise ArgumentError, "invalid uuid type"
18
+ end
19
+ end
20
+
21
+ class UUID
22
+ # Regular expression for matching UUID 128-bit string
23
+ REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
24
+ end
25
+ end
@@ -1,4 +1,9 @@
1
1
  module BLE
2
- # Library version
3
- VERSION = "0.0.2"
2
+ # Library version
3
+
4
+ #
5
+ # 0.0.3 PATCH
6
+ # - [FIX] NotSupported exeption was not declared.
7
+ #
8
+ VERSION = '0.0.3'
4
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ble
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephane D'Alu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-15 00:00:00.000000000 Z
11
+ date: 2016-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-dbus
@@ -94,10 +94,13 @@ files:
94
94
  - lib/ble/adapter.rb
95
95
  - lib/ble/agent.rb
96
96
  - lib/ble/characteristic.rb
97
- - lib/ble/db_characteristic.rb
98
- - lib/ble/db_service.rb
97
+ - lib/ble/db_eddystone.rb
98
+ - lib/ble/db_nordic.rb
99
+ - lib/ble/db_sig_characteristic.rb
100
+ - lib/ble/db_sig_service.rb
99
101
  - lib/ble/device.rb
100
102
  - lib/ble/service.rb
103
+ - lib/ble/uuid.rb
101
104
  - lib/ble/version.rb
102
105
  homepage: http://github.com/sdalu/ruby-ble
103
106
  licenses:
@@ -124,4 +127,3 @@ signing_key:
124
127
  specification_version: 4
125
128
  summary: Bluetooth Low Energy (BLE) API
126
129
  test_files: []
127
- has_rdoc: yard
@@ -1,70 +0,0 @@
1
- module BLE
2
- module Characteristic
3
- # C | Integer | 8-bit unsigned (unsigned char)
4
- # S | Integer | 16-bit unsigned, native endian (uint16_t)
5
- # L | Integer | 32-bit unsigned, native endian (uint32_t)
6
- # Q | Integer | 64-bit unsigned, native endian (uint64_t)
7
- # | |
8
- # c | Integer | 8-bit signed (signed char)
9
- # s | Integer | 16-bit signed, native endian (int16_t)
10
- # l | Integer | 32-bit signed, native endian (int32_t)
11
- # q | Integer | 64-bit signed, native endian (int64_t)
12
- #
13
- # < | Little endian
14
- # > | Big endian
15
-
16
-
17
- add 0x2A6E,
18
- name: 'Temperature',
19
- type: 'org.bluetooth.characteristic.temperature',
20
- vrfy: ->(x) { (0..100).include?(x) },
21
- in: ->(s) { s.unpack('s<').first.to_f / 100 },
22
- out: ->(v) { [ v*100 ].pack('s<') }
23
-
24
- add 0x2A76,
25
- name: 'UV Index',
26
- type: 'org.bluetooth.characteristic.uv_index',
27
- in: ->(s) { s.unpack('C').first },
28
- out: ->(v) { [ v ].pack('C') }
29
-
30
- add 0x2A77,
31
- name: 'Irradiance',
32
- type: 'org.bluetooth.characteristic.irradiance',
33
- in: ->(s) { s.unpack('S<').first.to_f / 10 },
34
- out: ->(v) { [ v*10 ].pack('S<') }
35
-
36
- add 0x2A7A,
37
- name: 'Heat Index',
38
- type: 'org.bluetooth.characteristic.heat_index',
39
- in: ->(s) { s.unpack('c').first },
40
- out: ->(v) { [ v ].pack('c') }
41
-
42
- add 0x2A19,
43
- name: 'Battery Level',
44
- type: 'org.bluetooth.characteristic.battery_level',
45
- vrfy: ->(x) { (0..100).include?(x) },
46
- in: ->(s) { s.unpack('C').first },
47
- out: ->(v) { [ v ].pack('C') }
48
-
49
- add 0x2A6F,
50
- name: 'Humidity',
51
- type: 'org.bluetooth.characteristic.humidity',
52
- vrfy: ->(x) { (0..100).include?(x) },
53
- in: ->(s) { s.unpack('S<').first.to_f / 100 },
54
- out: ->(v) { [ v*100 ].pack('S<') }
55
-
56
- add 0x2A6D,
57
- name: 'Pressure',
58
- type: 'org.bluetooth.characteristic.pressure',
59
- vrfy: ->(x) { x >= 0 },
60
- in: ->(s) { s.unpack('L<').first.to_f / 10 },
61
- out: ->(v) { [ v*10 ].pack('L<') }
62
-
63
- add 0x2AB3,
64
- name: 'Altitude',
65
- type: 'org.bluetooth.characteristic.altitude',
66
- vrfy: ->(x) { x >= 0 },
67
- in: ->(s) { s.unpack('S<').first },
68
- out: ->(v) { [ v ].pack('S<') }
69
- end
70
- end