ble 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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