Skip to content

Commit 62411ce

Browse files
committed
Added support for Google´s Bumble Bluetooth Controller stack
The backend supports direct use with Bumble. The HCI Controller is managed by the Bumble stack and the transport layer can be defined by the user (e.g. VHCI, Serial, TCP, android-netsim).
1 parent c98883b commit 62411ce

15 files changed

+741
-1
lines changed

Diff for: AUTHORS.rst

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Contributors
2424
* David Johansen <[email protected]>
2525
* JP Hutchins <[email protected]>
2626
* Bram Duvigneau <[email protected]>
27+
* Victor Chavez <[email protected]>
2728

2829
Sponsors
2930
--------

Diff for: CHANGELOG.rst

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0
99

1010
`Unreleased`_
1111
=============
12+
Added
13+
-----
14+
* Added support for Google's Bumble Bluetooth stack.
15+
1216

1317
`0.22.3`_ (2024-10-05)
1418
======================

Diff for: bleak/backends/bumble/__init__.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2024 Victor Chavez
3+
"""Bumble backend."""
4+
5+
from bumble.controller import Controller
6+
from bumble.link import LocalLink
7+
from bumble.transport import open_transport
8+
9+
transports = {}
10+
link = LocalLink()
11+
12+
13+
def get_default_transport():
14+
return "tcp-server:_:1234"
15+
16+
17+
async def start_transport(transport: str):
18+
if transport not in transports.keys():
19+
transports[transport] = await open_transport(transport)
20+
Controller(
21+
"ext",
22+
host_source=transports[transport].source,
23+
host_sink=transports[transport].sink,
24+
link=link,
25+
)
26+
27+
return transports[transport]
28+
29+
30+
def get_link():
31+
# Assume all transports are linked
32+
return link

Diff for: bleak/backends/bumble/characteristic.py

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2024 Victor Chavez
3+
4+
from typing import Callable, List, Union
5+
from uuid import UUID
6+
7+
from bumble.gatt_client import CharacteristicProxy, ServiceProxy
8+
9+
from ... import normalize_uuid_str
10+
from ..characteristic import BleakGATTCharacteristic
11+
from ..descriptor import BleakGATTDescriptor
12+
from .utils import uuid_bytes_to_str
13+
14+
15+
class BleakGATTCharacteristicBumble(BleakGATTCharacteristic):
16+
"""GATT Characteristic implementation for the Bumble backend."""
17+
18+
def __init__(
19+
self,
20+
obj: CharacteristicProxy,
21+
max_write_without_response_size: Callable[[], int],
22+
svc: ServiceProxy,
23+
):
24+
super().__init__(obj, max_write_without_response_size)
25+
self.__descriptors = []
26+
self.__props = [str(prop) for prop in obj.properties]
27+
self.__svc = svc
28+
uuid = uuid_bytes_to_str(obj.uuid.uuid_bytes)
29+
self.__uuid = normalize_uuid_str(uuid)
30+
31+
@property
32+
def service_uuid(self) -> str:
33+
"""The uuid of the Service containing this characteristic"""
34+
return self.__svc.uuid
35+
36+
@property
37+
def service_handle(self) -> int:
38+
"""The integer handle of the Service containing this characteristic"""
39+
return self.__svc.handle
40+
41+
@property
42+
def handle(self) -> int:
43+
"""The handle of this characteristic"""
44+
return int(self.obj.handle)
45+
46+
@property
47+
def uuid(self) -> str:
48+
"""The uuid of this characteristic"""
49+
return self.__uuid
50+
51+
@property
52+
def properties(self) -> List[str]:
53+
"""Properties of this characteristic"""
54+
return self.__props
55+
56+
@property
57+
def descriptors(self) -> List[BleakGATTDescriptor]:
58+
"""List of descriptors for this characteristic"""
59+
return self.__descriptors
60+
61+
def get_descriptor(
62+
self, specifier: Union[int, str, UUID]
63+
) -> Union[BleakGATTDescriptor, None]:
64+
"""Get a descriptor by handle (int) or UUID (str or uuid.UUID)"""
65+
try:
66+
if isinstance(specifier, int):
67+
return next(filter(lambda x: x.handle == specifier, self.descriptors))
68+
else:
69+
return next(
70+
filter(lambda x: x.uuid == str(specifier), self.descriptors)
71+
)
72+
except StopIteration:
73+
return None
74+
75+
def add_descriptor(self, descriptor: BleakGATTDescriptor):
76+
"""Add a :py:class:`~BleakGATTDescriptor` to the characteristic.
77+
78+
Should not be used by end user, but rather by `bleak` itself.
79+
"""
80+
self.__descriptors.append(descriptor)

0 commit comments

Comments
 (0)