bumble 0.0.220__py3-none-any.whl → 0.0.222__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 (102) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +5 -5
  3. bumble/apps/auracast.py +746 -473
  4. bumble/apps/bench.py +4 -5
  5. bumble/apps/console.py +5 -10
  6. bumble/apps/controller_info.py +12 -7
  7. bumble/apps/controller_loopback.py +1 -2
  8. bumble/apps/device_info.py +2 -3
  9. bumble/apps/gatt_dump.py +0 -1
  10. bumble/apps/lea_unicast/app.py +1 -1
  11. bumble/apps/pair.py +49 -46
  12. bumble/apps/pandora_server.py +2 -2
  13. bumble/apps/player/player.py +10 -12
  14. bumble/apps/rfcomm_bridge.py +10 -11
  15. bumble/apps/scan.py +1 -3
  16. bumble/apps/speaker/speaker.py +3 -4
  17. bumble/at.py +4 -5
  18. bumble/att.py +91 -25
  19. bumble/audio/io.py +5 -3
  20. bumble/avc.py +1 -2
  21. bumble/avctp.py +2 -3
  22. bumble/avdtp.py +53 -57
  23. bumble/avrcp.py +25 -27
  24. bumble/codecs.py +15 -15
  25. bumble/colors.py +7 -8
  26. bumble/controller.py +663 -391
  27. bumble/core.py +41 -49
  28. bumble/crypto/__init__.py +2 -1
  29. bumble/crypto/builtin.py +2 -8
  30. bumble/data_types.py +2 -1
  31. bumble/decoder.py +2 -3
  32. bumble/device.py +171 -142
  33. bumble/drivers/__init__.py +3 -2
  34. bumble/drivers/intel.py +6 -8
  35. bumble/drivers/rtk.py +1 -1
  36. bumble/gatt.py +9 -9
  37. bumble/gatt_adapters.py +6 -6
  38. bumble/gatt_client.py +110 -60
  39. bumble/gatt_server.py +209 -139
  40. bumble/hci.py +87 -74
  41. bumble/helpers.py +5 -5
  42. bumble/hfp.py +27 -26
  43. bumble/hid.py +9 -9
  44. bumble/host.py +44 -50
  45. bumble/keys.py +17 -17
  46. bumble/l2cap.py +1070 -218
  47. bumble/link.py +26 -159
  48. bumble/ll.py +200 -0
  49. bumble/pairing.py +14 -15
  50. bumble/pandora/__init__.py +2 -2
  51. bumble/pandora/device.py +6 -4
  52. bumble/pandora/host.py +19 -10
  53. bumble/pandora/l2cap.py +8 -9
  54. bumble/pandora/security.py +18 -16
  55. bumble/pandora/utils.py +4 -4
  56. bumble/profiles/aics.py +6 -8
  57. bumble/profiles/ams.py +3 -5
  58. bumble/profiles/ancs.py +11 -11
  59. bumble/profiles/ascs.py +5 -5
  60. bumble/profiles/asha.py +10 -9
  61. bumble/profiles/bass.py +9 -3
  62. bumble/profiles/battery_service.py +1 -2
  63. bumble/profiles/csip.py +9 -10
  64. bumble/profiles/device_information_service.py +16 -17
  65. bumble/profiles/gap.py +3 -4
  66. bumble/profiles/gatt_service.py +0 -1
  67. bumble/profiles/gmap.py +12 -13
  68. bumble/profiles/hap.py +3 -3
  69. bumble/profiles/heart_rate_service.py +7 -8
  70. bumble/profiles/le_audio.py +1 -1
  71. bumble/profiles/mcp.py +28 -28
  72. bumble/profiles/pacs.py +13 -17
  73. bumble/profiles/pbp.py +16 -0
  74. bumble/profiles/vcs.py +2 -2
  75. bumble/profiles/vocs.py +6 -9
  76. bumble/rfcomm.py +19 -18
  77. bumble/sdp.py +12 -11
  78. bumble/smp.py +20 -30
  79. bumble/snoop.py +2 -1
  80. bumble/tools/generate_company_id_list.py +1 -1
  81. bumble/tools/intel_util.py +2 -2
  82. bumble/tools/rtk_fw_download.py +1 -1
  83. bumble/tools/rtk_util.py +1 -1
  84. bumble/transport/__init__.py +1 -2
  85. bumble/transport/android_emulator.py +2 -3
  86. bumble/transport/android_netsim.py +49 -40
  87. bumble/transport/common.py +9 -9
  88. bumble/transport/file.py +1 -2
  89. bumble/transport/hci_socket.py +2 -3
  90. bumble/transport/pty.py +3 -5
  91. bumble/transport/pyusb.py +8 -5
  92. bumble/transport/serial.py +1 -2
  93. bumble/transport/vhci.py +1 -2
  94. bumble/transport/ws_server.py +2 -3
  95. bumble/utils.py +22 -9
  96. bumble/vendor/android/hci.py +4 -2
  97. {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/METADATA +3 -2
  98. {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/RECORD +102 -101
  99. {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/WHEEL +0 -0
  100. {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/entry_points.txt +0 -0
  101. {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/licenses/LICENSE +0 -0
  102. {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/top_level.txt +0 -0
bumble/core.py CHANGED
@@ -20,15 +20,11 @@ from __future__ import annotations
20
20
  import dataclasses
21
21
  import enum
22
22
  import struct
23
+ from collections.abc import Iterable
23
24
  from typing import (
24
- TYPE_CHECKING,
25
25
  Any,
26
26
  ClassVar,
27
- Iterable,
28
27
  Literal,
29
- Optional,
30
- Type,
31
- Union,
32
28
  cast,
33
29
  overload,
34
30
  )
@@ -103,7 +99,7 @@ class BaseError(BaseBumbleError):
103
99
 
104
100
  def __init__(
105
101
  self,
106
- error_code: Optional[int],
102
+ error_code: int | None,
107
103
  error_namespace: str = '',
108
104
  error_name: str = '',
109
105
  details: str = '',
@@ -216,11 +212,9 @@ class UUID:
216
212
  UUIDS: list[UUID] = [] # Registry of all instances created
217
213
 
218
214
  uuid_bytes: bytes
219
- name: Optional[str]
215
+ name: str | None
220
216
 
221
- def __init__(
222
- self, uuid_str_or_int: Union[str, int], name: Optional[str] = None
223
- ) -> None:
217
+ def __init__(self, uuid_str_or_int: str | int, name: str | None = None) -> None:
224
218
  if isinstance(uuid_str_or_int, int):
225
219
  self.uuid_bytes = struct.pack('<H', uuid_str_or_int)
226
220
  else:
@@ -253,7 +247,7 @@ class UUID:
253
247
  return self
254
248
 
255
249
  @classmethod
256
- def from_bytes(cls, uuid_bytes: bytes, name: Optional[str] = None) -> UUID:
250
+ def from_bytes(cls, uuid_bytes: bytes, name: str | None = None) -> UUID:
257
251
  if len(uuid_bytes) in (2, 4, 16):
258
252
  self = cls.__new__(cls)
259
253
  self.uuid_bytes = uuid_bytes
@@ -264,11 +258,11 @@ class UUID:
264
258
  raise InvalidArgumentError('only 2, 4 and 16 bytes are allowed')
265
259
 
266
260
  @classmethod
267
- def from_16_bits(cls, uuid_16: int, name: Optional[str] = None) -> UUID:
261
+ def from_16_bits(cls, uuid_16: int, name: str | None = None) -> UUID:
268
262
  return cls.from_bytes(struct.pack('<H', uuid_16), name)
269
263
 
270
264
  @classmethod
271
- def from_32_bits(cls, uuid_32: int, name: Optional[str] = None) -> UUID:
265
+ def from_32_bits(cls, uuid_32: int, name: str | None = None) -> UUID:
272
266
  return cls.from_bytes(struct.pack('<I', uuid_32), name)
273
267
 
274
268
  @classmethod
@@ -734,7 +728,7 @@ class ClassOfDevice:
734
728
  MajorDeviceClass.HEALTH: HEALTH_MINOR_DEVICE_CLASS_LABELS,
735
729
  }
736
730
 
737
- _MINOR_DEVICE_CLASSES: ClassVar[dict[MajorDeviceClass, Type]] = {
731
+ _MINOR_DEVICE_CLASSES: ClassVar[dict[MajorDeviceClass, type]] = {
738
732
  MajorDeviceClass.COMPUTER: ComputerMinorDeviceClass,
739
733
  MajorDeviceClass.PHONE: PhoneMinorDeviceClass,
740
734
  MajorDeviceClass.LAN_NETWORK_ACCESS_POINT: LanNetworkMinorDeviceClass,
@@ -749,17 +743,17 @@ class ClassOfDevice:
749
743
 
750
744
  major_service_classes: MajorServiceClasses
751
745
  major_device_class: MajorDeviceClass
752
- minor_device_class: Union[
753
- ComputerMinorDeviceClass,
754
- PhoneMinorDeviceClass,
755
- LanNetworkMinorDeviceClass,
756
- AudioVideoMinorDeviceClass,
757
- PeripheralMinorDeviceClass,
758
- WearableMinorDeviceClass,
759
- ToyMinorDeviceClass,
760
- HealthMinorDeviceClass,
761
- int,
762
- ]
746
+ minor_device_class: (
747
+ ComputerMinorDeviceClass
748
+ | PhoneMinorDeviceClass
749
+ | LanNetworkMinorDeviceClass
750
+ | AudioVideoMinorDeviceClass
751
+ | PeripheralMinorDeviceClass
752
+ | WearableMinorDeviceClass
753
+ | ToyMinorDeviceClass
754
+ | HealthMinorDeviceClass
755
+ | int
756
+ )
763
757
 
764
758
  @classmethod
765
759
  def from_int(cls, class_of_device: int) -> Self:
@@ -1548,7 +1542,7 @@ class DataType:
1548
1542
  return f"{self.__class__.__name__}({self.value_string()})"
1549
1543
 
1550
1544
  @classmethod
1551
- def from_advertising_data(cls, advertising_data: AdvertisingData) -> Optional[Self]:
1545
+ def from_advertising_data(cls, advertising_data: AdvertisingData) -> Self | None:
1552
1546
  if (data := advertising_data.get(cls.ad_type, raw=True)) is None:
1553
1547
  return None
1554
1548
 
@@ -1576,16 +1570,16 @@ class DataType:
1576
1570
  # -----------------------------------------------------------------------------
1577
1571
  # Advertising Data
1578
1572
  # -----------------------------------------------------------------------------
1579
- AdvertisingDataObject = Union[
1580
- list[UUID],
1581
- tuple[UUID, bytes],
1582
- bytes,
1583
- str,
1584
- int,
1585
- tuple[int, int],
1586
- tuple[int, bytes],
1587
- Appearance,
1588
- ]
1573
+ AdvertisingDataObject = (
1574
+ list[UUID]
1575
+ | tuple[UUID, bytes]
1576
+ | bytes
1577
+ | str
1578
+ | int
1579
+ | tuple[int, int]
1580
+ | tuple[int, bytes]
1581
+ | Appearance
1582
+ )
1589
1583
 
1590
1584
 
1591
1585
  class AdvertisingData:
@@ -1722,7 +1716,7 @@ class AdvertisingData:
1722
1716
 
1723
1717
  def __init__(
1724
1718
  self,
1725
- ad_structures: Optional[Iterable[Union[tuple[int, bytes], DataType]]] = None,
1719
+ ad_structures: Iterable[tuple[int, bytes] | DataType] | None = None,
1726
1720
  ) -> None:
1727
1721
  if ad_structures is None:
1728
1722
  ad_structures = []
@@ -2020,7 +2014,7 @@ class AdvertisingData:
2020
2014
  AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
2021
2015
  ],
2022
2016
  raw: Literal[False] = False,
2023
- ) -> Optional[list[UUID]]: ...
2017
+ ) -> list[UUID] | None: ...
2024
2018
 
2025
2019
  @overload
2026
2020
  def get(
@@ -2031,7 +2025,7 @@ class AdvertisingData:
2031
2025
  AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID,
2032
2026
  ],
2033
2027
  raw: Literal[False] = False,
2034
- ) -> Optional[tuple[UUID, bytes]]: ...
2028
+ ) -> tuple[UUID, bytes] | None: ...
2035
2029
 
2036
2030
  @overload
2037
2031
  def get(
@@ -2043,7 +2037,7 @@ class AdvertisingData:
2043
2037
  AdvertisingData.Type.BROADCAST_NAME,
2044
2038
  ],
2045
2039
  raw: Literal[False] = False,
2046
- ) -> Optional[Optional[str]]: ...
2040
+ ) -> str | None: ...
2047
2041
 
2048
2042
  @overload
2049
2043
  def get(
@@ -2055,38 +2049,36 @@ class AdvertisingData:
2055
2049
  AdvertisingData.Type.CLASS_OF_DEVICE,
2056
2050
  ],
2057
2051
  raw: Literal[False] = False,
2058
- ) -> Optional[int]: ...
2052
+ ) -> int | None: ...
2059
2053
 
2060
2054
  @overload
2061
2055
  def get(
2062
2056
  self,
2063
2057
  type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
2064
2058
  raw: Literal[False] = False,
2065
- ) -> Optional[tuple[int, int]]: ...
2059
+ ) -> tuple[int, int] | None: ...
2066
2060
 
2067
2061
  @overload
2068
2062
  def get(
2069
2063
  self,
2070
2064
  type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
2071
2065
  raw: Literal[False] = False,
2072
- ) -> Optional[tuple[int, bytes]]: ...
2066
+ ) -> tuple[int, bytes] | None: ...
2073
2067
 
2074
2068
  @overload
2075
2069
  def get(
2076
2070
  self,
2077
2071
  type_id: Literal[AdvertisingData.Type.APPEARANCE,],
2078
2072
  raw: Literal[False] = False,
2079
- ) -> Optional[Appearance]: ...
2073
+ ) -> Appearance | None: ...
2080
2074
 
2081
2075
  @overload
2082
- def get(self, type_id: int, raw: Literal[True]) -> Optional[bytes]: ...
2076
+ def get(self, type_id: int, raw: Literal[True]) -> bytes | None: ...
2083
2077
 
2084
2078
  @overload
2085
- def get(
2086
- self, type_id: int, raw: bool = False
2087
- ) -> Optional[AdvertisingDataObject]: ...
2079
+ def get(self, type_id: int, raw: bool = False) -> AdvertisingDataObject | None: ...
2088
2080
 
2089
- def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingDataObject]:
2081
+ def get(self, type_id: int, raw: bool = False) -> AdvertisingDataObject | None:
2090
2082
  '''
2091
2083
  Get advertising data as a simple AdvertisingDataObject object.
2092
2084
 
bumble/crypto/__init__.py CHANGED
@@ -25,10 +25,11 @@ try:
25
25
  from bumble.crypto.cryptography import EccKey, aes_cmac, e
26
26
  except ImportError:
27
27
  logging.getLogger(__name__).debug(
28
- "Unable to import cryptography, use built-in primitives."
28
+ "Unable to import cryptography, using built-in primitives."
29
29
  )
30
30
  from bumble.crypto.builtin import EccKey, aes_cmac, e # type: ignore[assignment]
31
31
 
32
+ _EccKey = EccKey # For the linter only
32
33
 
33
34
  # -----------------------------------------------------------------------------
34
35
  # Logging
bumble/crypto/builtin.py CHANGED
@@ -29,7 +29,6 @@ import dataclasses
29
29
  import functools
30
30
  import secrets
31
31
  import struct
32
- from typing import Optional
33
32
 
34
33
  from bumble import core
35
34
 
@@ -85,7 +84,6 @@ class _AES:
85
84
  # fmt: on
86
85
 
87
86
  def __init__(self, key: bytes) -> None:
88
-
89
87
  if len(key) not in (16, 24, 32):
90
88
  raise core.InvalidArgumentError(f'Invalid key size {len(key)}')
91
89
 
@@ -112,7 +110,6 @@ class _AES:
112
110
  r_con_pointer = 0
113
111
  t = kc
114
112
  while t < round_key_count:
115
-
116
113
  tt = tk[kc - 1]
117
114
  tk[0] ^= (
118
115
  (self._S[(tt >> 16) & 0xFF] << 24)
@@ -269,7 +266,6 @@ class _ECB:
269
266
 
270
267
 
271
268
  class _CBC:
272
-
273
269
  def __init__(self, key: bytes, iv: bytes = bytes(16)) -> None:
274
270
  if len(iv) != 16:
275
271
  raise core.InvalidArgumentError(
@@ -302,7 +298,6 @@ class _CBC:
302
298
 
303
299
 
304
300
  class _CMAC:
305
-
306
301
  def __init__(
307
302
  self,
308
303
  key: bytes,
@@ -313,7 +308,7 @@ class _CMAC:
313
308
  self.digest_size = mac_len
314
309
  self._key = key
315
310
  self._block_size = bs = 16
316
- self._mac_tag: Optional[bytes] = None
311
+ self._mac_tag: bytes | None = None
317
312
  self._update_after_digest = update_after_digest
318
313
 
319
314
  # Section 5.3 of NIST SP 800 38B and Appendix B
@@ -352,7 +347,7 @@ class _CMAC:
352
347
  self._last_ct = zero_block
353
348
 
354
349
  # Last block that was encrypted with AES
355
- self._last_pt: Optional[bytes] = None
350
+ self._last_pt: bytes | None = None
356
351
 
357
352
  # Counter for total message size
358
353
  self._data_size = 0
@@ -414,7 +409,6 @@ class _CMAC:
414
409
  self._last_pt = _xor(second_last, data_block[-bs:])
415
410
 
416
411
  def digest(self) -> bytes:
417
-
418
412
  bs = self._block_size
419
413
 
420
414
  if self._mac_tag is not None and not self._update_after_digest:
bumble/data_types.py CHANGED
@@ -25,7 +25,8 @@ from __future__ import annotations
25
25
  import dataclasses
26
26
  import math
27
27
  import struct
28
- from typing import Any, ClassVar, Sequence
28
+ from collections.abc import Sequence
29
+ from typing import Any, ClassVar
29
30
 
30
31
  from typing_extensions import Self
31
32
 
bumble/decoder.py CHANGED
@@ -12,7 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Union
16
15
 
17
16
  # -----------------------------------------------------------------------------
18
17
  # Constants
@@ -167,12 +166,12 @@ class G722Decoder:
167
166
  # The initial value in BLOCK 3H
168
167
  self._band[1].det = 8
169
168
 
170
- def decode_frame(self, encoded_data: Union[bytes, bytearray]) -> bytearray:
169
+ def decode_frame(self, encoded_data: bytes | bytearray) -> bytearray:
171
170
  result_array = bytearray(len(encoded_data) * 4)
172
171
  self.g722_decode(result_array, encoded_data)
173
172
  return result_array
174
173
 
175
- def g722_decode(self, result_array, encoded_data: Union[bytes, bytearray]) -> int:
174
+ def g722_decode(self, result_array, encoded_data: bytes | bytearray) -> int:
176
175
  """Decode the data frame using g722 decoder."""
177
176
  result_length = 0
178
177