diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 35389432..e2c468f6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,17 @@ and this project adheres to `Semantic Versioning None: self._is_connected = True except BaseException: # calling Disconnect cancels any pending connect request - try: - reply = await self._bus.call( - Message( - destination=defs.BLUEZ_SERVICE, - interface=defs.DEVICE_INTERFACE, - path=self._device_path, - member="Disconnect", - ) - ) + if self._bus: + # If disconnected callback already fired, this will be a no-op + # since self._bus will be None and the _cleanup_all call will + # have already disconnected. try: - assert_reply(reply) - except BleakDBusError as e: - # if the object no longer exists, then we know we - # are disconnected for sure, so don't need to log a - # warning about it - if e.dbus_error != ErrorType.UNKNOWN_OBJECT.value: - raise - except Exception as e: - logger.warning( - f"Failed to cancel connection ({self._device_path}): {e}" - ) + reply = await self._bus.call( + Message( + destination=defs.BLUEZ_SERVICE, + interface=defs.DEVICE_INTERFACE, + path=self._device_path, + member="Disconnect", + ) + ) + try: + assert_reply(reply) + except BleakDBusError as e: + # if the object no longer exists, then we know we + # are disconnected for sure, so don't need to log a + # warning about it + if e.dbus_error != ErrorType.UNKNOWN_OBJECT.value: + raise + except Exception as e: + logger.warning( + f"Failed to cancel connection ({self._device_path}): {e}" + ) raise diff --git a/bleak/backends/bluezdbus/manager.py b/bleak/backends/bluezdbus/manager.py index cb8834e2..8e137381 100644 --- a/bleak/backends/bluezdbus/manager.py +++ b/bleak/backends/bluezdbus/manager.py @@ -17,6 +17,7 @@ Iterable, List, NamedTuple, + Optional, Set, Tuple, cast, @@ -266,7 +267,7 @@ class BlueZManager: """ def __init__(self): - self._bus = MessageBus(bus_type=BusType.SYSTEM) + self._bus: Optional[MessageBus] = None self._bus_lock = asyncio.Lock() # dict of object path: dict of interface name: dict of property name: property value @@ -284,22 +285,26 @@ async def async_init(self): connected, no action is performed. """ async with self._bus_lock: - if self._bus.connected: + if self._bus and self._bus.connected: return - await self._bus.connect() + # We need to create a new MessageBus each time as + # dbus-next will destory the underlying file descriptors + # when the previous one is closed in its finalizer. + bus = MessageBus(bus_type=BusType.SYSTEM) + await bus.connect() try: # Add signal listeners - self._bus.add_message_handler(self._parse_msg) + bus.add_message_handler(self._parse_msg) rules = MatchRules( interface=defs.OBJECT_MANAGER_INTERFACE, member="InterfacesAdded", arg0path="/org/bluez/", ) - reply = await add_match(self._bus, rules) + reply = await add_match(bus, rules) assert_reply(reply) rules = MatchRules( @@ -307,7 +312,7 @@ async def async_init(self): member="InterfacesRemoved", arg0path="/org/bluez/", ) - reply = await add_match(self._bus, rules) + reply = await add_match(bus, rules) assert_reply(reply) rules = MatchRules( @@ -315,13 +320,13 @@ async def async_init(self): member="PropertiesChanged", path_namespace="/org/bluez", ) - reply = await add_match(self._bus, rules) + reply = await add_match(bus, rules) assert_reply(reply) # get existing objects after adding signal handlers to avoid # race condition - reply = await self._bus.call( + reply = await bus.call( Message( destination=defs.BLUEZ_SERVICE, path="/", @@ -341,9 +346,12 @@ async def async_init(self): except BaseException: # if setup failed, disconnect - await self._bus.disconnect() + bus.disconnect() raise + # Everything is setup, so save the bus + self._bus = bus + async def active_scan( self, adapter_path: str,