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.
- bumble/_version.py +2 -2
- bumble/a2dp.py +5 -5
- bumble/apps/auracast.py +746 -473
- bumble/apps/bench.py +4 -5
- bumble/apps/console.py +5 -10
- bumble/apps/controller_info.py +12 -7
- bumble/apps/controller_loopback.py +1 -2
- bumble/apps/device_info.py +2 -3
- bumble/apps/gatt_dump.py +0 -1
- bumble/apps/lea_unicast/app.py +1 -1
- bumble/apps/pair.py +49 -46
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/player/player.py +10 -12
- bumble/apps/rfcomm_bridge.py +10 -11
- bumble/apps/scan.py +1 -3
- bumble/apps/speaker/speaker.py +3 -4
- bumble/at.py +4 -5
- bumble/att.py +91 -25
- bumble/audio/io.py +5 -3
- bumble/avc.py +1 -2
- bumble/avctp.py +2 -3
- bumble/avdtp.py +53 -57
- bumble/avrcp.py +25 -27
- bumble/codecs.py +15 -15
- bumble/colors.py +7 -8
- bumble/controller.py +663 -391
- bumble/core.py +41 -49
- bumble/crypto/__init__.py +2 -1
- bumble/crypto/builtin.py +2 -8
- bumble/data_types.py +2 -1
- bumble/decoder.py +2 -3
- bumble/device.py +171 -142
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +6 -8
- bumble/drivers/rtk.py +1 -1
- bumble/gatt.py +9 -9
- bumble/gatt_adapters.py +6 -6
- bumble/gatt_client.py +110 -60
- bumble/gatt_server.py +209 -139
- bumble/hci.py +87 -74
- bumble/helpers.py +5 -5
- bumble/hfp.py +27 -26
- bumble/hid.py +9 -9
- bumble/host.py +44 -50
- bumble/keys.py +17 -17
- bumble/l2cap.py +1070 -218
- bumble/link.py +26 -159
- bumble/ll.py +200 -0
- bumble/pairing.py +14 -15
- bumble/pandora/__init__.py +2 -2
- bumble/pandora/device.py +6 -4
- bumble/pandora/host.py +19 -10
- bumble/pandora/l2cap.py +8 -9
- bumble/pandora/security.py +18 -16
- bumble/pandora/utils.py +4 -4
- bumble/profiles/aics.py +6 -8
- bumble/profiles/ams.py +3 -5
- bumble/profiles/ancs.py +11 -11
- bumble/profiles/ascs.py +5 -5
- bumble/profiles/asha.py +10 -9
- bumble/profiles/bass.py +9 -3
- bumble/profiles/battery_service.py +1 -2
- bumble/profiles/csip.py +9 -10
- bumble/profiles/device_information_service.py +16 -17
- bumble/profiles/gap.py +3 -4
- bumble/profiles/gatt_service.py +0 -1
- bumble/profiles/gmap.py +12 -13
- bumble/profiles/hap.py +3 -3
- bumble/profiles/heart_rate_service.py +7 -8
- bumble/profiles/le_audio.py +1 -1
- bumble/profiles/mcp.py +28 -28
- bumble/profiles/pacs.py +13 -17
- bumble/profiles/pbp.py +16 -0
- bumble/profiles/vcs.py +2 -2
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +19 -18
- bumble/sdp.py +12 -11
- bumble/smp.py +20 -30
- bumble/snoop.py +2 -1
- bumble/tools/generate_company_id_list.py +1 -1
- bumble/tools/intel_util.py +2 -2
- bumble/tools/rtk_fw_download.py +1 -1
- bumble/tools/rtk_util.py +1 -1
- bumble/transport/__init__.py +1 -2
- bumble/transport/android_emulator.py +2 -3
- bumble/transport/android_netsim.py +49 -40
- bumble/transport/common.py +9 -9
- bumble/transport/file.py +1 -2
- bumble/transport/hci_socket.py +2 -3
- bumble/transport/pty.py +3 -5
- bumble/transport/pyusb.py +8 -5
- bumble/transport/serial.py +1 -2
- bumble/transport/vhci.py +1 -2
- bumble/transport/ws_server.py +2 -3
- bumble/utils.py +22 -9
- bumble/vendor/android/hci.py +4 -2
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/METADATA +3 -2
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/RECORD +102 -101
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/WHEEL +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.222.dist-info}/licenses/LICENSE +0 -0
- {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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
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:
|
|
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) ->
|
|
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 =
|
|
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:
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
2073
|
+
) -> Appearance | None: ...
|
|
2080
2074
|
|
|
2081
2075
|
@overload
|
|
2082
|
-
def get(self, type_id: int, raw: Literal[True]) ->
|
|
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) ->
|
|
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,
|
|
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:
|
|
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:
|
|
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
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:
|
|
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:
|
|
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
|
|