bumble 0.0.203__py3-none-any.whl → 0.0.207__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/apps/auracast.py +626 -87
- bumble/apps/bench.py +227 -148
- bumble/apps/controller_info.py +23 -7
- bumble/apps/device_info.py +50 -4
- bumble/apps/lea_unicast/app.py +61 -201
- bumble/apps/pair.py +13 -8
- bumble/apps/show.py +6 -6
- bumble/att.py +10 -11
- bumble/audio/__init__.py +17 -0
- bumble/audio/io.py +553 -0
- bumble/controller.py +24 -9
- bumble/core.py +4 -1
- bumble/device.py +993 -48
- bumble/drivers/common.py +2 -0
- bumble/drivers/intel.py +593 -24
- bumble/gatt.py +67 -12
- bumble/gatt_client.py +14 -2
- bumble/gatt_server.py +12 -1
- bumble/hci.py +854 -33
- bumble/host.py +363 -64
- bumble/l2cap.py +3 -16
- bumble/pairing.py +3 -0
- bumble/profiles/aics.py +45 -80
- bumble/profiles/ascs.py +6 -18
- bumble/profiles/asha.py +5 -5
- bumble/profiles/bass.py +9 -21
- bumble/profiles/device_information_service.py +4 -1
- bumble/profiles/gatt_service.py +166 -0
- bumble/profiles/gmap.py +193 -0
- bumble/profiles/heart_rate_service.py +5 -6
- bumble/profiles/le_audio.py +87 -4
- bumble/profiles/pacs.py +48 -16
- bumble/profiles/tmap.py +3 -9
- bumble/profiles/{vcp.py → vcs.py} +33 -28
- bumble/profiles/vocs.py +299 -0
- bumble/sdp.py +223 -93
- bumble/smp.py +8 -3
- bumble/tools/intel_fw_download.py +130 -0
- bumble/tools/intel_util.py +154 -0
- bumble/transport/usb.py +8 -2
- bumble/utils.py +22 -7
- bumble/vendor/android/hci.py +29 -4
- {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/METADATA +12 -10
- {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/RECORD +49 -43
- {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/WHEEL +1 -1
- {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/entry_points.txt +3 -0
- bumble/apps/lea_unicast/liblc3.wasm +0 -0
- {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/LICENSE +0 -0
- {bumble-0.0.203.dist-info → bumble-0.0.207.dist-info}/top_level.txt +0 -0
bumble/gatt.py
CHANGED
|
@@ -28,23 +28,26 @@ import functools
|
|
|
28
28
|
import logging
|
|
29
29
|
import struct
|
|
30
30
|
from typing import (
|
|
31
|
+
Any,
|
|
31
32
|
Callable,
|
|
32
33
|
Dict,
|
|
33
34
|
Iterable,
|
|
34
35
|
List,
|
|
35
36
|
Optional,
|
|
36
37
|
Sequence,
|
|
38
|
+
SupportsBytes,
|
|
39
|
+
Type,
|
|
37
40
|
Union,
|
|
38
41
|
TYPE_CHECKING,
|
|
39
42
|
)
|
|
40
43
|
|
|
41
44
|
from bumble.colors import color
|
|
42
|
-
from bumble.core import BaseBumbleError, UUID
|
|
45
|
+
from bumble.core import BaseBumbleError, InvalidOperationError, UUID
|
|
43
46
|
from bumble.att import Attribute, AttributeValue
|
|
47
|
+
from bumble.utils import ByteSerializable
|
|
44
48
|
|
|
45
49
|
if TYPE_CHECKING:
|
|
46
50
|
from bumble.gatt_client import AttributeProxy
|
|
47
|
-
from bumble.device import Connection
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
# -----------------------------------------------------------------------------
|
|
@@ -275,6 +278,13 @@ GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2BCC, 'Sou
|
|
|
275
278
|
GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
|
|
276
279
|
GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
|
|
277
280
|
|
|
281
|
+
# Gaming Audio Service (GMAS)
|
|
282
|
+
GATT_GMAP_ROLE_CHARACTERISTIC = UUID.from_16_bits(0x2C00, 'GMAP Role')
|
|
283
|
+
GATT_UGG_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C01, 'UGG Features')
|
|
284
|
+
GATT_UGT_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C02, 'UGT Features')
|
|
285
|
+
GATT_BGS_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C03, 'BGS Features')
|
|
286
|
+
GATT_BGR_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2C04, 'BGR Features')
|
|
287
|
+
|
|
278
288
|
# Hearing Access Service
|
|
279
289
|
GATT_HEARING_AID_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2BDA, 'Hearing Aid Features')
|
|
280
290
|
GATT_HEARING_AID_PRESET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BDB, 'Hearing Aid Preset Control Point')
|
|
@@ -304,6 +314,7 @@ GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bi
|
|
|
304
314
|
GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B29, 'Client Supported Features')
|
|
305
315
|
GATT_DATABASE_HASH_CHARACTERISTIC = UUID.from_16_bits(0x2B2A, 'Database Hash')
|
|
306
316
|
GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bits(0x2B3A, 'Server Supported Features')
|
|
317
|
+
GATT_LE_GATT_SECURITY_LEVELS_CHARACTERISTIC = UUID.from_16_bits(0x2BF5, 'E GATT Security Levels')
|
|
307
318
|
|
|
308
319
|
# fmt: on
|
|
309
320
|
# pylint: enable=line-too-long
|
|
@@ -312,8 +323,6 @@ GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC = UUID.from_16_bi
|
|
|
312
323
|
# -----------------------------------------------------------------------------
|
|
313
324
|
# Utils
|
|
314
325
|
# -----------------------------------------------------------------------------
|
|
315
|
-
|
|
316
|
-
|
|
317
326
|
def show_services(services: Iterable[Service]) -> None:
|
|
318
327
|
for service in services:
|
|
319
328
|
print(color(str(service), 'cyan'))
|
|
@@ -343,7 +352,7 @@ class Service(Attribute):
|
|
|
343
352
|
def __init__(
|
|
344
353
|
self,
|
|
345
354
|
uuid: Union[str, UUID],
|
|
346
|
-
characteristics:
|
|
355
|
+
characteristics: Iterable[Characteristic],
|
|
347
356
|
primary=True,
|
|
348
357
|
included_services: Iterable[Service] = (),
|
|
349
358
|
) -> None:
|
|
@@ -362,7 +371,7 @@ class Service(Attribute):
|
|
|
362
371
|
)
|
|
363
372
|
self.uuid = uuid
|
|
364
373
|
self.included_services = list(included_services)
|
|
365
|
-
self.characteristics = characteristics
|
|
374
|
+
self.characteristics = list(characteristics)
|
|
366
375
|
self.primary = primary
|
|
367
376
|
|
|
368
377
|
def get_advertising_data(self) -> Optional[bytes]:
|
|
@@ -393,7 +402,7 @@ class TemplateService(Service):
|
|
|
393
402
|
|
|
394
403
|
def __init__(
|
|
395
404
|
self,
|
|
396
|
-
characteristics:
|
|
405
|
+
characteristics: Iterable[Characteristic],
|
|
397
406
|
primary: bool = True,
|
|
398
407
|
included_services: Iterable[Service] = (),
|
|
399
408
|
) -> None:
|
|
@@ -490,7 +499,7 @@ class Characteristic(Attribute):
|
|
|
490
499
|
uuid: Union[str, bytes, UUID],
|
|
491
500
|
properties: Characteristic.Properties,
|
|
492
501
|
permissions: Union[str, Attribute.Permissions],
|
|
493
|
-
value:
|
|
502
|
+
value: Any = b'',
|
|
494
503
|
descriptors: Sequence[Descriptor] = (),
|
|
495
504
|
):
|
|
496
505
|
super().__init__(uuid, permissions, value)
|
|
@@ -525,7 +534,11 @@ class CharacteristicDeclaration(Attribute):
|
|
|
525
534
|
|
|
526
535
|
characteristic: Characteristic
|
|
527
536
|
|
|
528
|
-
def __init__(
|
|
537
|
+
def __init__(
|
|
538
|
+
self,
|
|
539
|
+
characteristic: Characteristic,
|
|
540
|
+
value_handle: int,
|
|
541
|
+
) -> None:
|
|
529
542
|
declaration_bytes = (
|
|
530
543
|
struct.pack('<BH', characteristic.properties, value_handle)
|
|
531
544
|
+ characteristic.uuid.to_pdu_bytes()
|
|
@@ -665,10 +678,14 @@ class DelegatedCharacteristicAdapter(CharacteristicAdapter):
|
|
|
665
678
|
self.decode = decode
|
|
666
679
|
|
|
667
680
|
def encode_value(self, value):
|
|
668
|
-
|
|
681
|
+
if self.encode is None:
|
|
682
|
+
raise InvalidOperationError('delegated adapter does not have an encoder')
|
|
683
|
+
return self.encode(value)
|
|
669
684
|
|
|
670
685
|
def decode_value(self, value):
|
|
671
|
-
|
|
686
|
+
if self.decode is None:
|
|
687
|
+
raise InvalidOperationError('delegate adapter does not have a decoder')
|
|
688
|
+
return self.decode(value)
|
|
672
689
|
|
|
673
690
|
|
|
674
691
|
# -----------------------------------------------------------------------------
|
|
@@ -705,7 +722,7 @@ class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
|
|
|
705
722
|
'''
|
|
706
723
|
Adapter that packs/unpacks characteristic values according to a standard
|
|
707
724
|
Python `struct` format.
|
|
708
|
-
The adapted `read_value` and `write_value` methods return/accept
|
|
725
|
+
The adapted `read_value` and `write_value` methods return/accept a dictionary which
|
|
709
726
|
is packed/unpacked according to format, with the arguments extracted from the
|
|
710
727
|
dictionary by key, in the same order as they occur in the `keys` parameter.
|
|
711
728
|
'''
|
|
@@ -735,6 +752,24 @@ class UTF8CharacteristicAdapter(CharacteristicAdapter):
|
|
|
735
752
|
return value.decode('utf-8')
|
|
736
753
|
|
|
737
754
|
|
|
755
|
+
# -----------------------------------------------------------------------------
|
|
756
|
+
class SerializableCharacteristicAdapter(CharacteristicAdapter):
|
|
757
|
+
'''
|
|
758
|
+
Adapter that converts any class to/from bytes using the class'
|
|
759
|
+
`to_bytes` and `__bytes__` methods, respectively.
|
|
760
|
+
'''
|
|
761
|
+
|
|
762
|
+
def __init__(self, characteristic, cls: Type[ByteSerializable]):
|
|
763
|
+
super().__init__(characteristic)
|
|
764
|
+
self.cls = cls
|
|
765
|
+
|
|
766
|
+
def encode_value(self, value: SupportsBytes) -> bytes:
|
|
767
|
+
return bytes(value)
|
|
768
|
+
|
|
769
|
+
def decode_value(self, value: bytes) -> Any:
|
|
770
|
+
return self.cls.from_bytes(value)
|
|
771
|
+
|
|
772
|
+
|
|
738
773
|
# -----------------------------------------------------------------------------
|
|
739
774
|
class Descriptor(Attribute):
|
|
740
775
|
'''
|
|
@@ -769,3 +804,23 @@ class ClientCharacteristicConfigurationBits(enum.IntFlag):
|
|
|
769
804
|
DEFAULT = 0x0000
|
|
770
805
|
NOTIFICATION = 0x0001
|
|
771
806
|
INDICATION = 0x0002
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
# -----------------------------------------------------------------------------
|
|
810
|
+
class ClientSupportedFeatures(enum.IntFlag):
|
|
811
|
+
'''
|
|
812
|
+
See Vol 3, Part G - 7.2 - Table 7.6: Client Supported Features bit assignments.
|
|
813
|
+
'''
|
|
814
|
+
|
|
815
|
+
ROBUST_CACHING = 0x01
|
|
816
|
+
ENHANCED_ATT_BEARER = 0x02
|
|
817
|
+
MULTIPLE_HANDLE_VALUE_NOTIFICATIONS = 0x04
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
# -----------------------------------------------------------------------------
|
|
821
|
+
class ServerSupportedFeatures(enum.IntFlag):
|
|
822
|
+
'''
|
|
823
|
+
See Vol 3, Part G - 7.4 - Table 7.11: Server Supported Features bit assignments.
|
|
824
|
+
'''
|
|
825
|
+
|
|
826
|
+
EATT_SUPPORTED = 0x01
|
bumble/gatt_client.py
CHANGED
|
@@ -78,6 +78,7 @@ from .gatt import (
|
|
|
78
78
|
GATT_INCLUDE_ATTRIBUTE_TYPE,
|
|
79
79
|
Characteristic,
|
|
80
80
|
ClientCharacteristicConfigurationBits,
|
|
81
|
+
InvalidServiceError,
|
|
81
82
|
TemplateService,
|
|
82
83
|
)
|
|
83
84
|
|
|
@@ -162,12 +163,23 @@ class ServiceProxy(AttributeProxy):
|
|
|
162
163
|
self.uuid = uuid
|
|
163
164
|
self.characteristics = []
|
|
164
165
|
|
|
165
|
-
async def discover_characteristics(self, uuids=()):
|
|
166
|
+
async def discover_characteristics(self, uuids=()) -> list[CharacteristicProxy]:
|
|
166
167
|
return await self.client.discover_characteristics(uuids, self)
|
|
167
168
|
|
|
168
|
-
def get_characteristics_by_uuid(self, uuid):
|
|
169
|
+
def get_characteristics_by_uuid(self, uuid: UUID) -> list[CharacteristicProxy]:
|
|
170
|
+
"""Get all the characteristics with a specified UUID."""
|
|
169
171
|
return self.client.get_characteristics_by_uuid(uuid, self)
|
|
170
172
|
|
|
173
|
+
def get_required_characteristic_by_uuid(self, uuid: UUID) -> CharacteristicProxy:
|
|
174
|
+
"""
|
|
175
|
+
Get the first characteristic with a specified UUID.
|
|
176
|
+
|
|
177
|
+
If no characteristic with that UUID is found, an InvalidServiceError is raised.
|
|
178
|
+
"""
|
|
179
|
+
if not (characteristics := self.get_characteristics_by_uuid(uuid)):
|
|
180
|
+
raise InvalidServiceError(f'{uuid} characteristic not found')
|
|
181
|
+
return characteristics[0]
|
|
182
|
+
|
|
171
183
|
def __str__(self) -> str:
|
|
172
184
|
return f'Service(handle=0x{self.handle:04X}, uuid={self.uuid})'
|
|
173
185
|
|
bumble/gatt_server.py
CHANGED
|
@@ -28,7 +28,17 @@ import asyncio
|
|
|
28
28
|
import logging
|
|
29
29
|
from collections import defaultdict
|
|
30
30
|
import struct
|
|
31
|
-
from typing import
|
|
31
|
+
from typing import (
|
|
32
|
+
Dict,
|
|
33
|
+
Iterable,
|
|
34
|
+
List,
|
|
35
|
+
Optional,
|
|
36
|
+
Tuple,
|
|
37
|
+
TypeVar,
|
|
38
|
+
Type,
|
|
39
|
+
Union,
|
|
40
|
+
TYPE_CHECKING,
|
|
41
|
+
)
|
|
32
42
|
from pyee import EventEmitter
|
|
33
43
|
|
|
34
44
|
from bumble.colors import color
|
|
@@ -68,6 +78,7 @@ from bumble.gatt import (
|
|
|
68
78
|
GATT_REQUEST_TIMEOUT,
|
|
69
79
|
GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
|
|
70
80
|
Characteristic,
|
|
81
|
+
CharacteristicAdapter,
|
|
71
82
|
CharacteristicDeclaration,
|
|
72
83
|
CharacteristicValue,
|
|
73
84
|
IncludedServiceDeclaration,
|