bleak
bleak copied to clipboard
Exception message and behavior when device has gone and returned are inconsistent and not robust
- 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.
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.
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
)
Workaround in use now is to set
_device_path
toNone
in the disconnected callback.
Would you like to make a pull request with this fix?
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.
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
.
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?
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.
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?