diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index d39681cfa29e..b307a34f50a3 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -156,12 +156,16 @@ async def async_clean_import(hass: HomeAssistant, entry: ConfigEntry) -> None: entities = er.async_entries_for_config_entry(entity_reg, old_config_entry_id) old_entity_entry = entities[0] if entities else None + new_options = dict(entry.options) + new_options.pop("old_config_entry_id") + if not old_config_entry or not old_entity_entry: # User has manually removed the entry or entity before we came here # Skip the migration and just continue with setting up the group sensor _LOGGER.warning( "Min/Max helper was already removed, setting up group sensor without migration" ) + hass.config_entries.async_update_entry(entry, options=new_options) return if TYPE_CHECKING: @@ -184,8 +188,6 @@ async def async_clean_import(hass: HomeAssistant, entry: ConfigEntry) -> None: await hass.config_entries.async_remove(old_entity_entry.config_entry_id) # Update options to not run migration again - new_options = dict(entry.options) - new_options.pop("old_config_entry_id") hass.config_entries.async_update_entry(entry, options=new_options) diff --git a/homeassistant/components/min_max/repairs.py b/homeassistant/components/min_max/repairs.py index 51c5e54123d4..e483d8b3a06f 100644 --- a/homeassistant/components/min_max/repairs.py +++ b/homeassistant/components/min_max/repairs.py @@ -83,7 +83,8 @@ class MigrateToGroupSensorFlow(RepairsFlow): if new_entry.state is ConfigEntryState.LOADED: break if i == 9: - await self.hass.config_entries.async_remove(new_config_entry_id) + if self.hass.config_entries.async_get_entry(self.entry_id): + await self.hass.config_entries.async_remove(new_config_entry_id) return self.async_abort( reason="could_not_start_group_entity", ) diff --git a/tests/components/min_max/test_init.py b/tests/components/min_max/test_init.py index 0ee73ba8250a..923990cf4e27 100644 --- a/tests/components/min_max/test_init.py +++ b/tests/components/min_max/test_init.py @@ -297,8 +297,6 @@ async def test_issue_is_aborted_on_could_not_start_group( hass: HomeAssistant, entity_registry: er.EntityRegistry, issue_registry: ir.IssueRegistry, - freezer: FrozenDateTimeFactory, - snapshot: SnapshotAssertion, hass_client: ClientSessionGenerator, hass_ws_client: WebSocketGenerator, ) -> None: @@ -370,3 +368,85 @@ async def test_issue_is_aborted_on_could_not_start_group( assert entity.platform == DOMAIN assert not hass.config_entries.async_has_entries(GROUP_DOMAIN) + + +async def test_issue_is_aborted_on_could_not_start_group_entry_remains( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + issue_registry: ir.IssueRegistry, + hass_client: ClientSessionGenerator, + hass_ws_client: WebSocketGenerator, +) -> None: + """Test migrating to group sensors fails when group setup fails. + + Keep new Group entry in case of failure after min_max entry + has been removed. + """ + assert await async_setup_component(hass, "repairs", {}) + hass.states.async_set("sensor.input_one", "10") + hass.states.async_set("sensor.input_two", "20") + + input_sensors = ["sensor.input_one", "sensor.input_two"] + + min_max_entity_id = "sensor.my_min_max" + + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + entry_id="123", + options={ + "entity_ids": input_sensors, + "name": "My min_max", + "round_digits": 2.0, + "type": "max", + }, + title="My min_max", + ) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + entity = entity_registry.async_get(min_max_entity_id) + assert entity is not None + + issue = issue_registry.async_get_issue( + DOMAIN, f"migrate_to_group_sensor-{config_entry.entry_id}" + ) + assert issue is not None + assert issue.is_fixable is True + assert issue.breaks_in_ha_version == "2026.12.0" + + ws_client = await hass_ws_client(hass) + client = await hass_client() + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + + data = await start_repair_fix_flow( + client, DOMAIN, f"migrate_to_group_sensor-{config_entry.entry_id}" + ) + flow_id = data["flow_id"] + assert data["description_placeholders"] == {"title": "My min_max"} + assert data["step_id"] == "migrate" + + with ( + patch( + "homeassistant.config_entries.ConfigEntries.async_forward_entry_setups", + side_effect=ValueError(), + ) as mock_load_platforms, + patch("homeassistant.components.min_max.repairs.asyncio.sleep"), + ): + data = await process_repair_fix_flow(client, flow_id, json={}) + await hass.async_block_till_done() + + assert mock_load_platforms.called + assert data["type"] == FlowResultType.ABORT + + entity = entity_registry.async_get(min_max_entity_id) + assert entity.config_entry_id is not None + assert entity.config_entry_id != config_entry.entry_id + assert entity.unique_id != config_entry.entry_id + assert entity.platform == GROUP_DOMAIN + + assert not hass.config_entries.async_has_entries(DOMAIN)