mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 08:26:41 +01:00
Fix invalid device registry identifiers in eafm (#164654)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2,14 +2,39 @@
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EafmConfigEntry, EafmCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
def _fix_device_registry_identifiers(
|
||||
hass: HomeAssistant, entry: EafmConfigEntry
|
||||
) -> None:
|
||||
"""Fix invalid identifiers in device registry.
|
||||
|
||||
Added in 2026.4, can be removed in 2026.10 or later.
|
||||
"""
|
||||
device_registry = dr.async_get(hass)
|
||||
for device_entry in dr.async_entries_for_config_entry(
|
||||
device_registry, entry.entry_id
|
||||
):
|
||||
old_identifier = (DOMAIN, "measure-id", entry.data["station"])
|
||||
if old_identifier not in device_entry.identifiers: # type: ignore[comparison-overlap]
|
||||
continue
|
||||
new_identifiers = device_entry.identifiers.copy()
|
||||
new_identifiers.discard(old_identifier) # type: ignore[arg-type]
|
||||
new_identifiers.add((DOMAIN, entry.data["station"]))
|
||||
device_registry.async_update_device(
|
||||
device_entry.id, new_identifiers=new_identifiers
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: EafmConfigEntry) -> bool:
|
||||
"""Set up flood monitoring sensors for this config entry."""
|
||||
_fix_device_registry_identifiers(hass, entry)
|
||||
coordinator = EafmCoordinator(hass, entry=entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
@@ -94,11 +94,11 @@ class Measurement(CoordinatorEntity, SensorEntity):
|
||||
return self.coordinator.data["measures"][self.key]["parameterName"]
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device info."""
|
||||
return DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, "measure-id", self.station_id)},
|
||||
identifiers={(DOMAIN, self.station_id)},
|
||||
manufacturer="https://environment.data.gov.uk/",
|
||||
model=self.parameter_name,
|
||||
name=f"{self.station_name} {self.parameter_name} {self.qualifier}",
|
||||
|
||||
@@ -1,19 +1,64 @@
|
||||
"""eafm fixtures."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.eafm.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_stations():
|
||||
def mock_get_stations() -> Generator[AsyncMock]:
|
||||
"""Mock aioeafm.get_stations."""
|
||||
with patch("homeassistant.components.eafm.config_flow.get_stations") as patched:
|
||||
patched.return_value = [
|
||||
{"label": "My station", "stationReference": "L12345", "RLOIid": "R12345"}
|
||||
]
|
||||
yield patched
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_station():
|
||||
def mock_get_station(initial_value: dict[str, Any]) -> Generator[AsyncMock]:
|
||||
"""Mock aioeafm.get_station."""
|
||||
with patch("homeassistant.components.eafm.coordinator.get_station") as patched:
|
||||
patched.return_value = initial_value
|
||||
yield patched
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def initial_value() -> dict[str, Any]:
|
||||
"""Mock aioeafm.get_station."""
|
||||
return {
|
||||
"label": "My station",
|
||||
"measures": [
|
||||
{
|
||||
"@id": "really-long-unique-id",
|
||||
"label": "York Viking Recorder - level-stage-i-15_min----",
|
||||
"qualifier": "Stage",
|
||||
"parameterName": "Water Level",
|
||||
"latestReading": {"value": 5},
|
||||
"stationReference": "L1234",
|
||||
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
|
||||
"unitName": "m",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry(hass: HomeAssistant) -> MockConfigEntry:
|
||||
"""Create a dummy config entry for testing."""
|
||||
entry = MockConfigEntry(
|
||||
version=1,
|
||||
domain=DOMAIN,
|
||||
entry_id="VikingRecorder1234",
|
||||
data={"station": "L1234"},
|
||||
title="Viking Recorder",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
return entry
|
||||
|
||||
34
tests/components/eafm/snapshots/test_init.ambr
Normal file
34
tests/components/eafm/snapshots/test_init.ambr
Normal file
@@ -0,0 +1,34 @@
|
||||
# serializer version: 1
|
||||
# name: test_load_unload_entry
|
||||
list([
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': <DeviceEntryType.SERVICE: 'service'>,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'eafm',
|
||||
'L1234',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'https://environment.data.gov.uk/',
|
||||
'model': 'Water Level',
|
||||
'model_id': None,
|
||||
'name': 'My station Water Level Stage',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
}),
|
||||
])
|
||||
# ---
|
||||
64
tests/components/eafm/test_init.py
Normal file
64
tests/components/eafm/test_init.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Tests for initialization."""
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.eafm.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_get_station")
|
||||
async def test_load_unload_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test being able to load and unload an entry."""
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
dr.async_entries_for_config_entry(device_registry, mock_config_entry.entry_id)
|
||||
== snapshot
|
||||
)
|
||||
|
||||
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_get_station")
|
||||
async def test_update_device_identifiers(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test being able to update device identifiers."""
|
||||
device_entry = device_registry.async_get_or_create(
|
||||
config_entry_id=mock_config_entry.entry_id,
|
||||
identifiers={(DOMAIN, "measure-id", "L1234")},
|
||||
)
|
||||
|
||||
entries = dr.async_entries_for_config_entry(
|
||||
device_registry, mock_config_entry.entry_id
|
||||
)
|
||||
assert len(entries) == 1
|
||||
device_entry = entries[0]
|
||||
assert (DOMAIN, "measure-id", "L1234") in device_entry.identifiers
|
||||
assert (DOMAIN, "L1234") not in device_entry.identifiers
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entries = dr.async_entries_for_config_entry(
|
||||
device_registry, mock_config_entry.entry_id
|
||||
)
|
||||
assert len(entries) == 1
|
||||
device_entry = entries[0]
|
||||
assert (DOMAIN, "measure-id", "L1234") not in device_entry.identifiers
|
||||
assert (DOMAIN, "L1234") in device_entry.identifiers
|
||||
Reference in New Issue
Block a user