Skip to content

Commit e009898

Browse files
Remove template config entry from source device (home-assistant#157814)
Co-authored-by: Martin Hjelmare <[email protected]>
1 parent ceb13e7 commit e009898

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

homeassistant/components/template/__init__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
from homeassistant.helpers.device import (
2929
async_remove_stale_devices_links_keep_current_device,
3030
)
31+
from homeassistant.helpers.helper_integration import (
32+
async_remove_helper_config_entry_from_source_device,
33+
)
3134
from homeassistant.helpers.reload import async_reload_integration_platforms
3235
from homeassistant.helpers.service import async_register_admin_service
3336
from homeassistant.helpers.typing import ConfigType
@@ -116,6 +119,7 @@ def new_triggers_conditions_listener() -> None:
116119
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
117120
"""Set up a config entry."""
118121

122+
# This can be removed in HA Core 2026.7
119123
async_remove_stale_devices_links_keep_current_device(
120124
hass,
121125
entry.entry_id,
@@ -154,6 +158,41 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
154158
)
155159

156160

161+
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
162+
"""Migrate old entry."""
163+
164+
_LOGGER.debug(
165+
"Migrating configuration from version %s.%s",
166+
config_entry.version,
167+
config_entry.minor_version,
168+
)
169+
170+
if config_entry.version > 1:
171+
# This means the user has downgraded from a future version
172+
return False
173+
174+
if config_entry.version == 1:
175+
if config_entry.minor_version < 2:
176+
# Remove the template config entry from the source device
177+
if source_device_id := config_entry.options.get(CONF_DEVICE_ID):
178+
async_remove_helper_config_entry_from_source_device(
179+
hass,
180+
helper_config_entry_id=config_entry.entry_id,
181+
source_device_id=source_device_id,
182+
)
183+
hass.config_entries.async_update_entry(
184+
config_entry, version=1, minor_version=2
185+
)
186+
187+
_LOGGER.debug(
188+
"Migration to configuration version %s.%s successful",
189+
config_entry.version,
190+
config_entry.minor_version,
191+
)
192+
193+
return True
194+
195+
157196
async def _process_config(hass: HomeAssistant, hass_config: ConfigType) -> None:
158197
"""Process config."""
159198
coordinators = hass.data.pop(DATA_COORDINATORS, None)

homeassistant/components/template/config_flow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ class TemplateConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
697697
options_flow = OPTIONS_FLOW
698698
options_flow_reloads = True
699699

700+
MINOR_VERSION = 2
701+
VERSION = 1
702+
700703
@callback
701704
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
702705
"""Return config entry title."""

tests/components/template/test_init.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from homeassistant import config
99
from homeassistant.components import labs
1010
from homeassistant.components.template import DOMAIN
11+
from homeassistant.config_entries import ConfigEntryState
1112
from homeassistant.const import SERVICE_RELOAD
1213
from homeassistant.core import Context, HomeAssistant
1314
from homeassistant.helpers import (
@@ -754,3 +755,80 @@ async def test_config_entry_reload_when_labs_flag_changes(
754755

755756
assert hass.states.get("sensor.hello") is not None
756757
assert hass.states.get("sensor.hello").state == set_state
758+
759+
760+
async def test_migration_1_1(
761+
hass: HomeAssistant,
762+
device_registry: dr.DeviceRegistry,
763+
entity_registry: er.EntityRegistry,
764+
) -> None:
765+
"""Test migration from v1.1 removes template config entry from device."""
766+
767+
device_config_entry = MockConfigEntry()
768+
device_config_entry.add_to_hass(hass)
769+
device_entry = device_registry.async_get_or_create(
770+
config_entry_id=device_config_entry.entry_id,
771+
identifiers={("test", "identifier_test")},
772+
connections={("mac", "30:31:32:33:34:35")},
773+
)
774+
775+
template_config_entry = MockConfigEntry(
776+
data={},
777+
domain=DOMAIN,
778+
options={
779+
"name": "My template",
780+
"template_type": "sensor",
781+
"state": "{{ 'foo' }}",
782+
"device_id": device_entry.id,
783+
},
784+
title="My template",
785+
version=1,
786+
minor_version=1,
787+
)
788+
template_config_entry.add_to_hass(hass)
789+
790+
# Add the helper config entry to the device
791+
device_registry.async_update_device(
792+
device_entry.id, add_config_entry_id=template_config_entry.entry_id
793+
)
794+
795+
# Check preconditions
796+
device_entry = device_registry.async_get(device_entry.id)
797+
assert template_config_entry.entry_id in device_entry.config_entries
798+
799+
await hass.config_entries.async_setup(template_config_entry.entry_id)
800+
await hass.async_block_till_done()
801+
802+
assert template_config_entry.state is ConfigEntryState.LOADED
803+
804+
# Check that the helper config entry is removed from the device and the helper
805+
# entity is linked to the source device
806+
device_entry = device_registry.async_get(device_entry.id)
807+
assert template_config_entry.entry_id not in device_entry.config_entries
808+
template_entity_entry = entity_registry.async_get("sensor.my_template")
809+
assert template_entity_entry.device_id == device_entry.id
810+
811+
assert template_config_entry.version == 1
812+
assert template_config_entry.minor_version == 2
813+
814+
815+
async def test_migration_from_future_version(
816+
hass: HomeAssistant,
817+
) -> None:
818+
"""Test migration from future version."""
819+
config_entry = MockConfigEntry(
820+
data={},
821+
domain=DOMAIN,
822+
options={
823+
"name": "hello",
824+
"template_type": "sensor",
825+
"state": "{{ 'foo' }}",
826+
},
827+
title="My template",
828+
version=2,
829+
minor_version=1,
830+
)
831+
config_entry.add_to_hass(hass)
832+
await hass.config_entries.async_setup(config_entry.entry_id)
833+
await hass.async_block_till_done()
834+
assert config_entry.state is ConfigEntryState.MIGRATION_ERROR

0 commit comments

Comments
 (0)