diff --git a/homeassistant/components/nut/__init__.py b/homeassistant/components/nut/__init__.py index 9e1e77a2aaf..ae20ed39251 100644 --- a/homeassistant/components/nut/__init__.py +++ b/homeassistant/components/nut/__init__.py @@ -185,6 +185,20 @@ async def async_unload_entry(hass: HomeAssistant, entry: NutConfigEntry) -> bool return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) +async def async_remove_config_entry_device( + hass: HomeAssistant, + config_entry: ConfigEntry, + device_entry: dr.DeviceEntry, +) -> bool: + """Remove NUT config entry from a device.""" + return not any( + identifier + for identifier in device_entry.identifiers + if identifier[0] == DOMAIN + and identifier[1] in config_entry.runtime_data.unique_id + ) + + async def _async_update_listener(hass: HomeAssistant, entry: NutConfigEntry) -> None: """Handle options update.""" await hass.config_entries.async_reload(entry.entry_id) diff --git a/tests/components/nut/test_init.py b/tests/components/nut/test_init.py index b3cf23bddcc..8b3799caade 100644 --- a/tests/components/nut/test_init.py +++ b/tests/components/nut/test_init.py @@ -18,10 +18,12 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr +from homeassistant.setup import async_setup_component from .util import _get_mock_nutclient, async_init_integration from tests.common import MockConfigEntry +from tests.typing import WebSocketGenerator async def test_config_entry_migrations(hass: HomeAssistant) -> None: @@ -84,6 +86,78 @@ async def test_async_setup_entry(hass: HomeAssistant) -> None: assert not hass.data.get(DOMAIN) +async def test_remove_device_valid( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + device_registry: dr.DeviceRegistry, +) -> None: + """Test that we cannot remove a device that still exists.""" + assert await async_setup_component(hass, "config", {}) + + mock_serial_number = "A00000000000" + config_entry = await async_init_integration( + hass, + username="someuser", + password="somepassword", + list_vars={"ups.serial": mock_serial_number}, + list_ups={"ups1": "UPS 1"}, + list_commands_return_value=[], + ) + + device_registry = dr.async_get(hass) + assert device_registry is not None + + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, mock_serial_number)} + ) + + assert device_entry is not None + assert device_entry.serial_number == mock_serial_number + + client = await hass_ws_client(hass) + response = await client.remove_device(device_entry.id, config_entry.entry_id) + assert not response["success"] + + +async def test_remove_device_stale( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + device_registry: dr.DeviceRegistry, +) -> None: + """Test that we can remove a device that no longer exists.""" + assert await async_setup_component(hass, "config", {}) + + mock_serial_number = "A00000000000" + config_entry = await async_init_integration( + hass, + username="someuser", + password="somepassword", + list_vars={"ups.serial": mock_serial_number}, + list_ups={"ups1": "UPS 1"}, + list_commands_return_value=[], + ) + + device_registry = dr.async_get(hass) + assert device_registry is not None + + device_entry = device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + identifiers={(DOMAIN, "remove-device-id")}, + ) + + assert device_entry is not None + + client = await hass_ws_client(hass) + response = await client.remove_device(device_entry.id, config_entry.entry_id) + assert response["success"] + + # Verify that device entry is removed + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, "remove-device-id")} + ) + assert device_entry is None + + async def test_config_not_ready( hass: HomeAssistant, caplog: pytest.LogCaptureFixture,