hbldh / bleak

A cross platform Bluetooth Low Energy Client for Python using asyncio
MIT License
1.78k stars 294 forks source link

BLE Connections that work correctly in 0.14.3 fail with all versions 0.15.0 and above #971

Open drewancil opened 2 years ago

drewancil commented 2 years ago

Description

Connections to BLE devices which work perfectly on bleak 0.14.3 fail on 0.15.0, 0.15.1, 0.16.0

What I Did

The code to trigger the exception is very basic - create and start a BleakScanner and create and connect in a BleakClient using the known mac addresses.

Code below for connection throws the following exception: [org.bluez.Error.InProgress] Operation already in progress

Using PIP to restore to 0.14.3 makes the connections and my application work again.

async def ble_client_connect(self: OolerConfig):
    """Connects the Bluetooth to the Oolers"""
    logging.info(f"BLE Client: Connecting: {self.name}")
    try:
        ble_scanner = BleakScanner()
        await ble_scanner.start()
        await asyncio.sleep(2)
        ble_client = self.ble_client
        await ble_client.connect()
        # Connections won't succeed if scanner is stopped before connect
        await ble_scanner.stop()
        logging.info(f"BLE Client: Connected: {self.name}")

    except BleakError as ex:
        logging.error(f"Exception->ble_client_connect: {ex}")

The client gets put into an object on start of the app as:

ble_client = BleakClient(ooler_config["mac"])
dlech commented 2 years ago

Can you give the full stack trace of the error?

When creating a BleakClient object using a Bluetooth address instead of a BLEDevice it will implicitly scan when connecting, so no separate BleakScanner should be used in this case.

drewancil commented 2 years ago

Thanks for quick reply - yes, I did read that in the documentation, but I could not get it to connect unless it was with a scanner running.

Requested trace below

Traceback (most recent call last):
  File "/home/pi/ooler2mqtt/ooler2mqtt.py", line 185, in ble_client_connect
    await ble_client.connect()
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/client.py", line 109, in connect
    device = await BleakScannerBlueZDBus.find_device_by_address(
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/scanner.py", line 222, in find_device_by_address
    return await cls.find_device_by_filter(
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/scanner.py", line 251, in find_device_by_filter
    async with cls(detection_callback=apply_filter, **kwargs):
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/scanner.py", line 97, in __aenter__
    await self.start()
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/scanner.py", line 141, in start
    self._stop = await manager.active_scan(
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/manager.py", line 335, in active_scan
    assert_reply(reply)
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/utils.py", line 22, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.InProgress] Operation already in progress
dlech commented 2 years ago

If that is the case, then I would suggest using BleakScanner.find_device_by_address() to get a BLEDevice and then pass that object to the BleakClient constructor.

drewancil commented 2 years ago

Thanks for your reply. I did try using BLEDevice during my initial development and couldn't get it to work. I made another attempt with my code and also a minimum code example heavily copied from the examples directory in Github. The code example using BLEDevice fails to run under both bleak 0.14.3 and 0.17.0 but with different errors. It looks like find_device_by_address() is successful as it prints out the address and name of the device in that section of my code, but it will not connect. During my initial development of this application, I could never get a connection to succeed unless blescanner was running.

My original code continues to run fine under 0.14.3, but won't run on any bleak version beyond that (tested through 0.17.0. But, my code does run fine on MacOS, so it does seem to be specific to BlueZ backend.

Code example:

"""
Connect by BLEDevice
"""

import asyncio
import platform
import sys
from bleak import BleakClient, BleakScanner
from bleak.exc import BleakError

ADDRESS = "14:B4:57:AE:46:39"

async def main():
    device = await BleakScanner.find_device_by_address(ADDRESS, timeout=20.0)
    print (f"Device: {device}")
    if not device:
        raise BleakError(f"A device with address {ble_address} could not be found.")

    async with BleakClient(device) as client:
        svcs = await client.get_services()
        print("Services:")
        for service in svcs:
            print(service)

if __name__ == "__main__":
    asyncio.run(main())

Code example running under 0.17.0:

Device: 14:B4:57:AE:46:39: OOLER-9200200258
Traceback (most recent call last):
  File "/home/pi/ooler2mqtt/bledev.py", line 33, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/pi/ooler2mqtt/bledev.py", line 22, in main
    async with BleakClient(device) as client:
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/client.py", line 61, in __aenter__
    await self.connect()
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/client.py", line 176, in connect
    assert_reply(reply)
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/utils.py", line 22, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.freedesktop.DBus.Error.UnknownObject] Method "Connect" with signature "" on interface "org.bluez.Device1" doesn't exist

Code example running under bleak 0.14.3:

Device: 14:B4:57:AE:46:39: OOLER-9200200258
Traceback (most recent call last):
  File "/home/pi/ooler2mqtt/bledev.py", line 33, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/pi/ooler2mqtt/bledev.py", line 22, in main
    async with BleakClient(device) as client:
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/client.py", line 61, in __aenter__
    await self.connect()
  File "/home/pi/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/client.py", line 216, in connect
    raise BleakError(
bleak.exc.BleakError: Device with address 14:B4:57:AE:46:39 could not be found. Try increasing `timeout` value or moving the device closer.
dlech commented 2 years ago

bleak.exc.BleakDBusError: [org.freedesktop.DBus.Error.UnknownObject] Method "Connect" with signature "" on interface "org.bluez.Device1" doesn't exist

This probably means that BlueZ removes the device object from d-bus before we attempt to connect. You can confirm this by enabling Bleak logging.

dlech commented 2 years ago

Also, can you include the BlueZ version you are using with each log since you mentioned two different versions originally? BlueZ 5.55 has some other known issues, so I would suggest using v5.64 if possible.

drewancil commented 2 years ago

Both of the logs above were on the latest Raspberry OS, which is Bluez 5.55. However, I built a spare Raspberry Pi but used Ubuntu Server LTS 22.04.1 which includes Bluez 5.64. Loaded bleak 0.17.0 and ran the sample code above and the error was the same as the error above for 0.17.0. The Bluez version difference did not make any difference for the code example provided above.

dlech commented 1 year ago

Could this be caused by the BlueZ bug described in https://github.com/hbldh/bleak/issues/1176?