Add cleanup of unused devices in NINA (#176834)

This commit is contained in:
Maximilian
2026-08-24 21:00:10 +02:00
committed by GitHub
parent a3a4760a89
commit 09afc39ac4
4 changed files with 96 additions and 2 deletions
+20 -1
View File
@@ -13,7 +13,11 @@ from homeassistant.config_entries import (
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import section
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import VolDictType
@@ -245,6 +249,7 @@ class OptionsFlowHandler(OptionsFlowWithReload):
)
await self.remove_unused_entities(user_input)
await self.remove_unused_devices(user_input)
self.hass.config_entries.async_update_entry(
self.config_entry, data=user_input
@@ -264,6 +269,20 @@ class OptionsFlowHandler(OptionsFlowWithReload):
errors=errors,
)
async def remove_unused_devices(self, user_input: dict[str, Any]) -> None:
"""Remove devices from regions that are not selected."""
device_registry = dr.async_get(self.hass)
removed_regions = set(self.data[CONF_REGIONS]) - set(user_input[CONF_REGIONS])
for region in removed_regions:
if device := device_registry.async_get_device(
identifiers={(DOMAIN, region)}
):
device_registry.async_update_device(
device.id, remove_config_entry_id=self.config_entry.entry_id
)
async def remove_unused_entities(self, user_input: dict[str, Any]) -> None:
"""Remove entities which are not used anymore."""
entity_registry = er.async_get(self.hass)
+17
View File
@@ -14,6 +14,7 @@ from .const import (
DUMMY_CONFIG_ENTRY,
DUMMY_CONFIG_ENTRY_AREA_FILTERS,
DUMMY_CONFIG_ENTRY_DEFAULT_FILTERS,
DUMMY_CONFIG_ENTRY_MULTIPLE_REGIONS,
)
from tests.common import (
@@ -80,6 +81,22 @@ def mock_config_entry_area_filter(hass: HomeAssistant) -> MockConfigEntry:
return config_entry
@pytest.fixture
def mock_config_entry_multiple_regions(hass: HomeAssistant) -> MockConfigEntry:
"""Provide a common mock config entry with an area filter."""
config_entry = MockConfigEntry(
domain=DOMAIN,
title="NINA",
data=deepcopy(DUMMY_CONFIG_ENTRY_MULTIPLE_REGIONS),
version=1,
minor_version=3,
)
config_entry.add_to_hass(hass)
return config_entry
@pytest.fixture
def mock_nina_class(nina_region_codes: dict[str, str]) -> Generator[AsyncMock]:
"""Fixture to mock the NINA class."""
+9
View File
@@ -55,3 +55,12 @@ DUMMY_CONFIG_ENTRY_AREA_FILTERS: dict[str, Any] = {
CONF_AREA_FILTER: ".*nagold.*",
},
}
DUMMY_CONFIG_ENTRY_MULTIPLE_REGIONS: dict[str, Any] = {
CONF_MESSAGE_SLOTS: 5,
CONF_REGIONS: {"083350000000": "Aach, Stadt", "010590000000": "Test, Stadt"},
CONF_FILTERS: {
CONF_HEADLINE_FILTER: "/(?!)/",
CONF_AREA_FILTER: ".*nagold.*",
},
}
+50 -1
View File
@@ -25,7 +25,7 @@ from homeassistant.components.nina.const import (
from homeassistant.config_entries import SOURCE_USER
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import setup_platform
from .const import DUMMY_USER_INPUT
@@ -327,3 +327,52 @@ async def test_options_flow_entity_removal(
)
assert len(entries) == new_slot_count * entities_per_slot
async def test_options_flow_device_removal(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_config_entry_multiple_regions: MockConfigEntry,
mock_nina_class: AsyncMock,
nina_warnings: list[Warning],
) -> None:
"""Test if old devices are removed."""
await setup_platform(
hass, mock_config_entry_multiple_regions, mock_nina_class, nina_warnings
)
old_devices = dr.async_entries_for_config_entry(
device_registry, mock_config_entry_multiple_regions.entry_id
)
assert len(old_devices) == len(
mock_config_entry_multiple_regions.data.get(CONF_REGIONS, [])
)
result = await hass.config_entries.options.async_init(
mock_config_entry_multiple_regions.entry_id
)
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
CONF_MESSAGE_SLOTS: 5,
CONST_REGION_A_TO_D: ["095760000000_0"],
CONST_REGION_E_TO_H: [],
CONST_REGION_I_TO_L: [],
CONST_REGION_M_TO_Q: [],
CONST_REGION_R_TO_U: [],
CONST_REGION_V_TO_Z: [],
CONF_FILTERS: {},
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
devices = dr.async_entries_for_config_entry(
device_registry, mock_config_entry_multiple_regions.entry_id
)
assert not any(
old_device.id in (device.id for device in devices) for old_device in old_devices
)