bumble 0.0.207__py3-none-any.whl → 0.0.209__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.
Files changed (47) hide show
  1. bumble/_version.py +9 -4
  2. bumble/apps/auracast.py +29 -35
  3. bumble/apps/bench.py +13 -10
  4. bumble/apps/console.py +19 -12
  5. bumble/apps/gg_bridge.py +1 -1
  6. bumble/att.py +61 -39
  7. bumble/controller.py +7 -8
  8. bumble/core.py +306 -159
  9. bumble/device.py +127 -82
  10. bumble/gatt.py +25 -228
  11. bumble/gatt_adapters.py +374 -0
  12. bumble/gatt_client.py +38 -31
  13. bumble/gatt_server.py +5 -5
  14. bumble/hci.py +76 -71
  15. bumble/host.py +19 -8
  16. bumble/l2cap.py +2 -2
  17. bumble/link.py +2 -2
  18. bumble/pairing.py +5 -5
  19. bumble/pandora/host.py +19 -23
  20. bumble/pandora/security.py +2 -3
  21. bumble/pandora/utils.py +2 -2
  22. bumble/profiles/aics.py +33 -23
  23. bumble/profiles/ancs.py +514 -0
  24. bumble/profiles/ascs.py +2 -1
  25. bumble/profiles/asha.py +11 -9
  26. bumble/profiles/bass.py +8 -5
  27. bumble/profiles/battery_service.py +13 -3
  28. bumble/profiles/device_information_service.py +16 -14
  29. bumble/profiles/gap.py +12 -8
  30. bumble/profiles/gatt_service.py +1 -0
  31. bumble/profiles/gmap.py +16 -11
  32. bumble/profiles/hap.py +8 -6
  33. bumble/profiles/heart_rate_service.py +20 -4
  34. bumble/profiles/mcp.py +11 -9
  35. bumble/profiles/pacs.py +37 -24
  36. bumble/profiles/tmap.py +6 -4
  37. bumble/profiles/vcs.py +6 -5
  38. bumble/profiles/vocs.py +49 -41
  39. bumble/smp.py +3 -3
  40. bumble/transport/usb.py +1 -3
  41. bumble/utils.py +10 -0
  42. {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/METADATA +3 -3
  43. {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/RECORD +47 -45
  44. {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/WHEEL +1 -1
  45. {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/LICENSE +0 -0
  46. {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/entry_points.txt +0 -0
  47. {bumble-0.0.207.dist-info → bumble-0.0.209.dist-info}/top_level.txt +0 -0
@@ -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],
@@ -16,13 +16,14 @@
16
16
  # -----------------------------------------------------------------------------
17
17
  # Imports
18
18
  # -----------------------------------------------------------------------------
19
+ from __future__ import annotations
19
20
  from enum import IntEnum
20
21
  import struct
22
+ from typing import Optional
21
23
 
22
24
  from bumble import core
23
- from ..gatt_client import ProfileServiceProxy
24
- from ..att import ATT_Error
25
- from ..gatt import (
25
+ from bumble.att import ATT_Error
26
+ from bumble.gatt import (
26
27
  GATT_HEART_RATE_SERVICE,
27
28
  GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
28
29
  GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
@@ -30,10 +31,13 @@ from ..gatt import (
30
31
  TemplateService,
31
32
  Characteristic,
32
33
  CharacteristicValue,
33
- SerializableCharacteristicAdapter,
34
+ )
35
+ from bumble.gatt_adapters import (
34
36
  DelegatedCharacteristicAdapter,
35
37
  PackedCharacteristicAdapter,
38
+ SerializableCharacteristicAdapter,
36
39
  )
40
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy
37
41
 
38
42
 
39
43
  # -----------------------------------------------------------------------------
@@ -43,6 +47,10 @@ class HeartRateService(TemplateService):
43
47
  CONTROL_POINT_NOT_SUPPORTED = 0x80
44
48
  RESET_ENERGY_EXPENDED = 0x01
45
49
 
50
+ heart_rate_measurement_characteristic: Characteristic[HeartRateMeasurement]
51
+ body_sensor_location_characteristic: Characteristic[BodySensorLocation]
52
+ heart_rate_control_point_characteristic: Characteristic[int]
53
+
46
54
  class BodySensorLocation(IntEnum):
47
55
  OTHER = 0
48
56
  CHEST = 1
@@ -198,6 +206,14 @@ class HeartRateService(TemplateService):
198
206
  class HeartRateServiceProxy(ProfileServiceProxy):
199
207
  SERVICE_CLASS = HeartRateService
200
208
 
209
+ heart_rate_measurement: Optional[
210
+ CharacteristicProxy[HeartRateService.HeartRateMeasurement]
211
+ ]
212
+ body_sensor_location: Optional[
213
+ CharacteristicProxy[HeartRateService.BodySensorLocation]
214
+ ]
215
+ heart_rate_control_point: Optional[CharacteristicProxy[int]]
216
+
201
217
  def __init__(self, service_proxy):
202
218
  self.service_proxy = service_proxy
203
219
 
bumble/profiles/mcp.py CHANGED
@@ -208,7 +208,7 @@ class MediaControlService(gatt.TemplateService):
208
208
  properties=gatt.Characteristic.Properties.READ
209
209
  | gatt.Characteristic.Properties.NOTIFY,
210
210
  permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
211
- value=media_player_name or 'Bumble Player',
211
+ value=(media_player_name or 'Bumble Player').encode(),
212
212
  )
213
213
  self.track_changed_characteristic = gatt.Characteristic(
214
214
  uuid=gatt.GATT_TRACK_CHANGED_CHARACTERISTIC,
@@ -247,14 +247,16 @@ class MediaControlService(gatt.TemplateService):
247
247
  permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION,
248
248
  value=b'',
249
249
  )
250
- self.media_control_point_characteristic = gatt.Characteristic(
251
- uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
252
- properties=gatt.Characteristic.Properties.WRITE
253
- | gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
254
- | gatt.Characteristic.Properties.NOTIFY,
255
- permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
256
- | gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
257
- value=gatt.CharacteristicValue(write=self.on_media_control_point),
250
+ self.media_control_point_characteristic: gatt.Characteristic[bytes] = (
251
+ gatt.Characteristic(
252
+ uuid=gatt.GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC,
253
+ properties=gatt.Characteristic.Properties.WRITE
254
+ | gatt.Characteristic.Properties.WRITE_WITHOUT_RESPONSE
255
+ | gatt.Characteristic.Properties.NOTIFY,
256
+ permissions=gatt.Characteristic.Permissions.READ_REQUIRES_ENCRYPTION
257
+ | gatt.Characteristic.Permissions.WRITE_REQUIRES_ENCRYPTION,
258
+ value=gatt.CharacteristicValue(write=self.on_media_control_point),
259
+ )
258
260
  )
259
261
  self.media_control_point_opcodes_supported_characteristic = gatt.Characteristic(
260
262
  uuid=gatt.GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC,
bumble/profiles/pacs.py CHANGED
@@ -25,6 +25,7 @@ from typing import Optional, Sequence, Union
25
25
  from bumble.profiles.bap import AudioLocation, CodecSpecificCapabilities, ContextType
26
26
  from bumble.profiles import le_audio
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
 
@@ -185,34 +186,42 @@ class PublishedAudioCapabilitiesService(gatt.TemplateService):
185
186
  class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
186
187
  SERVICE_CLASS = PublishedAudioCapabilitiesService
187
188
 
188
- sink_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
189
- sink_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
190
- source_pac: Optional[gatt.DelegatedCharacteristicAdapter] = None
191
- source_audio_locations: Optional[gatt.DelegatedCharacteristicAdapter] = None
192
- available_audio_contexts: gatt.DelegatedCharacteristicAdapter
193
- supported_audio_contexts: gatt.DelegatedCharacteristicAdapter
189
+ sink_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
190
+ sink_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
191
+ None
192
+ )
193
+ source_pac: Optional[gatt_client.CharacteristicProxy[list[PacRecord]]] = None
194
+ source_audio_locations: Optional[gatt_client.CharacteristicProxy[AudioLocation]] = (
195
+ None
196
+ )
197
+ available_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
198
+ supported_audio_contexts: gatt_client.CharacteristicProxy[tuple[ContextType, ...]]
194
199
 
195
200
  def __init__(self, service_proxy: gatt_client.ServiceProxy):
196
201
  self.service_proxy = service_proxy
197
202
 
198
- self.available_audio_contexts = gatt.DelegatedCharacteristicAdapter(
199
- service_proxy.get_required_characteristic_by_uuid(
200
- gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
201
- ),
202
- decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
203
+ self.available_audio_contexts = (
204
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
205
+ service_proxy.get_required_characteristic_by_uuid(
206
+ gatt.GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC
207
+ ),
208
+ decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
209
+ )
203
210
  )
204
211
 
205
- self.supported_audio_contexts = gatt.DelegatedCharacteristicAdapter(
206
- service_proxy.get_required_characteristic_by_uuid(
207
- gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
208
- ),
209
- decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
212
+ self.supported_audio_contexts = (
213
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
214
+ service_proxy.get_required_characteristic_by_uuid(
215
+ gatt.GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC
216
+ ),
217
+ decode=lambda x: tuple(map(ContextType, struct.unpack('<HH', x))),
218
+ )
210
219
  )
211
220
 
212
221
  if characteristics := service_proxy.get_characteristics_by_uuid(
213
222
  gatt.GATT_SINK_PAC_CHARACTERISTIC
214
223
  ):
215
- self.sink_pac = gatt.DelegatedCharacteristicAdapter(
224
+ self.sink_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
216
225
  characteristics[0],
217
226
  decode=PacRecord.list_from_bytes,
218
227
  )
@@ -220,7 +229,7 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
220
229
  if characteristics := service_proxy.get_characteristics_by_uuid(
221
230
  gatt.GATT_SOURCE_PAC_CHARACTERISTIC
222
231
  ):
223
- self.source_pac = gatt.DelegatedCharacteristicAdapter(
232
+ self.source_pac = gatt_adapters.DelegatedCharacteristicProxyAdapter(
224
233
  characteristics[0],
225
234
  decode=PacRecord.list_from_bytes,
226
235
  )
@@ -228,15 +237,19 @@ class PublishedAudioCapabilitiesServiceProxy(gatt_client.ProfileServiceProxy):
228
237
  if characteristics := service_proxy.get_characteristics_by_uuid(
229
238
  gatt.GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC
230
239
  ):
231
- self.sink_audio_locations = gatt.DelegatedCharacteristicAdapter(
232
- characteristics[0],
233
- decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
240
+ self.sink_audio_locations = (
241
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
242
+ characteristics[0],
243
+ decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
244
+ )
234
245
  )
235
246
 
236
247
  if characteristics := service_proxy.get_characteristics_by_uuid(
237
248
  gatt.GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC
238
249
  ):
239
- self.source_audio_locations = gatt.DelegatedCharacteristicAdapter(
240
- characteristics[0],
241
- decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
250
+ self.source_audio_locations = (
251
+ gatt_adapters.DelegatedCharacteristicProxyAdapter(
252
+ characteristics[0],
253
+ decode=lambda x: AudioLocation(struct.unpack('<I', x)[0]),
254
+ )
242
255
  )
bumble/profiles/tmap.py CHANGED
@@ -24,11 +24,11 @@ import struct
24
24
  from bumble.gatt import (
25
25
  TemplateService,
26
26
  Characteristic,
27
- DelegatedCharacteristicAdapter,
28
27
  GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE,
29
28
  GATT_TMAP_ROLE_CHARACTERISTIC,
30
29
  )
31
- from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
30
+ from bumble.gatt_adapters import DelegatedCharacteristicProxyAdapter
31
+ from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy
32
32
 
33
33
 
34
34
  # -----------------------------------------------------------------------------
@@ -53,6 +53,8 @@ class Role(enum.IntFlag):
53
53
  class TelephonyAndMediaAudioService(TemplateService):
54
54
  UUID = GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE
55
55
 
56
+ role_characteristic: Characteristic[bytes]
57
+
56
58
  def __init__(self, role: Role):
57
59
  self.role_characteristic = Characteristic(
58
60
  GATT_TMAP_ROLE_CHARACTERISTIC,
@@ -68,12 +70,12 @@ class TelephonyAndMediaAudioService(TemplateService):
68
70
  class TelephonyAndMediaAudioServiceProxy(ProfileServiceProxy):
69
71
  SERVICE_CLASS = TelephonyAndMediaAudioService
70
72
 
71
- role: DelegatedCharacteristicAdapter
73
+ role: CharacteristicProxy[Role]
72
74
 
73
75
  def __init__(self, service_proxy: ServiceProxy):
74
76
  self.service_proxy = service_proxy
75
77
 
76
- self.role = DelegatedCharacteristicAdapter(
78
+ self.role = DelegatedCharacteristicProxyAdapter(
77
79
  service_proxy.get_required_characteristic_by_uuid(
78
80
  GATT_TMAP_ROLE_CHARACTERISTIC
79
81
  ),
bumble/profiles/vcs.py CHANGED
@@ -25,6 +25,7 @@ from typing import Optional, Sequence
25
25
  from bumble import att
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
 
30
31
 
@@ -209,14 +210,14 @@ class VolumeControlService(gatt.TemplateService):
209
210
  class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
210
211
  SERVICE_CLASS = VolumeControlService
211
212
 
212
- volume_control_point: gatt_client.CharacteristicProxy
213
- volume_state: gatt.SerializableCharacteristicAdapter
214
- volume_flags: gatt.DelegatedCharacteristicAdapter
213
+ volume_control_point: gatt_client.CharacteristicProxy[bytes]
214
+ volume_state: gatt_client.CharacteristicProxy[VolumeState]
215
+ volume_flags: gatt_client.CharacteristicProxy[VolumeFlags]
215
216
 
216
217
  def __init__(self, service_proxy: gatt_client.ServiceProxy) -> None:
217
218
  self.service_proxy = service_proxy
218
219
 
219
- self.volume_state = gatt.SerializableCharacteristicAdapter(
220
+ self.volume_state = gatt_adapters.SerializableCharacteristicProxyAdapter(
220
221
  service_proxy.get_required_characteristic_by_uuid(
221
222
  gatt.GATT_VOLUME_STATE_CHARACTERISTIC
222
223
  ),
@@ -227,7 +228,7 @@ class VolumeControlServiceProxy(gatt_client.ProfileServiceProxy):
227
228
  gatt.GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC
228
229
  )
229
230
 
230
- self.volume_flags = gatt.DelegatedCharacteristicAdapter(
231
+ self.volume_flags = gatt_adapters.DelegatedCharacteristicProxyAdapter(
231
232
  service_proxy.get_required_characteristic_by_uuid(
232
233
  gatt.GATT_VOLUME_FLAGS_CHARACTERISTIC
233
234
  ),