Add support to remove orphan devices in AVM FRITZ!SmartHome (#100739)

This commit is contained in:
Michael 2023-09-26 18:01:01 +02:00 committed by GitHub
parent 82fdd8313f
commit 18fad569e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 2 deletions

View File

@ -18,7 +18,7 @@ from homeassistant.const import (
) )
from homeassistant.core import Event, HomeAssistant from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceEntry, DeviceInfo
from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.entity_registry import RegistryEntry, async_migrate_entries from homeassistant.helpers.entity_registry import RegistryEntry, async_migrate_entries
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -103,6 +103,24 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok return unload_ok
async def async_remove_config_entry_device(
hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry
) -> bool:
"""Remove Fritzbox config entry from a device."""
coordinator: FritzboxDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
CONF_COORDINATOR
]
for identifier in device.identifiers:
if identifier[0] == DOMAIN and (
identifier[1] in coordinator.data.devices
or identifier[1] in coordinator.data.templates
):
return False
return True
class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator], ABC): class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator], ABC):
"""Basis FritzBox entity.""" """Basis FritzBox entity."""

View File

@ -21,12 +21,14 @@ from homeassistant.const import (
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component
from . import FritzDeviceSwitchMock, setup_config_entry from . import FritzDeviceSwitchMock, setup_config_entry
from .const import CONF_FAKE_AIN, CONF_FAKE_NAME, MOCK_CONFIG from .const import CONF_FAKE_AIN, CONF_FAKE_NAME, MOCK_CONFIG
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.typing import WebSocketGenerator
async def test_setup(hass: HomeAssistant, fritz: Mock) -> None: async def test_setup(hass: HomeAssistant, fritz: Mock) -> None:
@ -250,6 +252,68 @@ async def test_unload_remove(hass: HomeAssistant, fritz: Mock) -> None:
assert state is None assert state is None
async def test_remove_device(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
fritz: Mock,
) -> None:
"""Test removing of a device."""
assert await async_setup_component(hass, "config", {})
assert await setup_config_entry(
hass,
MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0],
f"{FB_DOMAIN}.{CONF_FAKE_NAME}",
FritzDeviceSwitchMock(),
fritz,
)
await hass.async_block_till_done()
entries = hass.config_entries.async_entries()
assert len(entries) == 1
entry = entries[0]
assert entry.supports_remove_device
entity = entity_registry.async_get("switch.fake_name")
good_device = device_registry.async_get(entity.device_id)
orphan_device = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(FB_DOMAIN, "0000 000000")},
)
# try to delete good_device
ws_client = await hass_ws_client(hass)
await ws_client.send_json(
{
"id": 5,
"type": "config/device_registry/remove_config_entry",
"config_entry_id": entry.entry_id,
"device_id": good_device.id,
}
)
response = await ws_client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "unknown_error"
await hass.async_block_till_done()
# try to delete orphan_device
ws_client = await hass_ws_client(hass)
await ws_client.send_json(
{
"id": 5,
"type": "config/device_registry/remove_config_entry",
"config_entry_id": entry.entry_id,
"device_id": orphan_device.id,
}
)
response = await ws_client.receive_json()
assert response["success"]
await hass.async_block_till_done()
async def test_raise_config_entry_not_ready_when_offline(hass: HomeAssistant) -> None: async def test_raise_config_entry_not_ready_when_offline(hass: HomeAssistant) -> None:
"""Config entry state is SETUP_RETRY when fritzbox is offline.""" """Config entry state is SETUP_RETRY when fritzbox is offline."""
entry = MockConfigEntry( entry = MockConfigEntry(