Skip to content

Commit

Permalink
Merge pull request #303 from hbldh/release/v0.8.0-2
Browse files Browse the repository at this point in the history
A follow up changed, pre-release of version 0.8.0.
  • Loading branch information
hbldh authored Sep 22, 2020
2 parents aa54b08 + e1378cf commit 99f3b73
Show file tree
Hide file tree
Showing 33 changed files with 551 additions and 393 deletions.
11 changes: 5 additions & 6 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: [3.5, 3.6, 3.7, 3.8]
exclude:
- os: macos-latest
python-version: 3.5
python-version: [3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -32,12 +29,14 @@ jobs:
run: pip install -r requirements.txt
- name: Install develoment dependencies
run: pip install -r requirements_dev.txt
- name: Check code formatting with black
run: black . --diff
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
# exit-zero treats all errors as warnings. Default line length of black is 88
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics
- name: Test with pytest
run: |
pytest tests --junitxml=junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml --cov=com --cov-report=xml --cov-report=html
Expand Down
15 changes: 13 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ 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>`_.

`0.8.0`_ (2020-09-02)

`0.8.0`_ (2020-09-22)
---------------------

Added
~~~~~

* Implemented ``set_disconnected_callback`` in the .NET backend ``BleakClient`` implementation.
* Added ``find_device_by_address`` method to the ``BleakScanner`` interface, for stopping scanning
when a desired address is found.
* Implemented ``find_device_by_address`` in the .NET backend ``BleakScanner`` implementation and
Expand All @@ -26,15 +28,24 @@ Added
* Implemented pairing method in .NET backend.
* Implemented pairing method in the BlueZ backend.
* Added stumps and ``NotImplementedError`` on pairing in macOS backend.
* Added the possibility to connect using ``BLEDevice`` instead of a string address. This
allows for skipping the discovery call when connecting.

Removed
~~~~~~~

* Support for Python 3.5.

Changed
~~~~~~~
* **BREAKING CHANGE** All notifications now have the characteristic's integer **handle** instead of its UUID as a
string as the first argument ``sender`` sent to notification callbacks. This provides the uniqueness of
sender in notifications as well.
* Renamed ``BleakClient`` argument ``address`` to ``address_or_ble_device``.
* Version 0.5.0 of BleakUWPBridge, with some modified methods and implementing ``IDisposable``.
* Merged #224. All storing and passing of event loops in bleak is removed.
* Removed Objective C delegate compliance checks. Merged #253.
* Made context managers for .NET ``DataReader`` and ``DataWriter``.

Fixed
~~~~~
Expand All @@ -56,7 +67,7 @@ Fixed
Changed
~~~~~~~

* Improved, more explantory error on BlueZ backend when ``BleakClient`` cannot find the desired device when trying to connect. (#238)
* Improved, more explanatory error on BlueZ backend when ``BleakClient`` cannot find the desired device when trying to connect. (#238)
* Better-than-nothing documentation about scanning filters added (#230).
* Ran black on code which was forgotten in 0.7.0. Large diffs due to that.
* Re-adding Python 3.8 CI "tests" on Windows again.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Before you submit a pull request, check that it meets these guidelines:
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.rst.
3. The pull request should work for Python 3.5+ on the following platforms:
3. The pull request should work for Python 3.6+ on the following platforms:
- Windows 10, version 16299 (Fall Creators Update) and greater
- Linux distributions with BlueZ >= 5.43
- OS X / macOS >= 10.11
Expand Down
3 changes: 1 addition & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
bleak
=====

.. image:: https://raw.githubusercontent.com/hbldh/bleak/master/Bleak_logo.png
.. figure:: https://raw.githubusercontent.com/hbldh/bleak/master/Bleak_logo.png
:target: https://github.com/hbldh/bleak
:alt: Bleak Logo
:scale: 50%



.. image:: https://github.com/hbldh/bleak/workflows/Build%20and%20Test/badge.svg
:target: https://github.com/hbldh/bleak/actions?query=workflow%3A%22Build+and+Test%22
:alt: Build and Test
Expand Down
8 changes: 0 additions & 8 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ jobs:
vmImage: 'Ubuntu 16.04'
strategy:
matrix:
Python35-x64:
python.version: '3.5'
python.architecture: 'x64'
Python36-x64:
python.version: '3.6'
Expand Down Expand Up @@ -53,8 +51,6 @@ jobs:
vmImage: 'windows-2019'
strategy:
matrix:
Python35-x64:
python.version: '3.5'
python.architecture: 'x64'
Python36-x64:
python.version: '3.6'
Expand Down Expand Up @@ -92,8 +88,6 @@ jobs:

strategy:
matrix:
Python35-x64:
python.version: '3.5'
python.architecture: 'x64'
Python36-x64:
python.version: '3.6'
Expand Down Expand Up @@ -131,8 +125,6 @@ jobs:

strategy:
matrix:
Python35-x64:
python.version: '3.5'
python.architecture: 'x64'
Python36-x64:
python.version: '3.6'
Expand Down
16 changes: 2 additions & 14 deletions bleak/backends/bluezdbus/characteristic.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import re
from uuid import UUID
from typing import Union, List

Expand Down Expand Up @@ -27,8 +26,6 @@
# "authorize"
}

_handle_regex = re.compile("/char([0-9a-fA-F]*)")


class BleakGATTCharacteristicBlueZDBus(BleakGATTCharacteristic):
"""GATT Characteristic implementation for the BlueZ DBus backend"""
Expand All @@ -39,17 +36,8 @@ def __init__(self, obj: dict, object_path: str, service_uuid: str):
self.__path = object_path
self.__service_uuid = service_uuid

# The `Handle` attribute is added in BlueZ Release 5.51. Empirically,
# it seems to hold true that the "/charYYYY" that is at the end of the
# DBUS path actually is the desired handle. Using regex to extract
# that and using as handle, since handle is mostly used for keeping
# track of characteristics (internally in bleak anyway).
self._handle = self.obj.get("Handle")
if not self._handle:
_handle_from_path = _handle_regex.search(self.path)
if _handle_from_path:
self._handle = int(_handle_from_path.groups()[0], 16)
self._handle = int(self._handle)
# D-Bus object path contains handle as last 4 characters of 'charYYYY'
self._handle = int(object_path[-4:], 16)

@property
def service_uuid(self) -> str:
Expand Down
93 changes: 39 additions & 54 deletions bleak/backends/bluezdbus/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
"""
BLE Client for BlueZ on Linux
"""
import logging
import asyncio
import os
Expand All @@ -12,6 +15,7 @@

from twisted.internet.error import ConnectionDone

from bleak.backends.device import BLEDevice
from bleak.backends.service import BleakGATTServiceCollection
from bleak.backends.characteristic import BleakGATTCharacteristic
from bleak.exc import BleakError
Expand All @@ -36,27 +40,28 @@ class BleakClientBlueZDBus(BaseBleakClient):
Implemented by using the `BlueZ DBUS API <https://docs.ubuntu.com/core/en/stacks/bluetooth/bluez/docs/reference/dbus-api>`_.
Args:
address (str): The address of the BLE peripheral to connect to.
address_or_ble_device (`BLEDevice` or str): The Bluetooth address of the BLE peripheral to connect to or the `BLEDevice` object representing it.
Keyword Args:
timeout (float): Timeout for required ``BleakScanner.find_device_by_address`` call. Defaults to 10.0.
"""

def __init__(self, address, **kwargs):
super(BleakClientBlueZDBus, self).__init__(address, **kwargs)
def __init__(self, address_or_ble_device: Union[BLEDevice, str], **kwargs):
super(BleakClientBlueZDBus, self).__init__(address_or_ble_device, **kwargs)
self.device = kwargs.get("device") if kwargs.get("device") else "hci0"
self.address = address
self.address = address_or_ble_device

# Backend specific, TXDBus objects and data
self._device_path = None
if isinstance(address_or_ble_device, BLEDevice):
self._device_path = address_or_ble_device.details["path"]
else:
self._device_path = None
self._bus = None
self._reactor = None
self._rules = {}
self._subscriptions = list()

self._disconnected_callback = None

# This maps DBus paths of GATT Characteristics to their BLE handles.
self._char_path_to_handle = {}

Expand All @@ -69,34 +74,6 @@ def __init__(self, address, **kwargs):

# Connectivity methods

def set_disconnected_callback(
self, callback: Callable[[BaseBleakClient, Future], None], **kwargs
) -> None:
"""Set the disconnected callback.
The callback will be called on DBus PropChanged event with
the 'Connected' key set to False.
A disconnect callback must accept two positional arguments,
the BleakClient and the Future that called it.
Example:
.. code-block::python
async with BleakClient(mac_addr) as client:
def disconnect_callback(client, future):
print(f"Disconnected callback called on {client}!")
client.set_disconnected_callback(disconnect_callback)
Args:
callback: callback to be called on disconnection.
"""

self._disconnected_callback = callback

async def connect(self, **kwargs) -> bool:
"""Connect to the specified GATT server.
Expand All @@ -109,17 +86,19 @@ async def connect(self, **kwargs) -> bool:
"""
# A Discover must have been run before connecting to any devices.
# Find the desired device before trying to connect.
timeout = kwargs.get("timeout", self._timeout)
device = await BleakScannerBlueZDBus.find_device_by_address(
self.address, timeout=timeout, device=self.device)

if device:
self._device_path = device.details["path"]
else:
raise BleakError(
"Device with address {0} was not found.".format(self.address)
if self._device_path is None:
timeout = kwargs.get("timeout", self._timeout)
device = await BleakScannerBlueZDBus.find_device_by_address(
self.address, timeout=timeout, device=self.device
)

if device:
self._device_path = device.details["path"]
else:
raise BleakError(
"Device with address {0} was not found.".format(self.address)
)

loop = asyncio.get_event_loop()
self._reactor = get_reactor(loop)

Expand Down Expand Up @@ -341,7 +320,9 @@ async def unpair(self) -> bool:
Boolean regarding success of unpairing.
"""
warnings.warn("Unpairing is seemingly unavailable in the BlueZ DBus API at the moment.")
warnings.warn(
"Unpairing is seemingly unavailable in the BlueZ DBus API at the moment."
)
return False

async def is_connected(self) -> bool:
Expand Down Expand Up @@ -429,7 +410,9 @@ async def get_services(self) -> BleakGATTServiceCollection:
self.services.add_characteristic(
BleakGATTCharacteristicBlueZDBus(char, object_path, _service[0].uuid)
)
self._char_path_to_handle[object_path] = char.get("Handle")

# D-Bus object path contains handle as last 4 characters of 'charYYYY'
self._char_path_to_handle[object_path] = int(object_path[-4:], 16)

for desc, object_path in _descs:
_characteristic = list(
Expand Down Expand Up @@ -569,14 +552,16 @@ async def write_gatt_char(
) -> None:
"""Perform a write operation on the specified GATT characteristic.
NB: the version check below is for the "type" option to the
"Characteristic.WriteValue" method that was added to Bluez in 5.51
https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit?id=fa9473bcc48417d69cc9ef81d41a72b18e34a55a
Before that commit, "Characteristic.WriteValue" was only "Write with
response". "Characteristic.AcquireWrite" was added in Bluez 5.46
https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/doc/gatt-api.txt?id=f59f3dedb2c79a75e51a3a0d27e2ae06fefc603e
which can be used to "Write without response", but for older versions
of Bluez, it is not possible to "Write without response".
.. note::
The version check below is for the "type" option to the
"Characteristic.WriteValue" method that was added to `Bluez in 5.51
<https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit?id=fa9473bcc48417d69cc9ef81d41a72b18e34a55a>`_
Before that commit, ``Characteristic.WriteValue`` was only "Write with
response". ``Characteristic.AcquireWrite`` was `added in Bluez 5.46
<https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/doc/gatt-api.txt?id=f59f3dedb2c79a75e51a3a0d27e2ae06fefc603e>`_
which can be used to "Write without response", but for older versions
of Bluez, it is not possible to "Write without response".
Args:
char_specifier (BleakGATTCharacteristicBlueZDBus, int, str or UUID): The characteristic to write
Expand Down
13 changes: 10 additions & 3 deletions bleak/backends/bluezdbus/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ def register_detection_callback(self, callback: Callable):
self._callback = callback

@classmethod
async def find_device_by_address(cls, device_identifier: str, timeout: float = 10.0, **kwargs) -> BLEDevice:
async def find_device_by_address(
cls, device_identifier: str, timeout: float = 10.0, **kwargs
) -> BLEDevice:
"""A convenience method for obtaining a ``BLEDevice`` object specified by Bluetooth address.
Args:
Expand All @@ -237,10 +239,15 @@ async def find_device_by_address(cls, device_identifier: str, timeout: float = 1
scanner = cls(timeout=timeout)

def stop_if_detected(message):
if any(device.get("Address", "").lower() == device_identifier for device in scanner._devices.values()):
if any(
device.get("Address", "").lower() == device_identifier
for device in scanner._devices.values()
):
loop.call_soon_threadsafe(stop_scanning_event.set)

return await scanner._find_device_by_address(device_identifier, stop_scanning_event, stop_if_detected, timeout)
return await scanner._find_device_by_address(
device_identifier, stop_scanning_event, stop_if_detected, timeout
)

# Helper methods

Expand Down
24 changes: 12 additions & 12 deletions bleak/backends/bluezdbus/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,21 @@ def get_device_object_path(hci_device, address):
def get_gatt_service_path(hci_device, address, service_id):
"""Get object path for a GATT Service for a Bluetooth device.
Service org.bluez
Service org.bluez
Interface org.bluez.GattService1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX
Service org.bluez
Service org.bluez
Interface org.bluez.GattService1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX
Args:
hci_device (str): Which bluetooth adapter to connect with.
address (str): The Bluetooth address of the bluetooth device.
service_id (int):
Args:
hci_device (str): Which bluetooth adapter to connect with.
address (str): The Bluetooth address of the bluetooth device.
service_id (int):
Returns:
String representation of GATT service object path on format
`/org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX`.
Returns:
String representation of GATT service object path on format
`/org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX`.
"""
"""
base = get_device_object_path(hci_device, address)
return base + "{0}/service{1:02d}".format(base, service_id)

Expand Down
Loading

0 comments on commit 99f3b73

Please sign in to comment.