bumble 0.0.203__py3-none-any.whl → 0.0.207__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 (50) hide show
  1. bumble/_version.py +2 -2
  2. bumble/apps/auracast.py +626 -87
  3. bumble/apps/bench.py +227 -148
  4. bumble/apps/controller_info.py +23 -7
  5. bumble/apps/device_info.py +50 -4
  6. bumble/apps/lea_unicast/app.py +61 -201
  7. bumble/apps/pair.py +13 -8
  8. bumble/apps/show.py +6 -6
  9. bumble/att.py +10 -11
  10. bumble/audio/__init__.py +17 -0
  11. bumble/audio/io.py +553 -0
  12. bumble/controller.py +24 -9
  13. bumble/core.py +4 -1
  14. bumble/device.py +993 -48
  15. bumble/drivers/common.py +2 -0
  16. bumble/drivers/intel.py +593 -24
  17. bumble/gatt.py +67 -12
  18. bumble/gatt_client.py +14 -2
  19. bumble/gatt_server.py +12 -1
  20. bumble/hci.py +854 -33
  21. bumble/host.py +363 -64
  22. bumble/l2cap.py +3 -16
  23. bumble/pairing.py +3 -0
  24. bumble/profiles/aics.py +45 -80
  25. bumble/profiles/ascs.py +6 -18
  26. bumble/profiles/asha.py +5 -5
  27. bumble/profiles/bass.py +9 -21
  28. bumble/profiles/device_information_service.py +4 -1
  29. bumble/profiles/gatt_service.py +166 -0
  30. bumble/profiles/gmap.py +193 -0
  31. bumble/profiles/heart_rate_service.py +5 -6
  32. bumble/profiles/le_audio.py +87 -4
  33. bumble/profiles/pacs.py +48 -16
  34. bumble/profiles/tmap.py +3 -9
  35. bumble/profiles/{vcp.py → vcs.py} +33 -28
  36. bumble/profiles/vocs.py +299 -0
  37. bumble/sdp.py +223 -93
  38. bumble/smp.py +8 -3
  39. bumble/tools/intel_fw_download.py +130 -0
  40. bumble/tools/intel_util.py +154 -0
  41. bumble/transport/usb.py +8 -2
  42. bumble/utils.py +22 -7
  43. bumble/vendor/android/hci.py +29 -4
  44. {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/METADATA +12 -10
  45. {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/RECORD +49 -43
  46. {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/WHEEL +1 -1
  47. {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/entry_points.txt +3 -0
  48. bumble/apps/lea_unicast/liblc3.wasm +0 -0
  49. {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/LICENSE +0 -0
  50. {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/top_level.txt +0 -0
bumble/gatt.py CHANGED
@@ -28,23 +28,26 @@ import functools
28
28
  import logging
29
29
  import struct
30
30
  from typing import (
31
+ Any,
31
32
  Callable,
32
33
  Dict,
33
34
  Iterable,
34
35
  List,
35
36
  Optional,
36
37
  Sequence,
38
+ SupportsBytes,
39
+ Type,
37
40
  Union,
38
41
  TYPE_CHECKING,
39
42
  )
40
43
 
41
44
  from bumble.colors import color
42
- from bumble.core import BaseBumbleError, UUID
45
+ from bumble.core import BaseBumbleError, InvalidOperationError, UUID
43
46
  from bumble.att import Attribute, AttributeValue
47
+ from bumble.utils import ByteSerializable
44
48
 
45
49
  if TYPE_CHECKING:
46
50
  from bumble.gatt_client import AttributeProxy
47
- from bumble.device import Connection
48
51
 
49
52
 
50
53
  # -----------------------------------------------------------------------------
@@ -275,6 +278,13 @@ GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCC, 'Sou
275
278
  GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
276
279
  GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
277
280
 
281
+ # Gaming Audio Service (GMAS)
282
+ GATT_GMAP_ROLE_CHARACTERISTIC = UUID.from_16_bits(0x2C00, 'GMAP Role')
283
+ GATT_UGG_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C01, 'UGG Features')
284
+ GATT_UGT_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C02, 'UGT Features')
285
+ GATT_BGS_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C03, 'BGS Features')
286
+ GATT_BGR_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C04, 'BGR Features')
287
+
278
288
  # Hearing Access Service
279
289
  GATT_HEARING_AID_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2BDA, 'Hearing Aid Features')
280
290
  GATT_HEARING_AID_PRESET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BDB, 'Hearing Aid Preset Control Point')
@@ -304,6 +314,7 @@ GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bi
304
314
  GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B29, 'Client Supported Features')
305
315
  GATT_DATABASE_HASH_CHARACTERISTIC = UUID.from_16_bits(0x2B2A, 'Database Hash')
306
316
  GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B3A, 'Server Supported Features')
317
+ GATT_LE_GATT_SECURITY_LEVELS_CHARACTERISTIC = UUID.from_16_bits(0x2BF5, 'E GATT Security Levels')
307
318
 
308
319
  # fmt: on
309
320
  # pylint: enable=line-too-long
@@ -312,8 +323,6 @@ GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bi
312
323
  # -----------------------------------------------------------------------------
313
324
  # Utils
314
325
  # -----------------------------------------------------------------------------
315
-
316
-
317
326
  def show_services(services: Iterable[Service]) -> None:
318
327
  for service in services:
319
328
  print(color(str(service), 'cyan'))
@@ -343,7 +352,7 @@ class Service(Attribute):
343
352
  def __init__(
344
353
  self,
345
354
  uuid: Union[str, UUID],
346
- characteristics: List[Characteristic],
355
+ characteristics: Iterable[Characteristic],
347
356
  primary=True,
348
357
  included_services: Iterable[Service] = (),
349
358
  ) -> None:
@@ -362,7 +371,7 @@ class Service(Attribute):
362
371
  )
363
372
  self.uuid = uuid
364
373
  self.included_services = list(included_services)
365
- self.characteristics = characteristics[:]
374
+ self.characteristics = list(characteristics)
366
375
  self.primary = primary
367
376
 
368
377
  def get_advertising_data(self) -> Optional[bytes]:
@@ -393,7 +402,7 @@ class TemplateService(Service):
393
402
 
394
403
  def __init__(
395
404
  self,
396
- characteristics: List[Characteristic],
405
+ characteristics: Iterable[Characteristic],
397
406
  primary: bool = True,
398
407
  included_services: Iterable[Service] = (),
399
408
  ) -> None:
@@ -490,7 +499,7 @@ class Characteristic(Attribute):
490
499
  uuid: Union[str, bytes, UUID],
491
500
  properties: Characteristic.Properties,
492
501
  permissions: Union[str, Attribute.Permissions],
493
- value: Union[str, bytes, CharacteristicValue] = b'',
502
+ value: Any = b'',
494
503
  descriptors: Sequence[Descriptor] = (),
495
504
  ):
496
505
  super().__init__(uuid, permissions, value)
@@ -525,7 +534,11 @@ class CharacteristicDeclaration(Attribute):
525
534
 
526
535
  characteristic: Characteristic
527
536
 
528
- def __init__(self, characteristic: Characteristic, value_handle: int) -> None:
537
+ def __init__(
538
+ self,
539
+ characteristic: Characteristic,
540
+ value_handle: int,
541
+ ) -> None:
529
542
  declaration_bytes = (
530
543
  struct.pack('<BH', characteristic.properties, value_handle)
531
544
  + characteristic.uuid.to_pdu_bytes()
@@ -665,10 +678,14 @@ class DelegatedCharacteristicAdapter(CharacteristicAdapter):
665
678
  self.decode = decode
666
679
 
667
680
  def encode_value(self, value):
668
- return self.encode(value) if self.encode else value
681
+ if self.encode is None:
682
+ raise InvalidOperationError('delegated adapter does not have an encoder')
683
+ return self.encode(value)
669
684
 
670
685
  def decode_value(self, value):
671
- return self.decode(value) if self.decode else value
686
+ if self.decode is None:
687
+ raise InvalidOperationError('delegate adapter does not have a decoder')
688
+ return self.decode(value)
672
689
 
673
690
 
674
691
  # -----------------------------------------------------------------------------
@@ -705,7 +722,7 @@ class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
705
722
  '''
706
723
  Adapter that packs/unpacks characteristic values according to a standard
707
724
  Python `struct` format.
708
- The adapted `read_value` and `write_value` methods return/accept aa dictionary which
725
+ The adapted `read_value` and `write_value` methods return/accept a dictionary which
709
726
  is packed/unpacked according to format, with the arguments extracted from the
710
727
  dictionary by key, in the same order as they occur in the `keys` parameter.
711
728
  '''
@@ -735,6 +752,24 @@ class UTF8CharacteristicAdapter(CharacteristicAdapter):
735
752
  return value.decode('utf-8')
736
753
 
737
754
 
755
+ # -----------------------------------------------------------------------------
756
+ class SerializableCharacteristicAdapter(CharacteristicAdapter):
757
+ '''
758
+ Adapter that converts any class to/from bytes using the class'
759
+ `to_bytes` and `__bytes__` methods, respectively.
760
+ '''
761
+
762
+ def __init__(self, characteristic, cls: Type[ByteSerializable]):
763
+ super().__init__(characteristic)
764
+ self.cls = cls
765
+
766
+ def encode_value(self, value: SupportsBytes) -> bytes:
767
+ return bytes(value)
768
+
769
+ def decode_value(self, value: bytes) -> Any:
770
+ return self.cls.from_bytes(value)
771
+
772
+
738
773
  # -----------------------------------------------------------------------------
739
774
  class Descriptor(Attribute):
740
775
  '''
@@ -769,3 +804,23 @@ class ClientCharacteristicConfigurationBits(enum.IntFlag):
769
804
  DEFAULT = 0x0000
770
805
  NOTIFICATION = 0x0001
771
806
  INDICATION = 0x0002
807
+
808
+
809
+ # -----------------------------------------------------------------------------
810
+ class ClientSupportedFeatures(enum.IntFlag):
811
+ '''
812
+ See Vol 3, Part G - 7.2 - Table 7.6: Client Supported Features bit assignments.
813
+ '''
814
+
815
+ ROBUST_CACHING = 0x01
816
+ ENHANCED_ATT_BEARER = 0x02
817
+ MULTIPLE_HANDLE_VALUE_NOTIFICATIONS = 0x04
818
+
819
+
820
+ # -----------------------------------------------------------------------------
821
+ class ServerSupportedFeatures(enum.IntFlag):
822
+ '''
823
+ See Vol 3, Part G - 7.4 - Table 7.11: Server Supported Features bit assignments.
824
+ '''
825
+
826
+ EATT_SUPPORTED = 0x01
bumble/gatt_client.py CHANGED
@@ -78,6 +78,7 @@ from .gatt import (
78
78
  GATT_INCLUDE_ATTRIBUTE_TYPE,
79
79
  Characteristic,
80
80
  ClientCharacteristicConfigurationBits,
81
+ InvalidServiceError,
81
82
  TemplateService,
82
83
  )
83
84
 
@@ -162,12 +163,23 @@ class ServiceProxy(AttributeProxy):
162
163
  self.uuid = uuid
163
164
  self.characteristics = []
164
165
 
165
- async def discover_characteristics(self, uuids=()):
166
+ async def discover_characteristics(self, uuids=()) -> list[CharacteristicProxy]:
166
167
  return await self.client.discover_characteristics(uuids, self)
167
168
 
168
- def get_characteristics_by_uuid(self, uuid):
169
+ def get_characteristics_by_uuid(self, uuid: UUID) -> list[CharacteristicProxy]:
170
+ """Get all the characteristics with a specified UUID."""
169
171
  return self.client.get_characteristics_by_uuid(uuid, self)
170
172
 
173
+ def get_required_characteristic_by_uuid(self, uuid: UUID) -> CharacteristicProxy:
174
+ """
175
+ Get the first characteristic with a specified UUID.
176
+
177
+ If no characteristic with that UUID is found, an InvalidServiceError is raised.
178
+ """
179
+ if not (characteristics := self.get_characteristics_by_uuid(uuid)):
180
+ raise InvalidServiceError(f'{uuid} characteristic not found')
181
+ return characteristics[0]
182
+
171
183
  def __str__(self) -> str:
172
184
  return f'Service(handle=0x{self.handle:04X}, uuid={self.uuid})'
173
185
 
bumble/gatt_server.py CHANGED
@@ -28,7 +28,17 @@ import asyncio
28
28
  import logging
29
29
  from collections import defaultdict
30
30
  import struct
31
- from typing import List, Tuple, Optional, TypeVar, Type, Dict, Iterable, TYPE_CHECKING
31
+ from typing import (
32
+ Dict,
33
+ Iterable,
34
+ List,
35
+ Optional,
36
+ Tuple,
37
+ TypeVar,
38
+ Type,
39
+ Union,
40
+ TYPE_CHECKING,
41
+ )
32
42
  from pyee import EventEmitter
33
43
 
34
44
  from bumble.colors import color
@@ -68,6 +78,7 @@ from bumble.gatt import (
68
78
  GATT_REQUEST_TIMEOUT,
69
79
  GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
70
80
  Characteristic,
81
+ CharacteristicAdapter,
71
82
  CharacteristicDeclaration,
72
83
  CharacteristicValue,
73
84
  IncludedServiceDeclaration,