diff --git a/homeassistant/components/bsblan/__init__.py b/homeassistant/components/bsblan/__init__.py index 623bfbfef56..a7beb4f8d44 100644 --- a/homeassistant/components/bsblan/__init__.py +++ b/homeassistant/components/bsblan/__init__.py @@ -2,7 +2,16 @@ import dataclasses -from bsblan import BSBLAN, BSBLANConfig, Device, Info, StaticState +from bsblan import ( + BSBLAN, + BSBLANAuthError, + BSBLANConfig, + BSBLANConnectionError, + BSBLANError, + Device, + Info, + StaticState, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -13,9 +22,14 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryError, + ConfigEntryNotReady, +) from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import CONF_PASSKEY +from .const import CONF_PASSKEY, DOMAIN from .coordinator import BSBLanUpdateCoordinator PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.WATER_HEATER] @@ -54,10 +68,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: BSBLanConfigEntry) -> bo coordinator = BSBLanUpdateCoordinator(hass, entry, bsblan) await coordinator.async_config_entry_first_refresh() - # Fetch all required data concurrently - device = await bsblan.device() - info = await bsblan.info() - static = await bsblan.static_values() + try: + # Fetch all required data sequentially + device = await bsblan.device() + info = await bsblan.info() + static = await bsblan.static_values() + except BSBLANConnectionError as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="setup_connection_error", + translation_placeholders={"host": entry.data[CONF_HOST]}, + ) from err + except BSBLANAuthError as err: + raise ConfigEntryAuthFailed( + translation_domain=DOMAIN, + translation_key="setup_auth_error", + ) from err + except BSBLANError as err: + raise ConfigEntryError( + translation_domain=DOMAIN, + translation_key="setup_general_error", + ) from err entry.runtime_data = BSBLanData( client=bsblan, diff --git a/homeassistant/components/bsblan/strings.json b/homeassistant/components/bsblan/strings.json index 86e52e76f41..b27be62e052 100644 --- a/homeassistant/components/bsblan/strings.json +++ b/homeassistant/components/bsblan/strings.json @@ -41,6 +41,11 @@ "passkey": "[%key:component::bsblan::config::step::user::data::passkey%]", "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]" + }, + "data_description": { + "passkey": "[%key:component::bsblan::config::step::user::data_description::passkey%]", + "username": "[%key:component::bsblan::config::step::user::data_description::username%]", + "password": "[%key:component::bsblan::config::step::user::data_description::password%]" } } }, @@ -66,6 +71,15 @@ }, "set_operation_mode_error": { "message": "An error occurred while setting the operation mode" + }, + "setup_connection_error": { + "message": "Failed to retrieve static device data from BSB-Lan device at {host}" + }, + "setup_auth_error": { + "message": "Authentication failed while retrieving static device data" + }, + "setup_general_error": { + "message": "An unknown error occurred while retrieving static device data" } }, "entity": { diff --git a/tests/components/bsblan/test_init.py b/tests/components/bsblan/test_init.py index cc52799d28b..10945a24878 100644 --- a/tests/components/bsblan/test_init.py +++ b/tests/components/bsblan/test_init.py @@ -2,8 +2,9 @@ from unittest.mock import MagicMock -from bsblan import BSBLANAuthError, BSBLANConnectionError +from bsblan import BSBLANAuthError, BSBLANConnectionError, BSBLANError from freezegun.api import FrozenDateTimeFactory +import pytest from homeassistant.components.bsblan.const import DOMAIN from homeassistant.config_entries import ConfigEntryState @@ -75,3 +76,38 @@ async def test_config_entry_auth_failed_triggers_reauth( assert len(flows) == 1 assert flows[0]["context"]["source"] == "reauth" assert flows[0]["context"]["entry_id"] == mock_config_entry.entry_id + + +@pytest.mark.parametrize( + ("method", "exception", "expected_state"), + [ + ( + "device", + BSBLANConnectionError("Connection failed"), + ConfigEntryState.SETUP_RETRY, + ), + ( + "info", + BSBLANAuthError("Authentication failed"), + ConfigEntryState.SETUP_ERROR, + ), + ("static_values", BSBLANError("General error"), ConfigEntryState.SETUP_ERROR), + ], +) +async def test_config_entry_static_data_errors( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, + method: str, + exception: Exception, + expected_state: ConfigEntryState, +) -> None: + """Test various errors during static data fetching trigger appropriate config entry states.""" + # Mock the specified method to raise the exception + getattr(mock_bsblan, method).side_effect = exception + + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert mock_config_entry.state is expected_state