Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidStateError after callback #675

Closed
Xlinx64 opened this issue Oct 21, 2021 · 7 comments · Fixed by #678
Closed

InvalidStateError after callback #675

Xlinx64 opened this issue Oct 21, 2021 · 7 comments · Fixed by #678
Assignees
Labels
Backend: Core Bluetooth Issues and PRs relating to the Core Bluetooth backend

Comments

@Xlinx64
Copy link

Xlinx64 commented Oct 21, 2021

  • bleak version: 0.13.0
  • Python version: 3.9.7
  • Operating System: MacOS 11.6

Description

When the callback of a subscribed characteristic is called I get an asyncio exception. The value which gets passed into the callback is correct.
This started happening with version 0.12.0. There is no error on 0.11.0.

#!python3
import struct
import asyncio
from bleak import BleakClient, BleakScanner


UUID_HEIGHT = '99fa0021-338a-1024-8a49-009c0215f78a'


async def scan(address=None):
    scanner = BleakScanner()
    devices = await scanner.discover(device="hci0", timeout=5)
    for device in devices:
        if (device.address == address):
            return device
    return None


def callback(sender: int, data: bytearray):
    height, speed = struct.unpack("<Hh", data)
    print(height)


async def main():
    device = await scan("15D4F5E0-8BBD-426C-8426-25A9D4E5BDE3")
    client = BleakClient(device, device="hci0")
    await client.connect(timeout=10)
    initial_height, speed = struct.unpack("<Hh", await client.read_gatt_char(UUID_HEIGHT))
    await client.start_notify(UUID_HEIGHT, callback)
    print("connected")
    loop = asyncio.get_event_loop()
    wait = loop.create_future()
    await wait


if __name__ == "__main__":
    asyncio.run(main())
2212
Exception in callback PeripheralDelegate.did_update_value_for_characteristic(<CBPeripheral...e = connected>, <CBCharacteri...tifying = YES>, {length = 4, ... = 0xa408c0f3}, None)
handle: <Handle PeripheralDelegate.did_update_value_for_characteristic(<CBPeripheral...e = connected>, <CBCharacteri...tifying = YES>, {length = 4, ... = 0xa408c0f3}, None)>
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.9/site-packages/bleak/backends/corebluetooth/PeripheralDelegate.py", line 333, in did_update_value_for_characteristic
    future.set_result(None)
asyncio.exceptions.InvalidStateError: invalid state
2201
Exception in callback PeripheralDelegate.did_update_value_for_characteristic(<CBPeripheral...e = connected>, <CBCharacteri...tifying = YES>, {length = 4, ... = 0x990890f2}, None)
handle: <Handle PeripheralDelegate.did_update_value_for_characteristic(<CBPeripheral...e = connected>, <CBCharacteri...tifying = YES>, {length = 4, ... = 0x990890f2}, None)>
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.9/site-packages/bleak/backends/corebluetooth/PeripheralDelegate.py", line 333, in did_update_value_for_characteristic
    future.set_result(None)
asyncio.exceptions.InvalidStateError: invalid state
2196
Exception in callback PeripheralDelegate.did_update_value_for_characteristic(<CBPeripheral...e = connected>, <CBCharacteri...tifying = YES>, {length = 4, ... = 0x94080000}, None)
handle: <Handle PeripheralDelegate.did_update_value_for_characteristic(<CBPeripheral...e = connected>, <CBCharacteri...tifying = YES>, {length = 4, ... = 0x94080000}, None)>
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.9/site-packages/bleak/backends/corebluetooth/PeripheralDelegate.py", line 333, in did_update_value_for_characteristic
    future.set_result(None)
asyncio.exceptions.InvalidStateError: invalid state
@dlech dlech added the Backend: Core Bluetooth Issues and PRs relating to the Core Bluetooth backend label Oct 21, 2021
@dlech
Copy link
Collaborator

dlech commented Oct 21, 2021

Can you please share a full program to reproduce the error?

@Xlinx64
Copy link
Author

Xlinx64 commented Oct 21, 2021

I added an example code. I also noticed that this line is the problem. When I remove it the error goes away.

initial_height, speed = struct.unpack("<Hh", await client.read_gatt_char(UUID_HEIGHT))

Why cant I read the value before adding the callback? Did I do something wrong here?

@dlech
Copy link
Collaborator

dlech commented Oct 21, 2021

Thanks for updating. Read and notify use the same underlying callback in CoreBluetooth, so I think I see how the bug is happening now. It is a problem in Bleak.

@dlech dlech self-assigned this Oct 23, 2021
dlech added a commit that referenced this issue Oct 23, 2021
In the CoreBluetooth backend, futures are used to pass results/errors
from the delegate callbacks to methods that are waiting for them. The
futures are stored as part of the object state so that the delegates
can access them.

Prior to this change, completed futures were not removed from the
object status. This worked most of the time because a second call
of the same method would replace the completed future from the prior
call. However, in the case of notifications and reads, the same
delegate callback is shared with two methods. So when both of these
were used, notifications would attempt to complete the already completed
read future which raises an InvalidStateError.

Fixes #675.
@dlech
Copy link
Collaborator

dlech commented Oct 23, 2021

Hi @Xlinx64, can you please test #678? (I'm unable to test right now due to #635).

@dlech dlech linked a pull request Oct 23, 2021 that will close this issue
@Xlinx64
Copy link
Author

Xlinx64 commented Oct 23, 2021

Thanks for your fast fix. Unfortunately I am unable to connect anymore.
I installed your branch with this command:

pip3 install git+https://github.com/hbldh/bleak.git@cb-futures

I got this error message:

Traceback (most recent call last):
  File "/Users/david/./Desktop/x.py", line 42, in <module>
    asyncio.run(main())
  File "/usr/local/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/Users/david/./Desktop/x.py", line 30, in main
    await client.connect(timeout=10)
  File "/usr/local/lib/python3.9/site-packages/bleak/backends/corebluetooth/client.py", line 118, in connect
    await manager.connect(self._peripheral, disconnect_callback, timeout=timeout)
  File "/usr/local/lib/python3.9/site-packages/bleak/backends/corebluetooth/CentralManagerDelegate.py", line 161, in connect
    del self._connect_futures[peripheral.identifier()]
KeyError: 15D4F5E0-8BBD-426C-8426-25A9D4E5BDE3

dlech added a commit that referenced this issue Oct 23, 2021
In the CoreBluetooth backend, futures are used to pass results/errors
from the delegate callbacks to methods that are waiting for them. The
futures are stored as part of the object state so that the delegates
can access them.

Prior to this change, completed futures were not removed from the
object status. This worked most of the time because a second call
of the same method would replace the completed future from the prior
call. However, in the case of notifications and reads, the same
delegate callback is shared with two methods. So when both of these
were used, notifications would attempt to complete the already completed
read future which raises an InvalidStateError.

Fixes #675.
@dlech
Copy link
Collaborator

dlech commented Oct 23, 2021

Thanks for trying. I just force pushed a change that should hopefully fix the KeyError.

@dlech dlech closed this as completed Oct 23, 2021
@dlech dlech reopened this Oct 23, 2021
@Xlinx64
Copy link
Author

Xlinx64 commented Oct 23, 2021

Thanks it works now. It connects successfully and the callback works without any errors.
Thanks for your great work. I am looking forward to the next release :)

@Xlinx64 Xlinx64 closed this as completed Oct 23, 2021
dlech added a commit that referenced this issue Jan 8, 2022
Added
-----

* Added ``service_uuids`` kwarg to  ``BleakScanner``. This can be used to work around issue of scanning not working on macOS 12. Fixes #230. Works around #635.
* Added UUIDs for LEGO Powered Up Smart Hubs.

Changed
-------

* Changed WinRT backend to use GATT session status instead of actual device connection status.
* Changed handling of scan response data on WinRT backend. Advertising data and scan response data is now combined in callbacks like other platforms.
* Updated ``bleak-winrt`` dependency to v1.1.0. Fixes #698.

Fixed
-----

* Fixed ``InvalidStateError`` in CoreBluetooth backend when read and notification of the same characteristic are used. Fixes #675.
* Fixed reading a characteristic on CoreBluetooth backend also triggers notification callback.
* Fixed in Linux, scanner callback not setting metadata parameters. Merged #715.
@dlech dlech mentioned this issue Jan 8, 2022
dlech added a commit that referenced this issue Jan 10, 2022
Added
-----

* Added ``service_uuids`` kwarg to  ``BleakScanner``. This can be used to work around issue of scanning not working on macOS 12. Fixes #230. Works around #635.
* Added UUIDs for LEGO Powered Up Smart Hubs.

Changed
-------

* Changed WinRT backend to use GATT session status instead of actual device connection status.
* Changed handling of scan response data on WinRT backend. Advertising data and scan response data is now combined in callbacks like other platforms.
* Updated ``bleak-winrt`` dependency to v1.1.0. Fixes #698.

Fixed
-----

* Fixed ``InvalidStateError`` in CoreBluetooth backend when read and notification of the same characteristic are used. Fixes #675.
* Fixed reading a characteristic on CoreBluetooth backend also triggers notification callback.
* Fixed in Linux, scanner callback not setting metadata parameters. Merged #715.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backend: Core Bluetooth Issues and PRs relating to the Core Bluetooth backend
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants