First at all, thanks for your work of making BLE accessible to many users!
I'm using Python3.13 under ubuntu 24.04. I found a regression from 3.0.1 to 3.0.2. The script below connects to a device and subscribes to notifications. For each call of the notification callback, it prints the system timestamp.
#!/usr/bin/env python3
"""Minimal isolation script for a bleak notification timing regression.
Connects to the Elbemotion device "3A" and subscribes to its sensor
characteristic. Prints time.time() on every notification callback.
"""
import asyncio
import signal
import time
import bleak
DEVICE_ADDRESS = "F2:FC:AE:3B:4F:81"
ELBEMOTION_SENSOR_CHARACTERISTIC_UUID = "00001526-1212-efde-1523-785feabcd123"
async def main() -> None:
client = bleak.BleakClient(DEVICE_ADDRESS)
print(f"Connecting to {DEVICE_ADDRESS}...")
await client.connect()
print("Connected.")
def onNotify(sender: bleak.BleakGATTCharacteristic, data: bytearray) -> None:
print(time.time())
print("Starting notifications...")
await client.start_notify(ELBEMOTION_SENSOR_CHARACTERISTIC_UUID, onNotify)
print("Notifications started.")
stop = asyncio.Event()
def requestStop() -> None:
print("\nStop requested.")
stop.set()
for sig in (signal.SIGINT, signal.SIGTERM):
asyncio.get_running_loop().add_signal_handler(sig, requestStop)
while not stop.is_set():
if not client.is_connected:
print("Connection lost. Reconnecting...")
await client.connect()
print("Reconnected. Restarting notifications...")
await client.start_notify(ELBEMOTION_SENSOR_CHARACTERISTIC_UUID, onNotify)
print("Notifications restarted.")
await asyncio.sleep(0.5)
print("Stopping notifications...")
await client.stop_notify(ELBEMOTION_SENSOR_CHARACTERISTIC_UUID)
print("Disconnecting...")
await client.disconnect()
print("Disconnected.")
if __name__ == "__main__":
asyncio.run(main())
Connecting to a device, bringing it out of range and back the reconnection is triggered, calling start_notify again. The device I connect to sends out a notification around every 20ms (the conenction interval). Sometimes, it omits one interval.
Using bleak==3.0.1, I get this output:
Connecting to F2:FC:AE:3B:4F:81...
Connected.
Starting notifications...
Notifications started.
1781596770.9105916
1781596770.94964 # a new notification every conenction interval (20ms)
1781596770.969623
1781596771.0094657
1781596771.0305853
1781596771.069584
1781596771.0894392
1781596771.1296659
1781596771.150648
1781596771.1895993
1781596771.2096086
[...]
1781596777.6496344 # here the retransmissions kick in, when the device goes out of range. So, this anomaly can be ignored.
1781596777.6499054
1781596777.6695802
1781596781.1499517
1781596781.1502357
Connection lost. Reconnecting...
Reconnected. Restarting notifications...
Notifications restarted.
1781596786.8499906
1781596786.8896546
1781596786.9096422
1781596786.949646 # After reconnection, the callback is triggered every conenction interval once.
1781596786.9696465
1781596787.0096452
1781596787.0296354
1781596787.0696278
1781596787.089588
1781596787.1296456
1781596787.1496456
1781596787.1895897
1781596787.2096355
1781596787.2497025
1781596787.2705946
1781596787.309614
1781596787.329654
[...]
1781596793.0906317
1781596793.1296427
1781596793.149618
1781596793.1896496
^C
Stop requested.
1781596793.2106457
1781596793.2496228
1781596793.2696447
1781596793.3099658
1781596793.3296416
Stopping notifications...
Disconnecting...
Disconnected.
Now, see the recent bleak version. Im connecting to the same device, doing the same operations:
Connecting to F2:FC:AE:3B:4F:81...
Connected.
Starting notifications...
Notifications started.
1781596866.0702953
1781596866.0905392
1781596866.130504
1781596866.1505387 # After first connection, the behaviour is similar to 3.0.1
1781596866.1905417
1781596866.210494
1781596866.2505052
1781596866.2705452
1781596866.310493
1781596866.3304932
1781596866.3704903
1781596866.3905525
1781596866.4305096
1781596866.4504738
[...]
1781596868.650504
1781596868.7498758
1781596868.750046
1781596868.7503827
1781596868.7506287
1781596868.7705307
Connection lost. Reconnecting...
1781596877.4897788 # Note there, that the notifications fire already, even though start_notifications hasn't been called
1781596877.5105057 # the device is still sending notifications (it was enabled from before and disconect doesn't disable
1781596877.5495288 # it on the device)
1781596877.5705934
1781596877.6096215
1781596877.630572
1781596877.6796327
Reconnected. Restarting notifications...
Notifications restarted.
1781596877.6906075
1781596877.690866
1781596877.729487 # Now, after notifications were restarted, the callback is called twice per connection interval, twice with the same data.
1781596877.7296576
1781596877.7506154
1781596877.750835
1781596877.7895846
1781596877.7898517
1781596877.8104606
1781596877.8106112
1781596877.850472
1781596877.8506072
1781596877.8706095
1781596877.8708186
1781596877.9104657
1781596877.9106133
1781596877.9304643
1781596877.9305894
1781596877.969457
1781596877.969571
1781596877.9906108
1781596877.9908288
1781596878.030597
1781596878.0308335
1781596878.0506446
1781596878.050847
[...]
1781596884.949701
1781596884.949898
1781596884.950158
1781596884.9504018
1781596884.9506295
1781596884.9508505
Connection lost. Reconnecting...
1781596893.7697828
1781596893.7699683
1781596893.8105574
[...]
1781596893.9306588
1781596893.9308972
1781596893.959771
1781596893.9599738
Reconnected. Restarting notifications...
Notifications restarted.
1781596893.9906766
1781596893.9908817
1781596893.991154
1781596894.0106587 # Then, after another reconnection event, the callback is called 3 times.
1781596894.0108547
1781596894.0110848
1781596894.050667
1781596894.0509045
1781596894.0511444
1781596894.0906591
1781596894.0909135
1781596894.0911543
1781596894.1296618
1781596894.1298869
1781596894.1301556
1781596894.130429
1781596894.1306531
1781596894.1309137
1781596894.170578
1781596894.170708
[...]
I would expect that the start_notifications call would at least override the old callback or that at disconnect all notifications are disabled anyway (as is stated in the doc actually): https://bleak.readthedocs.io/en/latest/api/client.html#bleak.BleakClient.stop_notify

First at all, thanks for your work of making BLE accessible to many users!
I'm using Python3.13 under ubuntu 24.04. I found a regression from 3.0.1 to 3.0.2. The script below connects to a device and subscribes to notifications. For each call of the notification callback, it prints the system timestamp.
Connecting to a device, bringing it out of range and back the reconnection is triggered, calling start_notify again. The device I connect to sends out a notification around every 20ms (the conenction interval). Sometimes, it omits one interval.
Using bleak==3.0.1, I get this output:
Now, see the recent bleak version. Im connecting to the same device, doing the same operations:
I would expect that the start_notifications call would at least override the old callback or that at disconnect all notifications are disabled anyway (as is stated in the doc actually): https://bleak.readthedocs.io/en/latest/api/client.html#bleak.BleakClient.stop_notify