bumble 0.0.219__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 -479
- 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 +8 -6
- 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 +1201 -643
- 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 +278 -325
- 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 +54 -284
- bumble/ll.py +200 -0
- bumble/lmp.py +324 -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 +12 -5
- 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 +23 -14
- bumble/vendor/android/hci.py +4 -2
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/METADATA +4 -3
- bumble-0.0.221.dist-info/RECORD +185 -0
- bumble-0.0.219.dist-info/RECORD +0 -183
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/att.py
CHANGED
|
@@ -29,18 +29,18 @@ import enum
|
|
|
29
29
|
import functools
|
|
30
30
|
import inspect
|
|
31
31
|
import struct
|
|
32
|
+
from collections.abc import Awaitable, Callable
|
|
32
33
|
from typing import (
|
|
33
34
|
TYPE_CHECKING,
|
|
34
|
-
Awaitable,
|
|
35
|
-
Callable,
|
|
36
35
|
ClassVar,
|
|
37
36
|
Generic,
|
|
38
|
-
|
|
37
|
+
TypeAlias,
|
|
39
38
|
TypeVar,
|
|
40
|
-
Union,
|
|
41
39
|
)
|
|
42
40
|
|
|
43
|
-
from
|
|
41
|
+
from typing_extensions import TypeIs
|
|
42
|
+
|
|
43
|
+
from bumble import hci, l2cap, utils
|
|
44
44
|
from bumble.colors import color
|
|
45
45
|
from bumble.core import UUID, InvalidOperationError, ProtocolError
|
|
46
46
|
from bumble.hci import HCI_Object
|
|
@@ -53,6 +53,14 @@ if TYPE_CHECKING:
|
|
|
53
53
|
|
|
54
54
|
_T = TypeVar('_T')
|
|
55
55
|
|
|
56
|
+
Bearer: TypeAlias = "Connection | l2cap.LeCreditBasedChannel"
|
|
57
|
+
EnhancedBearer: TypeAlias = l2cap.LeCreditBasedChannel
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def is_enhanced_bearer(bearer: Bearer) -> TypeIs[EnhancedBearer]:
|
|
61
|
+
return isinstance(bearer, EnhancedBearer)
|
|
62
|
+
|
|
63
|
+
|
|
56
64
|
# -----------------------------------------------------------------------------
|
|
57
65
|
# Constants
|
|
58
66
|
# -----------------------------------------------------------------------------
|
|
@@ -61,6 +69,7 @@ _T = TypeVar('_T')
|
|
|
61
69
|
|
|
62
70
|
ATT_CID = 0x04
|
|
63
71
|
ATT_PSM = 0x001F
|
|
72
|
+
EATT_PSM = 0x0027
|
|
64
73
|
|
|
65
74
|
class Opcode(hci.SpecableEnum):
|
|
66
75
|
ATT_ERROR_RESPONSE = 0x01
|
|
@@ -220,7 +229,7 @@ class ATT_PDU:
|
|
|
220
229
|
fields: ClassVar[hci.Fields] = ()
|
|
221
230
|
op_code: int = dataclasses.field(init=False)
|
|
222
231
|
name: str = dataclasses.field(init=False)
|
|
223
|
-
_payload:
|
|
232
|
+
_payload: bytes | None = dataclasses.field(default=None, init=False)
|
|
224
233
|
|
|
225
234
|
@classmethod
|
|
226
235
|
def from_bytes(cls, pdu: bytes) -> ATT_PDU:
|
|
@@ -760,31 +769,66 @@ class AttributeValue(Generic[_T]):
|
|
|
760
769
|
|
|
761
770
|
def __init__(
|
|
762
771
|
self,
|
|
763
|
-
read:
|
|
764
|
-
Callable[[Connection], _T],
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
None,
|
|
772
|
-
] = None,
|
|
772
|
+
read: (
|
|
773
|
+
Callable[[Connection], _T] | Callable[[Connection], Awaitable[_T]] | None
|
|
774
|
+
) = None,
|
|
775
|
+
write: (
|
|
776
|
+
Callable[[Connection, _T], None]
|
|
777
|
+
| Callable[[Connection, _T], Awaitable[None]]
|
|
778
|
+
| None
|
|
779
|
+
) = None,
|
|
773
780
|
):
|
|
774
781
|
self._read = read
|
|
775
782
|
self._write = write
|
|
776
783
|
|
|
777
|
-
def read(self, connection: Connection) ->
|
|
784
|
+
def read(self, connection: Connection) -> _T | Awaitable[_T]:
|
|
778
785
|
if self._read is None:
|
|
779
786
|
raise InvalidOperationError('AttributeValue has no read function')
|
|
780
787
|
return self._read(connection)
|
|
781
788
|
|
|
782
|
-
def write(self, connection: Connection, value: _T) ->
|
|
789
|
+
def write(self, connection: Connection, value: _T) -> Awaitable[None] | None:
|
|
783
790
|
if self._write is None:
|
|
784
791
|
raise InvalidOperationError('AttributeValue has no write function')
|
|
785
792
|
return self._write(connection, value)
|
|
786
793
|
|
|
787
794
|
|
|
795
|
+
# -----------------------------------------------------------------------------
|
|
796
|
+
class AttributeValueV2(Generic[_T]):
|
|
797
|
+
'''
|
|
798
|
+
Attribute value compatible with enhanced bearers.
|
|
799
|
+
|
|
800
|
+
The only difference between AttributeValue and AttributeValueV2 is that the actual
|
|
801
|
+
bearer (ACL connection for un-enhanced bearer, L2CAP channel for enhanced bearer)
|
|
802
|
+
will be passed into read and write callbacks in V2, while in V1 it is always
|
|
803
|
+
the base ACL connection.
|
|
804
|
+
|
|
805
|
+
This is only required when attributes must distinguish bearers, otherwise normal
|
|
806
|
+
`AttributeValue` objects are also applicable in enhanced bearers.
|
|
807
|
+
'''
|
|
808
|
+
|
|
809
|
+
def __init__(
|
|
810
|
+
self,
|
|
811
|
+
read: Callable[[Bearer], Awaitable[_T]] | Callable[[Bearer], _T] | None = None,
|
|
812
|
+
write: (
|
|
813
|
+
Callable[[Bearer, _T], Awaitable[None]]
|
|
814
|
+
| Callable[[Bearer, _T], None]
|
|
815
|
+
| None
|
|
816
|
+
) = None,
|
|
817
|
+
):
|
|
818
|
+
self._read = read
|
|
819
|
+
self._write = write
|
|
820
|
+
|
|
821
|
+
def read(self, bearer: Bearer) -> _T | Awaitable[_T]:
|
|
822
|
+
if self._read is None:
|
|
823
|
+
raise InvalidOperationError('AttributeValue has no read function')
|
|
824
|
+
return self._read(bearer)
|
|
825
|
+
|
|
826
|
+
def write(self, bearer: Bearer, value: _T) -> Awaitable[None] | None:
|
|
827
|
+
if self._write is None:
|
|
828
|
+
raise InvalidOperationError('AttributeValue has no write function')
|
|
829
|
+
return self._write(bearer, value)
|
|
830
|
+
|
|
831
|
+
|
|
788
832
|
# -----------------------------------------------------------------------------
|
|
789
833
|
class Attribute(utils.EventEmitter, Generic[_T]):
|
|
790
834
|
class Permissions(enum.IntFlag):
|
|
@@ -828,13 +872,13 @@ class Attribute(utils.EventEmitter, Generic[_T]):
|
|
|
828
872
|
EVENT_READ = "read"
|
|
829
873
|
EVENT_WRITE = "write"
|
|
830
874
|
|
|
831
|
-
value:
|
|
875
|
+
value: AttributeValue[_T] | _T | None
|
|
832
876
|
|
|
833
877
|
def __init__(
|
|
834
878
|
self,
|
|
835
|
-
attribute_type:
|
|
836
|
-
permissions:
|
|
837
|
-
value:
|
|
879
|
+
attribute_type: str | bytes | UUID,
|
|
880
|
+
permissions: str | Attribute.Permissions,
|
|
881
|
+
value: AttributeValue[_T] | _T | None = None,
|
|
838
882
|
) -> None:
|
|
839
883
|
utils.EventEmitter.__init__(self)
|
|
840
884
|
self.handle = 0
|
|
@@ -860,7 +904,8 @@ class Attribute(utils.EventEmitter, Generic[_T]):
|
|
|
860
904
|
def decode_value(self, value: bytes) -> _T:
|
|
861
905
|
return value # type: ignore
|
|
862
906
|
|
|
863
|
-
async def read_value(self,
|
|
907
|
+
async def read_value(self, bearer: Bearer) -> bytes:
|
|
908
|
+
connection = bearer.connection if is_enhanced_bearer(bearer) else bearer
|
|
864
909
|
if (
|
|
865
910
|
(self.permissions & self.READ_REQUIRES_ENCRYPTION)
|
|
866
911
|
and connection is not None
|
|
@@ -883,7 +928,7 @@ class Attribute(utils.EventEmitter, Generic[_T]):
|
|
|
883
928
|
error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle
|
|
884
929
|
)
|
|
885
930
|
|
|
886
|
-
value:
|
|
931
|
+
value: _T | None
|
|
887
932
|
if isinstance(self.value, AttributeValue):
|
|
888
933
|
try:
|
|
889
934
|
read_value = self.value.read(connection)
|
|
@@ -895,6 +940,17 @@ class Attribute(utils.EventEmitter, Generic[_T]):
|
|
|
895
940
|
raise ATT_Error(
|
|
896
941
|
error_code=error.error_code, att_handle=self.handle
|
|
897
942
|
) from error
|
|
943
|
+
elif isinstance(self.value, AttributeValueV2):
|
|
944
|
+
try:
|
|
945
|
+
read_value = self.value.read(bearer)
|
|
946
|
+
if inspect.isawaitable(read_value):
|
|
947
|
+
value = await read_value
|
|
948
|
+
else:
|
|
949
|
+
value = read_value
|
|
950
|
+
except ATT_Error as error:
|
|
951
|
+
raise ATT_Error(
|
|
952
|
+
error_code=error.error_code, att_handle=self.handle
|
|
953
|
+
) from error
|
|
898
954
|
else:
|
|
899
955
|
value = self.value
|
|
900
956
|
|
|
@@ -902,7 +958,8 @@ class Attribute(utils.EventEmitter, Generic[_T]):
|
|
|
902
958
|
|
|
903
959
|
return b'' if value is None else self.encode_value(value)
|
|
904
960
|
|
|
905
|
-
async def write_value(self,
|
|
961
|
+
async def write_value(self, bearer: Bearer, value: bytes) -> None:
|
|
962
|
+
connection = bearer.connection if is_enhanced_bearer(bearer) else bearer
|
|
906
963
|
if (
|
|
907
964
|
(self.permissions & self.WRITE_REQUIRES_ENCRYPTION)
|
|
908
965
|
and connection is not None
|
|
@@ -936,6 +993,15 @@ class Attribute(utils.EventEmitter, Generic[_T]):
|
|
|
936
993
|
raise ATT_Error(
|
|
937
994
|
error_code=error.error_code, att_handle=self.handle
|
|
938
995
|
) from error
|
|
996
|
+
elif isinstance(self.value, AttributeValueV2):
|
|
997
|
+
try:
|
|
998
|
+
result = self.value.write(bearer, decoded_value)
|
|
999
|
+
if inspect.isawaitable(result):
|
|
1000
|
+
await result
|
|
1001
|
+
except ATT_Error as error:
|
|
1002
|
+
raise ATT_Error(
|
|
1003
|
+
error_code=error.error_code, att_handle=self.handle
|
|
1004
|
+
) from error
|
|
939
1005
|
else:
|
|
940
1006
|
self.value = decoded_value
|
|
941
1007
|
|
bumble/audio/io.py
CHANGED
|
@@ -19,14 +19,15 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import abc
|
|
21
21
|
import asyncio
|
|
22
|
+
import concurrent.futures
|
|
22
23
|
import dataclasses
|
|
23
24
|
import enum
|
|
24
25
|
import logging
|
|
25
26
|
import pathlib
|
|
26
27
|
import sys
|
|
27
28
|
import wave
|
|
28
|
-
from
|
|
29
|
-
from typing import TYPE_CHECKING,
|
|
29
|
+
from collections.abc import AsyncGenerator
|
|
30
|
+
from typing import TYPE_CHECKING, BinaryIO
|
|
30
31
|
|
|
31
32
|
from bumble.colors import color
|
|
32
33
|
|
|
@@ -176,7 +177,7 @@ class ThreadedAudioOutput(AudioOutput):
|
|
|
176
177
|
"""
|
|
177
178
|
|
|
178
179
|
def __init__(self) -> None:
|
|
179
|
-
self._thread_pool = ThreadPoolExecutor(1)
|
|
180
|
+
self._thread_pool = concurrent.futures.ThreadPoolExecutor(1)
|
|
180
181
|
self._pcm_samples: asyncio.Queue[bytes] = asyncio.Queue()
|
|
181
182
|
self._write_task = asyncio.create_task(self._write_loop())
|
|
182
183
|
|
|
@@ -405,7 +406,7 @@ class ThreadedAudioInput(AudioInput):
|
|
|
405
406
|
"""Base class for AudioInput implementation where reading samples may block."""
|
|
406
407
|
|
|
407
408
|
def __init__(self) -> None:
|
|
408
|
-
self._thread_pool = ThreadPoolExecutor(1)
|
|
409
|
+
self._thread_pool = concurrent.futures.ThreadPoolExecutor(1)
|
|
409
410
|
self._pcm_samples: asyncio.Queue[bytes] = asyncio.Queue()
|
|
410
411
|
|
|
411
412
|
@abc.abstractmethod
|
|
@@ -545,5 +546,6 @@ class SoundDeviceAudioInput(ThreadedAudioInput):
|
|
|
545
546
|
return bytes(pcm_buffer)
|
|
546
547
|
|
|
547
548
|
def _close(self):
|
|
548
|
-
self._stream
|
|
549
|
-
|
|
549
|
+
if self._stream:
|
|
550
|
+
self._stream.stop()
|
|
551
|
+
self._stream = None
|
bumble/avc.py
CHANGED
|
@@ -19,7 +19,6 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import enum
|
|
21
21
|
import struct
|
|
22
|
-
from typing import Union
|
|
23
22
|
|
|
24
23
|
from bumble import core, utils
|
|
25
24
|
|
|
@@ -166,7 +165,7 @@ class Frame:
|
|
|
166
165
|
|
|
167
166
|
def to_bytes(
|
|
168
167
|
self,
|
|
169
|
-
ctype_or_response:
|
|
168
|
+
ctype_or_response: CommandFrame.CommandType | ResponseFrame.ResponseCode,
|
|
170
169
|
) -> bytes:
|
|
171
170
|
# TODO: support extended subunit types and ids.
|
|
172
171
|
return (
|
bumble/avctp.py
CHANGED
|
@@ -21,7 +21,6 @@ import logging
|
|
|
21
21
|
import struct
|
|
22
22
|
from collections.abc import Callable
|
|
23
23
|
from enum import IntEnum
|
|
24
|
-
from typing import Optional
|
|
25
24
|
|
|
26
25
|
from bumble import core, l2cap
|
|
27
26
|
from bumble.colors import color
|
|
@@ -147,7 +146,7 @@ class MessageAssembler:
|
|
|
147
146
|
class Protocol:
|
|
148
147
|
CommandHandler = Callable[[int, bytes], None]
|
|
149
148
|
command_handlers: dict[int, CommandHandler] # Command handlers, by PID
|
|
150
|
-
ResponseHandler = Callable[[int,
|
|
149
|
+
ResponseHandler = Callable[[int, bytes | None], None]
|
|
151
150
|
response_handlers: dict[int, ResponseHandler] # Response handlers, by PID
|
|
152
151
|
next_transaction_label: int
|
|
153
152
|
message_assembler: MessageAssembler
|
|
@@ -258,7 +257,7 @@ class Protocol:
|
|
|
258
257
|
|
|
259
258
|
def send_ipid(self, transaction_label: int, pid: int) -> None:
|
|
260
259
|
logger.debug(
|
|
261
|
-
">>> AVCTP ipid:
|
|
260
|
+
f">>> AVCTP ipid: transaction_label={transaction_label}, pid={pid}"
|
|
262
261
|
)
|
|
263
262
|
self.send_message(transaction_label, False, True, pid, b'')
|
|
264
263
|
|