Migrate tradfri to call async_remove_device

This commit is contained in:
Erik
2026-08-03 08:41:15 +02:00
parent e5aa33502c
commit aaff9519ae
2 changed files with 46 additions and 107 deletions
+2 -23
View File
@@ -168,10 +168,8 @@ def remove_stale_devices(
# If device_id is None an invalid device entry
# was found for this config entry.
# If the device_id is not in existing device ids it's a stale device entry.
# Remove config entry from this device entry in either case.
device_registry.async_update_device(
device_entry.id, remove_config_entry_id=config_entry.entry_id
)
# Remove the device entry in either case.
device_registry.async_remove_device(device_entry.id)
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
@@ -231,25 +229,6 @@ def migrate_config_entry_and_identifiers(
if not related_device_flag:
continue
# Loop through list of config_entry_ids for device
config_entry_ids = device.config_entries
for config_entry_id in config_entry_ids:
# Check that the config entry in list is not
# the device's primary config entry
if config_entry_id == device.primary_config_entry:
continue
# Check that the 'other' config entry is also a tradfri config entry
other_entry = hass.config_entries.async_get_entry(config_entry_id)
if other_entry is None or other_entry.domain != DOMAIN:
continue
# Remove non-primary 'tradfri' config entry from device's config_entry_ids
device_reg.async_update_device(
device.id, remove_config_entry_id=config_entry_id
)
if config_entry.data[CONF_GATEWAY_ID] in device_id:
continue
+44 -84
View File
@@ -105,7 +105,11 @@ async def test_migrate_config_entry_and_identifiers(
device_registry: dr.DeviceRegistry,
command_store: CommandStore,
) -> None:
"""Test correction of device registry entries."""
"""Test migration of device registry identifiers to the unique format.
A device belongs to a single config entry, so a stale tradfri device is
removed rather than moved to another config entry.
"""
config_entry1 = MockConfigEntry(
domain=tradfri.DOMAIN,
data={
@@ -131,57 +135,35 @@ async def test_migrate_config_entry_and_identifiers(
tradfri.CONF_GATEWAY_ID: GATEWAY_ID2,
},
)
config_entry2.add_to_hass(hass)
# Add non-tradfri config entry for use in testing negation logic
config_entry3 = MockConfigEntry(
domain="test_domain",
)
# A non-tradfri config entry, to verify its device and version are untouched.
config_entry3 = MockConfigEntry(domain="test_domain")
config_entry3.add_to_hass(hass)
# Create gateway device for config entry 1
gateway1_device = device_registry.async_get_or_create(
config_entry_id=config_entry1.entry_id,
identifiers={(config_entry1.domain, config_entry1.data["gateway_id"])},
name="Gateway",
)
# Create bulb 1 on gateway 1 in Device Registry
# this has the old identifiers format
# Bulb with the old, non-unique identifier format; it is still reported by
# the gateway (id 65537 from bulb_w.json), so migration renames its
# identifier and it is kept.
gateway1_bulb1 = device_registry.async_get_or_create(
config_entry_id=config_entry1.entry_id,
identifiers={(tradfri.DOMAIN, 65537)},
name="bulb1",
)
# Update bulb 1 device to have both config entry IDs
# This simulates existing data scenario with older tradfri version
device_registry.async_update_device(
gateway1_bulb1.id,
add_config_entry_id=config_entry2.entry_id,
)
# Create bulb 2 on gateway 1 in Device Registry
# this has the new identifiers format
gateway1_bulb2 = device_registry.async_get_or_create(
# Bulb with the new identifier format that is no longer reported by the
# gateway (id 65538), so it is a stale device that gets removed.
device_registry.async_get_or_create(
config_entry_id=config_entry1.entry_id,
identifiers={(tradfri.DOMAIN, f"{GATEWAY_ID1}-65538")},
name="bulb2",
)
# Update bulb 2 device to have an additional config entry from config_entry3
# This is to simulate scenario whereby a device entry
# is shared by multiple config entries
# and where at least one of those config entries is not the 'tradfri' domain
device_registry.async_update_device(
gateway1_bulb2.id,
add_config_entry_id=config_entry3.entry_id,
merge_identifiers={("test_domain", "config_entry_3-device2")},
)
# Create a device on config entry 3 in Device Registry
config_entry3_device = device_registry.async_get_or_create(
config_entry_id=config_entry3.entry_id,
identifiers={("test_domain", "config_entry_3-device1")},
@@ -192,75 +174,53 @@ async def test_migrate_config_entry_and_identifiers(
await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
# Validate that gateway 1 bulb 1 is still the same device entry
# This inherently also validates that the device's identifiers
# have been updated to the new unique format
device_entries = dr.async_entries_for_config_entry(
device_registry, config_entry1.entry_id
# Bulb 1 kept the same device entry, its identifier migrated to the unique
# format, and it is still owned only by gateway 1's config entry.
migrated_bulb1 = device_registry.async_get_device(
identifiers={(tradfri.DOMAIN, f"{GATEWAY_ID1}-65537")}
)
assert migrated_bulb1.id == gateway1_bulb1.id
assert migrated_bulb1.config_entry_id == config_entry1.entry_id
# The gateway device is unchanged.
migrated_gateway1 = device_registry.async_get_device(
identifiers={(tradfri.DOMAIN, GATEWAY_ID1)}
)
assert migrated_gateway1.id == gateway1_device.id
assert migrated_gateway1.identifiers == gateway1_device.identifiers
assert migrated_gateway1.config_entry_id == config_entry1.entry_id
# Bulb 2 is stale and has been removed, not moved to another config entry.
assert (
device_registry.async_get_device(
identifiers={(tradfri.DOMAIN, f"{GATEWAY_ID1}-65537")}
).id
== gateway1_bulb1.id
identifiers={(tradfri.DOMAIN, f"{GATEWAY_ID1}-65538")}
)
is None
)
# Validate that gateway 1 bulb 1 only has gateway 1's config ID associated to it
# (Device at index 0 is the gateway)
assert device_entries[1].config_entries == {config_entry1.entry_id}
# Validate that the gateway 1 device is unchanged
assert device_entries[0].id == gateway1_device.id
assert device_entries[0].identifiers == gateway1_device.identifiers
assert device_entries[0].config_entries == gateway1_device.config_entries
# Validate that gateway 1 bulb 2 now only exists associated to
# config entry 3. The device will have had its identifiers
# updated to the new format (for the tradfri
# domain) per migrate_config_entry_and_identifiers().
# The device will have then been removed from config entry 1 (gateway1)
# due to it not matching a device in the command store.
device_entry = device_registry.async_get_device(
identifiers={(tradfri.DOMAIN, f"{GATEWAY_ID1}-65538")}
)
assert device_entry.id == gateway1_bulb2.id
# Assert that the only config entry associated to this device is config entry 3
assert device_entry.config_entries == {config_entry3.entry_id}
# Assert that that device's other identifiers remain untouched
assert device_entry.identifiers == {
(tradfri.DOMAIN, f"{GATEWAY_ID1}-65538"),
("test_domain", "config_entry_3-device2"),
}
# Validate that gateway 2 bulb 1 has been added to device
# registry and with correct unique identifiers
# (This bulb device exists on gateway 2 because the
# command_store created above will be executed for each
# gateway being set up.)
# Gateway 2 discovered the same bulb and stored it with the unique
# identifier format, owned only by gateway 2's config entry. (This bulb
# exists on gateway 2 because the command store is shared between gateways.)
device_entries = dr.async_entries_for_config_entry(
device_registry, config_entry2.entry_id
)
assert len(device_entries) == 2
assert device_entries[1].identifiers == {(tradfri.DOMAIN, f"{GATEWAY_ID2}-65537")}
gateway2_bulb = device_registry.async_get_device(
identifiers={(tradfri.DOMAIN, f"{GATEWAY_ID2}-65537")}
)
assert gateway2_bulb.config_entry_id == config_entry2.entry_id
# Validate that gateway 2 bulb 1 only has gateway 2's config ID associated to it
assert device_entries[1].config_entries == {config_entry2.entry_id}
# Validate that config entry 3 device 1 is still present,
# and has not had its config entries or identifiers changed
# N.B. The gateway1_bulb2 device will qualify in this set
# because the config entry 3 was added to it above
# The non-tradfri device and config entry are untouched.
device_entries = dr.async_entries_for_config_entry(
device_registry, config_entry3.entry_id
)
assert len(device_entries) == 2
assert len(device_entries) == 1
assert device_entries[0].id == config_entry3_device.id
assert device_entries[0].identifiers == {("test_domain", "config_entry_3-device1")}
assert device_entries[0].config_entries == {config_entry3.entry_id}
assert device_entries[0].config_entry_id == config_entry3.entry_id
# Assert that the tradfri config entries have been migrated to v2 and
# the non-tradfri config entry remains at v1
# The tradfri config entries have been migrated to v2 and the non-tradfri
# config entry remains at v1.
assert config_entry1.version == 2
assert config_entry2.version == 2
assert config_entry3.version == 1