From 240bbfa0805ef28bf448abe7687904be3776e9f3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 23 Jul 2022 12:00:34 -0500 Subject: [PATCH] Retry later if bluetooth fails to start (#75647) --- .../components/bluetooth/__init__.py | 6 ++- tests/components/bluetooth/test_init.py | 42 +++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index a50be7f4ace..901b8ee0644 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -23,6 +23,7 @@ from homeassistant.core import ( HomeAssistant, callback as hass_callback, ) +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import discovery_flow from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo @@ -338,7 +339,6 @@ class BluetoothManager: except (FileNotFoundError, BleakError) as ex: raise RuntimeError(f"Failed to initialize Bluetooth: {ex}") from ex install_multiple_bleak_catcher() - self.async_setup_unavailable_tracking() # We have to start it right away as some integrations might # need it straight away. _LOGGER.debug("Starting bluetooth scanner") @@ -349,7 +349,9 @@ class BluetoothManager: try: await self.scanner.start() except (FileNotFoundError, BleakError) as ex: - raise RuntimeError(f"Failed to start Bluetooth: {ex}") from ex + self._cancel_device_detected() + raise ConfigEntryNotReady(f"Failed to start Bluetooth: {ex}") from ex + self.async_setup_unavailable_tracking() self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.async_stop) @hass_callback diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 5d932c56349..6dbec251026 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -15,6 +15,7 @@ from homeassistant.components.bluetooth import ( async_track_unavailable, models, ) +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.core import callback from homeassistant.setup import async_setup_component @@ -70,10 +71,7 @@ async def test_setup_and_stop_no_bluetooth(hass, caplog): async def test_setup_and_stop_broken_bluetooth(hass, caplog): """Test we fail gracefully when bluetooth/dbus is broken.""" - mock_bt = [ - {"domain": "switchbot", "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b"} - ] - + mock_bt = [] with patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch( "homeassistant.components.bluetooth.HaBleakScanner.start", side_effect=BleakError, @@ -93,6 +91,42 @@ async def test_setup_and_stop_broken_bluetooth(hass, caplog): assert len(bluetooth.async_discovered_service_info(hass)) == 0 +async def test_setup_and_retry_adapter_not_yet_available(hass, caplog): + """Test we retry if the adapter is not yet available.""" + mock_bt = [] + with patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch( + "homeassistant.components.bluetooth.HaBleakScanner.start", + side_effect=BleakError, + ), patch( + "homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt + ): + assert await async_setup_component( + hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}} + ) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + entry = hass.config_entries.async_entries(bluetooth.DOMAIN)[0] + + assert "Failed to start Bluetooth" in caplog.text + assert len(bluetooth.async_discovered_service_info(hass)) == 0 + assert entry.state == ConfigEntryState.SETUP_RETRY + + with patch( + "homeassistant.components.bluetooth.HaBleakScanner.start", + ): + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=10)) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.LOADED + + with patch( + "homeassistant.components.bluetooth.HaBleakScanner.stop", + ): + hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) + await hass.async_block_till_done() + + async def test_calling_async_discovered_devices_no_bluetooth(hass, caplog): """Test we fail gracefully when asking for discovered devices and there is no blueooth.""" mock_bt = []