From fe38e6ba875cb267da2ed066109b946a124166cc Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Feb 2022 21:09:57 +0100 Subject: [PATCH] Drop MQTT import flow (#66160) * Drop MQTT import flow * Reload manually configured MQTT entities when config entry is setup * Address review comments * Actually remove the import flow --- homeassistant/components/mqtt/__init__.py | 37 +++++------- .../components/mqtt/alarm_control_panel.py | 16 +++-- .../components/mqtt/binary_sensor.py | 11 ++-- homeassistant/components/mqtt/button.py | 16 +++-- homeassistant/components/mqtt/camera.py | 17 ++++-- homeassistant/components/mqtt/climate.py | 16 +++-- homeassistant/components/mqtt/config_flow.py | 11 ---- homeassistant/components/mqtt/const.py | 1 + homeassistant/components/mqtt/cover.py | 16 +++-- homeassistant/components/mqtt/fan.py | 16 +++-- homeassistant/components/mqtt/humidifier.py | 16 +++-- .../components/mqtt/light/__init__.py | 9 ++- homeassistant/components/mqtt/lock.py | 16 +++-- homeassistant/components/mqtt/mixins.py | 50 +++++++++++++++- homeassistant/components/mqtt/number.py | 16 +++-- homeassistant/components/mqtt/scene.py | 10 ++-- homeassistant/components/mqtt/select.py | 16 +++-- homeassistant/components/mqtt/sensor.py | 11 ++-- homeassistant/components/mqtt/siren.py | 16 +++-- homeassistant/components/mqtt/switch.py | 16 +++-- .../components/mqtt/vacuum/__init__.py | 13 ++--- .../mqtt/test_alarm_control_panel.py | 8 +++ tests/components/mqtt/test_binary_sensor.py | 8 +++ tests/components/mqtt/test_button.py | 8 +++ tests/components/mqtt/test_camera.py | 8 +++ tests/components/mqtt/test_climate.py | 8 +++ tests/components/mqtt/test_common.py | 58 ++++++++++++++++++- tests/components/mqtt/test_config_flow.py | 23 +++++++- tests/components/mqtt/test_cover.py | 8 +++ tests/components/mqtt/test_fan.py | 8 +++ tests/components/mqtt/test_humidifier.py | 8 +++ tests/components/mqtt/test_init.py | 44 +++++++++++--- tests/components/mqtt/test_legacy_vacuum.py | 8 +++ tests/components/mqtt/test_light.py | 8 +++ tests/components/mqtt/test_light_json.py | 8 +++ tests/components/mqtt/test_light_template.py | 8 +++ tests/components/mqtt/test_lock.py | 8 +++ tests/components/mqtt/test_number.py | 8 +++ tests/components/mqtt/test_scene.py | 8 +++ tests/components/mqtt/test_select.py | 8 +++ tests/components/mqtt/test_sensor.py | 8 +++ tests/components/mqtt/test_siren.py | 8 +++ tests/components/mqtt/test_state_vacuum.py | 8 +++ tests/components/mqtt/test_switch.py | 8 +++ tests/conftest.py | 18 ++++-- 45 files changed, 494 insertions(+), 155 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index b797491b7a9..6cb2eb41d9c 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -21,7 +21,6 @@ import certifi import jinja2 import voluptuous as vol -from homeassistant import config_entries from homeassistant.components import websocket_api from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -37,6 +36,7 @@ from homeassistant.const import ( CONF_VALUE_TEMPLATE, EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, + SERVICE_RELOAD, Platform, ) from homeassistant.core import ( @@ -76,6 +76,7 @@ from .const import ( CONF_TOPIC, CONF_WILL_MESSAGE, DATA_MQTT_CONFIG, + DATA_MQTT_RELOAD_NEEDED, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_ENCODING, @@ -580,22 +581,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: websocket_api.async_register_command(hass, websocket_mqtt_info) debug_info.initialize(hass) - if conf is None: - # If we have a config entry, setup is done by that config entry. - # If there is no config entry, this should fail. - return bool(hass.config_entries.async_entries(DOMAIN)) - - conf = dict(conf) - - hass.data[DATA_MQTT_CONFIG] = conf - - # Only import if we haven't before. - if not hass.config_entries.async_entries(DOMAIN): - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={} - ) - ) + if conf: + conf = dict(conf) + hass.data[DATA_MQTT_CONFIG] = conf return True @@ -609,12 +597,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Load a config entry.""" conf = hass.data.get(DATA_MQTT_CONFIG) - # Config entry was created because user had configuration.yaml entry - # They removed that, so remove entry. - if conf is None and entry.source == config_entries.SOURCE_IMPORT: - hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) - return False - # If user didn't have configuration.yaml config, generate defaults if conf is None: conf = CONFIG_SCHEMA({DOMAIN: dict(entry.data)})[DOMAIN] @@ -735,6 +717,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if conf.get(CONF_DISCOVERY): await _async_setup_discovery(hass, conf, entry) + if DATA_MQTT_RELOAD_NEEDED in hass.data: + hass.data.pop(DATA_MQTT_RELOAD_NEEDED) + await hass.services.async_call( + DOMAIN, + SERVICE_RELOAD, + {}, + blocking=False, + ) + return True diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 5d7c67d621b..cca4e58658c 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -35,10 +35,9 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -47,10 +46,14 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) _LOGGER = logging.getLogger(__name__) @@ -124,8 +127,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT alarm control panel through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, alarm.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 17daaaf08ee..166b7cda34c 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -28,20 +28,20 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.helpers.event as evt from homeassistant.helpers.event import async_track_point_in_utc_time -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util -from . import PLATFORMS, MqttValueTemplate, subscription +from . import MqttValueTemplate, subscription from .. import mqtt -from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, DOMAIN, PAYLOAD_NONE +from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, PAYLOAD_NONE from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttAvailability, MqttEntity, async_setup_entry_helper, + async_setup_platform_helper, ) _LOGGER = logging.getLogger(__name__) @@ -75,8 +75,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT binary sensor through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, binary_sensor.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index c4b4b19b120..22ee7b6d5ae 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -12,10 +12,9 @@ from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate +from . import MqttCommandTemplate from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -23,9 +22,13 @@ from .const import ( CONF_ENCODING, CONF_QOS, CONF_RETAIN, - DOMAIN, ) -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) CONF_PAYLOAD_PRESS = "payload_press" DEFAULT_NAME = "MQTT Button" @@ -52,8 +55,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT button through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, button.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 1c060f7f32a..0e387023a39 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -12,14 +12,18 @@ from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, subscription +from . import subscription from .. import mqtt -from .const import CONF_QOS, CONF_TOPIC, DOMAIN +from .const import CONF_QOS, CONF_TOPIC from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) DEFAULT_NAME = "MQTT Camera" @@ -49,8 +53,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT camera through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, camera.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 2b4e03f8663..043a291f159 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -53,20 +53,23 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import ( MQTT_BASE_PLATFORM_SCHEMA, - PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription, ) from .. import mqtt -from .const import CONF_ENCODING, CONF_QOS, CONF_RETAIN, DOMAIN, PAYLOAD_NONE +from .const import CONF_ENCODING, CONF_QOS, CONF_RETAIN, PAYLOAD_NONE from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) _LOGGER = logging.getLogger(__name__) @@ -303,8 +306,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT climate device through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, climate.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 84322ddc1ee..a26e62b6227 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -84,17 +84,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): step_id="broker", data_schema=vol.Schema(fields), errors=errors ) - async def async_step_import(self, user_input): - """Import a config entry. - - Special type of import, we're not actually going to store any data. - Instead, we're going to rely on the values that are in config file. - """ - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - - return self.async_create_entry(title="configuration.yaml", data={}) - async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult: """Receive a Hass.io discovery.""" await self._async_handle_discovery_without_unique_id() diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 46396820545..f04348ee002 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -23,6 +23,7 @@ CONF_TOPIC = "topic" CONF_WILL_MESSAGE = "will_message" DATA_MQTT_CONFIG = "mqtt_config" +DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed" DEFAULT_PREFIX = "homeassistant" DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status" diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 4dfa9e20798..282e57fea9e 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -37,10 +37,9 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TOPIC, @@ -48,10 +47,14 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) _LOGGER = logging.getLogger(__name__) @@ -217,8 +220,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT cover through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, cover.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 114c8f5729f..bedc3467c3b 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -32,7 +32,6 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util.percentage import ( int_states_in_range, @@ -40,7 +39,7 @@ from homeassistant.util.percentage import ( ranged_value_to_percentage, ) -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -50,11 +49,15 @@ from .const import ( CONF_RETAIN, CONF_STATE_TOPIC, CONF_STATE_VALUE_TEMPLATE, - DOMAIN, PAYLOAD_NONE, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic" CONF_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic" @@ -213,8 +216,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT fan through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index 0ab99cf0914..99674051521 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -27,10 +27,9 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -40,11 +39,15 @@ from .const import ( CONF_RETAIN, CONF_STATE_TOPIC, CONF_STATE_VALUE_TEMPLATE, - DOMAIN, PAYLOAD_NONE, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) CONF_AVAILABLE_MODES_LIST = "modes" CONF_DEVICE_CLASS = "device_class" @@ -157,8 +160,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT humidifier through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, humidifier.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 09dfb8417cc..d78cd5e7baa 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -8,11 +8,9 @@ import voluptuous as vol from homeassistant.components import light from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .. import DOMAIN, PLATFORMS -from ..mixins import async_setup_entry_helper +from ..mixins import async_setup_entry_helper, async_setup_platform_helper from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( DISCOVERY_SCHEMA_BASIC, @@ -69,8 +67,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT light through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, light.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry(hass, config_entry, async_add_entities): diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 788c6be1fef..1dd73175b3b 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -12,10 +12,9 @@ from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttValueTemplate, subscription +from . import MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TOPIC, @@ -23,10 +22,14 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) CONF_PAYLOAD_LOCK = "payload_lock" CONF_PAYLOAD_UNLOCK = "payload_unlock" @@ -73,8 +76,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT lock panel through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, lock.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index fe989acb98e..421ad3203b3 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -5,9 +5,11 @@ from abc import abstractmethod from collections.abc import Callable import json import logging +from typing import Any, Protocol import voluptuous as vol +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CONFIGURATION_URL, ATTR_MANUFACTURER, @@ -23,7 +25,7 @@ from homeassistant.const import ( CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import ( config_validation as cv, device_registry as dr, @@ -40,9 +42,18 @@ from homeassistant.helpers.entity import ( async_generate_entity_id, validate_entity_category, ) +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType -from . import DATA_MQTT, MqttValueTemplate, async_publish, debug_info, subscription +from . import ( + DATA_MQTT, + PLATFORMS, + MqttValueTemplate, + async_publish, + debug_info, + subscription, +) from .const import ( ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_PAYLOAD, @@ -51,6 +62,7 @@ from .const import ( CONF_ENCODING, CONF_QOS, CONF_TOPIC, + DATA_MQTT_RELOAD_NEEDED, DEFAULT_ENCODING, DEFAULT_PAYLOAD_AVAILABLE, DEFAULT_PAYLOAD_NOT_AVAILABLE, @@ -210,6 +222,20 @@ MQTT_ENTITY_COMMON_SCHEMA = MQTT_AVAILABILITY_SCHEMA.extend( ) +class SetupEntity(Protocol): + """Protocol type for async_setup_entities.""" + + async def __call__( + self, + hass: HomeAssistant, + async_add_entities: AddEntitiesCallback, + config: ConfigType, + config_entry: ConfigEntry | None = None, + discovery_data: dict[str, Any] | None = None, + ) -> None: + """Define setup_entities type.""" + + async def async_setup_entry_helper(hass, domain, async_setup, schema): """Set up entity, automation or tag creation dynamically through MQTT discovery.""" @@ -232,6 +258,26 @@ async def async_setup_entry_helper(hass, domain, async_setup, schema): ) +async def async_setup_platform_helper( + hass: HomeAssistant, + platform_domain: str, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + async_setup_entities: SetupEntity, +) -> None: + """Return true if platform setup should be aborted.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) + if not bool(hass.config_entries.async_entries(DOMAIN)): + hass.data[DATA_MQTT_RELOAD_NEEDED] = None + _LOGGER.warning( + "MQTT integration is not setup, skipping setup of manually configured " + "MQTT %s", + platform_domain, + ) + return + await async_setup_entities(hass, async_add_entities, config) + + def init_entity_id_from_config(hass, entity, config, entity_id_format): """Set entity_id from object_id if defined in config.""" if CONF_OBJECT_ID in config: diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 6f9c4c38ead..e13ae4ded84 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -23,11 +23,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -36,10 +35,14 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) _LOGGER = logging.getLogger(__name__) @@ -103,8 +106,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT number through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, number.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/scene.py b/homeassistant/components/mqtt/scene.py index b12eb2d336a..c44ea1dca53 100644 --- a/homeassistant/components/mqtt/scene.py +++ b/homeassistant/components/mqtt/scene.py @@ -12,18 +12,17 @@ from homeassistant.const import CONF_ICON, CONF_NAME, CONF_PAYLOAD_ON, CONF_UNIQ from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS from .. import mqtt -from .const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN, DOMAIN +from .const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN from .mixins import ( CONF_OBJECT_ID, MQTT_AVAILABILITY_SCHEMA, MqttAvailability, MqttDiscoveryUpdate, async_setup_entry_helper, + async_setup_platform_helper, init_entity_id_from_config, ) @@ -52,8 +51,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT scene through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, scene.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index 58e6e7e4d64..f873a32a5de 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -13,11 +13,10 @@ from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -26,10 +25,14 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) _LOGGER = logging.getLogger(__name__) @@ -67,8 +70,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT select through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, select.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 137627047bc..8dd53901755 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -30,20 +30,20 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_point_in_utc_time -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util -from . import PLATFORMS, MqttValueTemplate, subscription +from . import MqttValueTemplate, subscription from .. import mqtt -from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, DOMAIN +from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttAvailability, MqttEntity, async_setup_entry_helper, + async_setup_platform_helper, ) _LOGGER = logging.getLogger(__name__) @@ -120,8 +120,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT sensors through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, sensor.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index e43edb12873..8619bc799a4 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -36,10 +36,9 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TEMPLATE, @@ -49,12 +48,16 @@ from .const import ( CONF_RETAIN, CONF_STATE_TOPIC, CONF_STATE_VALUE_TEMPLATE, - DOMAIN, PAYLOAD_EMPTY_JSON, PAYLOAD_NONE, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) DEFAULT_NAME = "MQTT Siren" DEFAULT_PAYLOAD_ON = "ON" @@ -118,8 +121,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT siren through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, siren.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index f0ec18cc379..1a471ea2ad0 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -20,11 +20,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import PLATFORMS, MqttValueTemplate, subscription +from . import MqttValueTemplate, subscription from .. import mqtt from .const import ( CONF_COMMAND_TOPIC, @@ -32,11 +31,15 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - DOMAIN, PAYLOAD_NONE, ) from .debug_info import log_messages -from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + async_setup_platform_helper, +) MQTT_SWITCH_ATTRIBUTES_BLOCKED = frozenset( { @@ -75,8 +78,9 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT switch through configuration.yaml.""" - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, switch.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry( diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index 6ff03a437e5..f64a67820d4 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -3,11 +3,9 @@ import functools import voluptuous as vol -from homeassistant.components.vacuum import DOMAIN -from homeassistant.helpers.reload import async_setup_reload_service +from homeassistant.components import vacuum -from .. import DOMAIN as MQTT_DOMAIN, PLATFORMS -from ..mixins import async_setup_entry_helper +from ..mixins import async_setup_entry_helper, async_setup_platform_helper from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE from .schema_legacy import ( DISCOVERY_SCHEMA_LEGACY, @@ -44,8 +42,9 @@ PLATFORM_SCHEMA = vol.All( async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up MQTT vacuum through configuration.yaml.""" - await async_setup_reload_service(hass, MQTT_DOMAIN, PLATFORMS) - await _async_setup_entity(hass, async_add_entities, config) + await async_setup_platform_helper( + hass, vacuum.DOMAIN, config, async_add_entities, _async_setup_entity + ) async def async_setup_entry(hass, config_entry, async_add_entities): @@ -53,7 +52,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) - await async_setup_entry_helper(hass, DOMAIN, setup, DISCOVERY_SCHEMA) + await async_setup_entry_helper(hass, vacuum.DOMAIN, setup, DISCOVERY_SCHEMA) async def _async_setup_entity( diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index 091048513c7..a278cde768b 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -53,6 +53,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -840,3 +841,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = alarm_control_panel.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = alarm_control_panel.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index aa726f4fff2..5fa71d73632 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -38,6 +38,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_reload_with_config, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_unique_id, @@ -879,6 +880,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = binary_sensor.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + async def test_cleanup_triggers_and_restoring_state( hass, mqtt_mock, caplog, tmp_path, freezer ): diff --git a/tests/components/mqtt/test_button.py b/tests/components/mqtt/test_button.py index e4997085ce2..83ef7a42705 100644 --- a/tests/components/mqtt/test_button.py +++ b/tests/components/mqtt/test_button.py @@ -26,6 +26,7 @@ from .test_common import ( help_test_entity_id_update_discovery_update, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -405,3 +406,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = button.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = button.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index 936e4ef4664..07fd7dc2c14 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -27,6 +27,7 @@ from .test_common import ( help_test_entity_id_update_discovery_update, help_test_entity_id_update_subscriptions, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -252,3 +253,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = camera.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = camera.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index 16c765dc51a..624823e0ebb 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -58,6 +58,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -1422,3 +1423,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = CLIMATE_DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = CLIMATE_DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index 758cdd801ae..8cf7353d196 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -21,7 +21,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.setup import async_setup_component -from tests.common import async_fire_mqtt_message, mock_registry +from tests.common import MockConfigEntry, async_fire_mqtt_message, mock_registry DEFAULT_CONFIG_DEVICE_INFO_ID = { "identifiers": ["helloworld"], @@ -1619,7 +1619,61 @@ async def help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config assert hass.states.get(f"{domain}.test_old_2") assert len(hass.states.async_all(domain)) == 2 - # Create temporary fixture for configuration.yaml based on the supplied config and test a reload with this new config + # Create temporary fixture for configuration.yaml based on the supplied config and + # test a reload with this new config + new_config_1 = copy.deepcopy(config) + new_config_1["name"] = "test_new_1" + new_config_2 = copy.deepcopy(config) + new_config_2["name"] = "test_new_2" + new_config_3 = copy.deepcopy(config) + new_config_3["name"] = "test_new_3" + + await help_test_reload_with_config( + hass, caplog, tmp_path, domain, [new_config_1, new_config_2, new_config_3] + ) + + assert len(hass.states.async_all(domain)) == 3 + + assert hass.states.get(f"{domain}.test_new_1") + assert hass.states.get(f"{domain}.test_new_2") + assert hass.states.get(f"{domain}.test_new_3") + + +async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): + """Test reloading an MQTT platform when config entry is setup late.""" + # Create and test an old config of 2 entities based on the config supplied + old_config_1 = copy.deepcopy(config) + old_config_1["name"] = "test_old_1" + old_config_2 = copy.deepcopy(config) + old_config_2["name"] = "test_old_2" + + old_yaml_config_file = tmp_path / "configuration.yaml" + old_yaml_config = yaml.dump({domain: [old_config_1, old_config_2]}) + old_yaml_config_file.write_text(old_yaml_config) + assert old_yaml_config_file.read_text() == old_yaml_config + + assert await async_setup_component( + hass, domain, {domain: [old_config_1, old_config_2]} + ) + await hass.async_block_till_done() + + # No MQTT config entry, there should be a warning and no entities + assert ( + "MQTT integration is not setup, skipping setup of manually " + f"configured MQTT {domain}" + ) in caplog.text + assert len(hass.states.async_all(domain)) == 0 + + # User sets up a config entry, should succeed and entities will setup + entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) + entry.add_to_hass(hass) + with patch.object(hass_config, "YAML_CONFIG_FILE", old_yaml_config_file): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert len(hass.states.async_all(domain)) == 2 + + # Create temporary fixture for configuration.yaml based on the supplied config and + # test a reload with this new config new_config_1 = copy.deepcopy(config) new_config_1["name"] = "test_new_1" new_config_2 = copy.deepcopy(config) diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index befdc139eeb..83dafd6b436 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -82,17 +82,34 @@ async def test_user_connection_fails(hass, mock_try_connection, mock_finish_setu async def test_manual_config_set( hass, mock_try_connection, mock_finish_setup, mqtt_client_mock ): - """Test we ignore entry if manual config available.""" + """Test manual config does not create an entry, and entry can be setup late.""" + # MQTT config present in yaml config assert await async_setup_component(hass, "mqtt", {"mqtt": {"broker": "bla"}}) await hass.async_block_till_done() - assert len(mock_finish_setup.mock_calls) == 1 + assert len(mock_finish_setup.mock_calls) == 0 mock_try_connection.return_value = True + # Start config flow result = await hass.config_entries.flow.async_init( "mqtt", context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "abort" + assert result["type"] == "form" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"broker": "127.0.0.1"} + ) + + assert result["type"] == "create_entry" + assert result["result"].data == { + "broker": "127.0.0.1", + "port": 1883, + "discovery": True, + } + # Check we tried the connection, with precedence for config entry settings + mock_try_connection.assert_called_once_with("127.0.0.1", 1883, None, None) + # Check config entry got setup + assert len(mock_finish_setup.mock_calls) == 1 async def test_user_single_instance(hass): diff --git a/tests/components/mqtt/test_cover.py b/tests/components/mqtt/test_cover.py index 59e03dadfe8..aad6fa5d9ca 100644 --- a/tests/components/mqtt/test_cover.py +++ b/tests/components/mqtt/test_cover.py @@ -64,6 +64,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -3173,6 +3174,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = cover.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 9ce5e54262e..5418727ec0e 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -51,6 +51,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -1803,3 +1804,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = fan.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = fan.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_humidifier.py b/tests/components/mqtt/test_humidifier.py index f8685898ed5..4aa5ff2350b 100644 --- a/tests/components/mqtt/test_humidifier.py +++ b/tests/components/mqtt/test_humidifier.py @@ -52,6 +52,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -1174,3 +1175,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = humidifier.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = humidifier.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 3d249fa2144..92884dcef93 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1134,6 +1134,8 @@ async def test_setup_without_tls_config_uses_tlsv1_under_python36(hass): mqtt.CONF_BIRTH_MESSAGE: { mqtt.ATTR_TOPIC: "birth", mqtt.ATTR_PAYLOAD: "birth", + mqtt.ATTR_QOS: 0, + mqtt.ATTR_RETAIN: False, }, } ], @@ -1162,6 +1164,8 @@ async def test_custom_birth_message(hass, mqtt_client_mock, mqtt_mock): mqtt.CONF_BIRTH_MESSAGE: { mqtt.ATTR_TOPIC: "homeassistant/status", mqtt.ATTR_PAYLOAD: "online", + mqtt.ATTR_QOS: 0, + mqtt.ATTR_RETAIN: False, }, } ], @@ -1205,6 +1209,8 @@ async def test_no_birth_message(hass, mqtt_client_mock, mqtt_mock): mqtt.CONF_BIRTH_MESSAGE: { mqtt.ATTR_TOPIC: "homeassistant/status", mqtt.ATTR_PAYLOAD: "online", + mqtt.ATTR_QOS: 0, + mqtt.ATTR_RETAIN: False, }, } ], @@ -1214,17 +1220,16 @@ async def test_delayed_birth_message(hass, mqtt_client_mock, mqtt_config): hass.state = CoreState.starting birth = asyncio.Event() - result = await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: mqtt_config}) - assert result await hass.async_block_till_done() - # Workaround: asynctest==0.13 fails on @functools.lru_cache - spec = dir(hass.data["mqtt"]) - spec.remove("_matching_subscriptions") + entry = MockConfigEntry(domain=mqtt.DOMAIN, data=mqtt_config) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() mqtt_component_mock = MagicMock( return_value=hass.data["mqtt"], - spec_set=spec, + spec_set=hass.data["mqtt"], wraps=hass.data["mqtt"], ) mqtt_component_mock._mqttc = mqtt_client_mock @@ -1261,6 +1266,8 @@ async def test_delayed_birth_message(hass, mqtt_client_mock, mqtt_config): mqtt.CONF_WILL_MESSAGE: { mqtt.ATTR_TOPIC: "death", mqtt.ATTR_PAYLOAD: "death", + mqtt.ATTR_QOS: 0, + mqtt.ATTR_RETAIN: False, }, } ], @@ -1317,9 +1324,28 @@ async def test_mqtt_subscribes_topics_on_connect(hass, mqtt_client_mock, mqtt_mo assert calls == expected -async def test_setup_fails_without_config(hass): - """Test if the MQTT component fails to load with no config.""" - assert not await async_setup_component(hass, mqtt.DOMAIN, {}) +async def test_setup_entry_with_config_override(hass, device_reg, mqtt_client_mock): + """Test if the MQTT component loads with no config and config entry can be setup.""" + data = ( + '{ "device":{"identifiers":["0AFFD2"]},' + ' "state_topic": "foobar/sensor",' + ' "unique_id": "unique" }' + ) + + # mqtt present in yaml config + assert await async_setup_component(hass, mqtt.DOMAIN, {}) + + # User sets up a config entry + entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + + # Discover a device to verify the entry was setup correctly + async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) + await hass.async_block_till_done() + + device_entry = device_reg.async_get_device({("mqtt", "0AFFD2")}) + assert device_entry is not None @pytest.mark.no_fail_on_log_exception diff --git a/tests/components/mqtt/test_legacy_vacuum.py b/tests/components/mqtt/test_legacy_vacuum.py index d6e1524fc40..1667053f65b 100644 --- a/tests/components/mqtt/test_legacy_vacuum.py +++ b/tests/components/mqtt/test_legacy_vacuum.py @@ -51,6 +51,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -840,6 +841,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = vacuum.DOMAIN + config = DEFAULT_CONFIG + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 0eb77990d32..782ee40f0c8 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -202,6 +202,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -3496,6 +3497,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = light.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value,init_payload", [ diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 8f2dce599ac..93bb9b0f573 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -127,6 +127,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -1991,6 +1992,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = light.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value,init_payload", [ diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index c68ed8e7f35..4461cf14ef4 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -65,6 +65,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -1181,6 +1182,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = light.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value,init_payload", [ diff --git a/tests/components/mqtt/test_lock.py b/tests/components/mqtt/test_lock.py index 35849c4f9fc..86e21a261a3 100644 --- a/tests/components/mqtt/test_lock.py +++ b/tests/components/mqtt/test_lock.py @@ -40,6 +40,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -646,6 +647,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = LOCK_DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_number.py b/tests/components/mqtt/test_number.py index 70bc1b40e75..73c1da357fa 100644 --- a/tests/components/mqtt/test_number.py +++ b/tests/components/mqtt/test_number.py @@ -46,6 +46,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -699,6 +700,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = number.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_scene.py b/tests/components/mqtt/test_scene.py index 97f13ba90c0..1ccacc1c5ee 100644 --- a/tests/components/mqtt/test_scene.py +++ b/tests/components/mqtt/test_scene.py @@ -19,6 +19,7 @@ from .test_common import ( help_test_discovery_update, help_test_discovery_update_unchanged, help_test_reloadable, + help_test_reloadable_late, help_test_unique_id, ) @@ -183,3 +184,10 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): domain = scene.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + + +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = scene.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) diff --git a/tests/components/mqtt/test_select.py b/tests/components/mqtt/test_select.py index f6f005bbfb5..069c0dfe4c9 100644 --- a/tests/components/mqtt/test_select.py +++ b/tests/components/mqtt/test_select.py @@ -37,6 +37,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -578,6 +579,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = select.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index b556bcf7537..8a1be6b11e2 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -45,6 +45,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_reload_with_config, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -972,6 +973,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = sensor.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + async def test_cleanup_triggers_and_restoring_state( hass, mqtt_mock, caplog, tmp_path, freezer ): diff --git a/tests/components/mqtt/test_siren.py b/tests/components/mqtt/test_siren.py index 7f174582a46..f39154badc9 100644 --- a/tests/components/mqtt/test_siren.py +++ b/tests/components/mqtt/test_siren.py @@ -38,6 +38,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -866,6 +867,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = siren.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_state_vacuum.py b/tests/components/mqtt/test_state_vacuum.py index 7b8928ccb32..8691aa73323 100644 --- a/tests/components/mqtt/test_state_vacuum.py +++ b/tests/components/mqtt/test_state_vacuum.py @@ -54,6 +54,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -607,6 +608,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = vacuum.DOMAIN + config = DEFAULT_CONFIG + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/components/mqtt/test_switch.py b/tests/components/mqtt/test_switch.py index 79ee56998e8..a458ac03baa 100644 --- a/tests/components/mqtt/test_switch.py +++ b/tests/components/mqtt/test_switch.py @@ -36,6 +36,7 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, + help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -557,6 +558,13 @@ async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) +async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): + """Test reloading the MQTT platform with late entry setup.""" + domain = switch.DOMAIN + config = DEFAULT_CONFIG[domain] + await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ diff --git a/tests/conftest.py b/tests/conftest.py index 9f0958e6ace..564480a0e91 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -38,6 +38,7 @@ pytest.register_assert_rewrite("tests.common") from tests.common import ( # noqa: E402, isort:skip CLIENT_ID, INSTANCES, + MockConfigEntry, MockUser, async_fire_mqtt_message, async_test_home_assistant, @@ -590,17 +591,22 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): if mqtt_config is None: mqtt_config = {mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: {}} - result = await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: mqtt_config}) - assert result await hass.async_block_till_done() - # Workaround: asynctest==0.13 fails on @functools.lru_cache - spec = dir(hass.data["mqtt"]) - spec.remove("_matching_subscriptions") + entry = MockConfigEntry( + data=mqtt_config, + domain=mqtt.DOMAIN, + title="Tasmota", + ) + + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() mqtt_component_mock = MagicMock( return_value=hass.data["mqtt"], - spec_set=spec, + spec_set=hass.data["mqtt"], wraps=hass.data["mqtt"], ) mqtt_component_mock._mqttc = mqtt_client_mock