diff --git a/homeassistant/components/onvif/config_flow.py b/homeassistant/components/onvif/config_flow.py index aeac90b301f..1fa904e67e4 100644 --- a/homeassistant/components/onvif/config_flow.py +++ b/homeassistant/components/onvif/config_flow.py @@ -206,9 +206,12 @@ class OnvifFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if not self.device_id: try: network_interfaces = await device_mgmt.GetNetworkInterfaces() - for interface in network_interfaces: - if interface.Enabled: - self.device_id = interface.Info.HwAddress + interface = next( + filter(lambda interface: interface.Enabled, network_interfaces), + None, + ) + if interface: + self.device_id = interface.Info.HwAddress except Fault as fault: if "not implemented" not in fault.message: raise fault @@ -248,8 +251,6 @@ class OnvifFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if not h264: return self.async_abort(reason="no_h264") - await device.close() - title = f"{self.onvif_config[CONF_NAME]} - {self.device_id}" return self.async_create_entry(title=title, data=self.onvif_config) @@ -259,13 +260,14 @@ class OnvifFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self.onvif_config[CONF_NAME], err, ) - await device.close() return self.async_abort(reason="onvif_error") except Fault: errors["base"] = "cannot_connect" - await device.close() + finally: + await device.close() + return self.async_show_form(step_id="auth", errors=errors) async def async_step_import(self, user_input): diff --git a/tests/components/onvif/test_config_flow.py b/tests/components/onvif/test_config_flow.py index 2bacd61e09f..626eec433d1 100644 --- a/tests/components/onvif/test_config_flow.py +++ b/tests/components/onvif/test_config_flow.py @@ -41,6 +41,7 @@ def setup_mock_onvif_camera( with_h264=True, two_profiles=False, with_interfaces=True, + with_interfaces_not_implemented=False, with_serial=True, ): """Prepare mock onvif.ONVIFCamera.""" @@ -54,9 +55,14 @@ def setup_mock_onvif_camera( interface.Enabled = True interface.Info.HwAddress = MAC - devicemgmt.GetNetworkInterfaces = AsyncMock( - return_value=[interface] if with_interfaces else [] - ) + if with_interfaces_not_implemented: + devicemgmt.GetNetworkInterfaces = AsyncMock( + side_effect=Fault("not implemented") + ) + else: + devicemgmt.GetNetworkInterfaces = AsyncMock( + return_value=[interface] if with_interfaces else [] + ) media_service = MagicMock() @@ -413,6 +419,47 @@ async def test_flow_manual_entry(hass): } +async def test_flow_import_not_implemented(hass): + """Test that config flow uses Serial Number when no MAC available.""" + with patch( + "homeassistant.components.onvif.config_flow.get_device" + ) as mock_onvif_camera, patch( + "homeassistant.components.onvif.ONVIFDevice" + ) as mock_device, patch( + "homeassistant.components.onvif.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.onvif.async_setup_entry", return_value=True + ) as mock_setup_entry: + setup_mock_onvif_camera(mock_onvif_camera, with_interfaces_not_implemented=True) + setup_mock_device(mock_device) + + result = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + config_flow.CONF_NAME: NAME, + config_flow.CONF_HOST: HOST, + config_flow.CONF_PORT: PORT, + config_flow.CONF_USERNAME: USERNAME, + config_flow.CONF_PASSWORD: PASSWORD, + }, + ) + + await hass.async_block_till_done() + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == f"{NAME} - {SERIAL_NUMBER}" + assert result["data"] == { + config_flow.CONF_NAME: NAME, + config_flow.CONF_HOST: HOST, + config_flow.CONF_PORT: PORT, + config_flow.CONF_USERNAME: USERNAME, + config_flow.CONF_PASSWORD: PASSWORD, + } + + async def test_flow_import_no_mac(hass): """Test that config flow uses Serial Number when no MAC available.""" with patch(