diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index a764b24b2e8..9349dd5458b 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -218,7 +218,8 @@ async def async_start( # noqa: C901 discovery_hash = (component, discovery_id) if discovery_hash in mqtt_data.discovery_already_discovered or payload: - async def discovery_done(_: Any) -> None: + @callback + def discovery_done(_: Any) -> None: pending = mqtt_data.discovery_pending_discovered[discovery_hash][ "pending" ] diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index bd3722b37f3..69001b837a2 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -832,8 +832,37 @@ class MqttDiscoveryUpdate(Entity): else: await self.async_remove(force_remove=True) - async def discovery_callback(payload: MQTTDiscoveryPayload) -> None: - """Handle discovery update.""" + async def _async_process_discovery_update( + payload: MQTTDiscoveryPayload, + discovery_update: Callable[ + [MQTTDiscoveryPayload], Coroutine[Any, Any, None] + ], + discovery_data: DiscoveryInfoType, + ) -> None: + """Process discovery update.""" + try: + await discovery_update(payload) + finally: + send_discovery_done(self.hass, discovery_data) + + async def _async_process_discovery_update_and_remove( + payload: MQTTDiscoveryPayload, discovery_data: DiscoveryInfoType + ) -> None: + """Process discovery update and remove entity.""" + self._cleanup_discovery_on_remove() + await _async_remove_state_and_registry_entry(self) + send_discovery_done(self.hass, discovery_data) + + @callback + def discovery_callback(payload: MQTTDiscoveryPayload) -> None: + """Handle discovery update. + + If the payload has changed we will create a task to + do the discovery update. + + As this callback can fire when nothing has changed, this + is a normal function to avoid task creation until it is needed. + """ _LOGGER.debug( "Got update for entity with hash: %s '%s'", discovery_hash, @@ -846,17 +875,20 @@ class MqttDiscoveryUpdate(Entity): if not payload: # Empty payload: Remove component _LOGGER.info("Removing component: %s", self.entity_id) - self._cleanup_discovery_on_remove() - await _async_remove_state_and_registry_entry(self) - send_discovery_done(self.hass, self._discovery_data) + self.hass.async_create_task( + _async_process_discovery_update_and_remove( + payload, self._discovery_data + ) + ) elif self._discovery_update: if old_payload != self._discovery_data[ATTR_DISCOVERY_PAYLOAD]: # Non-empty, changed payload: Notify component _LOGGER.info("Updating component: %s", self.entity_id) - try: - await self._discovery_update(payload) - finally: - send_discovery_done(self.hass, self._discovery_data) + self.hass.async_create_task( + _async_process_discovery_update( + payload, self._discovery_update, self._discovery_data + ) + ) else: # Non-empty, unchanged payload: Ignore to avoid changing states _LOGGER.debug("Ignoring unchanged update for: %s", self.entity_id)