From ca5f13b576b8424cf66c59a0c9481576d9be1a34 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 31 May 2022 10:40:08 +0200 Subject: [PATCH] Allow removing a onewire device (#72710) --- homeassistant/components/onewire/__init__.py | 11 ++++ tests/components/onewire/__init__.py | 7 ++- tests/components/onewire/test_init.py | 66 +++++++++++++++++++- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/onewire/__init__.py b/homeassistant/components/onewire/__init__.py index c6f3d7dfa3f..e908a52a071 100644 --- a/homeassistant/components/onewire/__init__.py +++ b/homeassistant/components/onewire/__init__.py @@ -6,6 +6,7 @@ from pyownet import protocol from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from .const import DOMAIN, PLATFORMS from .onewirehub import CannotConnect, OneWireHub @@ -35,6 +36,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + onewirehub: OneWireHub = hass.data[DOMAIN][config_entry.entry_id] + return not device_entry.identifiers.intersection( + (DOMAIN, device.id) for device in onewirehub.devices or [] + ) + + async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms( diff --git a/tests/components/onewire/__init__.py b/tests/components/onewire/__init__.py index d189db8af1a..c916c777248 100644 --- a/tests/components/onewire/__init__.py +++ b/tests/components/onewire/__init__.py @@ -15,6 +15,7 @@ from homeassistant.const import ( ATTR_NAME, ATTR_STATE, ATTR_VIA_DEVICE, + Platform, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceRegistry @@ -90,7 +91,7 @@ def check_entities( def setup_owproxy_mock_devices( - owproxy: MagicMock, platform: str, device_ids: list[str] + owproxy: MagicMock, platform: Platform, device_ids: list[str] ) -> None: """Set up mock for owproxy.""" main_dir_return_value = [] @@ -125,7 +126,7 @@ def _setup_owproxy_mock_device( main_read_side_effect: list, sub_read_side_effect: list, device_id: str, - platform: str, + platform: Platform, ) -> None: """Set up mock for owproxy.""" mock_device = MOCK_OWPROXY_DEVICES[device_id] @@ -167,7 +168,7 @@ def _setup_owproxy_mock_device_reads( sub_read_side_effect: list, mock_device: Any, device_id: str, - platform: str, + platform: Platform, ) -> None: """Set up mock for owproxy.""" # Setup device reads diff --git a/tests/components/onewire/test_init.py b/tests/components/onewire/test_init.py index fecade521a8..bc09432ea5c 100644 --- a/tests/components/onewire/test_init.py +++ b/tests/components/onewire/test_init.py @@ -1,12 +1,35 @@ """Tests for 1-Wire config flow.""" -from unittest.mock import MagicMock +from collections.abc import Awaitable, Callable +from unittest.mock import MagicMock, patch +import aiohttp from pyownet import protocol import pytest from homeassistant.components.onewire.const import DOMAIN from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.const import Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr +from homeassistant.setup import async_setup_component + +from . import setup_owproxy_mock_devices + + +async def remove_device( + ws_client: aiohttp.ClientWebSocketResponse, 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"] @pytest.mark.usefixtures("owproxy_with_connerror") @@ -48,3 +71,44 @@ async def test_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): assert config_entry.state is ConfigEntryState.NOT_LOADED assert not hass.data.get(DOMAIN) + + +@patch("homeassistant.components.onewire.PLATFORMS", [Platform.SENSOR]) +async def test_registry_cleanup( + hass: HomeAssistant, + config_entry: ConfigEntry, + owproxy: MagicMock, + hass_ws_client: Callable[ + [HomeAssistant], Awaitable[aiohttp.ClientWebSocketResponse] + ], +): + """Test being able to remove a disconnected device.""" + assert await async_setup_component(hass, "config", {}) + + entry_id = config_entry.entry_id + device_registry = dr.async_get(hass) + live_id = "10.111111111111" + dead_id = "28.111111111111" + + # Initialise with two components + setup_owproxy_mock_devices(owproxy, Platform.SENSOR, [live_id, dead_id]) + await hass.config_entries.async_setup(entry_id) + await hass.async_block_till_done() + + # Reload with a device no longer on bus + setup_owproxy_mock_devices(owproxy, Platform.SENSOR, [live_id]) + await hass.config_entries.async_reload(entry_id) + await hass.async_block_till_done() + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 2 + + # Try to remove "10.111111111111" - fails as it is live + device = device_registry.async_get_device(identifiers={(DOMAIN, live_id)}) + assert await remove_device(await hass_ws_client(hass), device.id, entry_id) is False + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 2 + assert device_registry.async_get_device(identifiers={(DOMAIN, live_id)}) is not None + + # Try to remove "28.111111111111" - succeeds as it is dead + device = device_registry.async_get_device(identifiers={(DOMAIN, dead_id)}) + assert await remove_device(await hass_ws_client(hass), device.id, entry_id) is True + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 1 + assert device_registry.async_get_device(identifiers={(DOMAIN, dead_id)}) is None