bumble 0.0.207__py3-none-any.whl → 0.0.208__py3-none-any.whl

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.
bumble/pandora/host.py CHANGED
@@ -371,9 +371,7 @@ class HostService(HostServicer):
371
371
  scan_response_data=scan_response_data,
372
372
  )
373
373
 
374
- pending_connection: asyncio.Future[bumble.device.Connection] = (
375
- asyncio.get_running_loop().create_future()
376
- )
374
+ connections: asyncio.Queue[bumble.device.Connection] = asyncio.Queue()
377
375
 
378
376
  if request.connectable:
379
377
 
@@ -382,7 +380,7 @@ class HostService(HostServicer):
382
380
  connection.transport == BT_LE_TRANSPORT
383
381
  and connection.role == BT_PERIPHERAL_ROLE
384
382
  ):
385
- pending_connection.set_result(connection)
383
+ connections.put_nowait(connection)
386
384
 
387
385
  self.device.on('connection', on_connection)
388
386
 
@@ -397,8 +395,7 @@ class HostService(HostServicer):
397
395
  await asyncio.sleep(1)
398
396
  continue
399
397
 
400
- connection = await pending_connection
401
- pending_connection = asyncio.get_running_loop().create_future()
398
+ connection = await connections.get()
402
399
 
403
400
  cookie = any_pb2.Any(value=connection.handle.to_bytes(4, 'big'))
404
401
  yield AdvertiseResponse(connection=Connection(cookie=cookie))
@@ -492,6 +489,8 @@ class HostService(HostServicer):
492
489
  target = Address(target_bytes, Address.RANDOM_DEVICE_ADDRESS)
493
490
  advertising_type = AdvertisingType.DIRECTED_CONNECTABLE_LOW_DUTY
494
491
 
492
+ connections: asyncio.Queue[bumble.device.Connection] = asyncio.Queue()
493
+
495
494
  if request.connectable:
496
495
 
497
496
  def on_connection(connection: bumble.device.Connection) -> None:
@@ -499,7 +498,7 @@ class HostService(HostServicer):
499
498
  connection.transport == BT_LE_TRANSPORT
500
499
  and connection.role == BT_PERIPHERAL_ROLE
501
500
  ):
502
- pending_connection.set_result(connection)
501
+ connections.put_nowait(connection)
503
502
 
504
503
  self.device.on('connection', on_connection)
505
504
 
@@ -517,12 +516,8 @@ class HostService(HostServicer):
517
516
  await asyncio.sleep(1)
518
517
  continue
519
518
 
520
- pending_connection: asyncio.Future[bumble.device.Connection] = (
521
- asyncio.get_running_loop().create_future()
522
- )
523
-
524
519
  self.log.debug('Wait for LE connection...')
525
- connection = await pending_connection
520
+ connection = await connections.get()
526
521
 
527
522
  self.log.debug(
528
523
  f"Advertise: Connected to {connection.peer_address} (handle={connection.handle})"
bumble/profiles/aics.py CHANGED
@@ -24,16 +24,13 @@ import struct
24
24
  from dataclasses import dataclass
25
25
  from typing import Optional
26
26
 
27
- from bumble import gatt
28
27
  from bumble.device import Connection
29
28
  from bumble.att import ATT_Error
30
29
  from bumble.gatt import (
30
+ Attribute,
31
31
  Characteristic,
32
- SerializableCharacteristicAdapter,
33
- PackedCharacteristicAdapter,
34
32
  TemplateService,
35
33
  CharacteristicValue,
36
- UTF8CharacteristicAdapter,
37
34
  GATT_AUDIO_INPUT_CONTROL_SERVICE,
38
35
  GATT_AUDIO_INPUT_STATE_CHARACTERISTIC,
39
36
  GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC,
@@ -42,6 +39,14 @@ from bumble.gatt import (
42
39
  GATT_AUDIO_INPUT_CONTROL_POINT_CHARACTERISTIC,
43
40
  GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC,
44
41
  )
42
+ from bumble.gatt_adapters import (
43
+ CharacteristicProxy,
44
+ PackedCharacteristicProxyAdapter,
45
+ SerializableCharacteristicAdapter,
46
+ SerializableCharacteristicProxyAdapter,
47
+ UTF8CharacteristicAdapter,
48
+ UTF8CharacteristicProxyAdapter,
49
+ )
45
50
  from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
46
51
  from bumble.utils import OpenIntEnum
47
52
 
@@ -124,7 +129,7 @@ class AudioInputState:
124
129
  mute: Mute = Mute.NOT_MUTED
125
130
  gain_mode: GainMode = GainMode.MANUAL
126
131
  change_counter: int = 0
127
- attribute_value: Optional[CharacteristicValue] = None
132
+ attribute: Optional[Attribute] = None
128
133
 
129
134
  def __bytes__(self) -> bytes:
130
135
  return bytes(
@@ -151,10 +156,8 @@ class AudioInputState:
151
156
  self.change_counter = (self.change_counter + 1) % (CHANGE_COUNTER_MAX_VALUE + 1)
152
157
 
153
158
  async def notify_subscribers_via_connection(self, connection: Connection) -> None:
154
- assert self.attribute_value is not None
155
- await connection.device.notify_subscribers(
156
- attribute=self.attribute_value, value=bytes(self)
157
- )
159
+ assert self.attribute is not None
160
+ await connection.device.notify_subscribers(attribute=self.attribute)
158
161
 
159
162
 
160
163
  @dataclass
@@ -315,24 +318,28 @@ class AudioInputDescription:
315
318
  '''
316
319
 
317
320
  audio_input_description: str = "Bluetooth"
318
- attribute_value: Optional[CharacteristicValue] = None
321
+ attribute: Optional[Attribute] = None
319
322
 
320
323
  def on_read(self, _connection: Optional[Connection]) -> str:
321
324
  return self.audio_input_description
322
325
 
323
326
  async def on_write(self, connection: Optional[Connection], value: str) -> None:
324
327
  assert connection
325
- assert self.attribute_value
328
+ assert self.attribute
326
329
 
327
330
  self.audio_input_description = value
328
- await connection.device.notify_subscribers(
329
- attribute=self.attribute_value, value=value
330
- )
331
+ await connection.device.notify_subscribers(attribute=self.attribute)
331
332
 
332
333
 
333
334
  class AICSService(TemplateService):
334
335
  UUID = GATT_AUDIO_INPUT_CONTROL_SERVICE
335
336
 
337
+ audio_input_state_characteristic: Characteristic[AudioInputState]
338
+ audio_input_type_characteristic: Characteristic[bytes]
339
+ audio_input_status_characteristic: Characteristic[bytes]
340
+ audio_input_control_point_characteristic: Characteristic[bytes]
341
+ gain_settings_properties_characteristic: Characteristic[GainSettingsProperties]
342
+
336
343
  def __init__(
337
344
  self,
338
345
  audio_input_state: Optional[AudioInputState] = None,
@@ -374,9 +381,7 @@ class AICSService(TemplateService):
374
381
  ),
375
382
  AudioInputState,
376
383
  )
377
- self.audio_input_state.attribute_value = (
378
- self.audio_input_state_characteristic.value
379
- )
384
+ self.audio_input_state.attribute = self.audio_input_state_characteristic
380
385
 
381
386
  self.gain_settings_properties_characteristic = (
382
387
  SerializableCharacteristicAdapter(
@@ -425,8 +430,8 @@ class AICSService(TemplateService):
425
430
  ),
426
431
  )
427
432
  )
428
- self.audio_input_description.attribute_value = (
429
- self.audio_input_control_point_characteristic.value
433
+ self.audio_input_description.attribute = (
434
+ self.audio_input_control_point_characteristic
430
435
  )
431
436
 
432
437
  super().__init__(
@@ -448,24 +453,29 @@ class AICSService(TemplateService):
448
453
  class AICSServiceProxy(ProfileServiceProxy):
449
454
  SERVICE_CLASS = AICSService
450
455
 
456
+ audio_input_state: CharacteristicProxy[AudioInputState]
457
+ gain_settings_properties: CharacteristicProxy[GainSettingsProperties]
458
+ audio_input_status: CharacteristicProxy[int]
459
+ audio_input_control_point: CharacteristicProxy[bytes]
460
+
451
461
  def __init__(self, service_proxy: ServiceProxy) -> None:
452
462
  self.service_proxy = service_proxy
453
463
 
454
- self.audio_input_state = SerializableCharacteristicAdapter(
464
+ self.audio_input_state = SerializableCharacteristicProxyAdapter(
455
465
  service_proxy.get_required_characteristic_by_uuid(
456
466
  GATT_AUDIO_INPUT_STATE_CHARACTERISTIC
457
467
  ),
458
468
  AudioInputState,
459
469
  )
460
470
 
461
- self.gain_settings_properties = SerializableCharacteristicAdapter(
471
+ self.gain_settings_properties = SerializableCharacteristicProxyAdapter(
462
472
  service_proxy.get_required_characteristic_by_uuid(
463
473
  GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC
464
474
  ),
465
475
  GainSettingsProperties,
466
476
  )
467
477
 
468
- self.audio_input_status = PackedCharacteristicAdapter(
478
+ self.audio_input_status = PackedCharacteristicProxyAdapter(
469
479
  service_proxy.get_required_characteristic_by_uuid(
470
480
  GATT_AUDIO_INPUT_STATUS_CHARACTERISTIC
471
481
  ),
@@ -478,7 +488,7 @@ class AICSServiceProxy(ProfileServiceProxy):
478
488
  )
479
489
  )
480
490
 
481
- self.audio_input_description = UTF8CharacteristicAdapter(
491
+ self.audio_input_description = UTF8CharacteristicProxyAdapter(
482
492
  service_proxy.get_required_characteristic_by_uuid(
483
493
  GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC
484
494
  )
bumble/profiles/ascs.py CHANGED
@@ -301,7 +301,7 @@ class AseStateMachine(gatt.Characteristic):
301
301
  presentation_delay = 0
302
302
 
303
303
  # Additional parameters in ENABLING, STREAMING, DISABLING State
304
- metadata = le_audio.Metadata()
304
+ metadata: le_audio.Metadata
305
305
 
306
306
  def __init__(
307
307
  self,
@@ -313,6 +313,7 @@ class AseStateMachine(gatt.Characteristic):
313
313
  self.ase_id = ase_id
314
314
  self._state = AseStateMachine.State.IDLE
315
315
  self.role = role
316
+ self.metadata = le_audio.Metadata()
316
317
 
317
318
  uuid = (
318
319
  gatt.GATT_SINK_ASE_CHARACTERISTIC
bumble/profiles/asha.py CHANGED
@@ -134,12 +134,14 @@ class AshaService(gatt.TemplateService):
134
134
  ),
135
135
  )
136
136
 
137
- self.audio_control_point_characteristic = gatt.Characteristic(
138
- gatt.GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC,
139
- gatt.Characteristic.Properties.WRITE
140
- | gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
141
- gatt.Characteristic.WRITEABLE,
142
- gatt.CharacteristicValue(write=self._on_audio_control_point_write),
137
+ self.audio_control_point_characteristic: gatt.Characteristic[bytes] = (
138
+ gatt.Characteristic(
139
+ gatt.GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC,
140
+ gatt.Characteristic.Properties.WRITE
141
+ | gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
142
+ gatt.Characteristic.WRITEABLE,
143
+ gatt.CharacteristicValue(write=self._on_audio_control_point_write),
144
+ )
143
145
  )
144
146
  self.audio_status_characteristic = gatt.Characteristic(
145
147
  gatt.GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC,
@@ -147,7 +149,7 @@ class AshaService(gatt.TemplateService):
147
149
  gatt.Characteristic.READABLE,
148
150
  bytes([AudioStatus.OK]),
149
151
  )
150
- self.volume_characteristic = gatt.Characteristic(
152
+ self.volume_characteristic: gatt.Characteristic[bytes] = gatt.Characteristic(
151
153
  gatt.GATT_ASHA_VOLUME_CHARACTERISTIC,
152
154
  gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
153
155
  gatt.Characteristic.WRITEABLE,
@@ -166,13 +168,13 @@ class AshaService(gatt.TemplateService):
166
168
  struct.pack('<H', self.psm),
167
169
  )
168
170
 
169
- characteristics = [
171
+ characteristics = (
170
172
  self.read_only_properties_characteristic,
171
173
  self.audio_control_point_characteristic,
172
174
  self.audio_status_characteristic,
173
175
  self.volume_characteristic,
174
176
  self.le_psm_out_characteristic,
175
- ]
177
+ )
176
178
 
177
179
  super().__init__(characteristics)
178
180
 
bumble/profiles/bass.py CHANGED
@@ -20,11 +20,12 @@ from __future__ import annotations
20
20
  import dataclasses
21
21
  import logging
22
22
  import struct
23
- from typing import ClassVar, List, Optional, Sequence
23
+ from typing import ClassVar, Optional, Sequence
24
24
 
25
25
  from bumble import core
26
26
  from bumble import device
27
27
  from bumble import gatt
28
+ from bumble import gatt_adapters
28
29
  from bumble import gatt_client
29
30
  from bumble import hci
30
31
  from bumble import utils
@@ -52,7 +53,7 @@ def encode_subgroups(subgroups: Sequence[SubgroupInfo]) -> bytes:
52
53
  )
53
54
 
54
55
 
55
- def decode_subgroups(data: bytes) -> List[SubgroupInfo]:
56
+ def decode_subgroups(data: bytes) -> list[SubgroupInfo]:
56
57
  num_subgroups = data[0]
57
58
  offset = 1
58
59
  subgroups = []
@@ -273,7 +274,7 @@ class BroadcastReceiveState:
273
274
  pa_sync_state: PeriodicAdvertisingSyncState
274
275
  big_encryption: BigEncryption
275
276
  bad_code: bytes
276
- subgroups: List[SubgroupInfo]
277
+ subgroups: list[SubgroupInfo]
277
278
 
278
279
  @classmethod
279
280
  def from_bytes(cls, data: bytes) -> BroadcastReceiveState:
@@ -354,7 +355,9 @@ class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
354
355
  SERVICE_CLASS = BroadcastAudioScanService
355
356
 
356
357
  broadcast_audio_scan_control_point: gatt_client.CharacteristicProxy
357
- broadcast_receive_states: List[gatt.DelegatedCharacteristicAdapter]
358
+ broadcast_receive_states: list[
359
+ gatt_client.CharacteristicProxy[Optional[BroadcastReceiveState]]
360
+ ]
358
361
 
359
362
  def __init__(self, service_proxy: gatt_client.ServiceProxy):
360
363
  self.service_proxy = service_proxy
@@ -366,7 +369,7 @@ class BroadcastAudioScanServiceProxy(gatt_client.ProfileServiceProxy):
366
369
  )
367
370
 
368
371
  self.broadcast_receive_states = [
369
- gatt.DelegatedCharacteristicAdapter(
372
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
370
373
  characteristic,
371
374
  decode=lambda x: BroadcastReceiveState.from_bytes(x) if x else None,
372
375
  )
@@ -16,14 +16,20 @@
16
16
  # -----------------------------------------------------------------------------
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
- from ..gatt_client import ProfileServiceProxy
20
- from ..gatt import (
19
+ from typing import Optional
20
+
21
+ from bumble.gatt_client import ProfileServiceProxy
22
+ from bumble.gatt import (
21
23
  GATT_BATTERY_SERVICE,
22
24
  GATT_BATTERY_LEVEL_CHARACTERISTIC,
23
25
  TemplateService,
24
26
  Characteristic,
25
27
  CharacteristicValue,
28
+ )
29
+ from bumble.gatt_client import CharacteristicProxy
30
+ from bumble.gatt_adapters import (
26
31
  PackedCharacteristicAdapter,
32
+ PackedCharacteristicProxyAdapter,
27
33
  )
28
34
 
29
35
 
@@ -32,6 +38,8 @@ class BatteryService(TemplateService):
32
38
  UUID = GATT_BATTERY_SERVICE
33
39
  BATTERY_LEVEL_FORMAT = 'B'
34
40
 
41
+ battery_level_characteristic: Characteristic[int]
42
+
35
43
  def __init__(self, read_battery_level):
36
44
  self.battery_level_characteristic = PackedCharacteristicAdapter(
37
45
  Characteristic(
@@ -49,13 +57,15 @@ class BatteryService(TemplateService):
49
57
  class BatteryServiceProxy(ProfileServiceProxy):
50
58
  SERVICE_CLASS = BatteryService
51
59
 
60
+ battery_level: Optional[CharacteristicProxy[int]]
61
+
52
62
  def __init__(self, service_proxy):
53
63
  self.service_proxy = service_proxy
54
64
 
55
65
  if characteristics := service_proxy.get_characteristics_by_uuid(
56
66
  GATT_BATTERY_LEVEL_CHARACTERISTIC
57
67
  ):
58
- self.battery_level = PackedCharacteristicAdapter(
68
+ self.battery_level = PackedCharacteristicProxyAdapter(
59
69
  characteristics[0], pack_format=BatteryService.BATTERY_LEVEL_FORMAT
60
70
  )
61
71
  else:
@@ -19,7 +19,6 @@
19
19
  import struct
20
20
  from typing import Optional, Tuple
21
21
 
22
- from bumble.gatt_client import ServiceProxy, ProfileServiceProxy, CharacteristicProxy
23
22
  from bumble.gatt import (
24
23
  GATT_DEVICE_INFORMATION_SERVICE,
25
24
  GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC,
@@ -32,9 +31,12 @@ from bumble.gatt import (
32
31
  GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
33
32
  TemplateService,
34
33
  Characteristic,
35
- DelegatedCharacteristicAdapter,
36
- UTF8CharacteristicAdapter,
37
34
  )
35
+ from bumble.gatt_adapters import (
36
+ DelegatedCharacteristicProxyAdapter,
37
+ UTF8CharacteristicProxyAdapter,
38
+ )
39
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
38
40
 
39
41
 
40
42
  # -----------------------------------------------------------------------------
@@ -62,7 +64,7 @@ class DeviceInformationService(TemplateService):
62
64
  ieee_regulatory_certification_data_list: Optional[bytes] = None,
63
65
  # TODO: pnp_id
64
66
  ):
65
- characteristics = [
67
+ characteristics: list[Characteristic[bytes]] = [
66
68
  Characteristic(
67
69
  uuid,
68
70
  Characteristic.Properties.READ,
@@ -107,14 +109,14 @@ class DeviceInformationService(TemplateService):
107
109
  class DeviceInformationServiceProxy(ProfileServiceProxy):
108
110
  SERVICE_CLASS = DeviceInformationService
109
111
 
110
- manufacturer_name: Optional[UTF8CharacteristicAdapter]
111
- model_number: Optional[UTF8CharacteristicAdapter]
112
- serial_number: Optional[UTF8CharacteristicAdapter]
113
- hardware_revision: Optional[UTF8CharacteristicAdapter]
114
- firmware_revision: Optional[UTF8CharacteristicAdapter]
115
- software_revision: Optional[UTF8CharacteristicAdapter]
116
- system_id: Optional[DelegatedCharacteristicAdapter]
117
- ieee_regulatory_certification_data_list: Optional[CharacteristicProxy]
112
+ manufacturer_name: Optional[CharacteristicProxy[str]]
113
+ model_number: Optional[CharacteristicProxy[str]]
114
+ serial_number: Optional[CharacteristicProxy[str]]
115
+ hardware_revision: Optional[CharacteristicProxy[str]]
116
+ firmware_revision: Optional[CharacteristicProxy[str]]
117
+ software_revision: Optional[CharacteristicProxy[str]]
118
+ system_id: Optional[CharacteristicProxy[tuple[int, int]]]
119
+ ieee_regulatory_certification_data_list: Optional[CharacteristicProxy[bytes]]
118
120
 
119
121
  def __init__(self, service_proxy: ServiceProxy):
120
122
  self.service_proxy = service_proxy
@@ -128,7 +130,7 @@ class DeviceInformationServiceProxy(ProfileServiceProxy):
128
130
  ('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC),
129
131
  ):
130
132
  if characteristics := service_proxy.get_characteristics_by_uuid(uuid):
131
- characteristic = UTF8CharacteristicAdapter(characteristics[0])
133
+ characteristic = UTF8CharacteristicProxyAdapter(characteristics[0])
132
134
  else:
133
135
  characteristic = None
134
136
  self.__setattr__(field, characteristic)
@@ -136,7 +138,7 @@ class DeviceInformationServiceProxy(ProfileServiceProxy):
136
138
  if characteristics := service_proxy.get_characteristics_by_uuid(
137
139
  GATT_SYSTEM_ID_CHARACTERISTIC
138
140
  ):
139
- self.system_id = DelegatedCharacteristicAdapter(
141
+ self.system_id = DelegatedCharacteristicProxyAdapter(
140
142
  characteristics[0],
141
143
  encode=lambda v: DeviceInformationService.pack_system_id(*v),
142
144
  decode=DeviceInformationService.unpack_system_id,
bumble/profiles/gap.py CHANGED
@@ -25,14 +25,15 @@ from bumble.core import Appearance
25
25
  from bumble.gatt import (
26
26
  TemplateService,
27
27
  Characteristic,
28
- CharacteristicAdapter,
29
- DelegatedCharacteristicAdapter,
30
- UTF8CharacteristicAdapter,
31
28
  GATT_GENERIC_ACCESS_SERVICE,
32
29
  GATT_DEVICE_NAME_CHARACTERISTIC,
33
30
  GATT_APPEARANCE_CHARACTERISTIC,
34
31
  )
35
- from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
32
+ from bumble.gatt_adapters import (
33
+ DelegatedCharacteristicProxyAdapter,
34
+ UTF8CharacteristicProxyAdapter,
35
+ )
36
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
36
37
 
37
38
  # -----------------------------------------------------------------------------
38
39
  # Logging
@@ -49,6 +50,9 @@ logger = logging.getLogger(__name__)
49
50
  class GenericAccessService(TemplateService):
50
51
  UUID = GATT_GENERIC_ACCESS_SERVICE
51
52
 
53
+ device_name_characteristic: Characteristic[bytes]
54
+ appearance_characteristic: Characteristic[bytes]
55
+
52
56
  def __init__(
53
57
  self, device_name: str, appearance: Union[Appearance, Tuple[int, int], int] = 0
54
58
  ):
@@ -84,8 +88,8 @@ class GenericAccessService(TemplateService):
84
88
  class GenericAccessServiceProxy(ProfileServiceProxy):
85
89
  SERVICE_CLASS = GenericAccessService
86
90
 
87
- device_name: Optional[CharacteristicAdapter]
88
- appearance: Optional[DelegatedCharacteristicAdapter]
91
+ device_name: Optional[CharacteristicProxy[str]]
92
+ appearance: Optional[CharacteristicProxy[Appearance]]
89
93
 
90
94
  def __init__(self, service_proxy: ServiceProxy):
91
95
  self.service_proxy = service_proxy
@@ -93,14 +97,14 @@ class GenericAccessServiceProxy(ProfileServiceProxy):
93
97
  if characteristics := service_proxy.get_characteristics_by_uuid(
94
98
  GATT_DEVICE_NAME_CHARACTERISTIC
95
99
  ):
96
- self.device_name = UTF8CharacteristicAdapter(characteristics[0])
100
+ self.device_name = UTF8CharacteristicProxyAdapter(characteristics[0])
97
101
  else:
98
102
  self.device_name = None
99
103
 
100
104
  if characteristics := service_proxy.get_characteristics_by_uuid(
101
105
  GATT_APPEARANCE_CHARACTERISTIC
102
106
  ):
103
- self.appearance = DelegatedCharacteristicAdapter(
107
+ self.appearance = DelegatedCharacteristicProxyAdapter(
104
108
  characteristics[0],
105
109
  decode=lambda value: Appearance.from_int(
106
110
  struct.unpack_from('<H', value, 0)[0],
@@ -110,6 +110,7 @@ class GenericAttributeProfileService(gatt.TemplateService):
110
110
  gatt.GATT_CHARACTERISTIC_ATTRIBUTE_TYPE,
111
111
  gatt.GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR,
112
112
  ):
113
+ assert isinstance(attribute.value, bytes)
113
114
  return (
114
115
  struct.pack("<H", attribute.handle)
115
116
  + attribute.type.to_bytes()
bumble/profiles/gmap.py CHANGED
@@ -22,7 +22,6 @@ from typing import Optional
22
22
 
23
23
  from bumble.gatt import (
24
24
  TemplateService,
25
- DelegatedCharacteristicAdapter,
26
25
  Characteristic,
27
26
  GATT_GAMING_AUDIO_SERVICE,
28
27
  GATT_GMAP_ROLE_CHARACTERISTIC,
@@ -31,7 +30,8 @@ from bumble.gatt import (
31
30
  GATT_BGS_FEATURES_CHARACTERISTIC,
32
31
  GATT_BGR_FEATURES_CHARACTERISTIC,
33
32
  )
34
- from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
33
+ from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
34
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
35
35
  from enum import IntFlag
36
36
 
37
37
 
@@ -150,10 +150,15 @@ class GamingAudioService(TemplateService):
150
150
  class GamingAudioServiceProxy(ProfileServiceProxy):
151
151
  SERVICE_CLASS = GamingAudioService
152
152
 
153
+ ugg_features: Optional[CharacteristicProxy[UggFeatures]] = None
154
+ ugt_features: Optional[CharacteristicProxy[UgtFeatures]] = None
155
+ bgs_features: Optional[CharacteristicProxy[BgsFeatures]] = None
156
+ bgr_features: Optional[CharacteristicProxy[BgrFeatures]] = None
157
+
153
158
  def __init__(self, service_proxy: ServiceProxy) -> None:
154
159
  self.service_proxy = service_proxy
155
160
 
156
- self.gmap_role = DelegatedCharacteristicAdapter(
161
+ self.gmap_role = DelegatedCharacteristicProxyAdapter(
157
162
  service_proxy.get_required_characteristic_by_uuid(
158
163
  GATT_GMAP_ROLE_CHARACTERISTIC
159
164
  ),
@@ -163,31 +168,31 @@ class GamingAudioServiceProxy(ProfileServiceProxy):
163
168
  if characteristics := service_proxy.get_characteristics_by_uuid(
164
169
  GATT_UGG_FEATURES_CHARACTERISTIC
165
170
  ):
166
- self.ugg_features = DelegatedCharacteristicAdapter(
167
- characteristic=characteristics[0],
171
+ self.ugg_features = DelegatedCharacteristicProxyAdapter(
172
+ characteristics[0],
168
173
  decode=lambda value: UggFeatures(value[0]),
169
174
  )
170
175
 
171
176
  if characteristics := service_proxy.get_characteristics_by_uuid(
172
177
  GATT_UGT_FEATURES_CHARACTERISTIC
173
178
  ):
174
- self.ugt_features = DelegatedCharacteristicAdapter(
175
- characteristic=characteristics[0],
179
+ self.ugt_features = DelegatedCharacteristicProxyAdapter(
180
+ characteristics[0],
176
181
  decode=lambda value: UgtFeatures(value[0]),
177
182
  )
178
183
 
179
184
  if characteristics := service_proxy.get_characteristics_by_uuid(
180
185
  GATT_BGS_FEATURES_CHARACTERISTIC
181
186
  ):
182
- self.bgs_features = DelegatedCharacteristicAdapter(
183
- characteristic=characteristics[0],
187
+ self.bgs_features = DelegatedCharacteristicProxyAdapter(
188
+ characteristics[0],
184
189
  decode=lambda value: BgsFeatures(value[0]),
185
190
  )
186
191
 
187
192
  if characteristics := service_proxy.get_characteristics_by_uuid(
188
193
  GATT_BGR_FEATURES_CHARACTERISTIC
189
194
  ):
190
- self.bgr_features = DelegatedCharacteristicAdapter(
191
- characteristic=characteristics[0],
195
+ self.bgr_features = DelegatedCharacteristicProxyAdapter(
196
+ characteristics[0],
192
197
  decode=lambda value: BgrFeatures(value[0]),
193
198
  )
bumble/profiles/hap.py CHANGED
@@ -18,14 +18,15 @@
18
18
  from __future__ import annotations
19
19
  import asyncio
20
20
  import functools
21
- from bumble import att, gatt, gatt_client
21
+ from dataclasses import dataclass, field
22
+ import logging
23
+ from typing import Any, Dict, List, Optional, Set, Union
24
+
25
+ from bumble import att, gatt, gatt_adapters, gatt_client
22
26
  from bumble.core import InvalidArgumentError, InvalidStateError
23
27
  from bumble.device import Device, Connection
24
28
  from bumble.utils import AsyncRunner, OpenIntEnum
25
29
  from bumble.hci import Address
26
- from dataclasses import dataclass, field
27
- import logging
28
- from typing import Any, Dict, List, Optional, Set, Union
29
30
 
30
31
 
31
32
  # -----------------------------------------------------------------------------
@@ -631,11 +632,12 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
631
632
 
632
633
  hearing_aid_preset_control_point: gatt_client.CharacteristicProxy
633
634
  preset_control_point_indications: asyncio.Queue
635
+ active_preset_index_notification: asyncio.Queue
634
636
 
635
637
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
636
638
  self.service_proxy = service_proxy
637
639
 
638
- self.server_features = gatt.PackedCharacteristicAdapter(
640
+ self.server_features = gatt_adapters.PackedCharacteristicProxyAdapter(
639
641
  service_proxy.get_characteristics_by_uuid(
640
642
  gatt.GATT_HEARING_AID_FEATURES_CHARACTERISTIC
641
643
  )[0],
@@ -648,7 +650,7 @@ class HearingAccessServiceProxy(gatt_client.ProfileServiceProxy):
648
650
  )[0]
649
651
  )
650
652
 
651
- self.active_preset_index = gatt.PackedCharacteristicAdapter(
653
+ self.active_preset_index = gatt_adapters.PackedCharacteristicProxyAdapter(
652
654
  service_proxy.get_characteristics_by_uuid(
653
655
  gatt.GATT_ACTIVE_PRESET_INDEX_CHARACTERISTIC
654
656
  )[0],