From 87a387b86148d455dc880fef1e620f537868442b Mon Sep 17 00:00:00 2001 From: Philippe COTTIER Date: Sun, 27 Sep 2026 23:46:57 +0200 Subject: [PATCH] Allow removing devices that are no longer on the gateway --- README.md | 12 ++++++++++ .../powertag_gateway/__init__.py | 23 +++++++++++++++++++ custom_components/powertag_gateway/const.py | 1 + .../powertag_gateway/entity_base.py | 11 ++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7121522..3ca89bd 100644 --- a/README.md +++ b/README.md @@ -136,3 +136,15 @@ _The DPWS service configuration_ 2. Depending on where you added your PowerTags, press _ADD DEVICE_ or _ADD_SOLAR_PRODUCTION_. 3. Select the PowerTag entity you want to add (ends with _'total energy'_) 4. _SAVE_ + +### Removing old devices + +When a wireless device is removed from the gateway or replaced by another one +(e.g. a faulty PowerTag swapped for a new unit), its old device stays behind in Home Assistant. + + 1. Remove it from the gateway first, then reload the integration. + 2. Open the old device (_Settings_ > _Devices & Services_ > _PowerTag Link Gateway_). + 3. _⋮_ > _Delete_ + +Deleting is only allowed for devices that the gateway no longer reports: a device that is still +present on the gateway would simply be re-created on the next reload. diff --git a/custom_components/powertag_gateway/__init__.py b/custom_components/powertag_gateway/__init__.py index 44d1385..005b9b3 100644 --- a/custom_components/powertag_gateway/__init__.py +++ b/custom_components/powertag_gateway/__init__.py @@ -8,11 +8,13 @@ from homeassistant.const import Platform, CONF_HOST, CONF_PORT, CONF_INTERNAL_URL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from pymodbus.exceptions import ConnectionException from .const import ( CONF_CLIENT, CONF_SETUP_LOCK, + CONF_PRESENT_DEVICES, DOMAIN, CONF_TYPE_OF_GATEWAY, CONF_DEVICE_UNIQUE_ID_VERSION, @@ -60,6 +62,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: CONF_DEVICE_UNIQUE_ID_VERSION: unique_id_version, # Platforms scan the gateway one after another, see async_setup_entities. CONF_SETUP_LOCK: asyncio.Lock(), + # Identifiers of the devices found on the gateway during the last scan, + # see async_remove_config_entry_device. + CONF_PRESENT_DEVICES: set(), } await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -67,6 +72,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True +async def async_remove_config_entry_device( + hass: HomeAssistant, entry: ConfigEntry, device_entry: dr.DeviceEntry +) -> bool: + """Allow removing a device from the UI once it is no longer on the gateway. + + Devices that were removed or replaced in the gateway (e.g. a faulty PowerTag + swapped for a new one) otherwise stay behind in the device registry forever. + A device that the gateway still reports is refused, as it would simply be + re-created on the next reload. + """ + data = hass.data.get(DOMAIN, {}).get(entry.entry_id) + if data is None: + # Entry not loaded: we cannot tell what is on the gateway, be safe. + return False + present = data[CONF_PRESENT_DEVICES] + return not any(identifier in present for identifier in device_entry.identifiers) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data = hass.data.get(DOMAIN, {}).get(entry.entry_id) if data is None: diff --git a/custom_components/powertag_gateway/const.py b/custom_components/powertag_gateway/const.py index 1a67de6..5eda21b 100644 --- a/custom_components/powertag_gateway/const.py +++ b/custom_components/powertag_gateway/const.py @@ -18,3 +18,4 @@ DPWS_FRIENDLY_NAME = 'FriendlyName' DPWS_SERIAL_NUMBER = 'SerialNumber' CONF_SETUP_LOCK = 'setup_lock' +CONF_PRESENT_DEVICES = 'present_devices' diff --git a/custom_components/powertag_gateway/entity_base.py b/custom_components/powertag_gateway/entity_base.py index b68cd3a..6d7a6ac 100644 --- a/custom_components/powertag_gateway/entity_base.py +++ b/custom_components/powertag_gateway/entity_base.py @@ -10,7 +10,13 @@ from homeassistant.helpers import device_registry as dr from . import UniqueIdVersion -from .const import CONF_CLIENT, DOMAIN, CONF_DEVICE_UNIQUE_ID_VERSION, CONF_SETUP_LOCK +from .const import ( + CONF_CLIENT, + DOMAIN, + CONF_DEVICE_UNIQUE_ID_VERSION, + CONF_SETUP_LOCK, + CONF_PRESENT_DEVICES, +) from .const import GATEWAY_DOMAIN, TAG_DOMAIN from .device_features import ( FeatureClass, @@ -281,6 +287,7 @@ async def _async_setup_entities( client = data[CONF_CLIENT] presentation_url = data[CONF_INTERNAL_URL] device_unique_id_version = data[CONF_DEVICE_UNIQUE_ID_VERSION] + present_devices: set = data[CONF_PRESENT_DEVICES] entities = [] gateway_device = await gateway_device_info(client, presentation_url) @@ -296,6 +303,7 @@ async def _async_setup_entities( configuration_url=gateway_device.get("configuration_url"), serial_number=gateway_device.get("serial_number"), ) + present_devices.update(gateway_device["identifiers"]) _LOGGER.debug("Starting to scan for devices...") for i in range(1, 100): @@ -366,6 +374,7 @@ async def _async_setup_entities( ) if tag_device is None: continue + present_devices.update(tag_device["identifiers"]) device_name = tag_device["name"]