diff --git a/src/bleak_retry_connector/__init__.py b/src/bleak_retry_connector/__init__.py index 90ea736..1975bca 100644 --- a/src/bleak_retry_connector/__init__.py +++ b/src/bleak_retry_connector/__init__.py @@ -275,10 +275,21 @@ async def get_device_by_adapter(address: str, adapter: str) -> BLEDevice | None: return None +def _reset_dbus_socket_cache() -> None: + """Reset the dbus socket cache.""" + setattr(get_bluez_device, "_has_dbus_socket", None) + + async def get_bluez_device( name: str, path: str, rssi: int | None = None, _log_disappearance: bool = True ) -> BLEDevice | None: """Get a BLEDevice object for a BlueZ DBus path.""" + if getattr(get_bluez_device, "_has_dbus_socket", None) is False: + # We are not running on a system with DBus do don't + # keep trying to call get_global_bluez_manager as it + # waits for a bit trying to connect to DBus. + return None + best_path = device_path = path rssi_to_beat: int = rssi or NO_RSSI_VALUE @@ -339,9 +350,16 @@ async def get_bluez_device( return ble_device_from_properties( best_path, properties[best_path][defs.DEVICE_INTERFACE] ) + except FileNotFoundError as ex: + setattr(get_bluez_device, "_has_dbus_socket", False) + _LOGGER.debug( + "Dbus socket at %s not found, will not try again until next restart: %s", + ex.filename, + ex, + ) except Exception as ex: # pylint: disable=broad-except _LOGGER.debug( - "%s - %s: Freshen failed: %s", name, device_path, ex, exc_info=True + "%s - %s: get_bluez_device failed: %s", name, device_path, ex, exc_info=True ) return None diff --git a/tests/test_init.py b/tests/test_init.py index ce047c5..ce3bb44 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -23,6 +23,7 @@ BleakConnectionError, BleakNotFoundError, BleakOutOfConnectionSlotsError, + _reset_dbus_socket_cache, ble_device_has_changed, calculate_backoff_time, establish_connection, @@ -1562,3 +1563,83 @@ async def test_function(): await test_function() assert mock_calculate_backoff_time.call_count == 4 + + +@pytest.mark.asyncio +async def test_dbus_is_missing(): + """Test getting a device when dbus is missing.""" + + bleak_retry_connector.get_global_bluez_manager = AsyncMock( + side_effect=FileNotFoundError("dbus not here") + ) + bleak_retry_connector.defs = defs + + with patch.object(bleak_retry_connector, "IS_LINUX", True): + device = await get_device("FA:23:9D:AA:45:46") + + assert device is None + + class FakeBluezManager: + def __init__(self): + self._properties = { + "/org/bluez/hci0/dev_FA_23_9D_AA_45_46": { + "UUID": "service", + "Primary": True, + "Characteristics": [], + defs.DEVICE_INTERFACE: { + "Address": "FA:23:9D:AA:45:46", + "Alias": "FA:23:9D:AA:45:46", + "RSSI": -30, + }, + defs.GATT_SERVICE_INTERFACE: True, + }, + "/org/bluez/hci1/dev_FA_23_9D_AA_45_46": { + "UUID": "service", + "Primary": True, + "Characteristics": [], + defs.DEVICE_INTERFACE: { + "Address": "FA:23:9D:AA:45:46", + "Alias": "FA:23:9D:AA:45:46", + "RSSI": -79, + }, + defs.GATT_SERVICE_INTERFACE: True, + }, + "/org/bluez/hci2/dev_FA_23_9D_AA_45_46": { + "UUID": "service", + "Primary": True, + "Characteristics": [], + defs.DEVICE_INTERFACE: { + "Address": "FA:23:9D:AA:45:46", + "Alias": "FA:23:9D:AA:45:46", + "RSSI": -80, + }, + defs.GATT_SERVICE_INTERFACE: True, + }, + "/org/bluez/hci3/dev_FA_23_9D_AA_45_46": { + "UUID": "service", + "Primary": True, + "Characteristics": [], + defs.DEVICE_INTERFACE: { + "Address": "FA:23:9D:AA:45:46", + "Alias": "FA:23:9D:AA:45:46", + "RSSI": -31, + }, + defs.GATT_SERVICE_INTERFACE: True, + }, + } + + bleak_retry_connector.get_global_bluez_manager = AsyncMock( + return_value=FakeBluezManager() + ) + + with patch.object(bleak_retry_connector, "IS_LINUX", True): + device = await get_device("FA:23:9D:AA:45:46") + + assert device is None + + _reset_dbus_socket_cache() + + with patch.object(bleak_retry_connector, "IS_LINUX", True): + device = await get_device("FA:23:9D:AA:45:46") + + assert device is not None