From 5d37217d96dd73472db375fdc081a35b2acafdcf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 26 May 2024 01:22:44 -1000 Subject: [PATCH] Avoid expensive inspection of callbacks to setup mqtt subscriptions (#118161) --- .../components/mqtt/alarm_control_panel.py | 3 +- .../components/mqtt/binary_sensor.py | 3 +- homeassistant/components/mqtt/camera.py | 3 +- homeassistant/components/mqtt/client.py | 7 ++-- homeassistant/components/mqtt/climate.py | 3 +- homeassistant/components/mqtt/cover.py | 5 ++- .../components/mqtt/device_tracker.py | 3 +- homeassistant/components/mqtt/discovery.py | 12 ++++--- homeassistant/components/mqtt/event.py | 3 +- homeassistant/components/mqtt/fan.py | 3 +- homeassistant/components/mqtt/humidifier.py | 3 +- homeassistant/components/mqtt/image.py | 3 +- homeassistant/components/mqtt/lawn_mower.py | 3 +- .../components/mqtt/light/schema_basic.py | 3 +- .../components/mqtt/light/schema_json.py | 3 +- .../components/mqtt/light/schema_template.py | 3 +- homeassistant/components/mqtt/lock.py | 3 +- homeassistant/components/mqtt/mixins.py | 4 ++- homeassistant/components/mqtt/number.py | 3 +- homeassistant/components/mqtt/select.py | 3 +- homeassistant/components/mqtt/sensor.py | 9 +++++- homeassistant/components/mqtt/siren.py | 3 +- homeassistant/components/mqtt/subscription.py | 11 +++++-- homeassistant/components/mqtt/switch.py | 3 +- homeassistant/components/mqtt/tag.py | 32 +++++++++++-------- homeassistant/components/mqtt/text.py | 3 +- homeassistant/components/mqtt/trigger.py | 17 ++++++++-- homeassistant/components/mqtt/update.py | 3 +- homeassistant/components/mqtt/vacuum.py | 3 +- homeassistant/components/mqtt/valve.py | 3 +- tests/components/axis/test_hub.py | 4 +-- tests/components/mqtt/test_common.py | 10 ++++-- tests/components/mqtt/test_subscription.py | 4 +-- tests/components/mqtt/test_trigger.py | 10 ++++-- .../components/mqtt_eventstream/test_init.py | 2 +- tests/components/tasmota/test_common.py | 4 +-- tests/components/tasmota/test_discovery.py | 4 ++- 37 files changed, 137 insertions(+), 64 deletions(-) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index fe6650cbd0f..d0a71a5a109 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -25,7 +25,7 @@ from homeassistant.const import ( STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HassJobType, HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType @@ -220,6 +220,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 61e5074378d..f1baaf515f1 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -26,7 +26,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.helpers.event as evt @@ -248,6 +248,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 2c6346f5794..091db98b95a 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -13,7 +13,7 @@ from homeassistant.components import camera from homeassistant.components.camera import Camera from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -124,6 +124,7 @@ class MqttCamera(MqttEntity, Camera): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 16db9a45b58..50b953c22d8 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -201,6 +201,7 @@ def async_subscribe_internal( msg_callback: Callable[[ReceiveMessage], Coroutine[Any, Any, None] | None], qos: int = DEFAULT_QOS, encoding: str | None = DEFAULT_ENCODING, + job_type: HassJobType | None = None, ) -> CALLBACK_TYPE: """Subscribe to an MQTT topic. @@ -228,7 +229,7 @@ def async_subscribe_internal( translation_domain=DOMAIN, translation_placeholders={"topic": topic}, ) - return client.async_subscribe(topic, msg_callback, qos, encoding) + return client.async_subscribe(topic, msg_callback, qos, encoding, job_type) @bind_hass @@ -867,12 +868,14 @@ class MQTT: msg_callback: Callable[[ReceiveMessage], Coroutine[Any, Any, None] | None], qos: int, encoding: str | None = None, + job_type: HassJobType | None = None, ) -> Callable[[], None]: """Set up a subscription to a topic with the provided qos.""" if not isinstance(topic, str): raise HomeAssistantError("Topic needs to be a string!") - job_type = get_hassjob_callable_job_type(msg_callback) + if job_type is None: + job_type = get_hassjob_callable_job_type(msg_callback) if job_type is not HassJobType.Callback: # Only wrap the callback with catch_log_exception # if it is not a simple callback since we catch diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 57f71008ecc..5e866eedf17 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -43,7 +43,7 @@ from homeassistant.const import ( PRECISION_WHOLE, UnitOfTemperature, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.template import Template @@ -429,6 +429,7 @@ class MqttTemperatureControlEntity(MqttEntity, ABC): "entity_id": self.entity_id, "qos": qos, "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } def render_template( diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index a4c7c1d8b3b..33eb5d65c02 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -28,7 +28,7 @@ from homeassistant.const import ( STATE_OPEN, STATE_OPENING, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.service_info.mqtt import ReceivePayloadType @@ -478,6 +478,7 @@ class MqttCover(MqttEntity, CoverEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } if self._config.get(CONF_STATE_TOPIC): @@ -491,6 +492,7 @@ class MqttCover(MqttEntity, CoverEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } if self._config.get(CONF_TILT_STATUS_TOPIC) is not None: @@ -504,6 +506,7 @@ class MqttCover(MqttEntity, CoverEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } self._sub_state = subscription.async_prepare_subscribe_topics( diff --git a/homeassistant/components/mqtt/device_tracker.py b/homeassistant/components/mqtt/device_tracker.py index 87abba2ac95..2f6f1be9c42 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -25,7 +25,7 @@ from homeassistant.const import ( STATE_HOME, STATE_NOT_HOME, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType @@ -155,6 +155,7 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity): ), "entity_id": self.entity_id, "qos": self._config[CONF_QOS], + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index b34141cc440..675e7c460c2 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE, CONF_PLATFORM -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.data_entry_flow import FlowResultType import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( @@ -319,10 +319,14 @@ async def async_start( # noqa: C901 hass, MQTT_DISCOVERY_DONE.format(*discovery_hash), None ) - # async_subscribe will never suspend so there is no need to create a task - # here and its faster to await them in sequence mqtt_data.discovery_unsubscribe = [ - await mqtt.async_subscribe(hass, topic, async_discovery_message_received, 0) + mqtt.async_subscribe_internal( + hass, + topic, + async_discovery_message_received, + 0, + job_type=HassJobType.Callback, + ) for topic in ( f"{discovery_topic}/+/+/config", f"{discovery_topic}/+/+/+/config", diff --git a/homeassistant/components/mqtt/event.py b/homeassistant/components/mqtt/event.py index a09579fccef..6377732cd94 100644 --- a/homeassistant/components/mqtt/event.py +++ b/homeassistant/components/mqtt/event.py @@ -17,7 +17,7 @@ from homeassistant.components.event import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME, CONF_VALUE_TEMPLATE -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType @@ -200,6 +200,7 @@ class MqttEvent(MqttEntity, EventEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } self._sub_state = subscription.async_prepare_subscribe_topics( diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index a418131d5c5..65961f7967a 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -27,7 +27,7 @@ from homeassistant.const import ( CONF_PAYLOAD_ON, CONF_STATE, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.template import Template @@ -447,6 +447,7 @@ class MqttFan(MqttEntity, FanEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } return has_topic diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index 097018f008f..00619605771 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -30,7 +30,7 @@ from homeassistant.const import ( CONF_PAYLOAD_ON, CONF_STATE, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.template import Template @@ -293,6 +293,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): "entity_id": self.entity_id, "qos": qos, "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } @callback diff --git a/homeassistant/components/mqtt/image.py b/homeassistant/components/mqtt/image.py index 4fa410c4595..4ae7498a8f1 100644 --- a/homeassistant/components/mqtt/image.py +++ b/homeassistant/components/mqtt/image.py @@ -16,7 +16,7 @@ from homeassistant.components import image from homeassistant.components.image import DEFAULT_CONTENT_TYPE, ImageEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.httpx_client import get_async_client @@ -202,6 +202,7 @@ class MqttImage(MqttEntity, ImageEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": encoding, + "job_type": HassJobType.Callback, } return has_topic diff --git a/homeassistant/components/mqtt/lawn_mower.py b/homeassistant/components/mqtt/lawn_mower.py index 2452b511144..dc592f16b48 100644 --- a/homeassistant/components/mqtt/lawn_mower.py +++ b/homeassistant/components/mqtt/lawn_mower.py @@ -17,7 +17,7 @@ from homeassistant.components.lawn_mower import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -192,6 +192,7 @@ class MqttLawnMower(MqttEntity, LawnMowerEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 583374c8d20..394b34747b0 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -37,7 +37,7 @@ from homeassistant.const import ( CONF_PAYLOAD_ON, STATE_ON, ) -from homeassistant.core import callback +from homeassistant.core import HassJobType, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType @@ -580,6 +580,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } add_topic(CONF_STATE_TOPIC, self._state_received, {"_attr_is_on"}) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index f6dec17f8f3..3ae3e6a799d 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -47,7 +47,7 @@ from homeassistant.const import ( CONF_XY, STATE_ON, ) -from homeassistant.core import async_get_hass, callback +from homeassistant.core import HassJobType, async_get_hass, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.json import json_dumps @@ -522,6 +522,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 193b4d23931..5a86ba84285 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -29,7 +29,7 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) -from homeassistant.core import callback +from homeassistant.core import HassJobType, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, TemplateVarsType @@ -282,6 +282,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 52c2bea2cc3..f9da70377a7 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, TemplateVarsType @@ -232,6 +232,7 @@ class MqttLock(MqttEntity, LockEntity): "entity_id": self.entity_id, CONF_QOS: qos, CONF_ENCODING: encoding, + "job_type": HassJobType.Callback, } } diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index e3ac3676f2b..ed15a39a6bb 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -30,7 +30,7 @@ from homeassistant.const import ( CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, ) -from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.core import Event, HassJobType, HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.device_registry import ( DeviceEntry, @@ -405,6 +405,7 @@ class MqttAttributesMixin(Entity): "entity_id": self.entity_id, "qos": self._attributes_config.get(CONF_QOS), "encoding": self._attributes_config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) @@ -519,6 +520,7 @@ class MqttAvailabilityMixin(Entity): "entity_id": self.entity_id, "qos": self._avail_config[CONF_QOS], "encoding": self._avail_config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } for topic in self._avail_topics } diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 17e7cfe69e0..defc880794d 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -26,7 +26,7 @@ from homeassistant.const import ( CONF_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType @@ -214,6 +214,7 @@ class MqttNumber(MqttEntity, RestoreNumber): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index a2814055a7c..14671abeac9 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -12,7 +12,7 @@ from homeassistant.components import select from homeassistant.components.select import SelectEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -154,6 +154,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index c8fe932ed71..fc6b6dcf273 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -31,7 +31,13 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, State, callback +from homeassistant.core import ( + CALLBACK_TYPE, + HassJobType, + HomeAssistant, + State, + callback, +) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_call_later @@ -297,6 +303,7 @@ class MqttSensor(MqttEntity, RestoreSensor): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } self._sub_state = subscription.async_prepare_subscribe_topics( diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 06cb2677c09..819064e82dc 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -28,7 +28,7 @@ from homeassistant.const import ( CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.json import json_dumps @@ -282,6 +282,7 @@ class MqttSiren(MqttEntity, SirenEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/subscription.py b/homeassistant/components/mqtt/subscription.py index 9e3ea21222f..40f9f130134 100644 --- a/homeassistant/components/mqtt/subscription.py +++ b/homeassistant/components/mqtt/subscription.py @@ -7,7 +7,7 @@ from dataclasses import dataclass from functools import partial from typing import TYPE_CHECKING, Any -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from . import debug_info from .client import async_subscribe_internal @@ -27,6 +27,7 @@ class EntitySubscription: qos: int = 0 encoding: str = "utf-8" entity_id: str | None = None + job_type: HassJobType | None = None def resubscribe_if_necessary( self, hass: HomeAssistant, other: EntitySubscription | None @@ -62,7 +63,12 @@ class EntitySubscription: if not self.should_subscribe or not self.topic: return self.unsubscribe_callback = async_subscribe_internal( - self.hass, self.topic, self.message_callback, self.qos, self.encoding + self.hass, + self.topic, + self.message_callback, + self.qos, + self.encoding, + self.job_type, ) def _should_resubscribe(self, other: EntitySubscription | None) -> bool: @@ -112,6 +118,7 @@ def async_prepare_subscribe_topics( hass=hass, should_subscribe=None, entity_id=value.get("entity_id", None), + job_type=value.get("job_type", None), ) # Get the current subscription state current = current_subscriptions.pop(key, None) diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index 9f266a0e9ab..168cf903091 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -20,7 +20,7 @@ from homeassistant.const import ( CONF_VALUE_TEMPLATE, STATE_ON, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -145,6 +145,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/tag.py b/homeassistant/components/mqtt/tag.py index 55f7e775ae9..59d9c3f87ff 100644 --- a/homeassistant/components/mqtt/tag.py +++ b/homeassistant/components/mqtt/tag.py @@ -11,7 +11,7 @@ import voluptuous as vol from homeassistant.components import tag from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE, CONF_VALUE_TEMPLATE -from homeassistant.core import HomeAssistant +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -142,28 +142,32 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdateMixin): update_device(self.hass, self._config_entry, config) await self.subscribe_topics() + @callback + def _async_tag_scanned(self, msg: ReceiveMessage) -> None: + """Handle new tag scanned.""" + try: + tag_id = str(self._value_template(msg.payload, "")).strip() + except MqttValueTemplateException as exc: + _LOGGER.warning(exc) + return + if not tag_id: # No output from template, ignore + return + + self.hass.async_create_task( + tag.async_scan_tag(self.hass, tag_id, self.device_id) + ) + async def subscribe_topics(self) -> None: """Subscribe to MQTT topics.""" - - async def tag_scanned(msg: ReceiveMessage) -> None: - try: - tag_id = str(self._value_template(msg.payload, "")).strip() - except MqttValueTemplateException as exc: - _LOGGER.warning(exc) - return - if not tag_id: # No output from template, ignore - return - - await tag.async_scan_tag(self.hass, tag_id, self.device_id) - self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, self._sub_state, { "state_topic": { "topic": self._config[CONF_TOPIC], - "msg_callback": tag_scanned, + "msg_callback": self._async_tag_scanned, "qos": self._config[CONF_QOS], + "job_type": HassJobType.Callback, } }, ) diff --git a/homeassistant/components/mqtt/text.py b/homeassistant/components/mqtt/text.py index abced8b8744..5bbed474e43 100644 --- a/homeassistant/components/mqtt/text.py +++ b/homeassistant/components/mqtt/text.py @@ -20,7 +20,7 @@ from homeassistant.const import ( CONF_VALUE_TEMPLATE, MAX_LENGTH_STATE_STATE, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType @@ -183,6 +183,7 @@ class MqttTextEntity(MqttEntity, TextEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } add_subscription( diff --git a/homeassistant/components/mqtt/trigger.py b/homeassistant/components/mqtt/trigger.py index 7aa798a7a3c..91ac404a07a 100644 --- a/homeassistant/components/mqtt/trigger.py +++ b/homeassistant/components/mqtt/trigger.py @@ -10,7 +10,13 @@ from typing import Any import voluptuous as vol from homeassistant.const import CONF_PAYLOAD, CONF_PLATFORM, CONF_VALUE_TEMPLATE -from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback +from homeassistant.core import ( + CALLBACK_TYPE, + HassJob, + HassJobType, + HomeAssistant, + callback, +) from homeassistant.helpers import config_validation as cv from homeassistant.helpers.template import Template from homeassistant.helpers.trigger import TriggerActionType, TriggerData, TriggerInfo @@ -99,6 +105,11 @@ async def async_attach_trigger( "Attaching MQTT trigger for topic: '%s', payload: '%s'", topic, wanted_payload ) - return await mqtt.async_subscribe( - hass, topic, mqtt_automation_listener, encoding=encoding, qos=qos + return mqtt.async_subscribe_internal( + hass, + topic, + mqtt_automation_listener, + encoding=encoding, + qos=qos, + job_type=HassJobType.Callback, ) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index ee29601e585..37d74489bbf 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -16,7 +16,7 @@ from homeassistant.components.update import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME, CONF_VALUE_TEMPLATE -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -229,6 +229,7 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } add_subscription( diff --git a/homeassistant/components/mqtt/vacuum.py b/homeassistant/components/mqtt/vacuum.py index 5c8c2fd2ba5..15b85edb229 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -31,7 +31,7 @@ from homeassistant.const import ( STATE_IDLE, STATE_PAUSED, ) -from homeassistant.core import HomeAssistant, async_get_hass, callback +from homeassistant.core import HassJobType, HomeAssistant, async_get_hass, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue @@ -346,6 +346,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, self._sub_state, topics diff --git a/homeassistant/components/mqtt/valve.py b/homeassistant/components/mqtt/valve.py index ce89c6c2daf..dd6a6c3bf35 100644 --- a/homeassistant/components/mqtt/valve.py +++ b/homeassistant/components/mqtt/valve.py @@ -26,7 +26,7 @@ from homeassistant.const import ( STATE_OPEN, STATE_OPENING, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HassJobType, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType @@ -357,6 +357,7 @@ class MqttValve(MqttEntity, ValveEntity): "entity_id": self.entity_id, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, + "job_type": HassJobType.Callback, } self._sub_state = subscription.async_prepare_subscribe_topics( diff --git a/tests/components/axis/test_hub.py b/tests/components/axis/test_hub.py index 11ef1ef1cdf..c208f767bfc 100644 --- a/tests/components/axis/test_hub.py +++ b/tests/components/axis/test_hub.py @@ -2,7 +2,7 @@ from ipaddress import ip_address from unittest import mock -from unittest.mock import Mock, call, patch +from unittest.mock import ANY, Mock, call, patch import axis as axislib import pytest @@ -90,7 +90,7 @@ async def test_device_support_mqtt( hass: HomeAssistant, mqtt_mock: MqttMockHAClient, setup_config_entry ) -> None: """Successful setup.""" - mqtt_call = call(f"axis/{MAC}/#", mock.ANY, 0, "utf-8") + mqtt_call = call(f"axis/{MAC}/#", mock.ANY, 0, "utf-8", ANY) assert mqtt_call in mqtt_mock.async_subscribe.call_args_list topic = f"axis/{MAC}/event/tns:onvif/Device/tns:axis/Sensor/PIR/$source/sensor/0" diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index f33eb1c850b..5d451655307 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -27,7 +27,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, EntityCategory, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HassJobType, HomeAssistant from homeassistant.generated.mqtt import MQTT from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -1189,7 +1189,9 @@ async def help_test_entity_id_update_subscriptions( assert state is not None assert mqtt_mock.async_subscribe.call_count == len(topics) + 2 + DISCOVERY_COUNT for topic in topics: - mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY) + mqtt_mock.async_subscribe.assert_any_call( + topic, ANY, ANY, ANY, HassJobType.Callback + ) mqtt_mock.async_subscribe.reset_mock() entity_registry.async_update_entity( @@ -1203,7 +1205,9 @@ async def help_test_entity_id_update_subscriptions( state = hass.states.get(f"{domain}.milk") assert state is not None for topic in topics: - mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY) + mqtt_mock.async_subscribe.assert_any_call( + topic, ANY, ANY, ANY, HassJobType.Callback + ) async def help_test_entity_id_update_discovery_update( diff --git a/tests/components/mqtt/test_subscription.py b/tests/components/mqtt/test_subscription.py index 54acc935f1d..7247458a667 100644 --- a/tests/components/mqtt/test_subscription.py +++ b/tests/components/mqtt/test_subscription.py @@ -154,7 +154,7 @@ async def test_qos_encoding_default( {"test_topic1": {"topic": "test-topic1", "msg_callback": msg_callback}}, ) await async_subscribe_topics(hass, sub_state) - mqtt_mock.async_subscribe.assert_called_with("test-topic1", ANY, 0, "utf-8") + mqtt_mock.async_subscribe.assert_called_with("test-topic1", ANY, 0, "utf-8", None) async def test_qos_encoding_custom( @@ -183,7 +183,7 @@ async def test_qos_encoding_custom( }, ) await async_subscribe_topics(hass, sub_state) - mqtt_mock.async_subscribe.assert_called_with("test-topic1", ANY, 1, "utf-16") + mqtt_mock.async_subscribe.assert_called_with("test-topic1", ANY, 1, "utf-16", None) async def test_no_change( diff --git a/tests/components/mqtt/test_trigger.py b/tests/components/mqtt/test_trigger.py index ceb9207e0c2..56fc30f7354 100644 --- a/tests/components/mqtt/test_trigger.py +++ b/tests/components/mqtt/test_trigger.py @@ -6,7 +6,7 @@ import pytest from homeassistant.components import automation from homeassistant.const import ATTR_ENTITY_ID, ENTITY_MATCH_ALL, SERVICE_TURN_OFF -from homeassistant.core import HomeAssistant +from homeassistant.core import HassJobType, HomeAssistant from homeassistant.setup import async_setup_component from tests.common import async_fire_mqtt_message, async_mock_service, mock_component @@ -239,7 +239,9 @@ async def test_encoding_default(hass: HomeAssistant, calls, setup_comp) -> None: }, ) - setup_comp.async_subscribe.assert_called_with("test-topic", ANY, 0, "utf-8") + setup_comp.async_subscribe.assert_called_with( + "test-topic", ANY, 0, "utf-8", HassJobType.Callback + ) async def test_encoding_custom(hass: HomeAssistant, calls, setup_comp) -> None: @@ -255,4 +257,6 @@ async def test_encoding_custom(hass: HomeAssistant, calls, setup_comp) -> None: }, ) - setup_comp.async_subscribe.assert_called_with("test-topic", ANY, 0, None) + setup_comp.async_subscribe.assert_called_with( + "test-topic", ANY, 0, None, HassJobType.Callback + ) diff --git a/tests/components/mqtt_eventstream/test_init.py b/tests/components/mqtt_eventstream/test_init.py index 90034382fc8..82def7ef145 100644 --- a/tests/components/mqtt_eventstream/test_init.py +++ b/tests/components/mqtt_eventstream/test_init.py @@ -66,7 +66,7 @@ async def test_subscribe(hass: HomeAssistant, mqtt_mock: MqttMockHAClient) -> No await hass.async_block_till_done() # Verify that the this entity was subscribed to the topic - mqtt_mock.async_subscribe.assert_called_with(sub_topic, ANY, 0, ANY) + mqtt_mock.async_subscribe.assert_called_with(sub_topic, ANY, 0, ANY, ANY) async def test_state_changed_event_sends_message( diff --git a/tests/components/tasmota/test_common.py b/tests/components/tasmota/test_common.py index 0480520f469..f3d85f019f3 100644 --- a/tests/components/tasmota/test_common.py +++ b/tests/components/tasmota/test_common.py @@ -693,7 +693,7 @@ async def help_test_entity_id_update_subscriptions( assert state is not None assert mqtt_mock.async_subscribe.call_count == len(topics) for topic in topics: - mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY) + mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY, ANY) mqtt_mock.async_subscribe.reset_mock() entity_reg.async_update_entity( @@ -707,7 +707,7 @@ async def help_test_entity_id_update_subscriptions( state = hass.states.get(f"{domain}.milk") assert state is not None for topic in topics: - mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY) + mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY, ANY) async def help_test_entity_id_update_discovery_update( diff --git a/tests/components/tasmota/test_discovery.py b/tests/components/tasmota/test_discovery.py index 5a7635c72b2..91832f1f2f0 100644 --- a/tests/components/tasmota/test_discovery.py +++ b/tests/components/tasmota/test_discovery.py @@ -30,7 +30,9 @@ async def test_subscribing_config_topic( discovery_topic = DEFAULT_PREFIX assert mqtt_mock.async_subscribe.called - mqtt_mock.async_subscribe.assert_any_call(discovery_topic + "/#", ANY, 0, "utf-8") + mqtt_mock.async_subscribe.assert_any_call( + discovery_topic + "/#", ANY, 0, "utf-8", ANY + ) async def test_future_discovery_message(