WinRT: pairing crashes if accepting too soon
- bleak version: 0.13.0
- Python version: 3.8.2
- Operating System: Windows 10
Description
Trying to pair and connect to the device, after accepting to pair on the OS pop-up, Bleak fails. The device I am trying to connect to requires pairing. If I accept pairing as soon as it pops up, the script fails. If I wait until after services discovery has finished, and only then accept the pairing, it will work OK.
What I Did
With this code:
# bleak_minimal_example.py
import asyncio
from bleak import BleakScanner
from bleak import BleakClient
address = "AA:AA:AA:AA:AA:AA"
# Characteristics UUIDs
NUM_SENSORS_UUID = "b9e634a8-57ef-11e9-8647-d663bd873d93"
# ===============================================================
# Connect and read
# ===============================================================
async def run_connection(address, debug=False):
async with BleakClient(address) as client:
print(" >> Please accept pairing")
await asyncio.sleep(5.0)
print(" >> Reading...")
num_sensors = await client.read_gatt_char(NUM_SENSORS_UUID, use_cached = True)
print(" Number of sensors: {0}".format(num_sensors.hex()))
# ===============================================================
# MAIN
# ===============================================================
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(run_connection(address))
print ('>> Goodbye!')
exit(0)
Use case 1: fails Accepting the pairing before it finished discovering services - fails
- the script calls BleakClient.connect()
- connects to the device and Windows pop-up requests to pair
- Bleak.connect() is still doing the discovery (and it takes long, because the device has many services with many chars.)
- if I accept the pairing before it finished discovering it fails
The traceback of the error:
Traceback (most recent call last):
File "bleak_minimal_example.py", line 86, in <module>
loop.run_until_complete(run_connection(device_address))
File "C:\Program Files\Python38\lib\asyncio\base_events.py", line 616, in run_until_complete
return future.result()
File "bleak_minimal_example.py", line 50, in run_connection
async with BleakClient(address) as client:
File "C:\Program Files\Python38\lib\site-packages\bleak\backends\client.py", line 61, in __aenter__
await self.connect()
File "C:\Program Files\Python38\lib\site-packages\bleak\backends\winrt\client.py", line 258, in connect
await self.get_services()
File "C:\Program Files\Python38\lib\site-packages\bleak\backends\winrt\client.py", line 446, in get_services
await service.get_characteristics_async(
OSError: [WinError -2147418113] Catastrophic failure
Use case 2: succeeds Waiting to accept pairing, then succeeds
- the script calls BleakClient.connect()
- connects to the device and Windows pop-up requests to pair
- Bleak.connect() is still doing the discovery (and it takes long, because the device has many services with many chars.)
- after connect returned I print ('Please accept pairing')
- I accept the pairing
- the script reads + writes... all good
Alternative use of the API:
With the script modified to call pair() before connect():
# bleak_minimal_pair_example.py
import asyncio
from bleak import BleakScanner
from bleak import BleakClient
address = "AA:AA:AA:AA:AA:AA"
# Characteristics UUIDs
NUM_SENSORS_UUID = "b9e634a8-57ef-11e9-8647-d663bd873d93"
# ===============================================================
# Connect and read
# ===============================================================
async def run_connection(address, debug=False):
client = BleakClient(address)
if await client.pair():
pritnt("paired")
await client.connect()
print(" >> Reading...")
num_sensors = await client.read_gatt_char(NUM_SENSORS_UUID, use_cached = True)
print(" Number of sensors: {0}".format(num_sensors.hex()))
# ===============================================================
# MAIN
# ===============================================================
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(run_connection(address))
print ('>> Goodbye!')
exit(0)
The execution fails as follows:
The traceback of the error:
python bleak_minimal_pair_example.py
Traceback (most recent call last):
File "bleak_minimal_pair_example.py", line 47, in <module>
loop.run_until_complete(run_connection(address))
File "C:\Program Files\Python38\lib\asyncio\base_events.py", line 616, in run_until_complete
return future.result()
File "bleak_minimal_pair_example.py", line 31, in run_connection
if await client.pair():
File "C:\Program Files\Python38\lib\site-packages\bleak\backends\winrt\client.py", line 336, in pair
self._requester.device_information.pairing.can_pair
AttributeError: 'NoneType' object has no attribute 'device_information'
OSError: [WinError -2147418113] Catastrophic failure
Unfortunately, this means the error comes from the OS and there probably isn't a way to fix it in Bleak.
There is #640 that would allow implementing the pairing in Python instead of using the OS dialog, but it needs some more work.
Logging Bluetooth packets might also offer more insight in to what Windows is actually doing.
@marianacalzon can you perhaps try again using test pairing branch
pip install --force-reinstall git+https://github.com/bojanpotocnik/bleak@pairing_no_service_discovery
and using
client = BleakClient(address, skip_service_discovery=True)
as described here https://github.com/bojanpotocnik/bleak/blob/cfd09baddfbd27538e9a1cdd2ef3c7b8f2af229c/examples/pairing_agent.py#L82-L88 (from https://github.com/hbldh/bleak/pull/1133)? Based on my tests, this should solve your issue.