bumble 0.0.204__py3-none-any.whl → 0.0.208__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 +9 -4
- bumble/apps/auracast.py +631 -98
- bumble/apps/bench.py +238 -157
- bumble/apps/console.py +19 -12
- bumble/apps/controller_info.py +23 -7
- bumble/apps/device_info.py +50 -4
- bumble/apps/gg_bridge.py +1 -1
- bumble/apps/lea_unicast/app.py +61 -201
- bumble/att.py +51 -37
- bumble/audio/__init__.py +17 -0
- bumble/audio/io.py +553 -0
- bumble/controller.py +24 -9
- bumble/core.py +305 -156
- bumble/device.py +1090 -99
- bumble/gatt.py +36 -226
- bumble/gatt_adapters.py +374 -0
- bumble/gatt_client.py +52 -33
- bumble/gatt_server.py +5 -5
- bumble/hci.py +812 -14
- bumble/host.py +367 -65
- bumble/l2cap.py +3 -16
- bumble/pairing.py +5 -5
- bumble/pandora/host.py +7 -12
- bumble/profiles/aics.py +48 -57
- bumble/profiles/ascs.py +8 -19
- bumble/profiles/asha.py +16 -14
- bumble/profiles/bass.py +16 -22
- bumble/profiles/battery_service.py +13 -3
- bumble/profiles/device_information_service.py +16 -14
- bumble/profiles/gap.py +12 -8
- bumble/profiles/gatt_service.py +167 -0
- bumble/profiles/gmap.py +198 -0
- bumble/profiles/hap.py +8 -6
- bumble/profiles/heart_rate_service.py +20 -4
- bumble/profiles/le_audio.py +87 -4
- bumble/profiles/mcp.py +11 -9
- bumble/profiles/pacs.py +61 -16
- bumble/profiles/tmap.py +8 -12
- bumble/profiles/{vcp.py → vcs.py} +35 -29
- bumble/profiles/vocs.py +62 -85
- bumble/sdp.py +223 -93
- bumble/smp.py +1 -1
- bumble/utils.py +12 -2
- bumble/vendor/android/hci.py +1 -1
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/METADATA +13 -11
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/RECORD +50 -46
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/WHEEL +1 -1
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/entry_points.txt +1 -0
- bumble/apps/lea_unicast/liblc3.wasm +0 -0
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/LICENSE +0 -0
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/top_level.txt +0 -0
bumble/att.py
CHANGED
|
@@ -29,13 +29,14 @@ import functools
|
|
|
29
29
|
import inspect
|
|
30
30
|
import struct
|
|
31
31
|
from typing import (
|
|
32
|
-
Any,
|
|
33
32
|
Awaitable,
|
|
34
33
|
Callable,
|
|
34
|
+
Generic,
|
|
35
35
|
Dict,
|
|
36
36
|
List,
|
|
37
37
|
Optional,
|
|
38
38
|
Type,
|
|
39
|
+
TypeVar,
|
|
39
40
|
Union,
|
|
40
41
|
TYPE_CHECKING,
|
|
41
42
|
)
|
|
@@ -43,13 +44,18 @@ from typing import (
|
|
|
43
44
|
from pyee import EventEmitter
|
|
44
45
|
|
|
45
46
|
from bumble import utils
|
|
46
|
-
from bumble.core import UUID, name_or_number, ProtocolError
|
|
47
|
+
from bumble.core import UUID, name_or_number, InvalidOperationError, ProtocolError
|
|
47
48
|
from bumble.hci import HCI_Object, key_with_value
|
|
48
49
|
from bumble.colors import color
|
|
49
50
|
|
|
51
|
+
# -----------------------------------------------------------------------------
|
|
52
|
+
# Typing
|
|
53
|
+
# -----------------------------------------------------------------------------
|
|
50
54
|
if TYPE_CHECKING:
|
|
51
55
|
from bumble.device import Connection
|
|
52
56
|
|
|
57
|
+
_T = TypeVar('_T')
|
|
58
|
+
|
|
53
59
|
# -----------------------------------------------------------------------------
|
|
54
60
|
# Constants
|
|
55
61
|
# -----------------------------------------------------------------------------
|
|
@@ -748,7 +754,7 @@ class ATT_Handle_Value_Confirmation(ATT_PDU):
|
|
|
748
754
|
|
|
749
755
|
|
|
750
756
|
# -----------------------------------------------------------------------------
|
|
751
|
-
class AttributeValue:
|
|
757
|
+
class AttributeValue(Generic[_T]):
|
|
752
758
|
'''
|
|
753
759
|
Attribute value where reading and/or writing is delegated to functions
|
|
754
760
|
passed as arguments to the constructor.
|
|
@@ -757,33 +763,34 @@ class AttributeValue:
|
|
|
757
763
|
def __init__(
|
|
758
764
|
self,
|
|
759
765
|
read: Union[
|
|
760
|
-
Callable[[Optional[Connection]],
|
|
761
|
-
Callable[[Optional[Connection]], Awaitable[
|
|
766
|
+
Callable[[Optional[Connection]], _T],
|
|
767
|
+
Callable[[Optional[Connection]], Awaitable[_T]],
|
|
762
768
|
None,
|
|
763
769
|
] = None,
|
|
764
770
|
write: Union[
|
|
765
|
-
Callable[[Optional[Connection],
|
|
766
|
-
Callable[[Optional[Connection],
|
|
771
|
+
Callable[[Optional[Connection], _T], None],
|
|
772
|
+
Callable[[Optional[Connection], _T], Awaitable[None]],
|
|
767
773
|
None,
|
|
768
774
|
] = None,
|
|
769
775
|
):
|
|
770
776
|
self._read = read
|
|
771
777
|
self._write = write
|
|
772
778
|
|
|
773
|
-
def read(self, connection: Optional[Connection]) -> Union[
|
|
774
|
-
|
|
779
|
+
def read(self, connection: Optional[Connection]) -> Union[_T, Awaitable[_T]]:
|
|
780
|
+
if self._read is None:
|
|
781
|
+
raise InvalidOperationError('AttributeValue has no read function')
|
|
782
|
+
return self._read(connection)
|
|
775
783
|
|
|
776
784
|
def write(
|
|
777
|
-
self, connection: Optional[Connection], value:
|
|
785
|
+
self, connection: Optional[Connection], value: _T
|
|
778
786
|
) -> Union[Awaitable[None], None]:
|
|
779
|
-
if self._write:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
return None
|
|
787
|
+
if self._write is None:
|
|
788
|
+
raise InvalidOperationError('AttributeValue has no write function')
|
|
789
|
+
return self._write(connection, value)
|
|
783
790
|
|
|
784
791
|
|
|
785
792
|
# -----------------------------------------------------------------------------
|
|
786
|
-
class Attribute(EventEmitter):
|
|
793
|
+
class Attribute(EventEmitter, Generic[_T]):
|
|
787
794
|
class Permissions(enum.IntFlag):
|
|
788
795
|
READABLE = 0x01
|
|
789
796
|
WRITEABLE = 0x02
|
|
@@ -822,13 +829,13 @@ class Attribute(EventEmitter):
|
|
|
822
829
|
READ_REQUIRES_AUTHORIZATION = Permissions.READ_REQUIRES_AUTHORIZATION
|
|
823
830
|
WRITE_REQUIRES_AUTHORIZATION = Permissions.WRITE_REQUIRES_AUTHORIZATION
|
|
824
831
|
|
|
825
|
-
value:
|
|
832
|
+
value: Union[AttributeValue[_T], _T, None]
|
|
826
833
|
|
|
827
834
|
def __init__(
|
|
828
835
|
self,
|
|
829
836
|
attribute_type: Union[str, bytes, UUID],
|
|
830
837
|
permissions: Union[str, Attribute.Permissions],
|
|
831
|
-
value:
|
|
838
|
+
value: Union[AttributeValue[_T], _T, None] = None,
|
|
832
839
|
) -> None:
|
|
833
840
|
EventEmitter.__init__(self)
|
|
834
841
|
self.handle = 0
|
|
@@ -848,11 +855,11 @@ class Attribute(EventEmitter):
|
|
|
848
855
|
|
|
849
856
|
self.value = value
|
|
850
857
|
|
|
851
|
-
def encode_value(self, value:
|
|
852
|
-
return value
|
|
858
|
+
def encode_value(self, value: _T) -> bytes:
|
|
859
|
+
return value # type: ignore
|
|
853
860
|
|
|
854
|
-
def decode_value(self,
|
|
855
|
-
return
|
|
861
|
+
def decode_value(self, value: bytes) -> _T:
|
|
862
|
+
return value # type: ignore
|
|
856
863
|
|
|
857
864
|
async def read_value(self, connection: Optional[Connection]) -> bytes:
|
|
858
865
|
if (
|
|
@@ -877,11 +884,14 @@ class Attribute(EventEmitter):
|
|
|
877
884
|
error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle
|
|
878
885
|
)
|
|
879
886
|
|
|
880
|
-
|
|
887
|
+
value: Union[_T, None]
|
|
888
|
+
if isinstance(self.value, AttributeValue):
|
|
881
889
|
try:
|
|
882
|
-
|
|
883
|
-
if inspect.isawaitable(
|
|
884
|
-
value = await
|
|
890
|
+
read_value = self.value.read(connection)
|
|
891
|
+
if inspect.isawaitable(read_value):
|
|
892
|
+
value = await read_value
|
|
893
|
+
else:
|
|
894
|
+
value = read_value
|
|
885
895
|
except ATT_Error as error:
|
|
886
896
|
raise ATT_Error(
|
|
887
897
|
error_code=error.error_code, att_handle=self.handle
|
|
@@ -889,20 +899,24 @@ class Attribute(EventEmitter):
|
|
|
889
899
|
else:
|
|
890
900
|
value = self.value
|
|
891
901
|
|
|
892
|
-
self.emit('read', connection, value)
|
|
902
|
+
self.emit('read', connection, b'' if value is None else value)
|
|
893
903
|
|
|
894
|
-
return self.encode_value(value)
|
|
904
|
+
return b'' if value is None else self.encode_value(value)
|
|
895
905
|
|
|
896
|
-
async def write_value(self, connection: Connection,
|
|
906
|
+
async def write_value(self, connection: Optional[Connection], value: bytes) -> None:
|
|
897
907
|
if (
|
|
898
|
-
self.permissions & self.WRITE_REQUIRES_ENCRYPTION
|
|
899
|
-
|
|
908
|
+
(self.permissions & self.WRITE_REQUIRES_ENCRYPTION)
|
|
909
|
+
and connection is not None
|
|
910
|
+
and not connection.encryption
|
|
911
|
+
):
|
|
900
912
|
raise ATT_Error(
|
|
901
913
|
error_code=ATT_INSUFFICIENT_ENCRYPTION_ERROR, att_handle=self.handle
|
|
902
914
|
)
|
|
903
915
|
if (
|
|
904
|
-
self.permissions & self.WRITE_REQUIRES_AUTHENTICATION
|
|
905
|
-
|
|
916
|
+
(self.permissions & self.WRITE_REQUIRES_AUTHENTICATION)
|
|
917
|
+
and connection is not None
|
|
918
|
+
and not connection.authenticated
|
|
919
|
+
):
|
|
906
920
|
raise ATT_Error(
|
|
907
921
|
error_code=ATT_INSUFFICIENT_AUTHENTICATION_ERROR, att_handle=self.handle
|
|
908
922
|
)
|
|
@@ -912,11 +926,11 @@ class Attribute(EventEmitter):
|
|
|
912
926
|
error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle
|
|
913
927
|
)
|
|
914
928
|
|
|
915
|
-
|
|
929
|
+
decoded_value = self.decode_value(value)
|
|
916
930
|
|
|
917
|
-
if
|
|
931
|
+
if isinstance(self.value, AttributeValue):
|
|
918
932
|
try:
|
|
919
|
-
result = self.value.write(connection,
|
|
933
|
+
result = self.value.write(connection, decoded_value)
|
|
920
934
|
if inspect.isawaitable(result):
|
|
921
935
|
await result
|
|
922
936
|
except ATT_Error as error:
|
|
@@ -924,9 +938,9 @@ class Attribute(EventEmitter):
|
|
|
924
938
|
error_code=error.error_code, att_handle=self.handle
|
|
925
939
|
) from error
|
|
926
940
|
else:
|
|
927
|
-
self.value =
|
|
941
|
+
self.value = decoded_value
|
|
928
942
|
|
|
929
|
-
self.emit('write', connection,
|
|
943
|
+
self.emit('write', connection, decoded_value)
|
|
930
944
|
|
|
931
945
|
def __repr__(self):
|
|
932
946
|
if isinstance(self.value, bytes):
|
bumble/audio/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# -----------------------------------------------------------------------------
|
|
16
|
+
# Imports
|
|
17
|
+
# -----------------------------------------------------------------------------
|