diff --git a/homeassistant/components/bosch_alarm/config_flow.py b/homeassistant/components/bosch_alarm/config_flow.py index 71e15f5959a..e492e2e7c14 100644 --- a/homeassistant/components/bosch_alarm/config_flow.py +++ b/homeassistant/components/bosch_alarm/config_flow.py @@ -15,6 +15,7 @@ from homeassistant.config_entries import ( SOURCE_DHCP, SOURCE_RECONFIGURE, SOURCE_USER, + ConfigEntryState, ConfigFlow, ConfigFlowResult, ) @@ -152,7 +153,7 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN): return self.async_abort(reason="already_in_progress") for entry in self.hass.config_entries.async_entries(DOMAIN): - if entry.data[CONF_MAC] == self.mac: + if entry.data.get(CONF_MAC) == self.mac: result = self.hass.config_entries.async_update_entry( entry, data={ @@ -163,6 +164,21 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN): if result: self.hass.config_entries.async_schedule_reload(entry.entry_id) return self.async_abort(reason="already_configured") + if entry.data[CONF_HOST] == discovery_info.ip: + if ( + not entry.data.get(CONF_MAC) + and entry.state is ConfigEntryState.LOADED + ): + result = self.hass.config_entries.async_update_entry( + entry, + data={ + **entry.data, + CONF_MAC: self.mac, + }, + ) + if result: + self.hass.config_entries.async_schedule_reload(entry.entry_id) + return self.async_abort(reason="already_configured") try: # Use load_selector = 0 to fetch the panel model without authentication. (model, _) = await try_connect( diff --git a/tests/components/bosch_alarm/conftest.py b/tests/components/bosch_alarm/conftest.py index 283eb158d5c..01b6252229a 100644 --- a/tests/components/bosch_alarm/conftest.py +++ b/tests/components/bosch_alarm/conftest.py @@ -201,15 +201,16 @@ def mock_config_entry( mac_address: str | None, ) -> MockConfigEntry: """Mock config entry for bosch alarm.""" + data = { + CONF_HOST: "0.0.0.0", + CONF_PORT: 7700, + CONF_MODEL: "bosch_alarm_test_data.model", + } + if mac_address: + data[CONF_MAC] = format_mac(mac_address) return MockConfigEntry( domain=DOMAIN, unique_id=serial_number, entry_id="01JQ917ACKQ33HHM7YCFXYZX51", - data={ - CONF_HOST: "0.0.0.0", - CONF_PORT: 7700, - CONF_MODEL: "bosch_alarm_test_data.model", - CONF_MAC: mac_address and format_mac(mac_address), - } - | extra_config_entry_data, + data=data | extra_config_entry_data, ) diff --git a/tests/components/bosch_alarm/snapshots/test_diagnostics.ambr b/tests/components/bosch_alarm/snapshots/test_diagnostics.ambr index 670db709a1a..ad8b7cfbc38 100644 --- a/tests/components/bosch_alarm/snapshots/test_diagnostics.ambr +++ b/tests/components/bosch_alarm/snapshots/test_diagnostics.ambr @@ -89,7 +89,6 @@ 'entry_data': dict({ 'host': '0.0.0.0', 'installer_code': '**REDACTED**', - 'mac': None, 'model': 'AMAX 3000', 'password': '**REDACTED**', 'port': 7700, @@ -185,7 +184,6 @@ }), 'entry_data': dict({ 'host': '0.0.0.0', - 'mac': None, 'model': 'B5512 (US1B)', 'password': '**REDACTED**', 'port': 7700, @@ -281,7 +279,6 @@ }), 'entry_data': dict({ 'host': '0.0.0.0', - 'mac': None, 'model': 'Solution 3000', 'port': 7700, 'user_code': '**REDACTED**', diff --git a/tests/components/bosch_alarm/test_config_flow.py b/tests/components/bosch_alarm/test_config_flow.py index afdd98bb1c0..d39bff935d5 100644 --- a/tests/components/bosch_alarm/test_config_flow.py +++ b/tests/components/bosch_alarm/test_config_flow.py @@ -309,6 +309,55 @@ async def test_dhcp_updates_host( assert mock_config_entry.data[CONF_HOST] == "4.5.6.7" +@pytest.mark.parametrize("serial_number", ["12345678"]) +async def test_dhcp_discovery_if_panel_setup_config_flow( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, + mock_panel: AsyncMock, + serial_number: str, + model_name: str, + config_flow_data: dict[str, Any], +) -> None: + """Test DHCP discovery doesn't fail if a different panel was set up via config flow.""" + await setup_integration(hass, mock_config_entry) + + # change out the serial number so we can test discovery for a different panel + mock_panel.serial_number = "789101112" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=DhcpServiceInfo( + hostname="test", + ip="4.5.6.7", + macaddress="34ea34b43b5a", + ), + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "auth" + assert result["errors"] == {} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + config_flow_data, + ) + + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == f"Bosch {model_name}" + assert result["data"] == { + CONF_HOST: "4.5.6.7", + CONF_MAC: "34:ea:34:b4:3b:5a", + CONF_PORT: 7700, + CONF_MODEL: model_name, + **config_flow_data, + } + assert mock_config_entry.unique_id == serial_number + assert result["result"].unique_id == "789101112" + + @pytest.mark.parametrize("model", ["solution_3000", "amax_3000"]) async def test_dhcp_abort_ongoing_flow( hass: HomeAssistant, @@ -341,6 +390,35 @@ async def test_dhcp_abort_ongoing_flow( assert result["reason"] == "already_in_progress" +async def test_dhcp_updates_mac( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, + mock_panel: AsyncMock, + model_name: str, + serial_number: str, + config_flow_data: dict[str, Any], +) -> None: + """Test DHCP discovery flow updates mac if the previous entry did not have a mac address.""" + await setup_integration(hass, mock_config_entry) + assert CONF_MAC not in mock_config_entry.data + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=DhcpServiceInfo( + hostname="test", + ip="0.0.0.0", + macaddress="34ea34b43b5a", + ), + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert mock_config_entry.data[CONF_MAC] == "34:ea:34:b4:3b:5a" + + async def test_reauth_flow_success( hass: HomeAssistant, mock_setup_entry: AsyncMock,