bleak icon indicating copy to clipboard operation
bleak copied to clipboard

Exception message and behavior when device has gone and returned are inconsistent and not robust

Open jeffsf opened this issue 3 years ago • 7 comments

  • bleak version: 0.13.0
  • BlueZ version (bluetoothctl -v) in case of Linux: bluetoothctl: 5.55
  • Linux version: Raspberry Pi OS, based on Debian Bullseye

Description

The application at hand connects to a BLE device, which may be turned off by its operator. In the case of an unexpected disconnect, the application will periodically attempt to .connect() to the device. In many cases, the device will be offline for hours. It appears that Bleak is unable to connect as the device is no longer known by the BlueZ stack.

When bluetoothd is started with its OS defaults, the message in the exception is (line 216)

                    raise BleakError(
                        "Device with address {0} could not be found. "
                        "Try increasing `timeout` value or moving the device closer.".format(
                            self.address
                        )

However, this will not resolve the problem. Even if the device is present, this exception is raised. Examining the code reveals (line 213)

                    logger.debug(
                        f"org.bluez.Adapter1.ConnectDevice not found ({self._device_path}), try enabling bluetoothd --experimental"
                    )

which appears to be closer to the root cause.

It seems that if --experimental is not enabled, this exception will be raised repeatedly, without scanning, There is a retry loop that was originally based on the expectation that the .connect() attempt would take the default 10 seconds. There is no additional delay between attempts for the first minute or two. The device was on and discoverable at this time, after being off for a long time.

    # 2021-12-25 19:04:49,451 INFO BlueDOT:
    #       Connect requested for BlueDOT at 00:a0:50:aa:bb:cc
    # 2021-12-25 19:04:49,630 INFO BlueDOT:
    #       Device with address 00:a0:50:aa:bb:cc could not be found.
    #       Try increasing `timeout` value or moving the device closer.
    # 2021-12-25 19:04:49,632 INFO BlueDOT:
    #       Connect requested for BlueDOT at 00:a0:50:aa:bb:cc
    # 2021-12-25 19:04:49,787 INFO BlueDOT:
    #       Device with address 00:a0:50:aa:bb:cc could not be found.
    #       Try increasing `timeout` value or moving the device closer.
    # [...]

What I Expected

  • Exception strings would be consistent with the underlying cause
  • If the device was not found, in the absence of the non-standard --experimental, a scan or other robust connection approach would be initiated.
  • Users should not have to modify the standard services to have robust behavior.

jeffsf avatar Jan 01 '22 00:01 jeffsf

Some interesting comments on the ConnectDevice call on linux-bluetooth

https://lore.kernel.org/linux-bluetooth/MWHPR17MB19671EAD4D74EA7BC5915CA7C5B40@MWHPR17MB1967.namprd17.prod.outlook.com/t/#u

including

This API was added only for qualification purposes (there are some GAP tests specified in a way that upper tester is not doing discovery) and it shouldn't be used for 'normal' usage.

jeffsf avatar Jan 01 '22 01:01 jeffsf

Workaround in use now is to set _device_path to None in the disconnected callback. This will trigger the logic at line 111 and force a scan on every connect.

        if self._device_path is None:
            device = await BleakScannerBlueZDBus.find_device_by_address(
                self.address, timeout=timeout, adapter=self._adapter
            )

jeffsf avatar Jan 01 '22 03:01 jeffsf

Workaround in use now is to set _device_path to None in the disconnected callback.

Would you like to make a pull request with this fix?

dlech avatar Jan 02 '22 20:01 dlech

Right now, it's an unconfirmed hack. It seems to work, but I'd prefer something more robust if someone else's code is relying on it.

jeffsf avatar Jan 03 '22 03:01 jeffsf

OK. We should probably look at how this works on other operating systems as well. Reusing a BleakClient after disconnection probably isn't the best idea anyway. It would be better to use a BleakScanner to scan and find the device again and create a new BleakClient.

dlech avatar Jan 03 '22 17:01 dlech

Forcing a BleakClient to forget its device information received from previous scanning is preferable from a Bleak maintainer standpoint, but when you are a user you would like it to be simpler, don't you?

However, this would probably mean retaining the Bluetooth address and scan unbeknownst to the user which is something we want to move away from?

hbldh avatar Jan 03 '22 22:01 hbldh

Forcing a BleakClient to forget its device information received from previous scanning is preferable from a Bleak maintainer standpoint, but when you are a user you would like it to be simpler, don't you?

Yes, but it seems keeping BleakClient in a consistent state after disconnection is a difficult and complex problem that we have not fully solved yet (I'm starting to understand why some people have such strong feelings against OOP :wink:). These are some of the other issues related to reconnecting: #639, #376, #172, #676, #361, #121, #354, #477, #136, #110, #83.

However, this would probably mean retaining the Bluetooth address and scan unbeknownst to the user which is something we want to move away from?

For people who are connecting to multiple devices at the same time, I think this has been particularly problematic due to the fact that trying to do more than one thing at a time with Bluetooth tends to break Bluetooth at the OS level.

Also with the current macOS 12 bug, the scanner in the BleakClient is not going to work, so that is another reason to avoid relying on it.

dlech avatar Jan 04 '22 18:01 dlech

Yes, but it seems keeping BleakClient in a consistent state after disconnection is a difficult and complex problem that we have not fully solved yet

So the recommended solution is to create a new BleakClient? Does that fall under the "complex use cases" mentioned in the docs?

joooeey avatar Mar 16 '23 10:03 joooeey