Skip to content

Commit

Permalink
Merge pull request #1257 from hbldh/release/0.20.0
Browse files Browse the repository at this point in the history
Release/v0.20.0
  • Loading branch information
dlech authored Mar 17, 2023
2 parents b8f22bc + 1b93859 commit b6ab166
Show file tree
Hide file tree
Showing 43 changed files with 1,556 additions and 1,056 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ jobs:
name: "Build and test"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
id: py
Expand All @@ -24,6 +25,9 @@ jobs:
- name: Create virtual environment
run: pipx run poetry env use '${{ steps.py.outputs.python-path }}'
- name: Install dependencies
# work around https://github.com/python-poetry/poetry/issues/7161
env:
SYSTEM_VERSION_COMPAT: 0
run: pipx run poetry install --only main,test
- name: Test with pytest
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ on: workflow_dispatch
jobs:
build_android:
name: "Build Android"
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install dependencies
run: pip install buildozer cython
- name: Cache buildozer files
uses: actions/cache@v2
uses: actions/cache@v3
id: buildozer-cache
with:
path: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/format_and_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
name: "Format and lint"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
- name: Install development dependencies
run: pipx run poetry install --only docs,lint
- name: Check code formatting with black
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Build
run: pipx run poetry build
- name: Publish
Expand Down
44 changes: 43 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,47 @@ All notable changes to this project will be documented in this file.
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_,
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.

`Unreleased`_
=============

`0.20.0`_ (2023-03-17)
======================

Added
-----
* Added ``BLEAK_DBUS_AUTH_UID`` environment variable for hardcoding D-Bus UID. Merged #1182.
* Added return type ``None`` to some scanner methods.
* Added optional hack to use Bluetooth address instead of UUID on macOS. Merged #1073.
* Added ``BleakScanner.find_device_by_name()`` class method.
* Added optional command line argument to use debug log level to all applicable examples.
* Added ``bleak.uuids.normalize_uuid_str()`` function.
* Added optional ``services`` argument to ``BleakClient()`` to filter services. Merged #654.
* Added automatic retry on ``le-connection-abort-by-local`` in BlueZ backend. Fixes #1220.

Changed
-------
* Dropped ``async-timeout`` dependency on Python >= 3.11.
* Deprecated ``BLEDevice.rssi`` and ``BLEDevice.metadata``. Fixes #1025.
* ``BLEDevice`` now uses ``__slots__`` to reduce memory usage. Merged #1117.
* ``BaseBleakClient.services`` is now ``None`` instead of empty service collection
until services are discovered.
* Include thread name in ``BLEAK_LOGGING`` output. Merged #1144.
* Updated PyObjC dependency on macOS to v9.x.

Fixed
-----
* Fixed invalid UTF-8 in ``uuids.uuid16_dict``.
* Fixed ``AttributeError`` in ``_ensure_success`` in WinRT backend.
* Fixed ``BleakScanner.stop()`` can raise ``BleakDBusError`` with ``org.bluez.Error.NotReady`` in BlueZ backend.
* Fixed ``BleakScanner.stop()`` hanging in WinRT backend when Bluetooth is disabled.
* Fixed leaking services when ``get_services()`` is cancelled in WinRT backend.
* Fixed disconnect monitor task not always cancelled on the BlueZ client. Merged #1159.
* Fixed WinRT scanner never calling ``detection_callback`` when a device does
not send a scan response. Fixes #1211.
* Fixed ``BLEDevice`` name sometimes incorrectly ``None``.
* Fixed unhandled exception in ``CentralManagerDelegate`` destructor on macOS. Fixes #1219.
* Fixed object passed to ``disconnected_callback`` is not ``BleakClient``. Fixes #1200.

`0.19.5`_ (2022-11-19)
======================

Expand Down Expand Up @@ -880,7 +921,8 @@ Fixed
* Bleak created.


.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.19.5...develop
.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.20.0...develop
.. _0.20.0: https://github.com/hbldh/bleak/compare/v0.19.5...v0.20.0
.. _0.19.5: https://github.com/hbldh/bleak/compare/v0.19.4...v0.19.5
.. _0.19.4: https://github.com/hbldh/bleak/compare/v0.19.3...v0.19.4
.. _0.19.3: https://github.com/hbldh/bleak/compare/v0.19.2...v0.19.3
Expand Down
87 changes: 74 additions & 13 deletions bleak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Awaitable,
Callable,
Dict,
Iterable,
List,
Optional,
Tuple,
Expand All @@ -28,7 +29,10 @@
)
from warnings import warn

import async_timeout
if sys.version_info < (3, 11):
from async_timeout import timeout as async_timeout
else:
from asyncio import timeout as async_timeout

if sys.version_info[:2] < (3, 8):
from typing_extensions import Literal
Expand All @@ -47,16 +51,18 @@
)
from .backends.service import BleakGATTServiceCollection
from .exc import BleakError
from .uuids import normalize_uuid_str

if TYPE_CHECKING:
from .backends.bluezdbus.scanner import BlueZScannerArgs
from .backends.corebluetooth.scanner import CBScannerArgs
from .backends.winrt.client import WinRTClientArgs


_logger = logging.getLogger(__name__)
_logger.addHandler(logging.NullHandler())
if bool(os.environ.get("BLEAK_LOGGING", False)):
FORMAT = "%(asctime)-15s %(name)-8s %(levelname)s: %(message)s"
FORMAT = "%(asctime)-15s %(name)-8s %(threadName)s %(levelname)s: %(message)s"
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter(fmt=FORMAT))
Expand Down Expand Up @@ -89,12 +95,21 @@ class BleakScanner:
:class:`BleakError` if set to ``"passive"`` on macOS.
bluez:
Dictionary of arguments specific to the BlueZ backend.
cb:
Dictionary of arguments specific to the CoreBluetooth backend.
backend:
Used to override the automatically selected backend (i.e. for a
custom backend).
**kwargs:
Additional args for backwards compatibility.
.. tip:: The first received advertisement in ``detection_callback`` may or
may not include scan response data if the remote device supports it.
Be sure to take this into account when handing the callback. For example,
the scan response often contains the local name of the device so if you
are matching a device based on other data but want to display the local
name to the user, be sure to wait for ``adv_data.local_name is not None``.
.. versionchanged:: 0.15.0
``detection_callback``, ``service_uuids`` and ``scanning_mode`` are no longer keyword-only.
Added ``bluez`` parameter.
Expand All @@ -111,6 +126,7 @@ def __init__(
scanning_mode: Literal["active", "passive"] = "active",
*,
bluez: BlueZScannerArgs = {},
cb: CBScannerArgs = {},
backend: Optional[Type[BaseBleakScanner]] = None,
**kwargs,
):
Expand All @@ -119,7 +135,12 @@ def __init__(
)

self._backend = PlatformBleakScanner(
detection_callback, service_uuids, scanning_mode, bluez=bluez, **kwargs
detection_callback,
service_uuids,
scanning_mode,
bluez=bluez,
cb=cb,
**kwargs,
)

async def __aenter__(self):
Expand Down Expand Up @@ -188,7 +209,7 @@ async def discover(
@overload
@classmethod
async def discover(
cls, timeout: float = 5.0, *, return_adv: Literal[True], **kwargs
cls, timeout: float = 5.0, *, return_adv: Literal[True] = True, **kwargs
) -> Dict[str, Tuple[BLEDevice, AdvertisementData]]:
...

Expand Down Expand Up @@ -272,11 +293,9 @@ async def find_device_by_address(
"""Obtain a ``BLEDevice`` for a BLE server specified by Bluetooth address or (macOS) UUID address.
Args:
device_identifier (str): The Bluetooth/UUID address of the Bluetooth peripheral sought.
timeout (float): Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
Keyword Args:
adapter (str): Bluetooth adapter to use for discovery.
device_identifier: The Bluetooth/UUID address of the Bluetooth peripheral sought.
timeout: Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
**kwargs: additional args passed to the :class:`BleakScanner` constructor.
Returns:
The ``BLEDevice`` sought or ``None`` if not detected.
Expand All @@ -289,6 +308,28 @@ async def find_device_by_address(
**kwargs,
)

@classmethod
async def find_device_by_name(
cls, name: str, timeout: float = 10.0, **kwargs
) -> Optional[BLEDevice]:
"""Obtain a ``BLEDevice`` for a BLE server specified by the local name in the advertising data.
Args:
name: The name sought.
timeout: Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
**kwargs: additional args passed to the :class:`BleakScanner` constructor.
Returns:
The ``BLEDevice`` sought or ``None`` if not detected.
.. versionadded:: 0.20.0
"""
return await cls.find_device_by_filter(
lambda d, ad: ad.local_name == name,
timeout=timeout,
**kwargs,
)

@classmethod
async def find_device_by_filter(
cls, filterfunc: AdvertisementDataFilter, timeout: float = 10.0, **kwargs
Expand Down Expand Up @@ -322,7 +363,7 @@ def apply_filter(d: BLEDevice, ad: AdvertisementData):

async with cls(detection_callback=apply_filter, **kwargs):
try:
async with async_timeout.timeout(timeout):
async with async_timeout(timeout):
return await found_device_queue.get()
except asyncio.TimeoutError:
return None
Expand All @@ -345,6 +386,12 @@ class BleakClient:
Callback that will be scheduled in the event loop when the client is
disconnected. The callable must take one argument, which will be
this client object.
services:
Optional list of services to filter. If provided, only these services
will be resolved. This may or may not reduce the time needed to
enumerate the services depending on if the OS supports such filtering
in the Bluetooth stack or not (should affect Windows and Mac).
These can be 16-bit or 128-bit UUIDs.
timeout:
Timeout in seconds passed to the implicit ``discover`` call when
``address_or_ble_device`` is not a :class:`BLEDevice`. Defaults to 10.0.
Expand Down Expand Up @@ -381,6 +428,7 @@ def __init__(
self,
address_or_ble_device: Union[BLEDevice, str],
disconnected_callback: Optional[Callable[[BleakClient], None]] = None,
services: Optional[Iterable[str]] = None,
*,
timeout: float = 10.0,
winrt: WinRTClientArgs = {},
Expand All @@ -393,7 +441,12 @@ def __init__(

self._backend = PlatformBleakClient(
address_or_ble_device,
disconnected_callback=disconnected_callback,
disconnected_callback=None
if disconnected_callback is None
else functools.partial(disconnected_callback, self),
services=None
if services is None
else set(map(normalize_uuid_str, services)),
timeout=timeout,
winrt=winrt,
**kwargs,
Expand Down Expand Up @@ -456,7 +509,9 @@ def set_disconnected_callback(
FutureWarning,
stacklevel=2,
)
self._backend.set_disconnected_callback(callback, **kwargs)
self._backend.set_disconnected_callback(
None if callback is None else functools.partial(callback, self), **kwargs
)

async def connect(self, **kwargs) -> bool:
"""Connect to the specified GATT server.
Expand Down Expand Up @@ -545,7 +600,13 @@ def services(self) -> BleakGATTServiceCollection:
Gets the collection of GATT services available on the device.
The returned value is only valid as long as the device is connected.
Raises:
BleakError: if service discovery has not been performed yet during this connection.
"""
if not self._backend.services:
raise BleakError("Service Discovery has not been performed yet")

return self._backend.services

# I/O methods
Expand Down Expand Up @@ -608,7 +669,7 @@ async def start_notify(
.. code-block:: python
def callback(sender: int, data: bytearray):
def callback(sender: BleakGATTCharacteristic, data: bytearray):
print(f"{sender}: {data}")
client.start_notify(char_uuid, callback)
Expand Down
Loading

0 comments on commit b6ab166

Please sign in to comment.