diff --git a/homeassistant/components/hikvision/config_flow.py b/homeassistant/components/hikvision/config_flow.py index a38cf8d8ed5b79..ebfa0931f19c2b 100644 --- a/homeassistant/components/hikvision/config_flow.py +++ b/homeassistant/components/hikvision/config_flow.py @@ -52,8 +52,10 @@ async def async_step_user( HikCamera, url, port, username, password, ssl ) except requests.exceptions.RequestException: - _LOGGER.exception("Error connecting to Hikvision device") errors["base"] = "cannot_connect" + except Exception: + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" else: device_id = camera.get_id device_name = camera.get_name @@ -105,10 +107,10 @@ async def async_step_import(self, import_data: ConfigType) -> ConfigFlowResult: HikCamera, url, port, username, password, ssl ) except requests.exceptions.RequestException: - _LOGGER.exception( - "Error connecting to Hikvision device during import, aborting" - ) return self.async_abort(reason="cannot_connect") + except Exception: + _LOGGER.exception("Unexpected exception") + return self.async_abort(reason="unknown") device_id = camera.get_id device_name = camera.get_name @@ -118,10 +120,6 @@ async def async_step_import(self, import_data: ConfigType) -> ConfigFlowResult: await self.async_set_unique_id(device_id) self._abort_if_unique_id_configured() - _LOGGER.warning( - "Importing Hikvision config from configuration.yaml for %s", host - ) - return self.async_create_entry( title=name or device_name or host, data={ diff --git a/homeassistant/components/hikvision/strings.json b/homeassistant/components/hikvision/strings.json index 4501113d3099a3..ca25cccf772897 100644 --- a/homeassistant/components/hikvision/strings.json +++ b/homeassistant/components/hikvision/strings.json @@ -1,10 +1,12 @@ { "config": { "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "unknown": "[%key:common::config_flow::error::unknown%]" }, "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]" }, "step": { "user": { diff --git a/tests/components/hikvision/conftest.py b/tests/components/hikvision/conftest.py index faa612e31800db..4964b1f8f32f50 100644 --- a/tests/components/hikvision/conftest.py +++ b/tests/components/hikvision/conftest.py @@ -13,9 +13,6 @@ CONF_SSL, CONF_USERNAME, ) -from homeassistant.core import HomeAssistant - -from . import setup_integration from tests.common import MockConfigEntry @@ -94,12 +91,3 @@ def mock_hik_nvr(mock_hikcamera: MagicMock) -> MagicMock: camera.current_event_states = {} camera.get_event_triggers.return_value = {"Motion": [1, 2]} return mock_hikcamera - - -@pytest.fixture -async def init_integration( - hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_hikcamera: MagicMock -) -> MockConfigEntry: - """Set up the Hikvision integration for testing.""" - await setup_integration(hass, mock_config_entry) - return mock_config_entry diff --git a/tests/components/hikvision/test_config_flow.py b/tests/components/hikvision/test_config_flow.py index 46081077a17f28..9542fb86e5199f 100644 --- a/tests/components/hikvision/test_config_flow.py +++ b/tests/components/hikvision/test_config_flow.py @@ -161,6 +161,52 @@ async def test_form_exception( assert result["result"].unique_id == TEST_DEVICE_ID +async def test_form_unknown_exception( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_hikcamera: MagicMock, +) -> None: + """Test we handle unknown exception during connection and can recover.""" + mock_hikcamera.side_effect = Exception("Unexpected error") + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: TEST_HOST, + CONF_PORT: TEST_PORT, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_SSL: False, + }, + ) + + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": "unknown"} + + # Recover from error + mock_hikcamera.side_effect = None + mock_hikcamera.return_value.get_id = TEST_DEVICE_ID + mock_hikcamera.return_value.get_name = TEST_DEVICE_NAME + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: TEST_HOST, + CONF_PORT: TEST_PORT, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_SSL: False, + }, + ) + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["result"].unique_id == TEST_DEVICE_ID + + async def test_form_already_configured( hass: HomeAssistant, mock_setup_entry: AsyncMock, @@ -297,6 +343,30 @@ async def test_import_flow_no_device_id( assert result["reason"] == "cannot_connect" +async def test_import_flow_unknown_exception( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_hikcamera: MagicMock, +) -> None: + """Test YAML import flow aborts on unknown exception.""" + mock_hikcamera.side_effect = Exception("Unexpected error") + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={ + CONF_HOST: TEST_HOST, + CONF_PORT: TEST_PORT, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_SSL: False, + }, + ) + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "unknown" + + async def test_import_flow_already_configured( hass: HomeAssistant, mock_setup_entry: AsyncMock,