bumble 0.0.220__py3-none-any.whl → 0.0.221__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 +1015 -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.221.dist-info}/METADATA +3 -2
- {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/RECORD +102 -101
- {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/hci.py
CHANGED
|
@@ -24,17 +24,13 @@ import functools
|
|
|
24
24
|
import logging
|
|
25
25
|
import secrets
|
|
26
26
|
import struct
|
|
27
|
-
from collections.abc import Sequence
|
|
27
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
28
28
|
from dataclasses import field
|
|
29
29
|
from typing import (
|
|
30
30
|
Any,
|
|
31
|
-
Callable,
|
|
32
31
|
ClassVar,
|
|
33
|
-
Iterable,
|
|
34
32
|
Literal,
|
|
35
|
-
Optional,
|
|
36
33
|
TypeVar,
|
|
37
|
-
Union,
|
|
38
34
|
cast,
|
|
39
35
|
)
|
|
40
36
|
|
|
@@ -106,7 +102,7 @@ def map_class_of_device(class_of_device):
|
|
|
106
102
|
)
|
|
107
103
|
|
|
108
104
|
|
|
109
|
-
def phy_list_to_bits(phys:
|
|
105
|
+
def phy_list_to_bits(phys: Iterable[Phy] | None) -> int:
|
|
110
106
|
if phys is None:
|
|
111
107
|
return 0
|
|
112
108
|
|
|
@@ -119,7 +115,6 @@ def phy_list_to_bits(phys: Optional[Iterable[Phy]]) -> int:
|
|
|
119
115
|
|
|
120
116
|
|
|
121
117
|
class SpecableEnum(utils.OpenIntEnum):
|
|
122
|
-
|
|
123
118
|
@classmethod
|
|
124
119
|
def type_spec(cls, size: int, byteorder: Literal['little', 'big'] = 'little'):
|
|
125
120
|
return {
|
|
@@ -147,7 +142,6 @@ class SpecableEnum(utils.OpenIntEnum):
|
|
|
147
142
|
|
|
148
143
|
|
|
149
144
|
class SpecableFlag(enum.IntFlag):
|
|
150
|
-
|
|
151
145
|
@classmethod
|
|
152
146
|
def type_spec(cls, size: int, byteorder: Literal['little', 'big'] = 'little'):
|
|
153
147
|
return {
|
|
@@ -186,8 +180,8 @@ class SpecableFlag(enum.IntFlag):
|
|
|
186
180
|
# - "v" for variable length bytes with a leading length byte
|
|
187
181
|
# - an integer [1, 4] for 1-byte, 2-byte or 4-byte unsigned little-endian integers
|
|
188
182
|
# - an integer [-2, -1] for 1-byte, 2-byte signed little-endian integers
|
|
189
|
-
FieldSpec =
|
|
190
|
-
Fields = Sequence[
|
|
183
|
+
FieldSpec = dict[str, Any] | Callable[[bytes, int], tuple[int, Any]] | str | int
|
|
184
|
+
Fields = Sequence['tuple[str, FieldSpec] | Fields']
|
|
191
185
|
|
|
192
186
|
|
|
193
187
|
@dataclasses.dataclass
|
|
@@ -213,22 +207,44 @@ def metadata(
|
|
|
213
207
|
|
|
214
208
|
HCI_VENDOR_OGF = 0x3F
|
|
215
209
|
|
|
216
|
-
#
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
210
|
+
# Specification Version
|
|
211
|
+
class SpecificationVersion(utils.OpenIntEnum):
|
|
212
|
+
BLUETOOTH_CORE_1_0B = 0
|
|
213
|
+
BLUETOOTH_CORE_1_1 = 1
|
|
214
|
+
BLUETOOTH_CORE_1_2 = 2
|
|
215
|
+
BLUETOOTH_CORE_2_0_EDR = 3
|
|
216
|
+
BLUETOOTH_CORE_2_1_EDR = 4
|
|
217
|
+
BLUETOOTH_CORE_3_0_HS = 5
|
|
218
|
+
BLUETOOTH_CORE_4_0 = 6
|
|
219
|
+
BLUETOOTH_CORE_4_1 = 7
|
|
220
|
+
BLUETOOTH_CORE_4_2 = 8
|
|
221
|
+
BLUETOOTH_CORE_5_0 = 9
|
|
222
|
+
BLUETOOTH_CORE_5_1 = 10
|
|
223
|
+
BLUETOOTH_CORE_5_2 = 11
|
|
224
|
+
BLUETOOTH_CORE_5_3 = 12
|
|
225
|
+
BLUETOOTH_CORE_5_4 = 13
|
|
226
|
+
BLUETOOTH_CORE_6_0 = 14
|
|
227
|
+
BLUETOOTH_CORE_6_1 = 15
|
|
228
|
+
BLUETOOTH_CORE_6_2 = 16
|
|
229
|
+
|
|
230
|
+
# For backwards compatibility only
|
|
231
|
+
HCI_VERSION_BLUETOOTH_CORE_1_0B = SpecificationVersion.BLUETOOTH_CORE_1_0B
|
|
232
|
+
HCI_VERSION_BLUETOOTH_CORE_1_1 = SpecificationVersion.BLUETOOTH_CORE_1_1
|
|
233
|
+
HCI_VERSION_BLUETOOTH_CORE_1_2 = SpecificationVersion.BLUETOOTH_CORE_1_2
|
|
234
|
+
HCI_VERSION_BLUETOOTH_CORE_2_0_EDR = SpecificationVersion.BLUETOOTH_CORE_2_0_EDR
|
|
235
|
+
HCI_VERSION_BLUETOOTH_CORE_2_1_EDR = SpecificationVersion.BLUETOOTH_CORE_2_1_EDR
|
|
236
|
+
HCI_VERSION_BLUETOOTH_CORE_3_0_HS = SpecificationVersion.BLUETOOTH_CORE_3_0_HS
|
|
237
|
+
HCI_VERSION_BLUETOOTH_CORE_4_0 = SpecificationVersion.BLUETOOTH_CORE_4_0
|
|
238
|
+
HCI_VERSION_BLUETOOTH_CORE_4_1 = SpecificationVersion.BLUETOOTH_CORE_4_1
|
|
239
|
+
HCI_VERSION_BLUETOOTH_CORE_4_2 = SpecificationVersion.BLUETOOTH_CORE_4_2
|
|
240
|
+
HCI_VERSION_BLUETOOTH_CORE_5_0 = SpecificationVersion.BLUETOOTH_CORE_5_0
|
|
241
|
+
HCI_VERSION_BLUETOOTH_CORE_5_1 = SpecificationVersion.BLUETOOTH_CORE_5_1
|
|
242
|
+
HCI_VERSION_BLUETOOTH_CORE_5_2 = SpecificationVersion.BLUETOOTH_CORE_5_2
|
|
243
|
+
HCI_VERSION_BLUETOOTH_CORE_5_3 = SpecificationVersion.BLUETOOTH_CORE_5_3
|
|
244
|
+
HCI_VERSION_BLUETOOTH_CORE_5_4 = SpecificationVersion.BLUETOOTH_CORE_5_4
|
|
245
|
+
HCI_VERSION_BLUETOOTH_CORE_6_0 = SpecificationVersion.BLUETOOTH_CORE_6_0
|
|
246
|
+
HCI_VERSION_BLUETOOTH_CORE_6_1 = SpecificationVersion.BLUETOOTH_CORE_6_1
|
|
247
|
+
HCI_VERSION_BLUETOOTH_CORE_6_2 = SpecificationVersion.BLUETOOTH_CORE_6_2
|
|
232
248
|
|
|
233
249
|
HCI_VERSION_NAMES = {
|
|
234
250
|
HCI_VERSION_BLUETOOTH_CORE_1_0B: 'HCI_VERSION_BLUETOOTH_CORE_1_0B',
|
|
@@ -246,9 +262,10 @@ HCI_VERSION_NAMES = {
|
|
|
246
262
|
HCI_VERSION_BLUETOOTH_CORE_5_3: 'HCI_VERSION_BLUETOOTH_CORE_5_3',
|
|
247
263
|
HCI_VERSION_BLUETOOTH_CORE_5_4: 'HCI_VERSION_BLUETOOTH_CORE_5_4',
|
|
248
264
|
HCI_VERSION_BLUETOOTH_CORE_6_0: 'HCI_VERSION_BLUETOOTH_CORE_6_0',
|
|
265
|
+
HCI_VERSION_BLUETOOTH_CORE_6_1: 'HCI_VERSION_BLUETOOTH_CORE_6_1',
|
|
266
|
+
HCI_VERSION_BLUETOOTH_CORE_6_2: 'HCI_VERSION_BLUETOOTH_CORE_6_2',
|
|
249
267
|
}
|
|
250
268
|
|
|
251
|
-
# LMP Version
|
|
252
269
|
LMP_VERSION_NAMES = HCI_VERSION_NAMES
|
|
253
270
|
|
|
254
271
|
# HCI Packet types
|
|
@@ -1786,20 +1803,20 @@ class HCI_Object:
|
|
|
1786
1803
|
|
|
1787
1804
|
@classmethod
|
|
1788
1805
|
def dict_and_offset_from_bytes(
|
|
1789
|
-
cls, data: bytes, offset: int,
|
|
1806
|
+
cls, data: bytes, offset: int, object_fields: Fields
|
|
1790
1807
|
) -> tuple[int, collections.OrderedDict[str, Any]]:
|
|
1791
1808
|
result = collections.OrderedDict[str, Any]()
|
|
1792
|
-
for
|
|
1793
|
-
if isinstance(
|
|
1809
|
+
for object_field in object_fields:
|
|
1810
|
+
if isinstance(object_field, list):
|
|
1794
1811
|
# This is an array field, starting with a 1-byte item count.
|
|
1795
1812
|
item_count = data[offset]
|
|
1796
1813
|
offset += 1
|
|
1797
1814
|
# Set fields first, because item_count might be 0.
|
|
1798
|
-
for sub_field_name, _ in
|
|
1815
|
+
for sub_field_name, _ in object_field:
|
|
1799
1816
|
result[sub_field_name] = []
|
|
1800
1817
|
|
|
1801
1818
|
for _ in range(item_count):
|
|
1802
|
-
for sub_field_name, sub_field_type in
|
|
1819
|
+
for sub_field_name, sub_field_type in object_field:
|
|
1803
1820
|
value, size = HCI_Object.parse_field(
|
|
1804
1821
|
data, offset, sub_field_type
|
|
1805
1822
|
)
|
|
@@ -1807,7 +1824,7 @@ class HCI_Object:
|
|
|
1807
1824
|
offset += size
|
|
1808
1825
|
continue
|
|
1809
1826
|
|
|
1810
|
-
field_name, field_type =
|
|
1827
|
+
field_name, field_type = object_field
|
|
1811
1828
|
assert isinstance(field_name, str)
|
|
1812
1829
|
field_value, field_size = HCI_Object.parse_field(
|
|
1813
1830
|
data, offset, cast(FieldSpec, field_type)
|
|
@@ -1890,26 +1907,26 @@ class HCI_Object:
|
|
|
1890
1907
|
return field_bytes
|
|
1891
1908
|
|
|
1892
1909
|
@staticmethod
|
|
1893
|
-
def dict_to_bytes(hci_object,
|
|
1910
|
+
def dict_to_bytes(hci_object, object_fields):
|
|
1894
1911
|
result = bytearray()
|
|
1895
|
-
for
|
|
1896
|
-
if isinstance(
|
|
1912
|
+
for object_field in object_fields:
|
|
1913
|
+
if isinstance(object_field, list):
|
|
1897
1914
|
# The field is an array. The serialized form starts with a 1-byte
|
|
1898
1915
|
# item count. We use the length of the first array field as the
|
|
1899
1916
|
# array count, since all array fields have the same number of items.
|
|
1900
|
-
item_count = len(hci_object[
|
|
1917
|
+
item_count = len(hci_object[object_field[0][0]])
|
|
1901
1918
|
result += bytes([item_count]) + b''.join(
|
|
1902
1919
|
b''.join(
|
|
1903
1920
|
HCI_Object.serialize_field(
|
|
1904
1921
|
hci_object[sub_field_name][i], sub_field_type
|
|
1905
1922
|
)
|
|
1906
|
-
for sub_field_name, sub_field_type in
|
|
1923
|
+
for sub_field_name, sub_field_type in object_field
|
|
1907
1924
|
)
|
|
1908
1925
|
for i in range(item_count)
|
|
1909
1926
|
)
|
|
1910
1927
|
continue
|
|
1911
1928
|
|
|
1912
|
-
(field_name, field_type) =
|
|
1929
|
+
(field_name, field_type) = object_field
|
|
1913
1930
|
result += HCI_Object.serialize_field(hci_object[field_name], field_type)
|
|
1914
1931
|
|
|
1915
1932
|
return bytes(result)
|
|
@@ -1967,15 +1984,15 @@ class HCI_Object:
|
|
|
1967
1984
|
)
|
|
1968
1985
|
|
|
1969
1986
|
@staticmethod
|
|
1970
|
-
def format_fields(hci_object,
|
|
1971
|
-
if not
|
|
1987
|
+
def format_fields(hci_object, object_fields, indentation='', value_mappers=None):
|
|
1988
|
+
if not object_fields:
|
|
1972
1989
|
return ''
|
|
1973
1990
|
|
|
1974
1991
|
# Build array of formatted key:value pairs
|
|
1975
1992
|
field_strings = []
|
|
1976
|
-
for
|
|
1977
|
-
if isinstance(
|
|
1978
|
-
for sub_field in
|
|
1993
|
+
for object_field in object_fields:
|
|
1994
|
+
if isinstance(object_field, list):
|
|
1995
|
+
for sub_field in object_field:
|
|
1979
1996
|
sub_field_name, sub_field_type = sub_field
|
|
1980
1997
|
item_count = len(hci_object[sub_field_name])
|
|
1981
1998
|
for i in range(item_count):
|
|
@@ -1993,7 +2010,7 @@ class HCI_Object:
|
|
|
1993
2010
|
)
|
|
1994
2011
|
continue
|
|
1995
2012
|
|
|
1996
|
-
field_name, field_type =
|
|
2013
|
+
field_name, field_type = object_field
|
|
1997
2014
|
field_value = hci_object[field_name]
|
|
1998
2015
|
field_strings.append(
|
|
1999
2016
|
(
|
|
@@ -2016,16 +2033,16 @@ class HCI_Object:
|
|
|
2016
2033
|
@classmethod
|
|
2017
2034
|
def fields_from_dataclass(cls, obj: Any) -> list[Any]:
|
|
2018
2035
|
stack: list[list[Any]] = [[]]
|
|
2019
|
-
for
|
|
2036
|
+
for object_field in dataclasses.fields(obj):
|
|
2020
2037
|
# Fields without metadata should be ignored.
|
|
2021
2038
|
if not isinstance(
|
|
2022
|
-
(metadata :=
|
|
2039
|
+
(metadata := object_field.metadata.get("bumble.hci")), FieldMetadata
|
|
2023
2040
|
):
|
|
2024
2041
|
continue
|
|
2025
2042
|
if metadata.list_begin:
|
|
2026
2043
|
stack.append([])
|
|
2027
2044
|
if metadata.spec:
|
|
2028
|
-
stack[-1].append((
|
|
2045
|
+
stack[-1].append((object_field.name, metadata.spec))
|
|
2029
2046
|
if metadata.list_end:
|
|
2030
2047
|
top = stack.pop()
|
|
2031
2048
|
stack[-1].append(top)
|
|
@@ -2158,7 +2175,7 @@ class Address:
|
|
|
2158
2175
|
|
|
2159
2176
|
def __init__(
|
|
2160
2177
|
self,
|
|
2161
|
-
address:
|
|
2178
|
+
address: bytes | str,
|
|
2162
2179
|
address_type: AddressType = RANDOM_DEVICE_ADDRESS,
|
|
2163
2180
|
) -> None:
|
|
2164
2181
|
'''
|
|
@@ -2423,9 +2440,9 @@ class HCI_Command(HCI_Packet):
|
|
|
2423
2440
|
|
|
2424
2441
|
def __init__(
|
|
2425
2442
|
self,
|
|
2426
|
-
parameters:
|
|
2443
|
+
parameters: bytes | None = None,
|
|
2427
2444
|
*,
|
|
2428
|
-
op_code:
|
|
2445
|
+
op_code: int | None = None,
|
|
2429
2446
|
**kwargs,
|
|
2430
2447
|
) -> None:
|
|
2431
2448
|
# op_code should be set in cls.
|
|
@@ -5716,7 +5733,7 @@ class HCI_Event(HCI_Packet):
|
|
|
5716
5733
|
hci_packet_type = HCI_EVENT_PACKET
|
|
5717
5734
|
event_names: dict[int, str] = {}
|
|
5718
5735
|
event_classes: dict[int, type[HCI_Event]] = {}
|
|
5719
|
-
vendor_factories: list[Callable[[bytes],
|
|
5736
|
+
vendor_factories: list[Callable[[bytes], HCI_Event | None]] = []
|
|
5720
5737
|
event_code: int
|
|
5721
5738
|
fields: Fields = ()
|
|
5722
5739
|
_parameters: bytes = b''
|
|
@@ -5777,14 +5794,12 @@ class HCI_Event(HCI_Packet):
|
|
|
5777
5794
|
return event_class
|
|
5778
5795
|
|
|
5779
5796
|
@classmethod
|
|
5780
|
-
def add_vendor_factory(
|
|
5781
|
-
cls, factory: Callable[[bytes], Optional[HCI_Event]]
|
|
5782
|
-
) -> None:
|
|
5797
|
+
def add_vendor_factory(cls, factory: Callable[[bytes], HCI_Event | None]) -> None:
|
|
5783
5798
|
cls.vendor_factories.append(factory)
|
|
5784
5799
|
|
|
5785
5800
|
@classmethod
|
|
5786
5801
|
def remove_vendor_factory(
|
|
5787
|
-
cls, factory: Callable[[bytes],
|
|
5802
|
+
cls, factory: Callable[[bytes], HCI_Event | None]
|
|
5788
5803
|
) -> None:
|
|
5789
5804
|
if factory in cls.vendor_factories:
|
|
5790
5805
|
cls.vendor_factories.remove(factory)
|
|
@@ -5797,7 +5812,7 @@ class HCI_Event(HCI_Packet):
|
|
|
5797
5812
|
if len(parameters) != length:
|
|
5798
5813
|
raise InvalidPacketError('invalid packet length')
|
|
5799
5814
|
|
|
5800
|
-
subclass:
|
|
5815
|
+
subclass: type[HCI_Event] | None
|
|
5801
5816
|
if event_code == HCI_LE_META_EVENT:
|
|
5802
5817
|
# We do this dispatch here and not in the subclass in order to avoid call
|
|
5803
5818
|
# loops
|
|
@@ -5835,9 +5850,9 @@ class HCI_Event(HCI_Packet):
|
|
|
5835
5850
|
|
|
5836
5851
|
def __init__(
|
|
5837
5852
|
self,
|
|
5838
|
-
parameters:
|
|
5853
|
+
parameters: bytes | None = None,
|
|
5839
5854
|
*,
|
|
5840
|
-
event_code:
|
|
5855
|
+
event_code: int | None = None,
|
|
5841
5856
|
**kwargs,
|
|
5842
5857
|
):
|
|
5843
5858
|
if event_code is not None:
|
|
@@ -5946,9 +5961,7 @@ class HCI_Extended_Event(HCI_Event):
|
|
|
5946
5961
|
cls.subevent_names.update(cls.subevent_map(symbols))
|
|
5947
5962
|
|
|
5948
5963
|
@classmethod
|
|
5949
|
-
def subclass_from_parameters(
|
|
5950
|
-
cls, parameters: bytes
|
|
5951
|
-
) -> Optional[HCI_Extended_Event]:
|
|
5964
|
+
def subclass_from_parameters(cls, parameters: bytes) -> HCI_Extended_Event | None:
|
|
5952
5965
|
"""
|
|
5953
5966
|
Factory method that parses the subevent code, finds a registered subclass,
|
|
5954
5967
|
and creates an instance if found.
|
|
@@ -5968,9 +5981,9 @@ class HCI_Extended_Event(HCI_Event):
|
|
|
5968
5981
|
|
|
5969
5982
|
def __init__(
|
|
5970
5983
|
self,
|
|
5971
|
-
parameters:
|
|
5984
|
+
parameters: bytes | None = None,
|
|
5972
5985
|
*,
|
|
5973
|
-
subevent_code:
|
|
5986
|
+
subevent_code: int | None = None,
|
|
5974
5987
|
**kwargs,
|
|
5975
5988
|
) -> None:
|
|
5976
5989
|
if subevent_code is not None:
|
|
@@ -6966,7 +6979,7 @@ class HCI_Command_Complete_Event(HCI_Event):
|
|
|
6966
6979
|
command_opcode: int = field(
|
|
6967
6980
|
metadata=metadata({'size': 2, 'mapper': HCI_Command.command_name})
|
|
6968
6981
|
)
|
|
6969
|
-
return_parameters:
|
|
6982
|
+
return_parameters: bytes | HCI_Object | int = field(metadata=metadata("*"))
|
|
6970
6983
|
|
|
6971
6984
|
def map_return_parameters(self, return_parameters):
|
|
6972
6985
|
'''Map simple 'status' return parameters to their named constant form'''
|
|
@@ -7550,20 +7563,20 @@ class HCI_IsoDataPacket(HCI_Packet):
|
|
|
7550
7563
|
iso_sdu_fragment: bytes
|
|
7551
7564
|
pb_flag: int
|
|
7552
7565
|
ts_flag: int = 0
|
|
7553
|
-
time_stamp:
|
|
7554
|
-
packet_sequence_number:
|
|
7555
|
-
iso_sdu_length:
|
|
7556
|
-
packet_status_flag:
|
|
7566
|
+
time_stamp: int | None = None
|
|
7567
|
+
packet_sequence_number: int | None = None
|
|
7568
|
+
iso_sdu_length: int | None = None
|
|
7569
|
+
packet_status_flag: int | None = None
|
|
7557
7570
|
|
|
7558
7571
|
def __post_init__(self) -> None:
|
|
7559
7572
|
self.ts_flag = self.time_stamp is not None
|
|
7560
7573
|
|
|
7561
7574
|
@staticmethod
|
|
7562
7575
|
def from_bytes(packet: bytes) -> HCI_IsoDataPacket:
|
|
7563
|
-
time_stamp:
|
|
7564
|
-
packet_sequence_number:
|
|
7565
|
-
iso_sdu_length:
|
|
7566
|
-
packet_status_flag:
|
|
7576
|
+
time_stamp: int | None = None
|
|
7577
|
+
packet_sequence_number: int | None = None
|
|
7578
|
+
iso_sdu_length: int | None = None
|
|
7579
|
+
packet_status_flag: int | None = None
|
|
7567
7580
|
|
|
7568
7581
|
pos = 1
|
|
7569
7582
|
pdu_info, data_total_length = struct.unpack_from('<HH', packet, pos)
|
|
@@ -7646,7 +7659,7 @@ class HCI_IsoDataPacket(HCI_Packet):
|
|
|
7646
7659
|
|
|
7647
7660
|
# -----------------------------------------------------------------------------
|
|
7648
7661
|
class HCI_AclDataPacketAssembler:
|
|
7649
|
-
current_data:
|
|
7662
|
+
current_data: bytes | None
|
|
7650
7663
|
|
|
7651
7664
|
def __init__(self, callback: Callable[[bytes], Any]) -> None:
|
|
7652
7665
|
self.callback = callback
|
bumble/helpers.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import datetime
|
|
21
21
|
import logging
|
|
22
22
|
from collections.abc import Callable, MutableMapping
|
|
23
|
-
from typing import Any,
|
|
23
|
+
from typing import Any, cast
|
|
24
24
|
|
|
25
25
|
from bumble import avc, avctp, avdtp, avrcp, crypto, rfcomm, sdp
|
|
26
26
|
from bumble.att import ATT_CID, ATT_PDU
|
|
@@ -70,7 +70,7 @@ AVCTP_PID_NAMES = {avrcp.AVRCP_PID: 'AVRCP'}
|
|
|
70
70
|
class PacketTracer:
|
|
71
71
|
class AclStream:
|
|
72
72
|
psms: MutableMapping[int, int]
|
|
73
|
-
peer:
|
|
73
|
+
peer: PacketTracer.AclStream | None
|
|
74
74
|
avdtp_assemblers: MutableMapping[int, avdtp.MessageAssembler]
|
|
75
75
|
avctp_assemblers: MutableMapping[int, avctp.MessageAssembler]
|
|
76
76
|
|
|
@@ -201,7 +201,7 @@ class PacketTracer:
|
|
|
201
201
|
self.label = label
|
|
202
202
|
self.emit_message = emit_message
|
|
203
203
|
self.acl_streams = {} # ACL streams, by connection handle
|
|
204
|
-
self.packet_timestamp:
|
|
204
|
+
self.packet_timestamp: datetime.datetime | None = None
|
|
205
205
|
|
|
206
206
|
def start_acl_stream(self, connection_handle: int) -> PacketTracer.AclStream:
|
|
207
207
|
logger.info(
|
|
@@ -230,7 +230,7 @@ class PacketTracer:
|
|
|
230
230
|
self.peer.end_acl_stream(connection_handle)
|
|
231
231
|
|
|
232
232
|
def on_packet(
|
|
233
|
-
self, timestamp:
|
|
233
|
+
self, timestamp: datetime.datetime | None, packet: HCI_Packet
|
|
234
234
|
) -> None:
|
|
235
235
|
self.packet_timestamp = timestamp
|
|
236
236
|
self.emit(packet)
|
|
@@ -262,7 +262,7 @@ class PacketTracer:
|
|
|
262
262
|
self,
|
|
263
263
|
packet: HCI_Packet,
|
|
264
264
|
direction: int = 0,
|
|
265
|
-
timestamp:
|
|
265
|
+
timestamp: datetime.datetime | None = None,
|
|
266
266
|
) -> None:
|
|
267
267
|
if direction == 0:
|
|
268
268
|
self.host_to_controller_analyzer.on_packet(timestamp, packet)
|
bumble/hfp.py
CHANGED
|
@@ -25,7 +25,8 @@ import enum
|
|
|
25
25
|
import logging
|
|
26
26
|
import re
|
|
27
27
|
import traceback
|
|
28
|
-
from
|
|
28
|
+
from collections.abc import Iterable
|
|
29
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
29
30
|
|
|
30
31
|
from typing_extensions import Self
|
|
31
32
|
|
|
@@ -80,7 +81,7 @@ class HfpProtocol:
|
|
|
80
81
|
|
|
81
82
|
dlc.sink = self.feed
|
|
82
83
|
|
|
83
|
-
def feed(self, data:
|
|
84
|
+
def feed(self, data: bytes | str) -> None:
|
|
84
85
|
# Convert the data to a string if needed
|
|
85
86
|
if isinstance(data, bytes):
|
|
86
87
|
data = data.decode('utf-8')
|
|
@@ -324,8 +325,8 @@ class CallInfo:
|
|
|
324
325
|
status: CallInfoStatus
|
|
325
326
|
mode: CallInfoMode
|
|
326
327
|
multi_party: CallInfoMultiParty
|
|
327
|
-
number:
|
|
328
|
-
type:
|
|
328
|
+
number: str | None = None
|
|
329
|
+
type: int | None = None
|
|
329
330
|
|
|
330
331
|
|
|
331
332
|
@dataclasses.dataclass
|
|
@@ -353,10 +354,10 @@ class CallLineIdentification:
|
|
|
353
354
|
|
|
354
355
|
number: str
|
|
355
356
|
type: int
|
|
356
|
-
subaddr:
|
|
357
|
-
satype:
|
|
358
|
-
alpha:
|
|
359
|
-
cli_validity:
|
|
357
|
+
subaddr: str | None = None
|
|
358
|
+
satype: int | None = None
|
|
359
|
+
alpha: str | None = None
|
|
360
|
+
cli_validity: int | None = None
|
|
360
361
|
|
|
361
362
|
@classmethod
|
|
362
363
|
def parse_from(cls, parameters: list[bytes]) -> Self:
|
|
@@ -584,7 +585,7 @@ class AgIndicatorState:
|
|
|
584
585
|
indicator: AgIndicator
|
|
585
586
|
supported_values: set[int]
|
|
586
587
|
current_status: int
|
|
587
|
-
index:
|
|
588
|
+
index: int | None = None
|
|
588
589
|
enabled: bool = True
|
|
589
590
|
|
|
590
591
|
@property
|
|
@@ -597,7 +598,7 @@ class AgIndicatorState:
|
|
|
597
598
|
supported_values_text = (
|
|
598
599
|
f'({",".join(str(v) for v in self.supported_values)})'
|
|
599
600
|
)
|
|
600
|
-
return f'(
|
|
601
|
+
return f'("{self.indicator.value}",{supported_values_text})'
|
|
601
602
|
|
|
602
603
|
@classmethod
|
|
603
604
|
def call(cls: type[Self]) -> Self:
|
|
@@ -728,7 +729,7 @@ class HfProtocol(utils.EventEmitter):
|
|
|
728
729
|
command_lock: asyncio.Lock
|
|
729
730
|
if TYPE_CHECKING:
|
|
730
731
|
response_queue: asyncio.Queue[AtResponse]
|
|
731
|
-
unsolicited_queue: asyncio.Queue[
|
|
732
|
+
unsolicited_queue: asyncio.Queue[AtResponse | None]
|
|
732
733
|
else:
|
|
733
734
|
response_queue: asyncio.Queue
|
|
734
735
|
unsolicited_queue: asyncio.Queue
|
|
@@ -820,7 +821,7 @@ class HfProtocol(utils.EventEmitter):
|
|
|
820
821
|
cmd: str,
|
|
821
822
|
timeout: float = 1.0,
|
|
822
823
|
response_type: AtResponseType = AtResponseType.NONE,
|
|
823
|
-
) ->
|
|
824
|
+
) -> None | AtResponse | list[AtResponse]:
|
|
824
825
|
"""
|
|
825
826
|
Sends an AT command and wait for the peer response.
|
|
826
827
|
Wait for the AT responses sent by the peer, to the status code.
|
|
@@ -1351,7 +1352,7 @@ class AgProtocol(utils.EventEmitter):
|
|
|
1351
1352
|
logger.warning(f'AG indicator {indicator} is disabled')
|
|
1352
1353
|
|
|
1353
1354
|
indicator_state.current_status = value
|
|
1354
|
-
self.send_response(f'+CIEV: {index+1},{value}')
|
|
1355
|
+
self.send_response(f'+CIEV: {index + 1},{value}')
|
|
1355
1356
|
|
|
1356
1357
|
async def negotiate_codec(self, codec: AudioCodec) -> None:
|
|
1357
1358
|
"""Starts codec negotiation."""
|
|
@@ -1411,13 +1412,13 @@ class AgProtocol(utils.EventEmitter):
|
|
|
1411
1412
|
self.emit(self.EVENT_VOICE_RECOGNITION, VoiceRecognitionState(int(vrec)))
|
|
1412
1413
|
|
|
1413
1414
|
def _on_chld(self, operation_code: bytes) -> None:
|
|
1414
|
-
call_index:
|
|
1415
|
+
call_index: int | None = None
|
|
1415
1416
|
if len(operation_code) > 1:
|
|
1416
1417
|
call_index = int(operation_code[1:])
|
|
1417
1418
|
operation_code = operation_code[:1] + b'x'
|
|
1418
1419
|
try:
|
|
1419
1420
|
operation = CallHoldOperation(operation_code.decode())
|
|
1420
|
-
except:
|
|
1421
|
+
except Exception:
|
|
1421
1422
|
logger.error(f'Invalid operation: {operation_code.decode()}')
|
|
1422
1423
|
self.send_cme_error(CmeError.OPERATION_NOT_SUPPORTED)
|
|
1423
1424
|
return
|
|
@@ -1481,8 +1482,8 @@ class AgProtocol(utils.EventEmitter):
|
|
|
1481
1482
|
def _on_cmer(
|
|
1482
1483
|
self,
|
|
1483
1484
|
mode: bytes,
|
|
1484
|
-
keypad:
|
|
1485
|
-
display:
|
|
1485
|
+
keypad: bytes | None = None,
|
|
1486
|
+
display: bytes | None = None,
|
|
1486
1487
|
indicator: bytes = b'',
|
|
1487
1488
|
) -> None:
|
|
1488
1489
|
if (
|
|
@@ -1589,7 +1590,7 @@ class AgProtocol(utils.EventEmitter):
|
|
|
1589
1590
|
|
|
1590
1591
|
def _on_clcc(self) -> None:
|
|
1591
1592
|
for call in self.calls:
|
|
1592
|
-
number_text = f'
|
|
1593
|
+
number_text = f',"{call.number}"' if call.number is not None else ''
|
|
1593
1594
|
type_text = f',{call.type}' if call.type is not None else ''
|
|
1594
1595
|
response = (
|
|
1595
1596
|
f'+CLCC: {call.index}'
|
|
@@ -1844,7 +1845,7 @@ def make_ag_sdp_records(
|
|
|
1844
1845
|
|
|
1845
1846
|
async def find_hf_sdp_record(
|
|
1846
1847
|
connection: device.Connection,
|
|
1847
|
-
) ->
|
|
1848
|
+
) -> tuple[int, ProfileVersion, HfSdpFeature] | None:
|
|
1848
1849
|
"""Searches a Hands-Free SDP record from remote device.
|
|
1849
1850
|
|
|
1850
1851
|
Args:
|
|
@@ -1864,9 +1865,9 @@ async def find_hf_sdp_record(
|
|
|
1864
1865
|
],
|
|
1865
1866
|
)
|
|
1866
1867
|
for attribute_lists in search_result:
|
|
1867
|
-
channel:
|
|
1868
|
-
version:
|
|
1869
|
-
features:
|
|
1868
|
+
channel: int | None = None
|
|
1869
|
+
version: ProfileVersion | None = None
|
|
1870
|
+
features: HfSdpFeature | None = None
|
|
1870
1871
|
for attribute in attribute_lists:
|
|
1871
1872
|
# The layout is [[L2CAP_PROTOCOL], [RFCOMM_PROTOCOL, RFCOMM_CHANNEL]].
|
|
1872
1873
|
if attribute.id == sdp.SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
|
|
@@ -1896,7 +1897,7 @@ async def find_hf_sdp_record(
|
|
|
1896
1897
|
|
|
1897
1898
|
async def find_ag_sdp_record(
|
|
1898
1899
|
connection: device.Connection,
|
|
1899
|
-
) ->
|
|
1900
|
+
) -> tuple[int, ProfileVersion, AgSdpFeature] | None:
|
|
1900
1901
|
"""Searches an Audio-Gateway SDP record from remote device.
|
|
1901
1902
|
|
|
1902
1903
|
Args:
|
|
@@ -1915,9 +1916,9 @@ async def find_ag_sdp_record(
|
|
|
1915
1916
|
],
|
|
1916
1917
|
)
|
|
1917
1918
|
for attribute_lists in search_result:
|
|
1918
|
-
channel:
|
|
1919
|
-
version:
|
|
1920
|
-
features:
|
|
1919
|
+
channel: int | None = None
|
|
1920
|
+
version: ProfileVersion | None = None
|
|
1921
|
+
features: AgSdpFeature | None = None
|
|
1921
1922
|
for attribute in attribute_lists:
|
|
1922
1923
|
# The layout is [[L2CAP_PROTOCOL], [RFCOMM_PROTOCOL, RFCOMM_CHANNEL]].
|
|
1923
1924
|
if attribute.id == sdp.SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
|
bumble/hid.py
CHANGED
|
@@ -21,8 +21,8 @@ import enum
|
|
|
21
21
|
import logging
|
|
22
22
|
import struct
|
|
23
23
|
from abc import ABC, abstractmethod
|
|
24
|
+
from collections.abc import Callable
|
|
24
25
|
from dataclasses import dataclass
|
|
25
|
-
from typing import Callable, Optional
|
|
26
26
|
|
|
27
27
|
from typing_extensions import override
|
|
28
28
|
|
|
@@ -195,9 +195,9 @@ class SendHandshakeMessage(Message):
|
|
|
195
195
|
|
|
196
196
|
# -----------------------------------------------------------------------------
|
|
197
197
|
class HID(ABC, utils.EventEmitter):
|
|
198
|
-
l2cap_ctrl_channel:
|
|
199
|
-
l2cap_intr_channel:
|
|
200
|
-
connection:
|
|
198
|
+
l2cap_ctrl_channel: l2cap.ClassicChannel | None = None
|
|
199
|
+
l2cap_intr_channel: l2cap.ClassicChannel | None = None
|
|
200
|
+
connection: device.Connection | None = None
|
|
201
201
|
|
|
202
202
|
EVENT_INTERRUPT_DATA = "interrupt_data"
|
|
203
203
|
EVENT_CONTROL_DATA = "control_data"
|
|
@@ -212,7 +212,7 @@ class HID(ABC, utils.EventEmitter):
|
|
|
212
212
|
|
|
213
213
|
def __init__(self, device: device.Device, role: Role) -> None:
|
|
214
214
|
super().__init__()
|
|
215
|
-
self.remote_device_bd_address:
|
|
215
|
+
self.remote_device_bd_address: Address | None = None
|
|
216
216
|
self.device = device
|
|
217
217
|
self.role = role
|
|
218
218
|
|
|
@@ -353,10 +353,10 @@ class Device(HID):
|
|
|
353
353
|
data: bytes = b''
|
|
354
354
|
status: int = 0
|
|
355
355
|
|
|
356
|
-
get_report_cb:
|
|
357
|
-
set_report_cb:
|
|
358
|
-
get_protocol_cb:
|
|
359
|
-
set_protocol_cb:
|
|
356
|
+
get_report_cb: Callable[[int, int, int], GetSetStatus] | None = None
|
|
357
|
+
set_report_cb: Callable[[int, int, int, bytes], GetSetStatus] | None = None
|
|
358
|
+
get_protocol_cb: Callable[[], GetSetStatus] | None = None
|
|
359
|
+
set_protocol_cb: Callable[[int], GetSetStatus] | None = None
|
|
360
360
|
|
|
361
361
|
def __init__(self, device: device.Device) -> None:
|
|
362
362
|
super().__init__(device, HID.Role.DEVICE)
|