ble 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,8 +6,8 @@ end
6
6
  end
7
7
 
8
8
  module BLE
9
- module Characteristic
10
- add 'ee0c2081-8786-40ba-ab96-99b91ac981d8',
9
+ class Characteristic
10
+ add 'ee0c2081-8786-40ba-ab96-99b91ac981d8',
11
11
  name: 'Eddystone Lock State',
12
12
  nick: :esurl_lockstate
13
13
 
@@ -10,8 +10,8 @@ end
10
10
 
11
11
 
12
12
  module BLE
13
- module Characteristic
14
- add '00001531-1212-efde-1523-785feabcd123', # WriteWithoutResponse
13
+ class Characteristic
14
+ add '00001531-1212-efde-1523-785feabcd123', # WriteWithoutResponse
15
15
  name: 'DFU Packet'
16
16
 
17
17
  add '00001532-1212-efde-1523-785feabcd123', # Write,Notify
@@ -1,5 +1,5 @@
1
1
  module BLE
2
- module Characteristic
2
+ class Characteristic
3
3
  # C | Integer | 8-bit unsigned (unsigned char)
4
4
  # S | Integer | 16-bit unsigned, native endian (uint16_t)
5
5
  # L | Integer | 32-bit unsigned, native endian (uint32_t)
@@ -63,13 +63,13 @@ module Characteristic
63
63
  type: 'org.bluetooth.characteristic.manufacturer_name_string',
64
64
  in: ->(s) { s.force_encoding('UTF-8') },
65
65
  out: ->(v) { v.encode('UTF-8') }
66
-
66
+
67
67
  add 0x2A2A,
68
68
  name: 'IEEE 11073-20601 Regulatory Certification Data List',
69
69
  type: 'org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list',
70
70
  in: ->(s) { raise NotYetImplemented },
71
71
  out: ->(v) { raise NotYetImplemented }
72
-
72
+
73
73
  add 0x2A50,
74
74
  name: 'PnP ID',
75
75
  type: 'org.bluetooth.characteristic.pnp_id',
@@ -83,8 +83,8 @@ module Characteristic
83
83
  [ vendor_src, vendor_id,
84
84
  product_id, product_version ] },
85
85
  out: ->(v) { raise NotYetImplemented }
86
-
87
-
86
+
87
+
88
88
  add 0x2A6E,
89
89
  name: 'Temperature',
90
90
  type: 'org.bluetooth.characteristic.temperature',
@@ -1,13 +1,14 @@
1
1
  module BLE
2
- # Create de Device object
3
- # d = Device::new('hci0', 'aa:bb:dd:dd:ee:ff')
4
- # d = Adapter.new('hci0')['aa:bb:dd:dd:ee:ff']
5
- #
6
- # d.services
7
- # d.characteristics(:environmental_sensing)
8
- # d[:environmental_sensing, :temperature]
9
- #
10
- class Device
2
+ # Create de Device object
3
+ # d = Device::new('hci0', 'aa:bb:dd:dd:ee:ff')
4
+ # d = Adapter.new('hci0')['aa:bb:dd:dd:ee:ff']
5
+ #
6
+ # d.services
7
+ # d.characteristics(:environmental_sensing)
8
+ # d[:environmental_sensing, :temperature]
9
+ #
10
+ class Device
11
+ include Notifications
11
12
  # Notify that you need to have the device in a connected state
12
13
  class NotConnected < Error ; end
13
14
 
@@ -16,47 +17,47 @@ class Device
16
17
  # @param auto_refresh [Boolean] gather information about device
17
18
  # on connection
18
19
  def initialize(adapter, dev, auto_refresh: true)
19
- @adapter, @dev = adapter, dev
20
- @auto_refresh = auto_refresh
21
- @services = {}
22
-
23
- @n_adapter = adapter
24
- @p_adapter = "/org/bluez/#{@n_adapter}"
25
- @o_adapter = BLUEZ.object(@p_adapter)
26
- @o_adapter.introspect
27
-
28
- @n_dev = 'dev_' + dev.tr(':', '_')
29
- @p_dev = "/org/bluez/#{@n_adapter}/#{@n_dev}"
30
- @o_dev = BLUEZ.object(@p_dev)
31
- @o_dev.introspect
32
-
33
- self.refresh if @auto_refresh
34
-
35
- @o_dev[I_PROPERTIES]
36
- .on_signal('PropertiesChanged') do |intf, props|
37
- case intf
38
- when I_DEVICE
39
- case props['Connected']
40
- when true
41
- self.refresh if @auto_refresh
42
- end
43
- end
20
+ @adapter, @dev = adapter, dev
21
+ @auto_refresh = auto_refresh
22
+ @services = {}
23
+
24
+ @n_adapter = adapter
25
+ @p_adapter = "/org/bluez/#{@n_adapter}"
26
+ @o_adapter = BLUEZ.object(@p_adapter)
27
+ @o_adapter.introspect
28
+
29
+ @n_dev = 'dev_' + dev.tr(':', '_')
30
+ @p_dev = "/org/bluez/#{@n_adapter}/#{@n_dev}"
31
+ @o_dev = BLUEZ.object(@p_dev)
32
+ @o_dev.introspect
33
+
34
+ self.refresh if @auto_refresh
35
+
36
+ @o_dev[I_PROPERTIES]
37
+ .on_signal('PropertiesChanged') do |intf, props|
38
+ case intf
39
+ when I_DEVICE
40
+ case props['Connected']
41
+ when true
42
+ self.refresh if @auto_refresh
43
+ end
44
44
  end
45
+ end
45
46
  end
46
47
 
47
48
  # This removes the remote device object.
48
49
  # It will remove also the pairing information.
49
50
  # @return [Boolean]
50
51
  def remove
51
- @o_adapter[I_ADAPTER].RemoveDevice(@p_dev)
52
- true
52
+ @o_adapter[I_ADAPTER].RemoveDevice(@p_dev)
53
+ true
53
54
  rescue DBus::Error => e
54
- case e.name
55
- when E_FAILED then false
56
- when E_DOES_NOT_EXIST then raise StalledObject
57
- when E_UNKNOWN_OBJECT then raise StalledObject
58
- else raise ScriptError
59
- end
55
+ case e.name
56
+ when E_FAILED then false
57
+ when E_DOES_NOT_EXIST then raise StalledObject
58
+ when E_UNKNOWN_OBJECT then raise StalledObject
59
+ else raise ScriptError
60
+ end
60
61
  end
61
62
 
62
63
 
@@ -74,34 +75,34 @@ class Device
74
75
  #
75
76
  # @return [Boolean]
76
77
  def pair
77
- @o_dev[I_DEVICE].Pair
78
- true
78
+ @o_dev[I_DEVICE].Pair
79
+ true
79
80
  rescue DBus::Error => e
80
- case e.name
81
- when E_INVALID_ARGUMENTS then false
82
- when E_FAILED then false
83
- when E_ALREADY_EXISTS then true
84
- when E_AUTH_CANCELED then raise NotAutorized
85
- when E_AUTH_FAILED then raise NotAutorized
86
- when E_AUTH_REJECTED then raise NotAutorized
87
- when E_AUTH_TIMEOUT then raise NotAutorized
88
- when E_AUTH_ATTEMPT_FAILED then raise NotAutorized
89
- else raise ScriptError
90
- end
81
+ case e.name
82
+ when E_INVALID_ARGUMENTS then false
83
+ when E_FAILED then false
84
+ when E_ALREADY_EXISTS then true
85
+ when E_AUTH_CANCELED then raise NotAutorized
86
+ when E_AUTH_FAILED then raise NotAutorized
87
+ when E_AUTH_REJECTED then raise NotAutorized
88
+ when E_AUTH_TIMEOUT then raise NotAutorized
89
+ when E_AUTH_ATTEMPT_FAILED then raise NotAutorized
90
+ else raise ScriptError
91
+ end
91
92
  end
92
93
 
93
94
  # This method can be used to cancel a pairing
94
95
  # operation initiated by the Pair method.
95
96
  # @return [Boolean]
96
97
  def cancel_pairing
97
- @o_dev[I_DEVICE].CancelPairing
98
- true
98
+ @o_dev[I_DEVICE].CancelPairing
99
+ true
99
100
  rescue DBus::Error => e
100
- case e.name
101
- when E_DOES_NOT_EXIST then true
102
- when E_FAILED then false
103
- else raise ScriptError
104
- end
101
+ case e.name
102
+ when E_DOES_NOT_EXIST then true
103
+ when E_FAILED then false
104
+ else raise ScriptError
105
+ end
105
106
  end
106
107
 
107
108
  # This connect to the specified profile UUID or to any (:all)
@@ -113,26 +114,26 @@ class Device
113
114
  # success.
114
115
  # @return [Boolean]
115
116
  def connect(profile=:all)
116
- case profile
117
- when UUID::REGEX
118
- @o_dev[I_DEVICE].ConnectProfile(profile)
119
- when :all
120
- @o_dev[I_DEVICE].Connect()
121
- else raise ArgumentError, "profile uuid or :all expected"
122
- end
123
- true
117
+ case profile
118
+ when UUID::REGEX
119
+ @o_dev[I_DEVICE].ConnectProfile(profile)
120
+ when :all
121
+ @o_dev[I_DEVICE].Connect()
122
+ else raise ArgumentError, "profile uuid or :all expected"
123
+ end
124
+ true
124
125
  rescue DBus::Error => e
125
- case e.name
126
- when E_NOT_READY
127
- when E_FAILED
128
- when E_IN_PROGRESS
129
- false
130
- when E_ALREADY_CONNECTED
131
- true
132
- when E_UNKNOWN_OBJECT
133
- raise StalledObject
134
- else raise ScriptError
135
- end
126
+ case e.name
127
+ when E_NOT_READY
128
+ when E_FAILED
129
+ when E_IN_PROGRESS
130
+ false
131
+ when E_ALREADY_CONNECTED
132
+ true
133
+ when E_UNKNOWN_OBJECT
134
+ raise StalledObject
135
+ else raise ScriptError
136
+ end
136
137
  end
137
138
 
138
139
  # This method gracefully disconnects :all connected profiles
@@ -146,51 +147,51 @@ class Device
146
147
  # as long as the profile is registered this will always succeed
147
148
  # @return [Boolean]
148
149
  def disconnect(profile=:all)
149
- case profile
150
- when UUID::REGEX
151
- @o_dev[I_DEVICE].DisconnectProfile(profile)
152
- when :all
153
- @o_dev[I_DEVICE].Disconnect()
154
- else raise ArgumentError, "profile uuid or :all expected"
155
- end
156
- true
150
+ case profile
151
+ when UUID::REGEX
152
+ @o_dev[I_DEVICE].DisconnectProfile(profile)
153
+ when :all
154
+ @o_dev[I_DEVICE].Disconnect()
155
+ else raise ArgumentError, "profile uuid or :all expected"
156
+ end
157
+ true
157
158
  rescue DBus::Error => e
158
- case e.name
159
- when E_FAILED
160
- when E_IN_PROGRESS
161
- false
162
- when E_INVALID_ARGUMENTS
163
- raise ArgumentError, "unsupported profile (#{profile})"
164
- when E_NOT_SUPPORTED
165
- raise NotSupported
166
- when E_NOT_CONNECTED
167
- true
168
- when E_UNKNOWN_OBJECT
169
- raise StalledObject
170
- else raise ScriptError
171
- end
159
+ case e.name
160
+ when E_FAILED
161
+ when E_IN_PROGRESS
162
+ false
163
+ when E_INVALID_ARGUMENTS
164
+ raise ArgumentError, "unsupported profile (#{profile})"
165
+ when E_NOT_SUPPORTED
166
+ raise NotSupported
167
+ when E_NOT_CONNECTED
168
+ true
169
+ when E_UNKNOWN_OBJECT
170
+ raise StalledObject
171
+ else raise ScriptError
172
+ end
172
173
  end
173
174
 
174
175
  # Indicates if the remote device is paired
175
176
  def is_paired?
176
- @o_dev[I_DEVICE]['Paired']
177
+ @o_dev[I_DEVICE]['Paired']
177
178
  rescue DBus::Error => e
178
- case e.name
179
- when E_UNKNOWN_OBJECT
180
- raise StalledObject
181
- else raise ScriptError
182
- end
179
+ case e.name
180
+ when E_UNKNOWN_OBJECT
181
+ raise StalledObject
182
+ else raise ScriptError
183
+ end
183
184
  end
184
185
 
185
186
  # Indicates if the remote device is currently connected.
186
187
  def is_connected?
187
- @o_dev[I_DEVICE]['Connected']
188
+ @o_dev[I_DEVICE]['Connected']
188
189
  rescue DBus::Error => e
189
- case e.name
190
- when E_UNKNOWN_OBJECT
191
- raise StalledObject
192
- else raise ScriptError
193
- end
190
+ case e.name
191
+ when E_UNKNOWN_OBJECT
192
+ raise StalledObject
193
+ else raise ScriptError
194
+ end
194
195
  end
195
196
 
196
197
  # List of available services as UUID.
@@ -211,14 +212,14 @@ class Device
211
212
  #
212
213
  # @return [Array<String>] List of service UUID
213
214
  def services
214
- raise NotConnected unless is_connected?
215
- @services.keys
215
+ _require_connection!
216
+ @services.keys
216
217
  end
217
218
 
218
219
  # Check if service is available on the device
219
220
  # @return [Boolean]
220
221
  def has_service?(service)
221
- @service.key?(_uuid_service(service))
222
+ @service.key?(_uuid_service(service))
222
223
  end
223
224
 
224
225
  # List of available characteristics UUID for a service.
@@ -232,16 +233,16 @@ class Device
232
233
  # connected if auto_refresh is enable, otherwise
233
234
  # you need to call {#refresh}.
234
235
  def characteristics(service)
235
- raise NotConnected unless is_connected?
236
- if chars = _characteristics(service)
237
- chars.keys
238
- end
236
+ _require_connection!
237
+ if chars = _characteristics(service)
238
+ chars.keys
239
+ end
239
240
  end
240
241
 
241
242
  # The Bluetooth device address of the remote device.
242
243
  # @return [String] MAC address
243
244
  def address
244
- @o_dev[I_DEVICE]['Address']
245
+ @o_dev[I_DEVICE]['Address']
245
246
  end
246
247
 
247
248
  # The Bluetooth remote name.
@@ -249,7 +250,7 @@ class Device
249
250
  # devices name.
250
251
  # @return [String] name
251
252
  def name # optional
252
- @o_dev[I_DEVICE]['Name']
253
+ @o_dev[I_DEVICE]['Name']
253
254
  end
254
255
 
255
256
  # The name alias for the remote device.
@@ -258,20 +259,20 @@ class Device
258
259
  # In case no alias is set, it will return the remote device name.
259
260
  # @return [String]
260
261
  def alias
261
- @o_dev[I_DEVICE]['Alias']
262
+ @o_dev[I_DEVICE]['Alias']
262
263
  end
263
264
  # Setting an empty string or nil as alias will convert it
264
265
  # back to the remote device name.
265
266
  # @param val [String, nil]
266
267
  # @return [void]
267
268
  def alias=(val)
268
- @o_dev[I_DEVICE]['Alias'] = val.nil? ? "" : val.to_str
269
+ @o_dev[I_DEVICE]['Alias'] = val.nil? ? "" : val.to_str
269
270
  end
270
271
 
271
272
  # Is the device trusted?
272
273
  # @return [Boolean]
273
274
  def is_trusted?
274
- @o_dev[I_DEVICE]['Trusted']
275
+ @o_dev[I_DEVICE]['Trusted']
275
276
  end
276
277
 
277
278
  # Indicates if the remote is seen as trusted. This
@@ -279,16 +280,16 @@ class Device
279
280
  # @param val [Boolean]
280
281
  # @return [void]
281
282
  def trusted=(val)
282
- if ! [ true, false ].include?(val)
283
- raise ArgumentError, "value must be a boolean"
284
- end
285
- @o_dev[I_DEVICE]['Trusted'] = val
283
+ if ! [ true, false ].include?(val)
284
+ raise ArgumentError, "value must be a boolean"
285
+ end
286
+ @o_dev[I_DEVICE]['Trusted'] = val
286
287
  end
287
288
 
288
289
  # Is the device blocked?
289
290
  # @return [Boolean]
290
291
  def is_blocked?
291
- @o_dev[I_DEVICE]['Blocked']
292
+ @o_dev[I_DEVICE]['Blocked']
292
293
  end
293
294
 
294
295
  # If set to true any incoming connections from the
@@ -298,116 +299,110 @@ class Device
298
299
  # @param val [Boolean]
299
300
  # @return [void]
300
301
  def blocked=(val)
301
- if ! [ true, false ].include?(val)
302
- raise ArgumentError, "value must be a boolean"
303
- end
304
- @o_dev[I_DEVICE]['Blocked'] = val
302
+ if ! [ true, false ].include?(val)
303
+ raise ArgumentError, "value must be a boolean"
304
+ end
305
+ @o_dev[I_DEVICE]['Blocked'] = val
305
306
  end
306
307
 
307
308
  # Received Signal Strength Indicator of the remote
308
309
  # device (inquiry or advertising).
309
310
  # @return [Integer]
310
311
  def rssi # optional
311
- @o_dev[I_DEVICE]['RSSI']
312
+ @o_dev[I_DEVICE]['RSSI']
312
313
  rescue DBus::Error => e
313
- case e.name
314
- when E_INVALID_ARGS then raise NotSupported
315
- else raise ScriptError
316
- end
314
+ case e.name
315
+ when E_INVALID_ARGS then raise NotSupported
316
+ else raise ScriptError
317
+ end
317
318
  end
318
319
 
319
320
  # Advertised transmitted power level (inquiry or advertising).
320
321
  # @return [Integer]
321
322
  def tx_power # optional
322
- @o_dev[I_DEVICE]['TxPower']
323
+ @o_dev[I_DEVICE]['TxPower']
323
324
  rescue DBus::Error => e
324
- case e.name
325
- when E_INVALID_ARGS then raise NotSupported
326
- else raise ScriptError
327
- end
325
+ case e.name
326
+ when E_INVALID_ARGS then raise NotSupported
327
+ else raise ScriptError
328
+ end
328
329
  end
329
330
 
330
331
 
331
332
  # Refresh list of services and characteristics
332
333
  # @return [Boolean]
333
334
  def refresh
334
- refresh!
335
- true
335
+ refresh!
336
+ true
336
337
  rescue NotConnected, StalledObject
337
- false
338
+ false
338
339
  end
339
340
 
340
341
  # Refresh list of services and characteristics
341
342
  # @raise [NotConnected] if device is not in a connected state
342
343
  # @return [self]
343
344
  def refresh!
344
- raise NotConnected unless is_connected?
345
- max_wait ||= 1.5 # Use ||= due to the retry
346
- @services = Hash[@o_dev[I_DEVICE]['GattServices'].map {|p_srv|
347
- o_srv = BLUEZ.object(p_srv)
348
- o_srv.introspect
349
- srv = o_srv[I_PROPERTIES].GetAll(I_GATT_SERVICE).first
350
- char = Hash[srv['Characteristics'].map {|p_char|
351
- o_char = BLUEZ.object(p_char)
352
- o_char.introspect
353
- uuid = o_char[I_GATT_CHARACTERISTIC]['UUID' ].downcase
354
- flags = o_char[I_GATT_CHARACTERISTIC]['Flags']
355
- [ uuid, { :uuid => uuid, :flags => flags, :obj => o_char } ]
345
+ _require_connection!
346
+ max_wait ||= 1.5 # Use ||= due to the retry
347
+ @services = Hash[@o_dev[I_DEVICE]['GattServices'].map {|p_srv|
348
+ o_srv = BLUEZ.object(p_srv)
349
+ o_srv.introspect
350
+ srv = o_srv[I_PROPERTIES].GetAll(I_GATT_SERVICE).first
351
+ char = Hash[srv['Characteristics'].map {|p_char|
352
+ o_char = BLUEZ.object(p_char)
353
+ o_char.introspect
354
+ uuid = o_char[I_GATT_CHARACTERISTIC]['UUID' ].downcase
355
+ flags = o_char[I_GATT_CHARACTERISTIC]['Flags']
356
+ [ uuid, Characteristic.new({ :uuid => uuid, :flags => flags, :obj => o_char }) ]
356
357
  }]
357
- uuid = srv['UUID'].downcase
358
- [ uuid, { :uuid => uuid,
359
- :primary => srv['Primary'],
360
- :characteristics => char } ]
361
- }]
362
- self
358
+ uuid = srv['UUID'].downcase
359
+ [ uuid, { :uuid => uuid,
360
+ :primary => srv['Primary'],
361
+ :characteristics => char } ]
362
+ }]
363
+ self
363
364
  rescue DBus::Error => e
364
- case e.name
365
- when E_UNKNOWN_OBJECT
366
- raise StalledObject
367
- when E_INVALID_ARGS
368
- # That's probably because all the bluez information
369
- # haven't been collected yet on dbus for GattServices
370
- if max_wait > 0
371
- sleep(0.25) ; max_wait -= 0.25 ; retry
372
- end
373
- raise NotReady
374
-
375
- else raise ScriptError
365
+ case e.name
366
+ when E_UNKNOWN_OBJECT
367
+ raise StalledObject
368
+ when E_INVALID_ARGS
369
+ # That's probably because all the bluez information
370
+ # haven't been collected yet on dbus for GattServices
371
+ if max_wait > 0
372
+ sleep(0.25) ; max_wait -= 0.25 ; retry
376
373
  end
374
+ raise NotReady
375
+
376
+ else raise ScriptError
377
+ end
377
378
  end
378
379
 
379
380
  # Get value for a service/characteristic.
380
381
  #
381
382
  # @param service [String, Symbol]
382
383
  # @param characteristic [String, Symbol]
383
- # @param raw [Boolean]
384
+ # @param raw [Boolean] When raw is true the value get is a binary string, instead of an object corresponding to the decoded characteristic (float, integer, array, ...)
384
385
  # @raise [NotConnected] if device is not in a connected state
385
386
  # @raise [NotYetImplemented] encryption is not implemented yet
386
387
  # @raise [Service::NotFound, Characteristic::NotFound] if service/characteristic doesn't exist on this device
387
388
  # @raise [AccessUnavailable] if not available for reading
388
389
  # @return [Object]
389
390
  def [](service, characteristic, raw: false)
390
- raise NotConnected unless is_connected?
391
- uuid = _uuid_characteristic(characteristic)
392
- chars = _characteristics(service)
393
- raise Service::NotFound, service if chars.nil?
394
- char = chars[uuid]
395
- raise Characteristic::NotFound, characteristic if char.nil?
396
- flags = char[:flags]
397
- obj = char[:obj]
398
- info = Characteristic[uuid]
399
-
400
- if flags.include?('read')
401
- val = obj[I_GATT_CHARACTERISTIC].ReadValue().first
402
- val = val.pack('C*')
403
- val = info[:in].call(val) if !raw && info && info[:in]
404
- val
405
- elsif flags.include?('encrypt-read') ||
406
- flags.include?('encrypt-authenticated-read')
407
- raise NotYetImplemented
408
- else
409
- raise AccessUnavailable
410
- end
391
+ _require_connection!
392
+ uuid = _uuid_characteristic(characteristic)
393
+ chars = _characteristics(service)
394
+ raise Service::NotFound, service if chars.nil?
395
+ char = chars[uuid]
396
+ raise Characteristic::NotFound, characteristic if char.nil?
397
+
398
+ if char.flag?('read')
399
+ char.read(raw: raw)
400
+ elsif char.flag?('encrypt-read') ||
401
+ char.flag?('encrypt-authenticated-read')
402
+ raise NotYetImplemented
403
+ else
404
+ raise AccessUnavailable
405
+ end
411
406
  end
412
407
 
413
408
  # Set value for a service/characteristic
@@ -415,78 +410,85 @@ class Device
415
410
  # @param service [String, Symbol]
416
411
  # @param characteristic [String, Symbol]
417
412
  # @param val [Boolean]
413
+ # @param raw [Boolean] When raw is true the value set is a binary string, instead of an object corresponding to the decoded characteristic (float, integer, array, ...).
418
414
  # @raise [NotConnected] if device is not in a connected state
419
415
  # @raise [NotYetImplemented] encryption is not implemented yet
420
416
  # @raise [Service::NotFound, Characteristic::NotFound] if service/characteristic doesn't exist on this device
421
417
  # @raise [AccessUnavailable] if not available for writing
422
418
  # @return [void]
423
419
  def []=(service, characteristic, val, raw: false)
424
- raise NotConnected unless is_connected?
425
- uuid = _uuid_characteristic(characteristic)
426
- chars = _characteristics(service)
427
- raise ServiceNotFound, service if chars.nil?
428
- char = chars[uuid]
429
- raise CharacteristicNotFound, characteristic if char.nil?
430
- flags = char[:flags]
431
- obj = char[:obj]
432
- info = Characteristic[uuid]
433
-
434
- if flags.include?('write') ||
435
- flags.include?('write-without-response')
436
- if !raw && info
437
- if info[:vrfy] && !info[:vrfy].call(vall)
438
- raise ArgumentError,
439
- "bad value for characteristic '#{characteristic}'"
440
- end
441
- val = info[:out].call(val) if info[:out]
442
- end
443
- val = val.unpack('C*')
444
- obj[I_GATT_CHARACTERISTIC].WriteValue(val)
445
- elsif flags.include?('encrypt-write') ||
446
- flags.include?('encrypt-authenticated-write')
447
- raise NotYetImplemented
448
- else
449
- raise AccessUnavailable
450
- end
451
- nil
420
+ _require_connection!
421
+ uuid = _uuid_characteristic(characteristic)
422
+ chars = _characteristics(service)
423
+ raise ServiceNotFound, service if chars.nil?
424
+ char = chars[uuid]
425
+ raise CharacteristicNotFound, characteristic if char.nil?
426
+
427
+ if char.flag?('write') ||
428
+ char.flag?('write-without-response')
429
+ char.write(val, raw: raw)
430
+ elsif char.flag?('encrypt-write') ||
431
+ char.flag?('encrypt-authenticated-write')
432
+ raise NotYetImplemented
433
+ else
434
+ raise AccessUnavailable
435
+ end
436
+ nil
452
437
  end
453
438
 
439
+ #---------------------------------
454
440
  private
441
+ #---------------------------------
442
+ def _require_connection!
443
+ raise NotConnected unless is_connected?
444
+ end
455
445
 
446
+ def _find_characteristic(service_id, char_id)
447
+ uuid= _uuid_characteristic(char_id)
448
+ chars= _characteristics(service_id)
449
+ raise Service::NotFound, service_id if chars.nil?
450
+ char= chars[uuid]
451
+ raise Characteristic::NotFound, char_id if char.nil?
452
+ char
453
+ end
454
+
455
+ # @param service [String, Symbol] The id of the service.
456
+ # @return [Hash] The descriptions of the characteristics for the given service.
456
457
  def _characteristics(service)
457
- if srv = @services[_uuid_service(service)]
458
- srv[:characteristics]
459
- end
458
+ if srv = @services[_uuid_service(service)]
459
+ srv[:characteristics]
460
+ end
460
461
  end
461
462
  def _uuid_service(service)
462
- uuid = case service
463
- when UUID::REGEX
464
- service.downcase
465
- else
466
- if i = Service[service]
467
- i[:uuid]
468
- end
469
- end
470
- if uuid.nil?
471
- raise ArgumentError, "unable to get UUID for service"
463
+ uuid = case service
464
+ when UUID::REGEX
465
+ service.downcase
466
+ else
467
+ if i = Service[service]
468
+ i[:uuid]
472
469
  end
470
+ end
471
+ if uuid.nil?
472
+ raise ArgumentError, "unable to get UUID for service"
473
+ end
473
474
 
474
- uuid
475
+ uuid
475
476
  end
476
477
  def _uuid_characteristic(characteristic)
477
- uuid = case characteristic
478
- when UUID::REGEX
479
- characteristic.downcase
480
- else
481
- if i = Characteristic[characteristic]
482
- i[:uuid]
483
- end
484
- end
485
- if uuid.nil?
486
- raise ArgumentError, "unable to get UUID for service"
487
- end
488
-
489
- uuid
478
+ uuid = case characteristic
479
+ when UUID::REGEX
480
+ characteristic.downcase
481
+ else
482
+ if char = Characteristic[characteristic]
483
+ char.uuid
484
+ end
485
+ end
486
+ if uuid.nil?
487
+ raise ArgumentError, "unable to get UUID for characteristic"
488
+ end
489
+
490
+ uuid
490
491
  end
491
- end
492
+
493
+ end
492
494
  end