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

Improve error handling when a device is not present in BlueZ #1511

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ Changed
-------
* Updated PyObjC dependency on macOS to v10.x.
* Updated missing Bluetooth SIG characteristics and service UUIDs.
* Updated ``BlueZManager`` to remove empty interfaces from `_properties` during InterfacesRemoved message.

Fixed
-----
* Fixed BlueZ version in passive scanning error message. Fixes #1433.
* Fixed mypy requiring ``Unpack[ExtraArgs]`` that were intended to be optional. Fixes #1487.
* Fixed ``KeyError`` in BlueZ ``is_connected()`` and ``get_global_bluez_manager()`` when device is not present. Fixes #1507.

`0.21.1`_ (2023-09-08)
======================
Expand Down
41 changes: 33 additions & 8 deletions bleak/backends/bluezdbus/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,28 @@ def _check_device(self, device_path: str) -> None:
if device_path not in self._properties:
raise BleakError(f"device '{device_path.split('/')[-1]}' not found")

def _get_device_property(
self, device_path: str, interface: str, property_name: str
) -> Any:
self._check_device(device_path)
device_properties = self._properties[device_path]

try:
interface_properties = device_properties[interface]
except KeyError:
raise BleakError(
f"Interface {interface} not found for device '{device_path}'"
)

try:
value = interface_properties[property_name]
except KeyError:
raise BleakError(
f"Property '{property_name}' not found for '{interface}' in '{device_path}'"
)

return value

async def async_init(self):
"""
Connects to the D-Bus message bus and begins monitoring signals.
Expand Down Expand Up @@ -711,9 +733,7 @@ def get_device_name(self, device_path: str) -> str:
Raises:
BleakError: if the device is not present in BlueZ
"""
self._check_device(device_path)

return self._properties[device_path][defs.DEVICE_INTERFACE]["Name"]
return self._get_device_property(device_path, defs.DEVICE_INTERFACE, "Name")

def is_connected(self, device_path: str) -> bool:
"""
Expand Down Expand Up @@ -820,12 +840,11 @@ async def _wait_condition(
Raises:
BleakError: if the device is not present in BlueZ
"""
self._check_device(device_path)
value = self._get_device_property(
device_path, defs.DEVICE_INTERFACE, property_name
)

if (
self._properties[device_path][defs.DEVICE_INTERFACE][property_name]
== property_value
):
if value == property_value:
return

event = asyncio.Event()
Expand Down Expand Up @@ -942,6 +961,12 @@ def _parse_msg(self, message: Message):
del self._descriptor_map[obj_path]
except KeyError:
pass

# Remove empty properties when all interfaces have been removed.
# This avoids wasting memory for people who have noisy devices
# with private addresses that change frequently.
if obj_path in self._properties and not self._properties[obj_path]:
del self._properties[obj_path]
elif message.member == "PropertiesChanged":
interface, changed, invalidated = message.body
message_path = message.path
Expand Down
Loading