From 89956adf2eea8d3dd6630506f6e0d9fcab436a39 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Mon, 17 Feb 2025 01:47:11 -0600 Subject: [PATCH] Allow removal of stale HEOS devices (#138677) Allow device removal --- homeassistant/components/heos/__init__.py | 11 +++++++ .../components/heos/quality_scale.yaml | 2 +- tests/components/heos/test_init.py | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index 7bbd3765602..4df1a2fa0e1 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -69,3 +69,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: HeosConfigEntry) -> bool async def async_unload_entry(hass: HomeAssistant, entry: HeosConfigEntry) -> bool: """Unload a config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + + +async def async_remove_config_entry_device( + hass: HomeAssistant, entry: HeosConfigEntry, device: dr.DeviceEntry +) -> bool: + """Remove config entry from device if no longer present.""" + return not any( + (domain, key) + for domain, key in device.identifiers + if domain == DOMAIN and int(key) in entry.runtime_data.heos.players + ) diff --git a/homeassistant/components/heos/quality_scale.yaml b/homeassistant/components/heos/quality_scale.yaml index 67022ec492c..a1220366fa3 100644 --- a/homeassistant/components/heos/quality_scale.yaml +++ b/homeassistant/components/heos/quality_scale.yaml @@ -58,7 +58,7 @@ rules: icon-translations: done reconfiguration-flow: done repair-issues: todo - stale-devices: todo + stale-devices: done # Platinum async-dependency: done inject-websession: diff --git a/tests/components/heos/test_init.py b/tests/components/heos/test_init.py index 81acb7b3b8b..60bc2a72e51 100644 --- a/tests/components/heos/test_init.py +++ b/tests/components/heos/test_init.py @@ -11,10 +11,12 @@ from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr +from homeassistant.setup import async_setup_component from . import MockHeos from tests.common import MockConfigEntry +from tests.typing import WebSocketGenerator async def test_async_setup_entry_loads_platforms( @@ -226,3 +228,30 @@ async def test_device_id_migration_both_present( await hass.async_block_till_done(wait_background_tasks=True) assert device_registry.async_get_device({(DOMAIN, 1)}) is None # type: ignore[arg-type] assert device_registry.async_get_device({(DOMAIN, "1")}) is not None + + +@pytest.mark.parametrize( + ("player_id", "expected_result"), + [("1", False), ("5", True)], + ids=("Present device", "Stale device"), +) +async def test_remove_config_entry_device( + hass: HomeAssistant, + config_entry: MockConfigEntry, + device_registry: dr.DeviceRegistry, + hass_ws_client: WebSocketGenerator, + player_id: str, + expected_result: bool, +) -> None: + """Test manually removing an stale device.""" + assert await async_setup_component(hass, "config", {}) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + + device_entry = device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, identifiers={(DOMAIN, player_id)} + ) + + ws_client = await hass_ws_client(hass) + response = await ws_client.remove_device(device_entry.id, config_entry.entry_id) + assert response["success"] == expected_result