Add BleakAdapter.get_connected_devices()#1979
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #1979 +/- ##
===========================================
+ Coverage 51.26% 52.06% +0.79%
===========================================
Files 39 43 +4
Lines 3940 4091 +151
Branches 487 503 +16
===========================================
+ Hits 2020 2130 +110
- Misses 1794 1830 +36
- Partials 126 131 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
6570921 to
bdd7a36
Compare
Simply creating new arrays by calling the class method instead of alloc().initWithArray_().
We will want to reuse this logic in a future new module, so it makes sense to extract it into a helper function.
3ca6f7c to
c71fa1b
Compare
|
I ended up having to make major changes to the winrt backend to get it to pass the tests. Should be ready to merge now. |
There was a problem hiding this comment.
Pull request overview
This PR splits out part of the broader connected-device work from #1966 by introducing a new public BleakAdapter API for retrieving BLE devices that are already connected at the OS level, without starting a scan. It extends Bleak’s backend abstraction alongside the existing scanner/client layers, with desktop backend implementations, docs, typings, and integration tests.
Changes:
- Added a new public
BleakAdapterfacade plus backend-selection plumbing and Linux-specific adapter args. - Implemented
get_connected_devices()for BlueZ, CoreBluetooth, and WinRT backends. - Added documentation, platform-detection coverage, integration tests, and a small CoreBluetooth/Foundation typing update needed by the new code.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
typings/Foundation/__init__.pyi |
Tightens NSArray typing and adds arrayWithArray_ for CoreBluetooth calls. |
tests/test_platform_detection.py |
Extends backend-detection test coverage to adapter backend selection. |
tests/integration/test_adapter.py |
Adds integration tests for retrieving connected devices and UUID filtering. |
docs/api/index.rst |
Adds the new adapter API page to the docs toctree. |
docs/api/adapter.rst |
Documents BleakAdapter.get() and get_connected_devices(). |
bleak/backends/winrt/adapter.py |
Implements Windows adapter lookup for connected BLE devices. |
bleak/backends/corebluetooth/client.py |
Switches requested-service array construction to the new NSArray helper. |
bleak/backends/corebluetooth/adapter.py |
Implements macOS adapter lookup via retrieveConnectedPeripheralsWithServices_. |
bleak/backends/corebluetooth/CentralManagerDelegate.py |
Uses the new NSArray.arrayWithArray_ helper in scan setup. |
bleak/backends/bluezdbus/utils.py |
Extracts BlueZ device-name normalization into a reusable helper. |
bleak/backends/bluezdbus/scanner.py |
Reuses the new BlueZ device-name helper during scan updates. |
bleak/backends/bluezdbus/manager.py |
Adds manager-side filtering for connected BlueZ devices by adapter and UUIDs. |
bleak/backends/bluezdbus/adapter.py |
Implements Linux adapter lookup on top of the BlueZ manager. |
bleak/backends/adapter.py |
Introduces the base adapter abstraction and platform backend resolver. |
bleak/args/bluez.py |
Adds BlueZAdapterArgs for Linux adapter selection. |
bleak/__init__.py |
Exposes the public BleakAdapter class and input normalization logic. |
CHANGELOG.rst |
Records the new BleakAdapter capability in the unreleased changelog. |
AUTHORS.rst |
Adds a contributor entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
42d3ae5 to
f84fa69
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| get_platform_adapter_backend_type() | ||
| if backend is None | ||
| else (backend, backend.__name__) | ||
| ) | ||
| return cls(await PlatformBleakAdapter.get(bluez=bluez)) |
There was a problem hiding this comment.
We can always add this later.
| async def get_connected_devices( | ||
| self, | ||
| service_uuids: Iterable[str] = ["1801"], | ||
| ) -> list[BLEDevice]: |
There was a problem hiding this comment.
Since the others are list already, we can go with that. We can always relax them later if needed.
| On Linux, a specific adapter can be selected via the ``bluez`` argument:: | ||
|
|
||
| adapter = await BleakAdapter.get(bluez={"adapter": "hci1"}) |
| from bleak import BleakAdapter, BleakClient | ||
|
|
||
| adapter = await BleakAdapter.get() | ||
| devices = await adapter.get_connected_devices() | ||
|
|
||
| for d in devices: | ||
| print(d.name, d.address) | ||
|
|
||
| # Use an existing connection without scanning | ||
| async with BleakClient(devices[0]) as client: | ||
| ... | ||
|
|
| # Don't waste time if the default ("Generic Access") service is the only | ||
| # one we're looking for since that should always match. |
There was a problem hiding this comment.
Agreed, 0x1800 is Generic Access and 0x1801 is Generic Attribute.
| adapter_path = ( | ||
| f"/org/bluez/{adapter}" if adapter else manager.get_default_adapter() | ||
| ) |
There was a problem hiding this comment.
Agreed, would save users from silent empty-list bugs.
Could check manager.get_adapter_path in BleakAdapterBlueZDBus.get().
There was a problem hiding this comment.
We have similar logic in BleakScannerBlueZDBus.start(). I don't recall any reported issues with this. So think we should defer fixing this.
|
|
||
| async def get_connected_devices( | ||
| self, | ||
| service_uuids: Iterable[str] = ["1801"], |
There was a problem hiding this comment.
Iterable[str] is too broad.
await get_connected_devices("180F") would silently iterate characters. Worth tightening to Collection[str].
|
Ran a Windows smoke test against this branch with my BLE Device (random-static address, GATT 0x1801 default filter). |
Add a new BleakAdapter class for OS-level adapter operations that don't
require scanning. The first capability is get_connected_devices(), which
queries the OS for BLE devices that are already connected. The returned
BLEDevice objects can be passed directly to BleakClient to use the
existing OS-level connection without scanning.
Instances are obtained via the async BleakAdapter.get() factory.
Platform implementations:
- Windows: Uses BluetoothLEDevice.get_device_selector_from_connection_status()
with DeviceInformation.find_all_async() to enumerate connected devices.
service_uuids is currently ignored since BluetoothLEDevice already
excludes Bluetooth Classic devices.
- macOS: Uses CBCentralManager.retrieveConnectedPeripheralsWithServices()
to get connected peripherals filtered by service UUID.
- Linux: Adds BlueZManager.get_connected_devices() to query D-Bus for
connected BLE devices on the selected adapter, filtered by service
UUID. The adapter can be selected via the BlueZAdapterArgs typed dict
(e.g. bluez={"adapter": "hci1"}).
Other backends raise NotImplementedError.
Co-developed-by: David Lechner <david@pybricks.com>
Add a test for BleakAdapter.get_connected_devices(). This needs to test that the service UUID filter is working.
This is splitting out part of #1966 to make it easier to review, test and get merged.
I took ab05a28 and split it into a few commits, fixed them up a bit and added one more commit with tests.
I dropped the example since that requires further changes before it actually works.