Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions custom_components/parcelapp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,27 @@ async def cleanup_old_device(hass: HomeAssistant) -> None:

Older versions of this integration registered a device using a malformed
identifier -- a bare ``(DOMAIN,)`` tuple instead of the expected
``(DOMAIN, <unique_id>)``. ``device_registry.async_get_device`` is
deprecated (scheduled for removal in HA Core 2027.8.0) because device
identifiers are no longer guaranteed unique across config entries, and it
cannot be used to look up the malformed single-element identifier anyway.

Iterate the device registry and remove any device that still carries the
improper identifier.
``(DOMAIN, <unique_id>)``. That device cannot be looked up with
``device_registry.async_get_device`` (deprecated, and it requires a
well-formed identifier pair), and using ``device_registry.devices`` as a
mapping is deprecated too (stops working in HA Core 2027.9.0).

Walk this integration's own config entries with the supported
``async_entries_for_config_entry`` helper and remove any device that still
carries the improper identifier.
"""
device_reg = dr.async_get(hass)
for device in list(device_reg.devices.values()):
if any(
len(identifier) == 1 and identifier[0] == DOMAIN
for identifier in device.identifiers
removed: set[str] = set()
for config_entry in hass.config_entries.async_entries(DOMAIN):
for device in dr.async_entries_for_config_entry(
device_reg, config_entry.entry_id
):
_LOGGER.debug("Removing improper device %s", device.name)
device_reg.async_remove_device(device.id)
if device.id in removed:
continue
if any(
len(identifier) == 1 and identifier[0] == DOMAIN
for identifier in device.identifiers
):
_LOGGER.debug("Removing improper device %s", device.name)
device_reg.async_remove_device(device.id)
removed.add(device.id)
2 changes: 1 addition & 1 deletion custom_components/parcelapp/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"requests>=2.32.3",
"python-dateutil>=2.9.0"
],
"version": "1.8.2"
"version": "1.8.3"
}
32 changes: 32 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Tests for the integration setup helpers."""

import pytest
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.parcelapp import cleanup_old_device
from custom_components.parcelapp.const import DOMAIN


@pytest.mark.asyncio
async def test_cleanup_removes_only_the_malformed_device(hass: HomeAssistant) -> None:
"""The legacy bare (DOMAIN,) device is removed, a well-formed one is kept."""
entry = MockConfigEntry(
domain=DOMAIN, entry_id="entry_1", data={"api_key": "test_api_key"}
)
entry.add_to_hass(hass)
device_reg = dr.async_get(hass)
legacy = device_reg.async_get_or_create(
config_entry_id=entry.entry_id, identifiers={(DOMAIN,)}, name="Legacy"
)
current = device_reg.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, entry.entry_id)},
name="Parcel",
)

await cleanup_old_device(hass)

assert device_reg.async_get(legacy.id) is None
assert device_reg.async_get(current.id) is not None
Loading