diff --git a/custom_components/elro_connects/__init__.py b/custom_components/elro_connects/__init__.py index 2f57379..2cd71ac 100644 --- a/custom_components/elro_connects/__init__.py +++ b/custom_components/elro_connects/__init__.py @@ -11,6 +11,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import SERVICE_RELOAD, Platform from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import DEFAULT_INTERVAL, DOMAIN @@ -39,7 +40,7 @@ async def _async_update_data() -> dict[int, dict]: device_update = copy.deepcopy(elro_connects_api.data) for device_id, device_data in device_update.items(): if ATTR_DEVICE_STATE not in device_data: - # Skip entries without device state + # Unlink entry without device state continue if device_id not in coordinator_update: # new device, or known state @@ -109,3 +110,18 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN].pop(entry.entry_id) return unload_ok + + +async def async_remove_config_entry_device( + hass: HomeAssistant, entry: ConfigEntry, device_entry: DeviceEntry +) -> bool: + """Allow manual removal of a device if not in use.""" + elro_connects_api: ElroConnectsK1 = hass.data[DOMAIN][entry.entry_id] + device_unique_id: str = device_entry.identifiers.copy().pop()[1] + device_id_str = device_unique_id[len(elro_connects_api.connector_id) + 1 :] + if not device_id_str: + return False + device_id = int(device_id_str) + if device_id in elro_connects_api.data: + return False + return True diff --git a/custom_components/elro_connects/manifest.json b/custom_components/elro_connects/manifest.json index 81580ab..85c9f2c 100644 --- a/custom_components/elro_connects/manifest.json +++ b/custom_components/elro_connects/manifest.json @@ -7,5 +7,5 @@ "requirements": ["lib-elro-connects==0.5.2"], "codeowners": ["@jbouwh"], "iot_class": "local_polling", - "version": "0.2.0" + "version": "0.1.7" } diff --git a/hacs.json b/hacs.json index 0df50bc..4da6593 100644 --- a/hacs.json +++ b/hacs.json @@ -1,5 +1,5 @@ { "name": "Elro Connects", - "hacs": "1.6.0", + "hacs": "1.0.0", "homeassistant": "2022.7.0" } diff --git a/tests/test_init.py b/tests/test_init.py index 614c416..64f091c 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -8,15 +8,31 @@ import pytest from pytest_homeassistant_custom_component.common import async_fire_time_changed +from custom_components.elro_connects import async_remove_config_entry_device from custom_components.elro_connects.const import DOMAIN from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from homeassistant.setup import async_setup_component from homeassistant.util import dt from .test_common import MOCK_DEVICE_STATUS_DATA +async def help_remove_device(hass, ws_client, device_id, config_entry_id): + """Remove config entry from a device.""" + await ws_client.send_json( + { + "id": 5, + "type": "config/device_registry/remove_config_entry", + "config_entry_id": config_entry_id, + "device_id": device_id, + } + ) + response = await ws_client.receive_json() + assert response["success"] + + async def test_setup_integration_no_data( hass: HomeAssistant, mock_k1_connector: dict[AsyncMock], @@ -113,3 +129,52 @@ async def test_configure_platforms_dynamically( assert hass.states.get("siren.beganegrond").state == "off" assert hass.states.get("siren.eerste_etage") is not None assert hass.states.get("siren.zolder") is not None + + +async def test_remove_device_from_config_entry( + hass: HomeAssistant, + mock_k1_connector: dict[AsyncMock], + mock_entry: ConfigEntry, +) -> None: + """Test the removing a device would work.""" + # Initial status holds device info for device [1,2,4] + initial_status_data = copy.deepcopy(MOCK_DEVICE_STATUS_DATA) + # Updated status holds device info for device [1,2] + updated_status_data = copy.deepcopy(MOCK_DEVICE_STATUS_DATA) + updated_status_data.pop(4) + + # setup integration with 3 siren entities + mock_k1_connector["result"].return_value = initial_status_data + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + # Simulate deactivation of siren 4 + mock_k1_connector["result"].return_value = updated_status_data + time = dt.now() + timedelta(seconds=30) + async_fire_time_changed(hass, time) + # await coordinator.async_request_refresh() + await hass.async_block_till_done() + await hass.async_block_till_done() + + connector_id = hass.data[DOMAIN][mock_entry.entry_id].connector_id + device_registry = dr.async_get(hass) + # Test removing the device for siren 4 will work + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, f"{connector_id}_4")} + ) + assert device_entry + assert await async_remove_config_entry_device(hass, mock_entry, device_entry) + + # Test removing the device for siren 2 will not work because it is in use + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, f"{connector_id}_2")} + ) + assert device_entry + assert not await async_remove_config_entry_device(hass, mock_entry, device_entry) + + # Test removing the the K1 connector device will not work + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, f"{connector_id}")} + ) + assert device_entry + assert not await async_remove_config_entry_device(hass, mock_entry, device_entry) diff --git a/version b/version index a192233..a1e1395 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.1.6 \ No newline at end of file +0.1.7 \ No newline at end of file