ble 0.0.3 → 0.1.0

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.
@@ -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