From 3b61862c190cb5e0dcb514339a52b35c348d170f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sun, 27 Sep 2026 17:40:18 +0200 Subject: [PATCH] Disconnect geo_json_events entity signals when the entity is removed (#183322) Co-authored-by: Claude --- .../geo_json_events/geo_location.py | 25 +++++++++---------- .../geo_json_events/test_geo_location.py | 23 +++++++++++++++++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/geo_json_events/geo_location.py b/homeassistant/components/geo_json_events/geo_location.py index b9ad8057d122..7352e4b82239 100644 --- a/homeassistant/components/geo_json_events/geo_location.py +++ b/homeassistant/components/geo_json_events/geo_location.py @@ -1,6 +1,5 @@ """Support for generic GeoJSON events.""" -from collections.abc import Callable import logging from typing import Any, override @@ -61,28 +60,28 @@ class GeoJsonLocationEvent(GeolocationEvent): self._feed_manager = feed_manager self._external_id = external_id self._attr_unique_id = f"{feed_manager.entry_id}_{external_id}" - self._remove_signal_delete: Callable[[], None] - self._remove_signal_update: Callable[[], None] @override async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" - self._remove_signal_delete = async_dispatcher_connect( - self.hass, - SIGNAL_DELETE_ENTITY.format(self._external_id), - self._delete_callback, + self.async_on_remove( + async_dispatcher_connect( + self.hass, + SIGNAL_DELETE_ENTITY.format(self._external_id), + self._delete_callback, + ) ) - self._remove_signal_update = async_dispatcher_connect( - self.hass, - SIGNAL_UPDATE_ENTITY.format(self._external_id), - self._update_callback, + self.async_on_remove( + async_dispatcher_connect( + self.hass, + SIGNAL_UPDATE_ENTITY.format(self._external_id), + self._update_callback, + ) ) @callback def _delete_callback(self) -> None: """Remove this entity.""" - self._remove_signal_delete() - self._remove_signal_update() self.hass.async_create_task(self.async_remove(force_remove=True)) @callback diff --git a/tests/components/geo_json_events/test_geo_location.py b/tests/components/geo_json_events/test_geo_location.py index 173ba201888a..5b0cdd7caf17 100644 --- a/tests/components/geo_json_events/test_geo_location.py +++ b/tests/components/geo_json_events/test_geo_location.py @@ -8,6 +8,8 @@ from freezegun import freeze_time from homeassistant.components.geo_json_events.const import ( ATTR_EXTERNAL_ID, DEFAULT_UPDATE_INTERVAL, + SIGNAL_DELETE_ENTITY, + SIGNAL_UPDATE_ENTITY, ) from homeassistant.components.geo_location import ( ATTR_SOURCE, @@ -26,6 +28,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.dispatcher import DATA_DISPATCHER from homeassistant.util import dt as dt_util from . import _generate_mock_feed_entry @@ -145,3 +148,23 @@ async def test_entity_lifecycle( await hass.async_block_till_done() assert len(hass.states.async_entity_ids(GEO_LOCATION_DOMAIN)) == 0 + + +async def test_signals_disconnected_on_unload( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test entity signal listeners are disconnected when the entry unloads.""" + config_entry.add_to_hass(hass) + mock_entry = _generate_mock_feed_entry("1234", "Title 1", 15.5, (-31.0, 150.0)) + with patch("aio_geojson_client.feed.GeoJsonFeed.update") as mock_feed_update: + mock_feed_update.return_value = "OK", [mock_entry] + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + signals = (SIGNAL_DELETE_ENTITY.format("1234"), SIGNAL_UPDATE_ENTITY.format("1234")) + assert all(signal in hass.data[DATA_DISPATCHER] for signal in signals) + + assert await hass.config_entries.async_unload(config_entry.entry_id) + await hass.async_block_till_done() + + assert not any(signal in hass.data[DATA_DISPATCHER] for signal in signals)