diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index 322af8cf3ac..f402009e13b 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -25,6 +25,7 @@ from homeassistant.helpers import ( config_entry_oauth2_flow, config_validation as cv, ) +from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_call_later from homeassistant.helpers.start import async_at_started @@ -243,3 +244,20 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: await cloud.async_delete_cloudhook(hass, entry.data[CONF_WEBHOOK_ID]) except cloud.CloudNotAvailable: pass + + +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + data = hass.data[DOMAIN][config_entry.entry_id][DATA_HANDLER] + modules = [m for h in data.account.homes.values() for m in h.modules] + rooms = [r for h in data.account.homes.values() for r in h.rooms] + + return not any( + identifier + for identifier in device_entry.identifiers + if identifier[0] == DOMAIN + and identifier[1] in modules + or identifier[1] in rooms + ) diff --git a/tests/components/netatmo/test_init.py b/tests/components/netatmo/test_init.py index c68bd7df541..672084d644d 100644 --- a/tests/components/netatmo/test_init.py +++ b/tests/components/netatmo/test_init.py @@ -14,7 +14,7 @@ from homeassistant.components.netatmo import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_WEBHOOK_ID, Platform from homeassistant.core import CoreState, HomeAssistant -import homeassistant.helpers.device_registry as dr +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util @@ -31,6 +31,7 @@ from tests.common import ( async_get_persistent_notifications, ) from tests.components.cloud import mock_cloud +from tests.typing import MockHAClientWebSocket, WebSocketGenerator # Fake webhook thermostat mode change to "Max" FAKE_WEBHOOK = { @@ -520,3 +521,59 @@ async def test_devices( for device_entry in device_entries: identifier = list(device_entry.identifiers)[0] assert device_entry == snapshot(name=f"{identifier[0]}-{identifier[1]}") + + +async def remove_device( + ws_client: MockHAClientWebSocket, device_id: str, config_entry_id: str +) -> bool: + """Remove config entry from a device.""" + await ws_client.send_json( + { + "id": 1, + "type": "config/device_registry/remove_config_entry", + "config_entry_id": config_entry_id, + "device_id": device_id, + } + ) + response = await ws_client.receive_json() + return response["success"] + + +async def test_device_remove_devices( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, + config_entry: MockConfigEntry, + netatmo_auth: AsyncMock, +) -> None: + """Test we can only remove a device that no longer exists.""" + + assert await async_setup_component(hass, "config", {}) + + with selected_platforms([Platform.CLIMATE]): + assert await hass.config_entries.async_setup(config_entry.entry_id) + + await hass.async_block_till_done() + + climate_entity_livingroom = "climate.livingroom" + entity = entity_registry.async_get(climate_entity_livingroom) + + device_entry = device_registry.async_get(entity.device_id) + assert ( + await remove_device( + await hass_ws_client(hass), device_entry.id, config_entry.entry_id + ) + is False + ) + + dead_device_entry = device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + identifiers={(DOMAIN, "remove-device-id")}, + ) + assert ( + await remove_device( + await hass_ws_client(hass), dead_device_entry.id, config_entry.entry_id + ) + is True + )