diff --git a/homeassistant/components/baf/climate.py b/homeassistant/components/baf/climate.py index f785d18e06f..d4ed4ac4337 100644 --- a/homeassistant/components/baf/climate.py +++ b/homeassistant/components/baf/climate.py @@ -26,7 +26,7 @@ async def async_setup_entry( ) -> None: """Set up BAF fan auto comfort.""" data: BAFData = hass.data[DOMAIN][entry.entry_id] - if data.device.has_fan: + if data.device.has_fan and data.device.has_auto_comfort: async_add_entities( [BAFAutoComfort(data.device, f"{data.device.name} Auto Comfort")] ) diff --git a/homeassistant/components/baf/manifest.json b/homeassistant/components/baf/manifest.json index 9dfc35685e3..8143c35410e 100644 --- a/homeassistant/components/baf/manifest.json +++ b/homeassistant/components/baf/manifest.json @@ -3,7 +3,7 @@ "name": "Big Ass Fans", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/baf", - "requirements": ["aiobafi6==0.3.0"], + "requirements": ["aiobafi6==0.5.0"], "codeowners": ["@bdraco", "@jfroy"], "iot_class": "local_push", "zeroconf": [ diff --git a/homeassistant/components/baf/number.py b/homeassistant/components/baf/number.py index 84358e79669..32a3ea5e693 100644 --- a/homeassistant/components/baf/number.py +++ b/homeassistant/components/baf/number.py @@ -36,27 +36,7 @@ class BAFNumberDescription(NumberEntityDescription, BAFNumberDescriptionMixin): """Class describing BAF sensor entities.""" -FAN_NUMBER_DESCRIPTIONS = ( - BAFNumberDescription( - key="return_to_auto_timeout", - name="Return to Auto Timeout", - min_value=ONE_MIN_SECS, - max_value=HALF_DAY_SECS, - entity_category=EntityCategory.CONFIG, - unit_of_measurement=TIME_SECONDS, - value_fn=lambda device: cast(Optional[int], device.return_to_auto_timeout), - mode=NumberMode.SLIDER, - ), - BAFNumberDescription( - key="motion_sense_timeout", - name="Motion Sense Timeout", - min_value=ONE_MIN_SECS, - max_value=ONE_DAY_SECS, - entity_category=EntityCategory.CONFIG, - unit_of_measurement=TIME_SECONDS, - value_fn=lambda device: cast(Optional[int], device.motion_sense_timeout), - mode=NumberMode.SLIDER, - ), +AUTO_COMFORT_NUMBER_DESCRIPTIONS = ( BAFNumberDescription( key="comfort_min_speed", name="Auto Comfort Minimum Speed", @@ -86,6 +66,29 @@ FAN_NUMBER_DESCRIPTIONS = ( ), ) +FAN_NUMBER_DESCRIPTIONS = ( + BAFNumberDescription( + key="return_to_auto_timeout", + name="Return to Auto Timeout", + min_value=ONE_MIN_SECS, + max_value=HALF_DAY_SECS, + entity_category=EntityCategory.CONFIG, + unit_of_measurement=TIME_SECONDS, + value_fn=lambda device: cast(Optional[int], device.return_to_auto_timeout), + mode=NumberMode.SLIDER, + ), + BAFNumberDescription( + key="motion_sense_timeout", + name="Motion Sense Timeout", + min_value=ONE_MIN_SECS, + max_value=ONE_DAY_SECS, + entity_category=EntityCategory.CONFIG, + unit_of_measurement=TIME_SECONDS, + value_fn=lambda device: cast(Optional[int], device.motion_sense_timeout), + mode=NumberMode.SLIDER, + ), +) + LIGHT_NUMBER_DESCRIPTIONS = ( BAFNumberDescription( key="light_return_to_auto_timeout", @@ -125,6 +128,8 @@ async def async_setup_entry( descriptions.extend(FAN_NUMBER_DESCRIPTIONS) if device.has_light: descriptions.extend(LIGHT_NUMBER_DESCRIPTIONS) + if device.has_auto_comfort: + descriptions.extend(AUTO_COMFORT_NUMBER_DESCRIPTIONS) async_add_entities(BAFNumber(device, description) for description in descriptions) diff --git a/homeassistant/components/baf/sensor.py b/homeassistant/components/baf/sensor.py index 0f4239962cf..7b93b22fe2f 100644 --- a/homeassistant/components/baf/sensor.py +++ b/homeassistant/components/baf/sensor.py @@ -39,7 +39,7 @@ class BAFSensorDescription( """Class describing BAF sensor entities.""" -BASE_SENSORS = ( +AUTO_COMFORT_SENSORS = ( BAFSensorDescription( key="temperature", name="Temperature", @@ -103,10 +103,12 @@ async def async_setup_entry( """Set up BAF fan sensors.""" data: BAFData = hass.data[DOMAIN][entry.entry_id] device = data.device - sensors_descriptions = list(BASE_SENSORS) + sensors_descriptions: list[BAFSensorDescription] = [] for description in DEFINED_ONLY_SENSORS: if getattr(device, description.key): sensors_descriptions.append(description) + if device.has_auto_comfort: + sensors_descriptions.extend(AUTO_COMFORT_SENSORS) if device.has_fan: sensors_descriptions.extend(FAN_SENSORS) async_add_entities( diff --git a/homeassistant/components/baf/switch.py b/homeassistant/components/baf/switch.py index 6cefa0db65d..44671e68458 100644 --- a/homeassistant/components/baf/switch.py +++ b/homeassistant/components/baf/switch.py @@ -48,13 +48,16 @@ BASE_SWITCHES = [ ), ] -FAN_SWITCHES = [ +AUTO_COMFORT_SWITCHES = [ BAFSwitchDescription( key="comfort_heat_assist_enable", name="Auto Comfort Heat Assist", entity_category=EntityCategory.CONFIG, value_fn=lambda device: cast(Optional[bool], device.comfort_heat_assist_enable), ), +] + +FAN_SWITCHES = [ BAFSwitchDescription( key="fan_beep_enable", name="Beep", @@ -120,6 +123,8 @@ async def async_setup_entry( descriptions.extend(FAN_SWITCHES) if device.has_light: descriptions.extend(LIGHT_SWITCHES) + if device.has_auto_comfort: + descriptions.extend(AUTO_COMFORT_SWITCHES) async_add_entities(BAFSwitch(device, description) for description in descriptions) diff --git a/homeassistant/components/caldav/calendar.py b/homeassistant/components/caldav/calendar.py index c5b2b3a790a..17a8d5deb2f 100644 --- a/homeassistant/components/caldav/calendar.py +++ b/homeassistant/components/caldav/calendar.py @@ -1,7 +1,6 @@ """Support for WebDav Calendar.""" from __future__ import annotations -import copy from datetime import datetime, timedelta import logging import re @@ -143,15 +142,13 @@ class WebDavCalendarEntity(CalendarEntity): def update(self): """Update event data.""" self.data.update() - event = copy.deepcopy(self.data.event) - if event is None: - self._event = event - return - (summary, offset) = extract_offset(event.summary, OFFSET) - event.summary = summary - self._event = event + self._event = self.data.event self._attr_extra_state_attributes = { - "offset_reached": is_offset_reached(event.start_datetime_local, offset) + "offset_reached": is_offset_reached( + self._event.start_datetime_local, self.data.offset + ) + if self._event + else False } @@ -165,6 +162,7 @@ class WebDavCalendarData: self.include_all_day = include_all_day self.search = search self.event = None + self.offset = None async def async_get_events( self, hass: HomeAssistant, start_date: datetime, end_date: datetime @@ -264,13 +262,15 @@ class WebDavCalendarData: return # Populate the entity attributes with the event values + (summary, offset) = extract_offset(vevent.summary.value, OFFSET) self.event = CalendarEvent( - summary=vevent.summary.value, + summary=summary, start=vevent.dtstart.value, end=self.get_end_date(vevent), location=self.get_attr_value(vevent, "location"), description=self.get_attr_value(vevent, "description"), ) + self.offset = offset @staticmethod def is_matching(vevent, search): diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index a0a68aaf84a..81f00b69b23 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -39,7 +39,6 @@ class CloudGoogleConfig(AbstractConfig): self._cur_entity_prefs = self._prefs.google_entity_configs self._cur_default_expose = self._prefs.google_default_expose self._sync_entities_lock = asyncio.Lock() - self._sync_on_started = False @property def enabled(self): @@ -224,7 +223,7 @@ class CloudGoogleConfig(AbstractConfig): self._cur_entity_prefs = prefs.google_entity_configs self._cur_default_expose = prefs.google_default_expose - if sync_entities: + if sync_entities and self.hass.is_running: await self.async_sync_entities_all() @callback diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index d9e80b4eff8..7d07bbd543c 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220531.0"], + "requirements": ["home-assistant-frontend==20220601.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index 8153557422d..5466498fc32 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -73,7 +73,8 @@ class HistoryStats: # History cannot tell the future self._history_current_period = [] self._previous_run_before_start = True - + self._state = HistoryStatsState(None, None, self._period) + return self._state # # We avoid querying the database if the below did NOT happen: # @@ -82,7 +83,7 @@ class HistoryStats: # - The period shrank in size # - The previous period ended before now # - elif ( + if ( not self._previous_run_before_start and current_period_start_timestamp == previous_period_start_timestamp and ( @@ -117,10 +118,6 @@ class HistoryStats: ) self._previous_run_before_start = False - if not self._history_current_period: - self._state = HistoryStatsState(None, None, self._period) - return self._state - hours_matched, match_count = self._async_compute_hours_and_changes( now_timestamp, current_period_start_timestamp, diff --git a/homeassistant/components/hive/__init__.py b/homeassistant/components/hive/__init__.py index 292bbe62ae1..f3ed9674fcd 100644 --- a/homeassistant/components/hive/__init__.py +++ b/homeassistant/components/hive/__init__.py @@ -75,14 +75,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Hive from a config entry.""" - websession = aiohttp_client.async_get_clientsession(hass) + web_session = aiohttp_client.async_get_clientsession(hass) hive_config = dict(entry.data) - hive = Hive( - websession, - deviceGroupKey=hive_config["device_data"][0], - deviceKey=hive_config["device_data"][1], - devicePassword=hive_config["device_data"][2], - ) + hive = Hive(web_session) hive_config["options"] = {} hive_config["options"].update( diff --git a/homeassistant/components/hive/config_flow.py b/homeassistant/components/hive/config_flow.py index 9c391f13294..c713a3011f4 100644 --- a/homeassistant/components/hive/config_flow.py +++ b/homeassistant/components/hive/config_flow.py @@ -102,6 +102,7 @@ class HiveFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): raise UnknownHiveError # Setup the config entry + await self.hive_auth.device_registration("Home Assistant") self.data["tokens"] = self.tokens self.data["device_data"] = await self.hive_auth.getDeviceData() if self.context["source"] == config_entries.SOURCE_REAUTH: diff --git a/homeassistant/components/hive/manifest.json b/homeassistant/components/hive/manifest.json index 472adc137ba..d8cd56abe0b 100644 --- a/homeassistant/components/hive/manifest.json +++ b/homeassistant/components/hive/manifest.json @@ -3,7 +3,7 @@ "name": "Hive", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/hive", - "requirements": ["pyhiveapi==0.5.4"], + "requirements": ["pyhiveapi==0.5.5"], "codeowners": ["@Rendili", "@KJonline"], "iot_class": "cloud_polling", "loggers": ["apyhiveapi"] diff --git a/homeassistant/components/logbook/queries/common.py b/homeassistant/components/logbook/queries/common.py index 6049d6beb81..a7a4f84a59e 100644 --- a/homeassistant/components/logbook/queries/common.py +++ b/homeassistant/components/logbook/queries/common.py @@ -12,9 +12,11 @@ from sqlalchemy.sql.selectable import Select from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN from homeassistant.components.recorder.models import ( + EVENTS_CONTEXT_ID_INDEX, OLD_FORMAT_ATTRS_JSON, OLD_STATE, SHARED_ATTRS_JSON, + STATES_CONTEXT_ID_INDEX, EventData, Events, StateAttributes, @@ -121,9 +123,7 @@ def select_events_context_only() -> Select: By marking them as context_only we know they are only for linking context ids and we can avoid processing them. """ - return select(*EVENT_ROWS_NO_STATES, CONTEXT_ONLY).outerjoin( - EventData, (Events.data_id == EventData.data_id) - ) + return select(*EVENT_ROWS_NO_STATES, CONTEXT_ONLY) def select_states_context_only() -> Select: @@ -252,3 +252,17 @@ def _not_uom_attributes_matcher() -> ClauseList: return ~StateAttributes.shared_attrs.like( UNIT_OF_MEASUREMENT_JSON_LIKE ) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE) + + +def apply_states_context_hints(query: Query) -> Query: + """Force mysql to use the right index on large context_id selects.""" + return query.with_hint( + States, f"FORCE INDEX ({STATES_CONTEXT_ID_INDEX})", dialect_name="mysql" + ) + + +def apply_events_context_hints(query: Query) -> Query: + """Force mysql to use the right index on large context_id selects.""" + return query.with_hint( + Events, f"FORCE INDEX ({EVENTS_CONTEXT_ID_INDEX})", dialect_name="mysql" + ) diff --git a/homeassistant/components/logbook/queries/devices.py b/homeassistant/components/logbook/queries/devices.py index 64a6477017e..88e9f50a42c 100644 --- a/homeassistant/components/logbook/queries/devices.py +++ b/homeassistant/components/logbook/queries/devices.py @@ -4,15 +4,22 @@ from __future__ import annotations from collections.abc import Iterable from datetime import datetime as dt -from sqlalchemy import lambda_stmt, select, union_all +from sqlalchemy import lambda_stmt, select from sqlalchemy.orm import Query from sqlalchemy.sql.elements import ClauseList from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import CTE, CompoundSelect -from homeassistant.components.recorder.models import DEVICE_ID_IN_EVENT, Events, States +from homeassistant.components.recorder.models import ( + DEVICE_ID_IN_EVENT, + EventData, + Events, + States, +) from .common import ( + apply_events_context_hints, + apply_states_context_hints, select_events_context_id_subquery, select_events_context_only, select_events_without_states, @@ -27,13 +34,10 @@ def _select_device_id_context_ids_sub_query( json_quotable_device_ids: list[str], ) -> CompoundSelect: """Generate a subquery to find context ids for multiple devices.""" - return select( - union_all( - select_events_context_id_subquery(start_day, end_day, event_types).where( - apply_event_device_id_matchers(json_quotable_device_ids) - ), - ).c.context_id + inner = select_events_context_id_subquery(start_day, end_day, event_types).where( + apply_event_device_id_matchers(json_quotable_device_ids) ) + return select(inner.c.context_id).group_by(inner.c.context_id) def _apply_devices_context_union( @@ -51,8 +55,16 @@ def _apply_devices_context_union( json_quotable_device_ids, ).cte() return query.union_all( - select_events_context_only().where(Events.context_id.in_(devices_cte.select())), - select_states_context_only().where(States.context_id.in_(devices_cte.select())), + apply_events_context_hints( + select_events_context_only() + .select_from(devices_cte) + .outerjoin(Events, devices_cte.c.context_id == Events.context_id) + ).outerjoin(EventData, (Events.data_id == EventData.data_id)), + apply_states_context_hints( + select_states_context_only() + .select_from(devices_cte) + .outerjoin(States, devices_cte.c.context_id == States.context_id) + ), ) diff --git a/homeassistant/components/logbook/queries/entities.py b/homeassistant/components/logbook/queries/entities.py index 4fb211688f3..8de4a5eaf64 100644 --- a/homeassistant/components/logbook/queries/entities.py +++ b/homeassistant/components/logbook/queries/entities.py @@ -14,11 +14,14 @@ from homeassistant.components.recorder.models import ( ENTITY_ID_IN_EVENT, ENTITY_ID_LAST_UPDATED_INDEX, OLD_ENTITY_ID_IN_EVENT, + EventData, Events, States, ) from .common import ( + apply_events_context_hints, + apply_states_context_hints, apply_states_filters, select_events_context_id_subquery, select_events_context_only, @@ -36,16 +39,15 @@ def _select_entities_context_ids_sub_query( json_quotable_entity_ids: list[str], ) -> CompoundSelect: """Generate a subquery to find context ids for multiple entities.""" - return select( - union_all( - select_events_context_id_subquery(start_day, end_day, event_types).where( - apply_event_entity_id_matchers(json_quotable_entity_ids) - ), - apply_entities_hints(select(States.context_id)) - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id.in_(entity_ids)), - ).c.context_id + union = union_all( + select_events_context_id_subquery(start_day, end_day, event_types).where( + apply_event_entity_id_matchers(json_quotable_entity_ids) + ), + apply_entities_hints(select(States.context_id)) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id.in_(entity_ids)), ) + return select(union.c.context_id).group_by(union.c.context_id) def _apply_entities_context_union( @@ -64,14 +66,23 @@ def _apply_entities_context_union( entity_ids, json_quotable_entity_ids, ).cte() + # We used to optimize this to exclude rows we already in the union with + # a States.entity_id.not_in(entity_ids) but that made the + # query much slower on MySQL, and since we already filter them away + # in the python code anyways since they will have context_only + # set on them the impact is minimal. return query.union_all( states_query_for_entity_ids(start_day, end_day, entity_ids), - select_events_context_only().where( - Events.context_id.in_(entities_cte.select()) + apply_events_context_hints( + select_events_context_only() + .select_from(entities_cte) + .outerjoin(Events, entities_cte.c.context_id == Events.context_id) + ).outerjoin(EventData, (Events.data_id == EventData.data_id)), + apply_states_context_hints( + select_states_context_only() + .select_from(entities_cte) + .outerjoin(States, entities_cte.c.context_id == States.context_id) ), - select_states_context_only() - .where(States.entity_id.not_in(entity_ids)) - .where(States.context_id.in_(entities_cte.select())), ) diff --git a/homeassistant/components/logbook/queries/entities_and_devices.py b/homeassistant/components/logbook/queries/entities_and_devices.py index d1c86ddbec5..1c4271422b7 100644 --- a/homeassistant/components/logbook/queries/entities_and_devices.py +++ b/homeassistant/components/logbook/queries/entities_and_devices.py @@ -10,9 +10,11 @@ from sqlalchemy.orm import Query from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import CTE, CompoundSelect -from homeassistant.components.recorder.models import Events, States +from homeassistant.components.recorder.models import EventData, Events, States from .common import ( + apply_events_context_hints, + apply_states_context_hints, select_events_context_id_subquery, select_events_context_only, select_events_without_states, @@ -35,18 +37,17 @@ def _select_entities_device_id_context_ids_sub_query( json_quotable_device_ids: list[str], ) -> CompoundSelect: """Generate a subquery to find context ids for multiple entities and multiple devices.""" - return select( - union_all( - select_events_context_id_subquery(start_day, end_day, event_types).where( - _apply_event_entity_id_device_id_matchers( - json_quotable_entity_ids, json_quotable_device_ids - ) - ), - apply_entities_hints(select(States.context_id)) - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id.in_(entity_ids)), - ).c.context_id + union = union_all( + select_events_context_id_subquery(start_day, end_day, event_types).where( + _apply_event_entity_id_device_id_matchers( + json_quotable_entity_ids, json_quotable_device_ids + ) + ), + apply_entities_hints(select(States.context_id)) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id.in_(entity_ids)), ) + return select(union.c.context_id).group_by(union.c.context_id) def _apply_entities_devices_context_union( @@ -66,14 +67,23 @@ def _apply_entities_devices_context_union( json_quotable_entity_ids, json_quotable_device_ids, ).cte() + # We used to optimize this to exclude rows we already in the union with + # a States.entity_id.not_in(entity_ids) but that made the + # query much slower on MySQL, and since we already filter them away + # in the python code anyways since they will have context_only + # set on them the impact is minimal. return query.union_all( states_query_for_entity_ids(start_day, end_day, entity_ids), - select_events_context_only().where( - Events.context_id.in_(devices_entities_cte.select()) + apply_events_context_hints( + select_events_context_only() + .select_from(devices_entities_cte) + .outerjoin(Events, devices_entities_cte.c.context_id == Events.context_id) + ).outerjoin(EventData, (Events.data_id == EventData.data_id)), + apply_states_context_hints( + select_states_context_only() + .select_from(devices_entities_cte) + .outerjoin(States, devices_entities_cte.c.context_id == States.context_id) ), - select_states_context_only() - .where(States.entity_id.not_in(entity_ids)) - .where(States.context_id.in_(devices_entities_cte.select())), ) diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index 730d7ae1f9e..67675a44e22 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -110,7 +110,7 @@ def _state_schema(state): PLATFORM_SCHEMA = vol.Schema( vol.All( - mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( + mqtt.config.MQTT_BASE_SCHEMA.extend( { vol.Required(CONF_PLATFORM): "manual_mqtt", vol.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): cv.string, diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 1728dd7f2c7..e21885d2585 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -1,23 +1,13 @@ """Support for MQTT message handling.""" from __future__ import annotations -from ast import literal_eval import asyncio -from collections.abc import Awaitable, Callable +from collections.abc import Callable from dataclasses import dataclass import datetime as dt -from functools import lru_cache, partial, wraps -import inspect -from itertools import groupby import logging -from operator import attrgetter -import ssl -import time -from typing import TYPE_CHECKING, Any, Union, cast -import uuid +from typing import Any, cast -import attr -import certifi import jinja2 import voluptuous as vol @@ -25,120 +15,73 @@ from homeassistant import config_entries from homeassistant.components import websocket_api from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ATTR_ENTITY_ID, - ATTR_NAME, - CONF_CLIENT_ID, CONF_DISCOVERY, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, - CONF_PROTOCOL, CONF_USERNAME, - CONF_VALUE_TEMPLATE, - EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, SERVICE_RELOAD, - Platform, -) -from homeassistant.core import ( - CoreState, - Event, - HassJob, - HomeAssistant, - ServiceCall, - callback, ) +from homeassistant.core import Event, HassJob, HomeAssistant, ServiceCall, callback from homeassistant.data_entry_flow import BaseServiceInfo -from homeassistant.exceptions import HomeAssistantError, TemplateError, Unauthorized +from homeassistant.exceptions import TemplateError, Unauthorized from homeassistant.helpers import config_validation as cv, event, template from homeassistant.helpers.device_registry import DeviceEntry -from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.typing import ConfigType, TemplateVarsType -from homeassistant.loader import bind_hass -from homeassistant.util import dt as dt_util -from homeassistant.util.async_ import run_callback_threadsafe -from homeassistant.util.logging import catch_log_exception +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.typing import ConfigType # Loading the config flow file will register the flow from . import debug_info, discovery -from .const import ( +from .client import ( # noqa: F401 + MQTT, + async_publish, + async_subscribe, + publish, + subscribe, +) +from .config import CONFIG_SCHEMA_BASE, DEFAULT_VALUES, DEPRECATED_CONFIG_KEYS +from .const import ( # noqa: F401 ATTR_PAYLOAD, ATTR_QOS, ATTR_RETAIN, ATTR_TOPIC, CONF_BIRTH_MESSAGE, CONF_BROKER, - CONF_CERTIFICATE, - CONF_CLIENT_CERT, - CONF_CLIENT_KEY, CONF_COMMAND_TOPIC, - CONF_ENCODING, + CONF_DISCOVERY_PREFIX, CONF_QOS, - CONF_RETAIN, CONF_STATE_TOPIC, - CONF_TLS_INSECURE, CONF_TLS_VERSION, CONF_TOPIC, CONF_WILL_MESSAGE, CONFIG_ENTRY_IS_SETUP, DATA_CONFIG_ENTRY_LOCK, + DATA_MQTT, DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_NEEDED, - DEFAULT_BIRTH, - DEFAULT_DISCOVERY, DEFAULT_ENCODING, - DEFAULT_PREFIX, DEFAULT_QOS, DEFAULT_RETAIN, - DEFAULT_WILL, DOMAIN, MQTT_CONNECTED, MQTT_DISCONNECTED, - PROTOCOL_31, - PROTOCOL_311, + PLATFORMS, ) -from .discovery import LAST_DISCOVERY -from .models import ( - AsyncMessageCallbackType, - MessageCallbackType, - PublishMessage, +from .models import ( # noqa: F401 + MqttCommandTemplate, + MqttValueTemplate, PublishPayloadType, ReceiveMessage, ReceivePayloadType, ) from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic -if TYPE_CHECKING: - # Only import for paho-mqtt type checking here, imports are done locally - # because integrations should be able to optionally rely on MQTT. - import paho.mqtt.client as mqtt - _LOGGER = logging.getLogger(__name__) -_SENTINEL = object() - -DATA_MQTT = "mqtt" - SERVICE_PUBLISH = "publish" SERVICE_DUMP = "dump" -CONF_DISCOVERY_PREFIX = "discovery_prefix" -CONF_KEEPALIVE = "keepalive" - -DEFAULT_PORT = 1883 -DEFAULT_KEEPALIVE = 60 -DEFAULT_PROTOCOL = PROTOCOL_311 -DEFAULT_TLS_PROTOCOL = "auto" - -DEFAULT_VALUES = { - CONF_BIRTH_MESSAGE: DEFAULT_BIRTH, - CONF_DISCOVERY: DEFAULT_DISCOVERY, - CONF_PORT: DEFAULT_PORT, - CONF_TLS_VERSION: DEFAULT_TLS_PROTOCOL, - CONF_WILL_MESSAGE: DEFAULT_WILL, -} - MANDATORY_DEFAULT_VALUES = (CONF_PORT,) ATTR_TOPIC_TEMPLATE = "topic_template" @@ -150,93 +93,6 @@ CONNECTION_SUCCESS = "connection_success" CONNECTION_FAILED = "connection_failed" CONNECTION_FAILED_RECOVERABLE = "connection_failed_recoverable" -DISCOVERY_COOLDOWN = 2 -TIMEOUT_ACK = 10 - -PLATFORMS = [ - Platform.ALARM_CONTROL_PANEL, - Platform.BINARY_SENSOR, - Platform.BUTTON, - Platform.CAMERA, - Platform.CLIMATE, - Platform.DEVICE_TRACKER, - Platform.COVER, - Platform.FAN, - Platform.HUMIDIFIER, - Platform.LIGHT, - Platform.LOCK, - Platform.NUMBER, - Platform.SELECT, - Platform.SCENE, - Platform.SENSOR, - Platform.SIREN, - Platform.SWITCH, - Platform.VACUUM, -] - -CLIENT_KEY_AUTH_MSG = ( - "client_key and client_cert must both be present in " - "the MQTT broker configuration" -) - -MQTT_WILL_BIRTH_SCHEMA = vol.Schema( - { - vol.Inclusive(ATTR_TOPIC, "topic_payload"): valid_publish_topic, - vol.Inclusive(ATTR_PAYLOAD, "topic_payload"): cv.string, - vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, - vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean, - }, - required=True, -) - -PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( - {vol.Optional(platform.value): cv.ensure_list for platform in PLATFORMS} -) - -CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( - { - vol.Optional(CONF_CLIENT_ID): cv.string, - vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All( - vol.Coerce(int), vol.Range(min=15) - ), - vol.Optional(CONF_BROKER): cv.string, - vol.Optional(CONF_PORT): cv.port, - vol.Optional(CONF_USERNAME): cv.string, - vol.Optional(CONF_PASSWORD): cv.string, - vol.Optional(CONF_CERTIFICATE): vol.Any("auto", cv.isfile), - vol.Inclusive( - CONF_CLIENT_KEY, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG - ): cv.isfile, - vol.Inclusive( - CONF_CLIENT_CERT, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG - ): cv.isfile, - vol.Optional(CONF_TLS_INSECURE): cv.boolean, - vol.Optional(CONF_TLS_VERSION): vol.Any("auto", "1.0", "1.1", "1.2"), - vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All( - cv.string, vol.In([PROTOCOL_31, PROTOCOL_311]) - ), - vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, - vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, - vol.Optional(CONF_DISCOVERY): cv.boolean, - # discovery_prefix must be a valid publish topic because if no - # state topic is specified, it will be created with the given prefix. - vol.Optional( - CONF_DISCOVERY_PREFIX, default=DEFAULT_PREFIX - ): valid_publish_topic, - } -) - -DEPRECATED_CONFIG_KEYS = [ - CONF_BIRTH_MESSAGE, - CONF_BROKER, - CONF_DISCOVERY, - CONF_PASSWORD, - CONF_PORT, - CONF_TLS_VERSION, - CONF_USERNAME, - CONF_WILL_MESSAGE, -] - CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.All( @@ -254,47 +110,6 @@ CONFIG_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) -SCHEMA_BASE = { - vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, - vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, -} - -MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE) - -# Will be removed when all platforms support a modern platform schema -MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE) -# Will be removed when all platforms support a modern platform schema -MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - } -) -# Will be removed when all platforms support a modern platform schema -MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, - vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, - vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, - } -) - -# Sensor type platforms subscribe to MQTT events -MQTT_RO_SCHEMA = MQTT_BASE_SCHEMA.extend( - { - vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - } -) - -# Switch type platforms publish to MQTT and may subscribe -MQTT_RW_SCHEMA = MQTT_BASE_SCHEMA.extend( - { - vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, - vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, - vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, - } -) # Service call validation schema MQTT_PUBLISH_SCHEMA = vol.All( @@ -313,124 +128,6 @@ MQTT_PUBLISH_SCHEMA = vol.All( ) -SubscribePayloadType = Union[str, bytes] # Only bytes if encoding is None - - -class MqttCommandTemplate: - """Class for rendering MQTT payload with command templates.""" - - def __init__( - self, - command_template: template.Template | None, - *, - hass: HomeAssistant | None = None, - entity: Entity | None = None, - ) -> None: - """Instantiate a command template.""" - self._attr_command_template = command_template - if command_template is None: - return - - self._entity = entity - - command_template.hass = hass - - if entity: - command_template.hass = entity.hass - - @callback - def async_render( - self, - value: PublishPayloadType = None, - variables: TemplateVarsType = None, - ) -> PublishPayloadType: - """Render or convert the command template with given value or variables.""" - - def _convert_outgoing_payload( - payload: PublishPayloadType, - ) -> PublishPayloadType: - """Ensure correct raw MQTT payload is passed as bytes for publishing.""" - if isinstance(payload, str): - try: - native_object = literal_eval(payload) - if isinstance(native_object, bytes): - return native_object - - except (ValueError, TypeError, SyntaxError, MemoryError): - pass - - return payload - - if self._attr_command_template is None: - return value - - values = {"value": value} - if self._entity: - values[ATTR_ENTITY_ID] = self._entity.entity_id - values[ATTR_NAME] = self._entity.name - if variables is not None: - values.update(variables) - return _convert_outgoing_payload( - self._attr_command_template.async_render(values, parse_result=False) - ) - - -class MqttValueTemplate: - """Class for rendering MQTT value template with possible json values.""" - - def __init__( - self, - value_template: template.Template | None, - *, - hass: HomeAssistant | None = None, - entity: Entity | None = None, - config_attributes: TemplateVarsType = None, - ) -> None: - """Instantiate a value template.""" - self._value_template = value_template - self._config_attributes = config_attributes - if value_template is None: - return - - value_template.hass = hass - self._entity = entity - - if entity: - value_template.hass = entity.hass - - @callback - def async_render_with_possible_json_value( - self, - payload: ReceivePayloadType, - default: ReceivePayloadType | object = _SENTINEL, - variables: TemplateVarsType = None, - ) -> ReceivePayloadType: - """Render with possible json value or pass-though a received MQTT value.""" - if self._value_template is None: - return payload - - values: dict[str, Any] = {} - - if variables is not None: - values.update(variables) - - if self._config_attributes is not None: - values.update(self._config_attributes) - - if self._entity: - values[ATTR_ENTITY_ID] = self._entity.entity_id - values[ATTR_NAME] = self._entity.name - - if default == _SENTINEL: - return self._value_template.async_render_with_possible_json_value( - payload, variables=values - ) - - return self._value_template.async_render_with_possible_json_value( - payload, default, variables=values - ) - - @dataclass class MqttServiceInfo(BaseServiceInfo): """Prepared info from mqtt entries.""" @@ -443,163 +140,6 @@ class MqttServiceInfo(BaseServiceInfo): timestamp: dt.datetime -def publish( - hass: HomeAssistant, - topic: str, - payload: PublishPayloadType, - qos: int | None = 0, - retain: bool | None = False, - encoding: str | None = DEFAULT_ENCODING, -) -> None: - """Publish message to a MQTT topic.""" - hass.add_job(async_publish, hass, topic, payload, qos, retain, encoding) - - -async def async_publish( - hass: HomeAssistant, - topic: str, - payload: PublishPayloadType, - qos: int | None = 0, - retain: bool | None = False, - encoding: str | None = DEFAULT_ENCODING, -) -> None: - """Publish message to a MQTT topic.""" - - outgoing_payload = payload - if not isinstance(payload, bytes): - if not encoding: - _LOGGER.error( - "Can't pass-through payload for publishing %s on %s with no encoding set, need 'bytes' got %s", - payload, - topic, - type(payload), - ) - return - outgoing_payload = str(payload) - if encoding != DEFAULT_ENCODING: - # a string is encoded as utf-8 by default, other encoding requires bytes as payload - try: - outgoing_payload = outgoing_payload.encode(encoding) - except (AttributeError, LookupError, UnicodeEncodeError): - _LOGGER.error( - "Can't encode payload for publishing %s on %s with encoding %s", - payload, - topic, - encoding, - ) - return - - await hass.data[DATA_MQTT].async_publish(topic, outgoing_payload, qos, retain) - - -AsyncDeprecatedMessageCallbackType = Callable[ - [str, ReceivePayloadType, int], Awaitable[None] -] -DeprecatedMessageCallbackType = Callable[[str, ReceivePayloadType, int], None] - - -def wrap_msg_callback( - msg_callback: AsyncDeprecatedMessageCallbackType | DeprecatedMessageCallbackType, -) -> AsyncMessageCallbackType | MessageCallbackType: - """Wrap an MQTT message callback to support deprecated signature.""" - # Check for partials to properly determine if coroutine function - check_func = msg_callback - while isinstance(check_func, partial): - check_func = check_func.func - - wrapper_func: AsyncMessageCallbackType | MessageCallbackType - if asyncio.iscoroutinefunction(check_func): - - @wraps(msg_callback) - async def async_wrapper(msg: ReceiveMessage) -> None: - """Call with deprecated signature.""" - await cast(AsyncDeprecatedMessageCallbackType, msg_callback)( - msg.topic, msg.payload, msg.qos - ) - - wrapper_func = async_wrapper - else: - - @wraps(msg_callback) - def wrapper(msg: ReceiveMessage) -> None: - """Call with deprecated signature.""" - msg_callback(msg.topic, msg.payload, msg.qos) - - wrapper_func = wrapper - return wrapper_func - - -@bind_hass -async def async_subscribe( - hass: HomeAssistant, - topic: str, - msg_callback: AsyncMessageCallbackType - | MessageCallbackType - | DeprecatedMessageCallbackType - | AsyncDeprecatedMessageCallbackType, - qos: int = DEFAULT_QOS, - encoding: str | None = "utf-8", -): - """Subscribe to an MQTT topic. - - Call the return value to unsubscribe. - """ - # Count callback parameters which don't have a default value - non_default = 0 - if msg_callback: - non_default = sum( - p.default == inspect.Parameter.empty - for _, p in inspect.signature(msg_callback).parameters.items() - ) - - wrapped_msg_callback = msg_callback - # If we have 3 parameters with no default value, wrap the callback - if non_default == 3: - module = inspect.getmodule(msg_callback) - _LOGGER.warning( - "Signature of MQTT msg_callback '%s.%s' is deprecated", - module.__name__ if module else "", - msg_callback.__name__, - ) - wrapped_msg_callback = wrap_msg_callback( - cast(DeprecatedMessageCallbackType, msg_callback) - ) - - async_remove = await hass.data[DATA_MQTT].async_subscribe( - topic, - catch_log_exception( - wrapped_msg_callback, - lambda msg: ( - f"Exception in {msg_callback.__name__} when handling msg on " - f"'{msg.topic}': '{msg.payload}'" - ), - ), - qos, - encoding, - ) - return async_remove - - -@bind_hass -def subscribe( - hass: HomeAssistant, - topic: str, - msg_callback: MessageCallbackType, - qos: int = DEFAULT_QOS, - encoding: str = "utf-8", -) -> Callable[[], None]: - """Subscribe to an MQTT topic.""" - async_remove = asyncio.run_coroutine_threadsafe( - async_subscribe(hass, topic, msg_callback, qos, encoding), hass.loop - ).result() - - def remove(): - """Remove listener convert.""" - run_callback_threadsafe(hass.loop, async_remove).result() - - return remove - - async def _async_setup_discovery( hass: HomeAssistant, conf: ConfigType, config_entry ) -> None: @@ -667,6 +207,26 @@ def _merge_extended_config(entry, conf): return {**conf, **entry.data} +async def _async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Handle signals of config entry being updated. + + Causes for this is config entry options changing. + """ + mqtt_client = hass.data[DATA_MQTT] + + if (conf := hass.data.get(DATA_MQTT_CONFIG)) is None: + conf = CONFIG_SCHEMA_BASE(dict(entry.data)) + + mqtt_client.conf = _merge_extended_config(entry, conf) + await mqtt_client.async_disconnect() + mqtt_client.init_client() + await mqtt_client.async_connect() + + await discovery.async_stop(hass) + if mqtt_client.conf.get(CONF_DISCOVERY): + await _async_setup_discovery(hass, mqtt_client.conf, entry) + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Load a config entry.""" # Merge basic configuration, and add missing defaults for basic options @@ -703,6 +263,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry, conf, ) + entry.add_update_listener(_async_config_entry_updated) await hass.data[DATA_MQTT].async_connect() @@ -831,459 +392,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -@attr.s(slots=True, frozen=True) -class Subscription: - """Class to hold data about an active subscription.""" - - topic: str = attr.ib() - matcher: Any = attr.ib() - job: HassJob = attr.ib() - qos: int = attr.ib(default=0) - encoding: str | None = attr.ib(default="utf-8") - - -class MqttClientSetup: - """Helper class to setup the paho mqtt client from config.""" - - def __init__(self, config: ConfigType) -> None: - """Initialize the MQTT client setup helper.""" - - # We don't import on the top because some integrations - # should be able to optionally rely on MQTT. - import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel - - if config[CONF_PROTOCOL] == PROTOCOL_31: - proto = mqtt.MQTTv31 - else: - proto = mqtt.MQTTv311 - - if (client_id := config.get(CONF_CLIENT_ID)) is None: - # PAHO MQTT relies on the MQTT server to generate random client IDs. - # However, that feature is not mandatory so we generate our own. - client_id = mqtt.base62(uuid.uuid4().int, padding=22) - self._client = mqtt.Client(client_id, protocol=proto) - - # Enable logging - self._client.enable_logger() - - username = config.get(CONF_USERNAME) - password = config.get(CONF_PASSWORD) - if username is not None: - self._client.username_pw_set(username, password) - - if (certificate := config.get(CONF_CERTIFICATE)) == "auto": - certificate = certifi.where() - - client_key = config.get(CONF_CLIENT_KEY) - client_cert = config.get(CONF_CLIENT_CERT) - tls_insecure = config.get(CONF_TLS_INSECURE) - if certificate is not None: - self._client.tls_set( - certificate, - certfile=client_cert, - keyfile=client_key, - tls_version=ssl.PROTOCOL_TLS, - ) - - if tls_insecure is not None: - self._client.tls_insecure_set(tls_insecure) - - @property - def client(self) -> mqtt.Client: - """Return the paho MQTT client.""" - return self._client - - -class MQTT: - """Home Assistant MQTT client.""" - - def __init__( - self, - hass: HomeAssistant, - config_entry, - conf, - ) -> None: - """Initialize Home Assistant MQTT client.""" - # We don't import on the top because some integrations - # should be able to optionally rely on MQTT. - import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel - - self.hass = hass - self.config_entry = config_entry - self.conf = conf - self.subscriptions: list[Subscription] = [] - self.connected = False - self._ha_started = asyncio.Event() - self._last_subscribe = time.time() - self._mqttc: mqtt.Client = None - self._paho_lock = asyncio.Lock() - - self._pending_operations: dict[str, asyncio.Event] = {} - - if self.hass.state == CoreState.running: - self._ha_started.set() - else: - - @callback - def ha_started(_): - self._ha_started.set() - - self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, ha_started) - - self.init_client() - self.config_entry.add_update_listener(self.async_config_entry_updated) - - @staticmethod - async def async_config_entry_updated( - hass: HomeAssistant, entry: ConfigEntry - ) -> None: - """Handle signals of config entry being updated. - - This is a static method because a class method (bound method), can not be used with weak references. - Causes for this is config entry options changing. - """ - self = hass.data[DATA_MQTT] - - if (conf := hass.data.get(DATA_MQTT_CONFIG)) is None: - conf = CONFIG_SCHEMA_BASE(dict(entry.data)) - - self.conf = _merge_extended_config(entry, conf) - await self.async_disconnect() - self.init_client() - await self.async_connect() - - await discovery.async_stop(hass) - if self.conf.get(CONF_DISCOVERY): - await _async_setup_discovery(hass, self.conf, entry) - - def init_client(self): - """Initialize paho client.""" - self._mqttc = MqttClientSetup(self.conf).client - self._mqttc.on_connect = self._mqtt_on_connect - self._mqttc.on_disconnect = self._mqtt_on_disconnect - self._mqttc.on_message = self._mqtt_on_message - self._mqttc.on_publish = self._mqtt_on_callback - self._mqttc.on_subscribe = self._mqtt_on_callback - self._mqttc.on_unsubscribe = self._mqtt_on_callback - - if ( - CONF_WILL_MESSAGE in self.conf - and ATTR_TOPIC in self.conf[CONF_WILL_MESSAGE] - ): - will_message = PublishMessage(**self.conf[CONF_WILL_MESSAGE]) - else: - will_message = None - - if will_message is not None: - self._mqttc.will_set( - topic=will_message.topic, - payload=will_message.payload, - qos=will_message.qos, - retain=will_message.retain, - ) - - async def async_publish( - self, topic: str, payload: PublishPayloadType, qos: int, retain: bool - ) -> None: - """Publish a MQTT message.""" - async with self._paho_lock: - msg_info = await self.hass.async_add_executor_job( - self._mqttc.publish, topic, payload, qos, retain - ) - _LOGGER.debug( - "Transmitting message on %s: '%s', mid: %s", - topic, - payload, - msg_info.mid, - ) - _raise_on_error(msg_info.rc) - await self._wait_for_mid(msg_info.mid) - - async def async_connect(self) -> None: - """Connect to the host. Does not process messages yet.""" - # pylint: disable-next=import-outside-toplevel - import paho.mqtt.client as mqtt - - result: int | None = None - try: - result = await self.hass.async_add_executor_job( - self._mqttc.connect, - self.conf[CONF_BROKER], - self.conf[CONF_PORT], - self.conf[CONF_KEEPALIVE], - ) - except OSError as err: - _LOGGER.error("Failed to connect to MQTT server due to exception: %s", err) - - if result is not None and result != 0: - _LOGGER.error( - "Failed to connect to MQTT server: %s", mqtt.error_string(result) - ) - - self._mqttc.loop_start() - - async def async_disconnect(self): - """Stop the MQTT client.""" - - def stop(): - """Stop the MQTT client.""" - # Do not disconnect, we want the broker to always publish will - self._mqttc.loop_stop() - - await self.hass.async_add_executor_job(stop) - - async def async_subscribe( - self, - topic: str, - msg_callback: MessageCallbackType, - qos: int, - encoding: str | None = None, - ) -> Callable[[], None]: - """Set up a subscription to a topic with the provided qos. - - This method is a coroutine. - """ - if not isinstance(topic, str): - raise HomeAssistantError("Topic needs to be a string!") - - subscription = Subscription( - topic, _matcher_for_topic(topic), HassJob(msg_callback), qos, encoding - ) - self.subscriptions.append(subscription) - self._matching_subscriptions.cache_clear() - - # Only subscribe if currently connected. - if self.connected: - self._last_subscribe = time.time() - await self._async_perform_subscription(topic, qos) - - @callback - def async_remove() -> None: - """Remove subscription.""" - if subscription not in self.subscriptions: - raise HomeAssistantError("Can't remove subscription twice") - self.subscriptions.remove(subscription) - self._matching_subscriptions.cache_clear() - - # Only unsubscribe if currently connected. - if self.connected: - self.hass.async_create_task(self._async_unsubscribe(topic)) - - return async_remove - - async def _async_unsubscribe(self, topic: str) -> None: - """Unsubscribe from a topic. - - This method is a coroutine. - """ - if any(other.topic == topic for other in self.subscriptions): - # Other subscriptions on topic remaining - don't unsubscribe. - return - - async with self._paho_lock: - result: int | None = None - result, mid = await self.hass.async_add_executor_job( - self._mqttc.unsubscribe, topic - ) - _LOGGER.debug("Unsubscribing from %s, mid: %s", topic, mid) - _raise_on_error(result) - await self._wait_for_mid(mid) - - async def _async_perform_subscription(self, topic: str, qos: int) -> None: - """Perform a paho-mqtt subscription.""" - async with self._paho_lock: - result: int | None = None - result, mid = await self.hass.async_add_executor_job( - self._mqttc.subscribe, topic, qos - ) - _LOGGER.debug("Subscribing to %s, mid: %s", topic, mid) - _raise_on_error(result) - await self._wait_for_mid(mid) - - def _mqtt_on_connect(self, _mqttc, _userdata, _flags, result_code: int) -> None: - """On connect callback. - - Resubscribe to all topics we were subscribed to and publish birth - message. - """ - # pylint: disable-next=import-outside-toplevel - import paho.mqtt.client as mqtt - - if result_code != mqtt.CONNACK_ACCEPTED: - _LOGGER.error( - "Unable to connect to the MQTT broker: %s", - mqtt.connack_string(result_code), - ) - return - - self.connected = True - dispatcher_send(self.hass, MQTT_CONNECTED) - _LOGGER.info( - "Connected to MQTT server %s:%s (%s)", - self.conf[CONF_BROKER], - self.conf[CONF_PORT], - result_code, - ) - - # Group subscriptions to only re-subscribe once for each topic. - keyfunc = attrgetter("topic") - for topic, subs in groupby(sorted(self.subscriptions, key=keyfunc), keyfunc): - # Re-subscribe with the highest requested qos - max_qos = max(subscription.qos for subscription in subs) - self.hass.add_job(self._async_perform_subscription, topic, max_qos) - - if ( - CONF_BIRTH_MESSAGE in self.conf - and ATTR_TOPIC in self.conf[CONF_BIRTH_MESSAGE] - ): - - async def publish_birth_message(birth_message): - await self._ha_started.wait() # Wait for Home Assistant to start - await self._discovery_cooldown() # Wait for MQTT discovery to cool down - await self.async_publish( - topic=birth_message.topic, - payload=birth_message.payload, - qos=birth_message.qos, - retain=birth_message.retain, - ) - - birth_message = PublishMessage(**self.conf[CONF_BIRTH_MESSAGE]) - asyncio.run_coroutine_threadsafe( - publish_birth_message(birth_message), self.hass.loop - ) - - def _mqtt_on_message(self, _mqttc, _userdata, msg) -> None: - """Message received callback.""" - self.hass.add_job(self._mqtt_handle_message, msg) - - @lru_cache(2048) - def _matching_subscriptions(self, topic): - subscriptions = [] - for subscription in self.subscriptions: - if subscription.matcher(topic): - subscriptions.append(subscription) - return subscriptions - - @callback - def _mqtt_handle_message(self, msg) -> None: - _LOGGER.debug( - "Received message on %s%s: %s", - msg.topic, - " (retained)" if msg.retain else "", - msg.payload[0:8192], - ) - timestamp = dt_util.utcnow() - - subscriptions = self._matching_subscriptions(msg.topic) - - for subscription in subscriptions: - - payload: SubscribePayloadType = msg.payload - if subscription.encoding is not None: - try: - payload = msg.payload.decode(subscription.encoding) - except (AttributeError, UnicodeDecodeError): - _LOGGER.warning( - "Can't decode payload %s on %s with encoding %s (for %s)", - msg.payload[0:8192], - msg.topic, - subscription.encoding, - subscription.job, - ) - continue - - self.hass.async_run_hass_job( - subscription.job, - ReceiveMessage( - msg.topic, - payload, - msg.qos, - msg.retain, - subscription.topic, - timestamp, - ), - ) - - def _mqtt_on_callback(self, _mqttc, _userdata, mid, _granted_qos=None) -> None: - """Publish / Subscribe / Unsubscribe callback.""" - self.hass.add_job(self._mqtt_handle_mid, mid) - - @callback - def _mqtt_handle_mid(self, mid) -> None: - # Create the mid event if not created, either _mqtt_handle_mid or _wait_for_mid - # may be executed first. - if mid not in self._pending_operations: - self._pending_operations[mid] = asyncio.Event() - self._pending_operations[mid].set() - - def _mqtt_on_disconnect(self, _mqttc, _userdata, result_code: int) -> None: - """Disconnected callback.""" - self.connected = False - dispatcher_send(self.hass, MQTT_DISCONNECTED) - _LOGGER.warning( - "Disconnected from MQTT server %s:%s (%s)", - self.conf[CONF_BROKER], - self.conf[CONF_PORT], - result_code, - ) - - async def _wait_for_mid(self, mid): - """Wait for ACK from broker.""" - # Create the mid event if not created, either _mqtt_handle_mid or _wait_for_mid - # may be executed first. - if mid not in self._pending_operations: - self._pending_operations[mid] = asyncio.Event() - try: - await asyncio.wait_for(self._pending_operations[mid].wait(), TIMEOUT_ACK) - except asyncio.TimeoutError: - _LOGGER.warning( - "No ACK from MQTT server in %s seconds (mid: %s)", TIMEOUT_ACK, mid - ) - finally: - del self._pending_operations[mid] - - async def _discovery_cooldown(self): - now = time.time() - # Reset discovery and subscribe cooldowns - self.hass.data[LAST_DISCOVERY] = now - self._last_subscribe = now - - last_discovery = self.hass.data[LAST_DISCOVERY] - last_subscribe = self._last_subscribe - wait_until = max( - last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN - ) - while now < wait_until: - await asyncio.sleep(wait_until - now) - now = time.time() - last_discovery = self.hass.data[LAST_DISCOVERY] - last_subscribe = self._last_subscribe - wait_until = max( - last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN - ) - - -def _raise_on_error(result_code: int | None) -> None: - """Raise error if error result.""" - # pylint: disable-next=import-outside-toplevel - import paho.mqtt.client as mqtt - - if result_code is not None and result_code != 0: - raise HomeAssistantError( - f"Error talking to MQTT: {mqtt.error_string(result_code)}" - ) - - -def _matcher_for_topic(subscription: str) -> Any: - # pylint: disable-next=import-outside-toplevel - from paho.mqtt.matcher import MQTTMatcher - - matcher = MQTTMatcher() - matcher[subscription] = True - - return lambda topic: next(matcher.iter_match(topic), False) - - @websocket_api.websocket_command( {vol.Required("type"): "mqtt/device/debug_info", vol.Required("device_id"): str} ) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 06c013ec744..c0c6f9732d7 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -1,7 +1,6 @@ """This platform enables the possibility to control a MQTT alarm.""" from __future__ import annotations -import asyncio import functools import logging import re @@ -31,8 +30,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import DEFAULT_RETAIN, MQTT_BASE_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -45,11 +44,13 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate +from .util import valid_publish_topic, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -85,7 +86,7 @@ DEFAULT_NAME = "MQTT Alarm" REMOTE_CODE = "REMOTE_CODE" REMOTE_CODE_TEXT = "REMOTE_CODE_TEXT" -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_CODE): cv.string, vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, @@ -94,7 +95,7 @@ PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( vol.Optional( CONF_COMMAND_TEMPLATE, default=DEFAULT_COMMAND_TEMPLATE ): cv.template, - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string, vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string, @@ -107,8 +108,8 @@ PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( ): cv.string, vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string, vol.Optional(CONF_PAYLOAD_TRIGGER, default=DEFAULT_TRIGGER): cv.string, - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, - vol.Required(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -131,7 +132,11 @@ async def async_setup_platform( """Set up MQTT alarm control panel configured under the alarm_control_panel key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, alarm.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + alarm.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -142,13 +147,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT alarm control panel through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, alarm.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, alarm.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index b9ab190cc9b..cec065e20f2 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -1,7 +1,6 @@ """Support for MQTT binary sensors.""" from __future__ import annotations -import asyncio from datetime import timedelta import functools import logging @@ -34,19 +33,20 @@ from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util -from . import MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RO_SCHEMA 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_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttValueTemplate _LOGGER = logging.getLogger(__name__) @@ -57,7 +57,7 @@ DEFAULT_PAYLOAD_ON = "ON" DEFAULT_FORCE_UPDATE = False CONF_EXPIRE_AFTER = "expire_after" -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RO_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, @@ -87,7 +87,11 @@ async def async_setup_platform( """Set up MQTT binary sensor configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, binary_sensor.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + binary_sensor.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -98,12 +102,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT binary sensor through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, binary_sensor.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, binary_sensor.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index 47e96ff3e1a..afa9900db35 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -1,7 +1,6 @@ """Support for MQTT buttons.""" from __future__ import annotations -import asyncio import functools import voluptuous as vol @@ -15,8 +14,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate -from .. import mqtt +from .config import DEFAULT_RETAIN, MQTT_BASE_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -27,24 +25,26 @@ from .const import ( from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate +from .util import valid_publish_topic CONF_PAYLOAD_PRESS = "payload_press" DEFAULT_NAME = "MQTT Button" DEFAULT_PAYLOAD_PRESS = "PRESS" -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_DEVICE_CLASS): button.DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_PRESS, default=DEFAULT_PAYLOAD_PRESS): cv.string, - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -67,7 +67,11 @@ async def async_setup_platform( """Set up MQTT button configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, button.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + button.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -78,12 +82,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT button through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, button.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, button.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 2e5d95ebda4..86db828b111 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -1,7 +1,6 @@ """Camera that loads a picture from an MQTT topic.""" from __future__ import annotations -import asyncio from base64 import b64decode import functools @@ -17,17 +16,18 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription -from .. import mqtt +from .config import MQTT_BASE_SCHEMA from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .util import valid_subscribe_topic DEFAULT_NAME = "MQTT Camera" @@ -40,10 +40,10 @@ MQTT_CAMERA_ATTRIBUTES_BLOCKED = frozenset( } ) -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic, + vol.Required(CONF_TOPIC): valid_subscribe_topic, } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -65,7 +65,11 @@ async def async_setup_platform( """Set up MQTT camera configured under the camera platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, camera.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + camera.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -76,12 +80,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT camera through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, camera.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, camera.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py new file mode 100644 index 00000000000..66699372516 --- /dev/null +++ b/homeassistant/components/mqtt/client.py @@ -0,0 +1,659 @@ +"""Support for MQTT message handling.""" +from __future__ import annotations + +import asyncio +from collections.abc import Awaitable, Callable +from functools import lru_cache, partial, wraps +import inspect +from itertools import groupby +import logging +from operator import attrgetter +import ssl +import time +from typing import TYPE_CHECKING, Any, Union, cast +import uuid + +import attr +import certifi + +from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_PASSWORD, + CONF_PORT, + CONF_PROTOCOL, + CONF_USERNAME, + EVENT_HOMEASSISTANT_STARTED, +) +from homeassistant.core import CoreState, HassJob, HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.typing import ConfigType +from homeassistant.loader import bind_hass +from homeassistant.util import dt as dt_util +from homeassistant.util.async_ import run_callback_threadsafe +from homeassistant.util.logging import catch_log_exception + +from .const import ( + ATTR_TOPIC, + CONF_BIRTH_MESSAGE, + CONF_BROKER, + CONF_CERTIFICATE, + CONF_CLIENT_CERT, + CONF_CLIENT_KEY, + CONF_KEEPALIVE, + CONF_TLS_INSECURE, + CONF_WILL_MESSAGE, + DATA_MQTT, + DEFAULT_ENCODING, + DEFAULT_QOS, + MQTT_CONNECTED, + MQTT_DISCONNECTED, + PROTOCOL_31, +) +from .discovery import LAST_DISCOVERY +from .models import ( + AsyncMessageCallbackType, + MessageCallbackType, + PublishMessage, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) + +if TYPE_CHECKING: + # Only import for paho-mqtt type checking here, imports are done locally + # because integrations should be able to optionally rely on MQTT. + import paho.mqtt.client as mqtt + +_LOGGER = logging.getLogger(__name__) + +DISCOVERY_COOLDOWN = 2 +TIMEOUT_ACK = 10 + +SubscribePayloadType = Union[str, bytes] # Only bytes if encoding is None + + +def publish( + hass: HomeAssistant, + topic: str, + payload: PublishPayloadType, + qos: int | None = 0, + retain: bool | None = False, + encoding: str | None = DEFAULT_ENCODING, +) -> None: + """Publish message to a MQTT topic.""" + hass.add_job(async_publish, hass, topic, payload, qos, retain, encoding) + + +async def async_publish( + hass: HomeAssistant, + topic: str, + payload: PublishPayloadType, + qos: int | None = 0, + retain: bool | None = False, + encoding: str | None = DEFAULT_ENCODING, +) -> None: + """Publish message to a MQTT topic.""" + + outgoing_payload = payload + if not isinstance(payload, bytes): + if not encoding: + _LOGGER.error( + "Can't pass-through payload for publishing %s on %s with no encoding set, need 'bytes' got %s", + payload, + topic, + type(payload), + ) + return + outgoing_payload = str(payload) + if encoding != DEFAULT_ENCODING: + # a string is encoded as utf-8 by default, other encoding requires bytes as payload + try: + outgoing_payload = outgoing_payload.encode(encoding) + except (AttributeError, LookupError, UnicodeEncodeError): + _LOGGER.error( + "Can't encode payload for publishing %s on %s with encoding %s", + payload, + topic, + encoding, + ) + return + + await hass.data[DATA_MQTT].async_publish(topic, outgoing_payload, qos, retain) + + +AsyncDeprecatedMessageCallbackType = Callable[ + [str, ReceivePayloadType, int], Awaitable[None] +] +DeprecatedMessageCallbackType = Callable[[str, ReceivePayloadType, int], None] + + +def wrap_msg_callback( + msg_callback: AsyncDeprecatedMessageCallbackType | DeprecatedMessageCallbackType, +) -> AsyncMessageCallbackType | MessageCallbackType: + """Wrap an MQTT message callback to support deprecated signature.""" + # Check for partials to properly determine if coroutine function + check_func = msg_callback + while isinstance(check_func, partial): + check_func = check_func.func + + wrapper_func: AsyncMessageCallbackType | MessageCallbackType + if asyncio.iscoroutinefunction(check_func): + + @wraps(msg_callback) + async def async_wrapper(msg: ReceiveMessage) -> None: + """Call with deprecated signature.""" + await cast(AsyncDeprecatedMessageCallbackType, msg_callback)( + msg.topic, msg.payload, msg.qos + ) + + wrapper_func = async_wrapper + else: + + @wraps(msg_callback) + def wrapper(msg: ReceiveMessage) -> None: + """Call with deprecated signature.""" + msg_callback(msg.topic, msg.payload, msg.qos) + + wrapper_func = wrapper + return wrapper_func + + +@bind_hass +async def async_subscribe( + hass: HomeAssistant, + topic: str, + msg_callback: AsyncMessageCallbackType + | MessageCallbackType + | DeprecatedMessageCallbackType + | AsyncDeprecatedMessageCallbackType, + qos: int = DEFAULT_QOS, + encoding: str | None = "utf-8", +): + """Subscribe to an MQTT topic. + + Call the return value to unsubscribe. + """ + # Count callback parameters which don't have a default value + non_default = 0 + if msg_callback: + non_default = sum( + p.default == inspect.Parameter.empty + for _, p in inspect.signature(msg_callback).parameters.items() + ) + + wrapped_msg_callback = msg_callback + # If we have 3 parameters with no default value, wrap the callback + if non_default == 3: + module = inspect.getmodule(msg_callback) + _LOGGER.warning( + "Signature of MQTT msg_callback '%s.%s' is deprecated", + module.__name__ if module else "", + msg_callback.__name__, + ) + wrapped_msg_callback = wrap_msg_callback( + cast(DeprecatedMessageCallbackType, msg_callback) + ) + + async_remove = await hass.data[DATA_MQTT].async_subscribe( + topic, + catch_log_exception( + wrapped_msg_callback, + lambda msg: ( + f"Exception in {msg_callback.__name__} when handling msg on " + f"'{msg.topic}': '{msg.payload}'" + ), + ), + qos, + encoding, + ) + return async_remove + + +@bind_hass +def subscribe( + hass: HomeAssistant, + topic: str, + msg_callback: MessageCallbackType, + qos: int = DEFAULT_QOS, + encoding: str = "utf-8", +) -> Callable[[], None]: + """Subscribe to an MQTT topic.""" + async_remove = asyncio.run_coroutine_threadsafe( + async_subscribe(hass, topic, msg_callback, qos, encoding), hass.loop + ).result() + + def remove(): + """Remove listener convert.""" + run_callback_threadsafe(hass.loop, async_remove).result() + + return remove + + +@attr.s(slots=True, frozen=True) +class Subscription: + """Class to hold data about an active subscription.""" + + topic: str = attr.ib() + matcher: Any = attr.ib() + job: HassJob = attr.ib() + qos: int = attr.ib(default=0) + encoding: str | None = attr.ib(default="utf-8") + + +class MqttClientSetup: + """Helper class to setup the paho mqtt client from config.""" + + def __init__(self, config: ConfigType) -> None: + """Initialize the MQTT client setup helper.""" + + # We don't import on the top because some integrations + # should be able to optionally rely on MQTT. + import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel + + if config[CONF_PROTOCOL] == PROTOCOL_31: + proto = mqtt.MQTTv31 + else: + proto = mqtt.MQTTv311 + + if (client_id := config.get(CONF_CLIENT_ID)) is None: + # PAHO MQTT relies on the MQTT server to generate random client IDs. + # However, that feature is not mandatory so we generate our own. + client_id = mqtt.base62(uuid.uuid4().int, padding=22) + self._client = mqtt.Client(client_id, protocol=proto) + + # Enable logging + self._client.enable_logger() + + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + if username is not None: + self._client.username_pw_set(username, password) + + if (certificate := config.get(CONF_CERTIFICATE)) == "auto": + certificate = certifi.where() + + client_key = config.get(CONF_CLIENT_KEY) + client_cert = config.get(CONF_CLIENT_CERT) + tls_insecure = config.get(CONF_TLS_INSECURE) + if certificate is not None: + self._client.tls_set( + certificate, + certfile=client_cert, + keyfile=client_key, + tls_version=ssl.PROTOCOL_TLS, + ) + + if tls_insecure is not None: + self._client.tls_insecure_set(tls_insecure) + + @property + def client(self) -> mqtt.Client: + """Return the paho MQTT client.""" + return self._client + + +class MQTT: + """Home Assistant MQTT client.""" + + def __init__( + self, + hass: HomeAssistant, + config_entry, + conf, + ) -> None: + """Initialize Home Assistant MQTT client.""" + # We don't import on the top because some integrations + # should be able to optionally rely on MQTT. + import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel + + self.hass = hass + self.config_entry = config_entry + self.conf = conf + self.subscriptions: list[Subscription] = [] + self.connected = False + self._ha_started = asyncio.Event() + self._last_subscribe = time.time() + self._mqttc: mqtt.Client = None + self._paho_lock = asyncio.Lock() + + self._pending_operations: dict[str, asyncio.Event] = {} + + if self.hass.state == CoreState.running: + self._ha_started.set() + else: + + @callback + def ha_started(_): + self._ha_started.set() + + self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, ha_started) + + self.init_client() + + def init_client(self): + """Initialize paho client.""" + self._mqttc = MqttClientSetup(self.conf).client + self._mqttc.on_connect = self._mqtt_on_connect + self._mqttc.on_disconnect = self._mqtt_on_disconnect + self._mqttc.on_message = self._mqtt_on_message + self._mqttc.on_publish = self._mqtt_on_callback + self._mqttc.on_subscribe = self._mqtt_on_callback + self._mqttc.on_unsubscribe = self._mqtt_on_callback + + if ( + CONF_WILL_MESSAGE in self.conf + and ATTR_TOPIC in self.conf[CONF_WILL_MESSAGE] + ): + will_message = PublishMessage(**self.conf[CONF_WILL_MESSAGE]) + else: + will_message = None + + if will_message is not None: + self._mqttc.will_set( + topic=will_message.topic, + payload=will_message.payload, + qos=will_message.qos, + retain=will_message.retain, + ) + + async def async_publish( + self, topic: str, payload: PublishPayloadType, qos: int, retain: bool + ) -> None: + """Publish a MQTT message.""" + async with self._paho_lock: + msg_info = await self.hass.async_add_executor_job( + self._mqttc.publish, topic, payload, qos, retain + ) + _LOGGER.debug( + "Transmitting message on %s: '%s', mid: %s", + topic, + payload, + msg_info.mid, + ) + _raise_on_error(msg_info.rc) + await self._wait_for_mid(msg_info.mid) + + async def async_connect(self) -> None: + """Connect to the host. Does not process messages yet.""" + # pylint: disable-next=import-outside-toplevel + import paho.mqtt.client as mqtt + + result: int | None = None + try: + result = await self.hass.async_add_executor_job( + self._mqttc.connect, + self.conf[CONF_BROKER], + self.conf[CONF_PORT], + self.conf[CONF_KEEPALIVE], + ) + except OSError as err: + _LOGGER.error("Failed to connect to MQTT server due to exception: %s", err) + + if result is not None and result != 0: + _LOGGER.error( + "Failed to connect to MQTT server: %s", mqtt.error_string(result) + ) + + self._mqttc.loop_start() + + async def async_disconnect(self): + """Stop the MQTT client.""" + + def stop(): + """Stop the MQTT client.""" + # Do not disconnect, we want the broker to always publish will + self._mqttc.loop_stop() + + await self.hass.async_add_executor_job(stop) + + async def async_subscribe( + self, + topic: str, + msg_callback: MessageCallbackType, + qos: int, + encoding: str | None = None, + ) -> Callable[[], None]: + """Set up a subscription to a topic with the provided qos. + + This method is a coroutine. + """ + if not isinstance(topic, str): + raise HomeAssistantError("Topic needs to be a string!") + + subscription = Subscription( + topic, _matcher_for_topic(topic), HassJob(msg_callback), qos, encoding + ) + self.subscriptions.append(subscription) + self._matching_subscriptions.cache_clear() + + # Only subscribe if currently connected. + if self.connected: + self._last_subscribe = time.time() + await self._async_perform_subscription(topic, qos) + + @callback + def async_remove() -> None: + """Remove subscription.""" + if subscription not in self.subscriptions: + raise HomeAssistantError("Can't remove subscription twice") + self.subscriptions.remove(subscription) + self._matching_subscriptions.cache_clear() + + # Only unsubscribe if currently connected. + if self.connected: + self.hass.async_create_task(self._async_unsubscribe(topic)) + + return async_remove + + async def _async_unsubscribe(self, topic: str) -> None: + """Unsubscribe from a topic. + + This method is a coroutine. + """ + if any(other.topic == topic for other in self.subscriptions): + # Other subscriptions on topic remaining - don't unsubscribe. + return + + async with self._paho_lock: + result: int | None = None + result, mid = await self.hass.async_add_executor_job( + self._mqttc.unsubscribe, topic + ) + _LOGGER.debug("Unsubscribing from %s, mid: %s", topic, mid) + _raise_on_error(result) + await self._wait_for_mid(mid) + + async def _async_perform_subscription(self, topic: str, qos: int) -> None: + """Perform a paho-mqtt subscription.""" + async with self._paho_lock: + result: int | None = None + result, mid = await self.hass.async_add_executor_job( + self._mqttc.subscribe, topic, qos + ) + _LOGGER.debug("Subscribing to %s, mid: %s", topic, mid) + _raise_on_error(result) + await self._wait_for_mid(mid) + + def _mqtt_on_connect(self, _mqttc, _userdata, _flags, result_code: int) -> None: + """On connect callback. + + Resubscribe to all topics we were subscribed to and publish birth + message. + """ + # pylint: disable-next=import-outside-toplevel + import paho.mqtt.client as mqtt + + if result_code != mqtt.CONNACK_ACCEPTED: + _LOGGER.error( + "Unable to connect to the MQTT broker: %s", + mqtt.connack_string(result_code), + ) + return + + self.connected = True + dispatcher_send(self.hass, MQTT_CONNECTED) + _LOGGER.info( + "Connected to MQTT server %s:%s (%s)", + self.conf[CONF_BROKER], + self.conf[CONF_PORT], + result_code, + ) + + # Group subscriptions to only re-subscribe once for each topic. + keyfunc = attrgetter("topic") + for topic, subs in groupby(sorted(self.subscriptions, key=keyfunc), keyfunc): + # Re-subscribe with the highest requested qos + max_qos = max(subscription.qos for subscription in subs) + self.hass.add_job(self._async_perform_subscription, topic, max_qos) + + if ( + CONF_BIRTH_MESSAGE in self.conf + and ATTR_TOPIC in self.conf[CONF_BIRTH_MESSAGE] + ): + + async def publish_birth_message(birth_message): + await self._ha_started.wait() # Wait for Home Assistant to start + await self._discovery_cooldown() # Wait for MQTT discovery to cool down + await self.async_publish( + topic=birth_message.topic, + payload=birth_message.payload, + qos=birth_message.qos, + retain=birth_message.retain, + ) + + birth_message = PublishMessage(**self.conf[CONF_BIRTH_MESSAGE]) + asyncio.run_coroutine_threadsafe( + publish_birth_message(birth_message), self.hass.loop + ) + + def _mqtt_on_message(self, _mqttc, _userdata, msg) -> None: + """Message received callback.""" + self.hass.add_job(self._mqtt_handle_message, msg) + + @lru_cache(2048) + def _matching_subscriptions(self, topic): + subscriptions = [] + for subscription in self.subscriptions: + if subscription.matcher(topic): + subscriptions.append(subscription) + return subscriptions + + @callback + def _mqtt_handle_message(self, msg) -> None: + _LOGGER.debug( + "Received message on %s%s: %s", + msg.topic, + " (retained)" if msg.retain else "", + msg.payload[0:8192], + ) + timestamp = dt_util.utcnow() + + subscriptions = self._matching_subscriptions(msg.topic) + + for subscription in subscriptions: + + payload: SubscribePayloadType = msg.payload + if subscription.encoding is not None: + try: + payload = msg.payload.decode(subscription.encoding) + except (AttributeError, UnicodeDecodeError): + _LOGGER.warning( + "Can't decode payload %s on %s with encoding %s (for %s)", + msg.payload[0:8192], + msg.topic, + subscription.encoding, + subscription.job, + ) + continue + + self.hass.async_run_hass_job( + subscription.job, + ReceiveMessage( + msg.topic, + payload, + msg.qos, + msg.retain, + subscription.topic, + timestamp, + ), + ) + + def _mqtt_on_callback(self, _mqttc, _userdata, mid, _granted_qos=None) -> None: + """Publish / Subscribe / Unsubscribe callback.""" + self.hass.add_job(self._mqtt_handle_mid, mid) + + @callback + def _mqtt_handle_mid(self, mid) -> None: + # Create the mid event if not created, either _mqtt_handle_mid or _wait_for_mid + # may be executed first. + if mid not in self._pending_operations: + self._pending_operations[mid] = asyncio.Event() + self._pending_operations[mid].set() + + def _mqtt_on_disconnect(self, _mqttc, _userdata, result_code: int) -> None: + """Disconnected callback.""" + self.connected = False + dispatcher_send(self.hass, MQTT_DISCONNECTED) + _LOGGER.warning( + "Disconnected from MQTT server %s:%s (%s)", + self.conf[CONF_BROKER], + self.conf[CONF_PORT], + result_code, + ) + + async def _wait_for_mid(self, mid): + """Wait for ACK from broker.""" + # Create the mid event if not created, either _mqtt_handle_mid or _wait_for_mid + # may be executed first. + if mid not in self._pending_operations: + self._pending_operations[mid] = asyncio.Event() + try: + await asyncio.wait_for(self._pending_operations[mid].wait(), TIMEOUT_ACK) + except asyncio.TimeoutError: + _LOGGER.warning( + "No ACK from MQTT server in %s seconds (mid: %s)", TIMEOUT_ACK, mid + ) + finally: + del self._pending_operations[mid] + + async def _discovery_cooldown(self): + now = time.time() + # Reset discovery and subscribe cooldowns + self.hass.data[LAST_DISCOVERY] = now + self._last_subscribe = now + + last_discovery = self.hass.data[LAST_DISCOVERY] + last_subscribe = self._last_subscribe + wait_until = max( + last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN + ) + while now < wait_until: + await asyncio.sleep(wait_until - now) + now = time.time() + last_discovery = self.hass.data[LAST_DISCOVERY] + last_subscribe = self._last_subscribe + wait_until = max( + last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN + ) + + +def _raise_on_error(result_code: int | None) -> None: + """Raise error if error result.""" + # pylint: disable-next=import-outside-toplevel + import paho.mqtt.client as mqtt + + if result_code is not None and result_code != 0: + raise HomeAssistantError( + f"Error talking to MQTT: {mqtt.error_string(result_code)}" + ) + + +def _matcher_for_topic(subscription: str) -> Any: + # pylint: disable-next=import-outside-toplevel + from paho.mqtt.matcher import MQTTMatcher + + matcher = MQTTMatcher() + matcher[subscription] = True + + return lambda topic: next(matcher.iter_match(topic), False) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 52465bbba24..bdcc82f2c39 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -1,7 +1,6 @@ """Support for MQTT climate devices.""" from __future__ import annotations -import asyncio import functools import logging @@ -44,18 +43,20 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import DEFAULT_RETAIN, MQTT_BASE_SCHEMA 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_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate +from .util import valid_publish_topic, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -232,33 +233,33 @@ def valid_preset_mode_configuration(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( { - vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_AUX_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_AUX_STATE_TOPIC): valid_subscribe_topic, # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 - vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_AWAY_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_CURRENT_TEMP_TEMPLATE): cv.template, - vol.Optional(CONF_CURRENT_TEMP_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_CURRENT_TEMP_TOPIC): valid_subscribe_topic, vol.Optional(CONF_FAN_MODE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): valid_publish_topic, vol.Optional( CONF_FAN_MODE_LIST, default=[FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH], ): cv.ensure_list, vol.Optional(CONF_FAN_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_FAN_MODE_STATE_TOPIC): valid_subscribe_topic, # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 vol.Optional(CONF_HOLD_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_HOLD_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_HOLD_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_HOLD_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_HOLD_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_HOLD_LIST): cv.ensure_list, vol.Optional(CONF_MODE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_MODE_COMMAND_TOPIC): valid_publish_topic, vol.Optional( CONF_MODE_LIST, default=[ @@ -271,54 +272,54 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_SCHEMA.extend( ], ): cv.ensure_list, vol.Optional(CONF_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_ON, default="ON"): cv.string, vol.Optional(CONF_PAYLOAD_OFF, default="OFF"): cv.string, - vol.Optional(CONF_POWER_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_POWER_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_POWER_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_POWER_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_POWER_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_PRECISION): vol.In( [PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE] ), - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, # CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9 vol.Optional(CONF_SEND_IF_OFF): cv.boolean, vol.Optional(CONF_ACTION_TEMPLATE): cv.template, - vol.Optional(CONF_ACTION_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_ACTION_TOPIC): valid_subscribe_topic, # CONF_PRESET_MODE_COMMAND_TOPIC and CONF_PRESET_MODES_LIST must be used together vol.Inclusive( CONF_PRESET_MODE_COMMAND_TOPIC, "preset_modes" - ): mqtt.valid_publish_topic, + ): valid_publish_topic, vol.Inclusive( CONF_PRESET_MODES_LIST, "preset_modes", default=[] ): cv.ensure_list, vol.Optional(CONF_PRESET_MODE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_PRESET_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_PRESET_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_PRESET_MODE_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_SWING_MODE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): valid_publish_topic, vol.Optional( CONF_SWING_MODE_LIST, default=[SWING_ON, SWING_OFF] ): cv.ensure_list, vol.Optional(CONF_SWING_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_SWING_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_SWING_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_TEMP_INITIAL, default=21): cv.positive_int, vol.Optional(CONF_TEMP_MIN, default=DEFAULT_MIN_TEMP): vol.Coerce(float), vol.Optional(CONF_TEMP_MAX, default=DEFAULT_MAX_TEMP): vol.Coerce(float), vol.Optional(CONF_TEMP_STEP, default=1.0): vol.Coerce(float), vol.Optional(CONF_TEMP_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TEMP_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_TEMP_HIGH_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_TEMP_HIGH_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_TEMP_HIGH_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_TEMP_HIGH_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_TEMP_HIGH_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_TEMP_HIGH_STATE_TEMPLATE): cv.template, vol.Optional(CONF_TEMP_LOW_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_TEMP_LOW_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TEMP_LOW_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_TEMP_LOW_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_TEMP_LOW_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_TEMP_LOW_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_TEMP_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_TEMP_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, } @@ -375,7 +376,11 @@ async def async_setup_platform( """Set up MQTT climate configured under the fan platform key (deprecated).""" # The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, climate.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + climate.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -386,12 +391,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT climate device through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, climate.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, climate.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/config.py b/homeassistant/components/mqtt/config.py new file mode 100644 index 00000000000..4f84d911418 --- /dev/null +++ b/homeassistant/components/mqtt/config.py @@ -0,0 +1,148 @@ +"""Support for MQTT message handling.""" +from __future__ import annotations + +import voluptuous as vol + +from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_DISCOVERY, + CONF_PASSWORD, + CONF_PORT, + CONF_PROTOCOL, + CONF_USERNAME, + CONF_VALUE_TEMPLATE, +) +from homeassistant.helpers import config_validation as cv + +from .const import ( + ATTR_PAYLOAD, + ATTR_QOS, + ATTR_RETAIN, + ATTR_TOPIC, + CONF_BIRTH_MESSAGE, + CONF_BROKER, + CONF_CERTIFICATE, + CONF_CLIENT_CERT, + CONF_CLIENT_KEY, + CONF_COMMAND_TOPIC, + CONF_DISCOVERY_PREFIX, + CONF_ENCODING, + CONF_KEEPALIVE, + CONF_QOS, + CONF_RETAIN, + CONF_STATE_TOPIC, + CONF_TLS_INSECURE, + CONF_TLS_VERSION, + CONF_WILL_MESSAGE, + DEFAULT_BIRTH, + DEFAULT_DISCOVERY, + DEFAULT_ENCODING, + DEFAULT_PREFIX, + DEFAULT_QOS, + DEFAULT_RETAIN, + DEFAULT_WILL, + PLATFORMS, + PROTOCOL_31, + PROTOCOL_311, +) +from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic + +DEFAULT_PORT = 1883 +DEFAULT_KEEPALIVE = 60 +DEFAULT_PROTOCOL = PROTOCOL_311 +DEFAULT_TLS_PROTOCOL = "auto" + +DEFAULT_VALUES = { + CONF_BIRTH_MESSAGE: DEFAULT_BIRTH, + CONF_DISCOVERY: DEFAULT_DISCOVERY, + CONF_PORT: DEFAULT_PORT, + CONF_TLS_VERSION: DEFAULT_TLS_PROTOCOL, + CONF_WILL_MESSAGE: DEFAULT_WILL, +} + +CLIENT_KEY_AUTH_MSG = ( + "client_key and client_cert must both be present in " + "the MQTT broker configuration" +) + +MQTT_WILL_BIRTH_SCHEMA = vol.Schema( + { + vol.Inclusive(ATTR_TOPIC, "topic_payload"): valid_publish_topic, + vol.Inclusive(ATTR_PAYLOAD, "topic_payload"): cv.string, + vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, + vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + }, + required=True, +) + +PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( + {vol.Optional(platform.value): cv.ensure_list for platform in PLATFORMS} +) + +CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( + { + vol.Optional(CONF_CLIENT_ID): cv.string, + vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All( + vol.Coerce(int), vol.Range(min=15) + ), + vol.Optional(CONF_BROKER): cv.string, + vol.Optional(CONF_PORT): cv.port, + vol.Optional(CONF_USERNAME): cv.string, + vol.Optional(CONF_PASSWORD): cv.string, + vol.Optional(CONF_CERTIFICATE): vol.Any("auto", cv.isfile), + vol.Inclusive( + CONF_CLIENT_KEY, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG + ): cv.isfile, + vol.Inclusive( + CONF_CLIENT_CERT, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG + ): cv.isfile, + vol.Optional(CONF_TLS_INSECURE): cv.boolean, + vol.Optional(CONF_TLS_VERSION): vol.Any("auto", "1.0", "1.1", "1.2"), + vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All( + cv.string, vol.In([PROTOCOL_31, PROTOCOL_311]) + ), + vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, + vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, + vol.Optional(CONF_DISCOVERY): cv.boolean, + # discovery_prefix must be a valid publish topic because if no + # state topic is specified, it will be created with the given prefix. + vol.Optional( + CONF_DISCOVERY_PREFIX, default=DEFAULT_PREFIX + ): valid_publish_topic, + } +) + +DEPRECATED_CONFIG_KEYS = [ + CONF_BIRTH_MESSAGE, + CONF_BROKER, + CONF_DISCOVERY, + CONF_PASSWORD, + CONF_PORT, + CONF_TLS_VERSION, + CONF_USERNAME, + CONF_WILL_MESSAGE, +] + +SCHEMA_BASE = { + vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, + vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, +} + +MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE) + +# Sensor type platforms subscribe to MQTT events +MQTT_RO_SCHEMA = MQTT_BASE_SCHEMA.extend( + { + vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + } +) + +# Switch type platforms publish to MQTT and may subscribe +MQTT_RW_SCHEMA = MQTT_BASE_SCHEMA.extend( + { + vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, + } +) diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 6697b17dfdc..7137b93118f 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -17,7 +17,7 @@ from homeassistant.const import ( ) from homeassistant.data_entry_flow import FlowResult -from . import MqttClientSetup +from .client import MqttClientSetup from .const import ( ATTR_PAYLOAD, ATTR_QOS, diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 106d0310158..2f7e27e7252 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -1,5 +1,5 @@ """Constants used by multiple MQTT modules.""" -from homeassistant.const import CONF_PAYLOAD +from homeassistant.const import CONF_PAYLOAD, Platform ATTR_DISCOVERY_HASH = "discovery_hash" ATTR_DISCOVERY_PAYLOAD = "discovery_payload" @@ -14,7 +14,9 @@ CONF_BROKER = "broker" CONF_BIRTH_MESSAGE = "birth_message" CONF_COMMAND_TEMPLATE = "command_template" CONF_COMMAND_TOPIC = "command_topic" +CONF_DISCOVERY_PREFIX = "discovery_prefix" CONF_ENCODING = "encoding" +CONF_KEEPALIVE = "keepalive" CONF_QOS = ATTR_QOS CONF_RETAIN = ATTR_RETAIN CONF_STATE_TOPIC = "state_topic" @@ -30,6 +32,7 @@ CONF_TLS_VERSION = "tls_version" CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock" +DATA_MQTT = "mqtt" DATA_MQTT_CONFIG = "mqtt_config" DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed" @@ -66,3 +69,24 @@ PAYLOAD_NONE = "None" PROTOCOL_31 = "3.1" PROTOCOL_311 = "3.1.1" + +PLATFORMS = [ + Platform.ALARM_CONTROL_PANEL, + Platform.BINARY_SENSOR, + Platform.BUTTON, + Platform.CAMERA, + Platform.CLIMATE, + Platform.DEVICE_TRACKER, + Platform.COVER, + Platform.FAN, + Platform.HUMIDIFIER, + Platform.LIGHT, + Platform.LOCK, + Platform.NUMBER, + Platform.SELECT, + Platform.SCENE, + Platform.SENSOR, + Platform.SIREN, + Platform.SWITCH, + Platform.VACUUM, +] diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 8e36329946a..325433817c0 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -1,7 +1,6 @@ """Support for MQTT cover devices.""" from __future__ import annotations -import asyncio import functools from json import JSONDecodeError, loads as json_loads import logging @@ -33,8 +32,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_BASE_SCHEMA from .const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -46,11 +45,13 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate +from .util import valid_publish_topic, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -152,11 +153,11 @@ def validate_options(value): return value -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( { - vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, - vol.Optional(CONF_GET_POSITION_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_GET_POSITION_TOPIC): valid_subscribe_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_CLOSE, default=DEFAULT_PAYLOAD_CLOSE): vol.Any( @@ -172,24 +173,24 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_SCHEMA.extend( vol.Optional(CONF_POSITION_OPEN, default=DEFAULT_POSITION_OPEN): int, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, vol.Optional(CONF_SET_POSITION_TEMPLATE): cv.template, - vol.Optional(CONF_SET_POSITION_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SET_POSITION_TOPIC): valid_publish_topic, vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string, vol.Optional(CONF_STATE_CLOSING, default=STATE_CLOSING): cv.string, vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string, vol.Optional(CONF_STATE_OPENING, default=STATE_OPENING): cv.string, vol.Optional(CONF_STATE_STOPPED, default=DEFAULT_STATE_STOPPED): cv.string, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Optional( CONF_TILT_CLOSED_POSITION, default=DEFAULT_TILT_CLOSED_POSITION ): int, - vol.Optional(CONF_TILT_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TILT_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_TILT_MAX, default=DEFAULT_TILT_MAX): int, vol.Optional(CONF_TILT_MIN, default=DEFAULT_TILT_MIN): int, vol.Optional(CONF_TILT_OPEN_POSITION, default=DEFAULT_TILT_OPEN_POSITION): int, vol.Optional( CONF_TILT_STATE_OPTIMISTIC, default=DEFAULT_TILT_OPTIMISTIC ): cv.boolean, - vol.Optional(CONF_TILT_STATUS_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_TILT_STATUS_TOPIC): valid_subscribe_topic, vol.Optional(CONF_TILT_STATUS_TEMPLATE): cv.template, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_GET_POSITION_TEMPLATE): cv.template, @@ -225,7 +226,11 @@ async def async_setup_platform( """Set up MQTT covers configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, cover.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + cover.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -236,13 +241,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT cover through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, cover.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, cover.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( diff --git a/homeassistant/components/mqtt/device_automation.py b/homeassistant/components/mqtt/device_automation.py index cafbd66b098..0646a5bda0c 100644 --- a/homeassistant/components/mqtt/device_automation.py +++ b/homeassistant/components/mqtt/device_automation.py @@ -3,8 +3,10 @@ import functools import voluptuous as vol +import homeassistant.helpers.config_validation as cv + from . import device_trigger -from .. import mqtt +from .config import MQTT_BASE_SCHEMA from .mixins import async_setup_entry_helper AUTOMATION_TYPE_TRIGGER = "trigger" @@ -12,10 +14,10 @@ AUTOMATION_TYPES = [AUTOMATION_TYPE_TRIGGER] AUTOMATION_TYPES_SCHEMA = vol.In(AUTOMATION_TYPES) CONF_AUTOMATION_TYPE = "automation_type" -PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( {vol.Required(CONF_AUTOMATION_TYPE): AUTOMATION_TYPES_SCHEMA}, extra=vol.ALLOW_EXTRA, -) +).extend(MQTT_BASE_SCHEMA.schema) async def async_setup_entry(hass, config_entry): diff --git a/homeassistant/components/mqtt/device_tracker/schema_discovery.py b/homeassistant/components/mqtt/device_tracker/schema_discovery.py index aa7506bd5e3..1b48e15b80e 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_discovery.py +++ b/homeassistant/components/mqtt/device_tracker/schema_discovery.py @@ -19,8 +19,8 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv -from .. import MqttValueTemplate, subscription -from ... import mqtt +from .. import subscription +from ..config import MQTT_RO_SCHEMA from ..const import CONF_QOS, CONF_STATE_TOPIC from ..debug_info import log_messages from ..mixins import ( @@ -29,12 +29,13 @@ from ..mixins import ( async_get_platform_config_from_yaml, async_setup_entry_helper, ) +from ..models import MqttValueTemplate CONF_PAYLOAD_HOME = "payload_home" CONF_PAYLOAD_NOT_HOME = "payload_not_home" CONF_SOURCE_TYPE = "source_type" -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RO_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string, diff --git a/homeassistant/components/mqtt/device_tracker/schema_yaml.py b/homeassistant/components/mqtt/device_tracker/schema_yaml.py index f871ac89c2d..2dfa5b7134c 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_yaml.py +++ b/homeassistant/components/mqtt/device_tracker/schema_yaml.py @@ -7,16 +7,18 @@ from homeassistant.const import CONF_DEVICES, STATE_HOME, STATE_NOT_HOME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv -from ... import mqtt +from ..client import async_subscribe +from ..config import SCHEMA_BASE from ..const import CONF_QOS +from ..util import valid_subscribe_topic CONF_PAYLOAD_HOME = "payload_home" CONF_PAYLOAD_NOT_HOME = "payload_not_home" CONF_SOURCE_TYPE = "source_type" -PLATFORM_SCHEMA_YAML = PLATFORM_SCHEMA.extend(mqtt.SCHEMA_BASE).extend( +PLATFORM_SCHEMA_YAML = PLATFORM_SCHEMA.extend(SCHEMA_BASE).extend( { - vol.Required(CONF_DEVICES): {cv.string: mqtt.valid_subscribe_topic}, + vol.Required(CONF_DEVICES): {cv.string: valid_subscribe_topic}, vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string, vol.Optional(CONF_PAYLOAD_NOT_HOME, default=STATE_NOT_HOME): cv.string, vol.Optional(CONF_SOURCE_TYPE): vol.In(SOURCE_TYPES), @@ -50,6 +52,6 @@ async def async_setup_scanner_from_yaml(hass, config, async_see, discovery_info= hass.async_create_task(async_see(**see_args)) - await mqtt.async_subscribe(hass, topic, async_message_received, qos) + await async_subscribe(hass, topic, async_message_received, qos) return True diff --git a/homeassistant/components/mqtt/device_trigger.py b/homeassistant/components/mqtt/device_trigger.py index 42ffcee1644..0b4bcbfcbc2 100644 --- a/homeassistant/components/mqtt/device_trigger.py +++ b/homeassistant/components/mqtt/device_trigger.py @@ -29,8 +29,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import ConfigType from . import debug_info, trigger as mqtt_trigger -from .. import mqtt -from .const import ATTR_DISCOVERY_HASH, CONF_PAYLOAD, CONF_QOS, CONF_TOPIC, DOMAIN +from .config import MQTT_BASE_SCHEMA +from .const import ( + ATTR_DISCOVERY_HASH, + CONF_ENCODING, + CONF_PAYLOAD, + CONF_QOS, + CONF_TOPIC, + DOMAIN, +) from .discovery import MQTT_DISCOVERY_DONE from .mixins import ( MQTT_ENTITY_DEVICE_INFO_SCHEMA, @@ -64,7 +71,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( } ) -TRIGGER_DISCOVERY_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +TRIGGER_DISCOVERY_SCHEMA = MQTT_BASE_SCHEMA.extend( { vol.Required(CONF_AUTOMATION_TYPE): str, vol.Required(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA, @@ -94,10 +101,10 @@ class TriggerInstance: async def async_attach_trigger(self) -> None: """Attach MQTT trigger.""" mqtt_config = { - mqtt_trigger.CONF_PLATFORM: mqtt.DOMAIN, - mqtt_trigger.CONF_TOPIC: self.trigger.topic, - mqtt_trigger.CONF_ENCODING: DEFAULT_ENCODING, - mqtt_trigger.CONF_QOS: self.trigger.qos, + CONF_PLATFORM: DOMAIN, + CONF_TOPIC: self.trigger.topic, + CONF_ENCODING: DEFAULT_ENCODING, + CONF_QOS: self.trigger.qos, } if self.trigger.payload: mqtt_config[CONF_PAYLOAD] = self.trigger.payload diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index f2b738cd2bb..d0b4ff10692 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -1,7 +1,6 @@ """Support for MQTT fans.""" from __future__ import annotations -import asyncio import functools import logging import math @@ -34,8 +33,8 @@ from homeassistant.util.percentage import ( ranged_value_to_percentage, ) -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -50,11 +49,13 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate +from .util import valid_publish_topic, valid_subscribe_topic CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic" CONF_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic" @@ -125,28 +126,28 @@ def valid_preset_mode_configuration(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_OSCILLATION_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_OSCILLATION_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_OSCILLATION_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_OSCILLATION_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_PERCENTAGE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_PERCENTAGE_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_PERCENTAGE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_PERCENTAGE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_PERCENTAGE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_PERCENTAGE_VALUE_TEMPLATE): cv.template, # CONF_PRESET_MODE_COMMAND_TOPIC and CONF_PRESET_MODES_LIST must be used together vol.Inclusive( CONF_PRESET_MODE_COMMAND_TOPIC, "preset_modes" - ): mqtt.valid_publish_topic, + ): valid_publish_topic, vol.Inclusive( CONF_PRESET_MODES_LIST, "preset_modes", default=[] ): cv.ensure_list, vol.Optional(CONF_PRESET_MODE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_PRESET_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_PRESET_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_PRESET_MODE_VALUE_TEMPLATE): cv.template, vol.Optional( CONF_SPEED_RANGE_MIN, default=DEFAULT_SPEED_RANGE_MIN @@ -168,8 +169,8 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( vol.Optional( CONF_PAYLOAD_OSCILLATION_ON, default=OSCILLATE_ON_PAYLOAD ): cv.string, - vol.Optional(CONF_SPEED_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SPEED_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_SPEED_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_SPEED_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_SPEED_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, } @@ -215,7 +216,11 @@ async def async_setup_platform( """Set up MQTT fans configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + fan.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -226,13 +231,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index f6d4aa01dab..1c9ec5dc201 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -1,7 +1,6 @@ """Support for MQTT humidifiers.""" from __future__ import annotations -import asyncio import functools import logging @@ -30,8 +29,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -46,11 +45,13 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate +from .util import valid_publish_topic, valid_subscribe_topic CONF_AVAILABLE_MODES_LIST = "modes" CONF_DEVICE_CLASS = "device_class" @@ -103,15 +104,13 @@ def valid_humidity_range_configuration(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( { # CONF_AVAIALABLE_MODES_LIST and CONF_MODE_COMMAND_TOPIC must be used together vol.Inclusive( CONF_AVAILABLE_MODES_LIST, "available_modes", default=[] ): cv.ensure_list, - vol.Inclusive( - CONF_MODE_COMMAND_TOPIC, "available_modes" - ): mqtt.valid_publish_topic, + vol.Inclusive(CONF_MODE_COMMAND_TOPIC, "available_modes"): valid_publish_topic, vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Optional( CONF_DEVICE_CLASS, default=HumidifierDeviceClass.HUMIDIFIER @@ -119,14 +118,14 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( [HumidifierDeviceClass.HUMIDIFIER, HumidifierDeviceClass.DEHUMIDIFIER] ), vol.Optional(CONF_MODE_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_MODE_STATE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, - vol.Required(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Required(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_TARGET_HUMIDITY_COMMAND_TEMPLATE): cv.template, vol.Optional( CONF_TARGET_HUMIDITY_MAX, default=DEFAULT_MAX_HUMIDITY @@ -135,7 +134,7 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( CONF_TARGET_HUMIDITY_MIN, default=DEFAULT_MIN_HUMIDITY ): cv.positive_int, vol.Optional(CONF_TARGET_HUMIDITY_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): valid_subscribe_topic, vol.Optional( CONF_PAYLOAD_RESET_HUMIDITY, default=DEFAULT_PAYLOAD_RESET ): cv.string, @@ -173,7 +172,11 @@ async def async_setup_platform( """Set up MQTT humidifier configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, humidifier.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + humidifier.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -184,14 +187,12 @@ async def async_setup_entry( ) -> None: """Set up MQTT humidifier through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, humidifier.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, humidifier.DOMAIN, PLATFORM_SCHEMA_MODERN ) - ) # setup for discovery + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index ab2a3462615..158ea6ffa0d 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -1,7 +1,6 @@ """Support for MQTT lights.""" from __future__ import annotations -import asyncio import functools import voluptuous as vol @@ -14,8 +13,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from ..mixins import ( - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) @@ -97,7 +96,11 @@ async def async_setup_platform( """Set up MQTT light through configuration.yaml (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, light.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + light.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -108,13 +111,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT lights configured under the light platform key (deprecated).""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index eb4ec264981..1c94fa82f73 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -42,8 +42,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity import homeassistant.util.color as color_util -from .. import MqttCommandTemplate, MqttValueTemplate, subscription -from ... import mqtt +from .. import subscription +from ..config import MQTT_RW_SCHEMA from ..const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -55,6 +55,8 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity +from ..models import MqttCommandTemplate, MqttValueTemplate +from ..util import valid_publish_topic, valid_subscribe_topic from .schema import MQTT_LIGHT_SCHEMA_SCHEMA _LOGGER = logging.getLogger(__name__) @@ -156,28 +158,28 @@ VALUE_TEMPLATE_KEYS = [ ] _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_SCHEMA.extend( + MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BRIGHTNESS_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): valid_publish_topic, vol.Optional( CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE ): vol.All(vol.Coerce(int), vol.Range(min=1)), - vol.Optional(CONF_BRIGHTNESS_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_BRIGHTNESS_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_BRIGHTNESS_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_COLOR_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_COLOR_MODE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_COLOR_MODE_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_COLOR_TEMP_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_EFFECT_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_EFFECT_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_EFFECT_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_EFFECT_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_EFFECT_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_HS_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_HS_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_HS_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_HS_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_HS_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_MAX_MIREDS): cv.positive_int, vol.Optional(CONF_MIN_MIREDS): cv.positive_int, @@ -189,30 +191,30 @@ _PLATFORM_SCHEMA_BASE = ( vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, vol.Optional(CONF_RGB_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_RGB_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_RGB_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_RGB_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_RGB_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_RGB_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_RGBW_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_RGBW_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_RGBW_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_RGBW_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_RGBW_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_RGBW_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_RGBWW_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_RGBWW_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_RGBWW_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_RGBWW_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_RGBWW_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_RGBWW_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_WHITE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_WHITE_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_WHITE_SCALE, default=DEFAULT_WHITE_SCALE): vol.All( vol.Coerce(int), vol.Range(min=1) ), - vol.Optional(CONF_WHITE_VALUE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_WHITE_VALUE_COMMAND_TOPIC): valid_publish_topic, vol.Optional( CONF_WHITE_VALUE_SCALE, default=DEFAULT_WHITE_VALUE_SCALE ): vol.All(vol.Coerce(int), vol.Range(min=1)), - vol.Optional(CONF_WHITE_VALUE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_WHITE_VALUE_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_XY_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_XY_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_XY_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_XY_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template, }, ) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 2049818ab31..be49f1ad2e3 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -51,7 +51,7 @@ from homeassistant.helpers.typing import ConfigType import homeassistant.util.color as color_util from .. import subscription -from ... import mqtt +from ..config import DEFAULT_QOS, DEFAULT_RETAIN, MQTT_RW_SCHEMA from ..const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -61,6 +61,7 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity +from ..util import valid_subscribe_topic from .schema import MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import CONF_BRIGHTNESS_SCALE, MQTT_LIGHT_ATTRIBUTES_BLOCKED @@ -103,7 +104,7 @@ def valid_color_configuration(config): _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_SCHEMA.extend( + MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BRIGHTNESS, default=DEFAULT_BRIGHTNESS): cv.boolean, vol.Optional( @@ -126,12 +127,12 @@ _PLATFORM_SCHEMA_BASE = ( vol.Optional(CONF_MIN_MIREDS): cv.positive_int, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, - vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS): vol.All( + vol.Optional(CONF_QOS, default=DEFAULT_QOS): vol.All( vol.Coerce(int), vol.In([0, 1, 2]) ), - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, vol.Optional(CONF_RGB, default=DEFAULT_RGB): cv.boolean, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Inclusive(CONF_SUPPORTED_COLOR_MODES, "color_mode"): vol.All( cv.ensure_list, [vol.In(VALID_COLOR_MODES)], diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 0165bfc8efa..779f2f17e24 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -31,8 +31,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity import homeassistant.util.color as color_util -from .. import MqttValueTemplate, subscription -from ... import mqtt +from .. import subscription +from ..config import MQTT_RW_SCHEMA from ..const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -43,6 +43,7 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity +from ..models import MqttValueTemplate from .schema import MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import MQTT_LIGHT_ATTRIBUTES_BLOCKED @@ -67,7 +68,7 @@ CONF_RED_TEMPLATE = "red_template" CONF_WHITE_VALUE_TEMPLATE = "white_value_template" _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_SCHEMA.extend( + MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BLUE_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 5dc0a974d26..862e76635f7 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -1,7 +1,6 @@ """Support for MQTT locks.""" from __future__ import annotations -import asyncio import functools import voluptuous as vol @@ -15,8 +14,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -28,11 +27,12 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttValueTemplate CONF_PAYLOAD_LOCK = "payload_lock" CONF_PAYLOAD_UNLOCK = "payload_unlock" @@ -56,7 +56,7 @@ MQTT_LOCK_ATTRIBUTES_BLOCKED = frozenset( } ) -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -87,7 +87,11 @@ async def async_setup_platform( """Set up MQTT locks configured under the lock platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, lock.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + lock.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -98,13 +102,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT lock through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, lock.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, lock.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index a46debeae54..b0f17cc335b 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -2,6 +2,7 @@ from __future__ import annotations from abc import abstractmethod +import asyncio from collections.abc import Callable import json import logging @@ -27,10 +28,11 @@ from homeassistant.const import ( CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, ) -from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback from homeassistant.helpers import ( config_validation as cv, device_registry as dr, + discovery, entity_registry as er, ) from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED @@ -46,17 +48,14 @@ from homeassistant.helpers.entity import ( async_generate_entity_id, ) from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import async_setup_reload_service +from homeassistant.helpers.reload import ( + async_integration_yaml_config, + async_setup_reload_service, +) from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import ( - DATA_MQTT, - PLATFORMS, - MqttValueTemplate, - async_publish, - debug_info, - subscription, -) +from . import debug_info, subscription +from .client import async_publish from .const import ( ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_PAYLOAD, @@ -65,6 +64,7 @@ from .const import ( CONF_ENCODING, CONF_QOS, CONF_TOPIC, + DATA_MQTT, DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_NEEDED, DEFAULT_ENCODING, @@ -73,6 +73,7 @@ from .const import ( DOMAIN, MQTT_CONNECTED, MQTT_DISCONNECTED, + PLATFORMS, ) from .debug_info import log_message, log_messages from .discovery import ( @@ -82,7 +83,7 @@ from .discovery import ( clear_discovery_hash, set_discovery_hash, ) -from .models import PublishPayloadType, ReceiveMessage +from .models import MqttValueTemplate, PublishPayloadType, ReceiveMessage from .subscription import ( async_prepare_subscribe_topics, async_subscribe_topics, @@ -264,8 +265,44 @@ class SetupEntity(Protocol): """Define setup_entities type.""" +async def async_setup_platform_discovery( + hass: HomeAssistant, platform_domain: str, schema: vol.Schema +) -> CALLBACK_TYPE: + """Set up platform discovery for manual config.""" + + async def _async_discover_entities(event: Event | None) -> None: + """Discover entities for a platform.""" + if event: + # The platform has been reloaded + config_yaml = await async_integration_yaml_config(hass, DOMAIN) + if not config_yaml: + return + config_yaml = config_yaml.get(DOMAIN, {}) + else: + config_yaml = hass.data.get(DATA_MQTT_CONFIG, {}) + if not config_yaml: + return + if platform_domain not in config_yaml: + return + await asyncio.gather( + *( + discovery.async_load_platform(hass, platform_domain, DOMAIN, config, {}) + for config in await async_get_platform_config_from_yaml( + hass, platform_domain, schema, config_yaml + ) + ) + ) + + unsub = hass.bus.async_listen("event_mqtt_reloaded", _async_discover_entities) + await _async_discover_entities(None) + return unsub + + async def async_get_platform_config_from_yaml( - hass: HomeAssistant, domain: str, schema: vol.Schema + hass: HomeAssistant, + platform_domain: str, + schema: vol.Schema, + config_yaml: ConfigType = None, ) -> list[ConfigType]: """Return a list of validated configurations for the domain.""" @@ -279,12 +316,15 @@ async def async_get_platform_config_from_yaml( try: validated_config.append(schema(config_item)) except vol.MultipleInvalid as err: - async_log_exception(err, domain, config_item, hass) + async_log_exception(err, platform_domain, config_item, hass) return validated_config - config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {}) - if not (platform_configs := config_yaml.get(domain)): + if config_yaml is None: + config_yaml = hass.data.get(DATA_MQTT_CONFIG) + if not config_yaml: + return [] + if not (platform_configs := config_yaml.get(platform_domain)): return [] return async_validate_config(hass, platform_configs) @@ -314,7 +354,7 @@ async def async_setup_entry_helper(hass, domain, async_setup, schema): async def async_setup_platform_helper( hass: HomeAssistant, platform_domain: str, - config: ConfigType, + config: ConfigType | DiscoveryInfoType, async_add_entities: AddEntitiesCallback, async_setup_entities: SetupEntity, ) -> None: diff --git a/homeassistant/components/mqtt/models.py b/homeassistant/components/mqtt/models.py index 9cec65d7254..9bce6baab8b 100644 --- a/homeassistant/components/mqtt/models.py +++ b/homeassistant/components/mqtt/models.py @@ -1,12 +1,21 @@ """Models used by multiple MQTT modules.""" from __future__ import annotations +from ast import literal_eval from collections.abc import Awaitable, Callable import datetime as dt -from typing import Union +from typing import Any, Union import attr +from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import template +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.typing import TemplateVarsType + +_SENTINEL = object() + PublishPayloadType = Union[str, bytes, int, float, None] ReceivePayloadType = Union[str, bytes] @@ -35,3 +44,118 @@ class ReceiveMessage: AsyncMessageCallbackType = Callable[[ReceiveMessage], Awaitable[None]] MessageCallbackType = Callable[[ReceiveMessage], None] + + +class MqttCommandTemplate: + """Class for rendering MQTT payload with command templates.""" + + def __init__( + self, + command_template: template.Template | None, + *, + hass: HomeAssistant | None = None, + entity: Entity | None = None, + ) -> None: + """Instantiate a command template.""" + self._attr_command_template = command_template + if command_template is None: + return + + self._entity = entity + + command_template.hass = hass + + if entity: + command_template.hass = entity.hass + + @callback + def async_render( + self, + value: PublishPayloadType = None, + variables: TemplateVarsType = None, + ) -> PublishPayloadType: + """Render or convert the command template with given value or variables.""" + + def _convert_outgoing_payload( + payload: PublishPayloadType, + ) -> PublishPayloadType: + """Ensure correct raw MQTT payload is passed as bytes for publishing.""" + if isinstance(payload, str): + try: + native_object = literal_eval(payload) + if isinstance(native_object, bytes): + return native_object + + except (ValueError, TypeError, SyntaxError, MemoryError): + pass + + return payload + + if self._attr_command_template is None: + return value + + values = {"value": value} + if self._entity: + values[ATTR_ENTITY_ID] = self._entity.entity_id + values[ATTR_NAME] = self._entity.name + if variables is not None: + values.update(variables) + return _convert_outgoing_payload( + self._attr_command_template.async_render(values, parse_result=False) + ) + + +class MqttValueTemplate: + """Class for rendering MQTT value template with possible json values.""" + + def __init__( + self, + value_template: template.Template | None, + *, + hass: HomeAssistant | None = None, + entity: Entity | None = None, + config_attributes: TemplateVarsType = None, + ) -> None: + """Instantiate a value template.""" + self._value_template = value_template + self._config_attributes = config_attributes + if value_template is None: + return + + value_template.hass = hass + self._entity = entity + + if entity: + value_template.hass = entity.hass + + @callback + def async_render_with_possible_json_value( + self, + payload: ReceivePayloadType, + default: ReceivePayloadType | object = _SENTINEL, + variables: TemplateVarsType = None, + ) -> ReceivePayloadType: + """Render with possible json value or pass-though a received MQTT value.""" + if self._value_template is None: + return payload + + values: dict[str, Any] = {} + + if variables is not None: + values.update(variables) + + if self._config_attributes is not None: + values.update(self._config_attributes) + + if self._entity: + values[ATTR_ENTITY_ID] = self._entity.entity_id + values[ATTR_NAME] = self._entity.name + + if default == _SENTINEL: + return self._value_template.async_render_with_possible_json_value( + payload, variables=values + ) + + return self._value_template.async_render_with_possible_json_value( + payload, default, variables=values + ) diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 001f9f4f668..1404dc86a3c 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -1,7 +1,6 @@ """Configure number in a device through MQTT topic.""" from __future__ import annotations -import asyncio import functools import logging @@ -27,8 +26,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -41,11 +40,12 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate _LOGGER = logging.getLogger(__name__) @@ -75,7 +75,7 @@ def validate_config(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): vol.Coerce(float), @@ -118,7 +118,11 @@ async def async_setup_platform( """Set up MQTT number configured under the number platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, number.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + number.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -129,12 +133,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT number through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, number.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, number.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/scene.py b/homeassistant/components/mqtt/scene.py index 98c692ceaff..9c4a212bd8e 100644 --- a/homeassistant/components/mqtt/scene.py +++ b/homeassistant/components/mqtt/scene.py @@ -1,7 +1,6 @@ """Support for MQTT scenes.""" from __future__ import annotations -import asyncio import functools import voluptuous as vol @@ -15,25 +14,27 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .. import mqtt +from .client import async_publish +from .config import MQTT_BASE_SCHEMA from .const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN from .mixins import ( CONF_ENABLED_BY_DEFAULT, CONF_OBJECT_ID, MQTT_AVAILABILITY_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .util import valid_publish_topic DEFAULT_NAME = "MQTT Scene" DEFAULT_RETAIN = False -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( { - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_ON): cv.string, @@ -63,7 +64,11 @@ async def async_setup_platform( """Set up MQTT scene configured under the scene platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, scene.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + scene.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -74,13 +79,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT scene through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, scene.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, scene.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( @@ -128,7 +128,7 @@ class MqttScene( This method is a coroutine. """ - await mqtt.async_publish( + await async_publish( self.hass, self._config[CONF_COMMAND_TOPIC], self._config[CONF_PAYLOAD_ON], diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index 0765eb7f176..994c11653b7 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -1,7 +1,6 @@ """Configure select in a device through MQTT topic.""" from __future__ import annotations -import asyncio import functools import logging @@ -17,8 +16,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -31,11 +30,12 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate _LOGGER = logging.getLogger(__name__) @@ -51,7 +51,7 @@ MQTT_SELECT_ATTRIBUTES_BLOCKED = frozenset( ) -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, @@ -79,7 +79,11 @@ async def async_setup_platform( """Set up MQTT select configured under the select platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, select.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + select.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -90,12 +94,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT select through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, select.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, select.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index d865d90c4ee..f9e0b5151bb 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -1,7 +1,6 @@ """Support for MQTT sensors.""" from __future__ import annotations -import asyncio from datetime import timedelta import functools import logging @@ -34,19 +33,21 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util -from . import MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RO_SCHEMA 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_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttValueTemplate +from .util import valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -89,12 +90,12 @@ def validate_options(conf): return conf -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RO_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, - vol.Optional(CONF_LAST_RESET_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_LAST_RESET_TOPIC): valid_subscribe_topic, vol.Optional(CONF_LAST_RESET_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, @@ -131,7 +132,11 @@ async def async_setup_platform( """Set up MQTT sensors configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, sensor.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + sensor.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -142,12 +147,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT sensor through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, sensor.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, sensor.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index c3a41c3618e..fef2a4fb3dd 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -1,7 +1,6 @@ """Support for MQTT sirens.""" from __future__ import annotations -import asyncio import copy import functools import json @@ -35,8 +34,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttCommandTemplate, MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, @@ -52,11 +51,12 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttCommandTemplate, MqttValueTemplate DEFAULT_NAME = "MQTT Siren" DEFAULT_PAYLOAD_ON = "ON" @@ -74,7 +74,7 @@ CONF_SUPPORT_VOLUME_SET = "support_volume_set" STATE = "state" -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_AVAILABLE_TONES): cv.ensure_list, vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, @@ -128,7 +128,11 @@ async def async_setup_platform( """Set up MQTT sirens configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, siren.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + siren.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -139,13 +143,8 @@ async def async_setup_entry( ) -> None: """Set up MQTT siren through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, siren.DOMAIN, PLATFORM_SCHEMA_MODERN - ) - ) + config_entry.async_on_unload( + await async_setup_platform_discovery(hass, siren.DOMAIN, PLATFORM_SCHEMA_MODERN) ) # setup for discovery setup = functools.partial( diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index f5f8363eb33..be7fc655e1e 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -1,7 +1,6 @@ """Support for MQTT switches.""" from __future__ import annotations -import asyncio import functools import voluptuous as vol @@ -24,8 +23,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_RW_SCHEMA from .const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -38,11 +37,12 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import MqttValueTemplate DEFAULT_NAME = "MQTT Switch" DEFAULT_PAYLOAD_ON = "ON" @@ -51,7 +51,7 @@ DEFAULT_OPTIMISTIC = False CONF_STATE_ON = "state_on" CONF_STATE_OFF = "state_off" -PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -82,7 +82,11 @@ async def async_setup_platform( """Set up MQTT switch configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, switch.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + switch.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -93,12 +97,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT switch through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, switch.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, switch.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/tag.py b/homeassistant/components/mqtt/tag.py index 5bfbbd73bce..9452d5fc259 100644 --- a/homeassistant/components/mqtt/tag.py +++ b/homeassistant/components/mqtt/tag.py @@ -11,8 +11,8 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -from . import MqttValueTemplate, subscription -from .. import mqtt +from . import subscription +from .config import MQTT_BASE_SCHEMA from .const import ATTR_DISCOVERY_HASH, CONF_QOS, CONF_TOPIC from .mixins import ( MQTT_ENTITY_DEVICE_INFO_SCHEMA, @@ -21,7 +21,7 @@ from .mixins import ( send_discovery_done, update_device, ) -from .models import ReceiveMessage +from .models import MqttValueTemplate, ReceiveMessage from .subscription import EntitySubscription from .util import valid_subscribe_topic @@ -30,7 +30,7 @@ LOG_NAME = "Tag" TAG = "tag" TAGS = "mqtt_tags" -PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_PLATFORM): "mqtt", diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index 34205ab7780..206a15a024a 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -1,7 +1,6 @@ """Support for MQTT vacuums.""" from __future__ import annotations -import asyncio import functools import voluptuous as vol @@ -13,8 +12,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from ..mixins import ( - async_get_platform_config_from_yaml, async_setup_entry_helper, + async_setup_platform_discovery, async_setup_platform_helper, ) from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE @@ -77,7 +76,11 @@ async def async_setup_platform( """Set up MQTT vacuum through configuration.yaml.""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( - hass, vacuum.DOMAIN, config, async_add_entities, _async_setup_entity + hass, + vacuum.DOMAIN, + discovery_info or config, + async_add_entities, + _async_setup_entity, ) @@ -88,12 +91,9 @@ async def async_setup_entry( ) -> None: """Set up MQTT vacuum through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - await asyncio.gather( - *( - _async_setup_entity(hass, async_add_entities, config, config_entry) - for config in await async_get_platform_config_from_yaml( - hass, vacuum.DOMAIN, PLATFORM_SCHEMA_MODERN - ) + config_entry.async_on_unload( + await async_setup_platform_discovery( + hass, vacuum.DOMAIN, PLATFORM_SCHEMA_MODERN ) ) # setup for discovery diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index eb5e01b6251..f25131c43b7 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -15,11 +15,13 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.icon import icon_for_battery_level -from .. import MqttValueTemplate, subscription -from ... import mqtt +from .. import subscription +from ..config import MQTT_BASE_SCHEMA from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema +from ..models import MqttValueTemplate +from ..util import valid_publish_topic from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services @@ -96,25 +98,23 @@ MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED = MQTT_VACUUM_ATTRIBUTES_BLOCKED | frozens ) PLATFORM_SCHEMA_LEGACY_MODERN = ( - mqtt.MQTT_BASE_SCHEMA.extend( + MQTT_BASE_SCHEMA.extend( { vol.Inclusive(CONF_BATTERY_LEVEL_TEMPLATE, "battery"): cv.template, - vol.Inclusive( - CONF_BATTERY_LEVEL_TOPIC, "battery" - ): mqtt.valid_publish_topic, + vol.Inclusive(CONF_BATTERY_LEVEL_TOPIC, "battery"): valid_publish_topic, vol.Inclusive(CONF_CHARGING_TEMPLATE, "charging"): cv.template, - vol.Inclusive(CONF_CHARGING_TOPIC, "charging"): mqtt.valid_publish_topic, + vol.Inclusive(CONF_CHARGING_TOPIC, "charging"): valid_publish_topic, vol.Inclusive(CONF_CLEANING_TEMPLATE, "cleaning"): cv.template, - vol.Inclusive(CONF_CLEANING_TOPIC, "cleaning"): mqtt.valid_publish_topic, + vol.Inclusive(CONF_CLEANING_TOPIC, "cleaning"): valid_publish_topic, vol.Inclusive(CONF_DOCKED_TEMPLATE, "docked"): cv.template, - vol.Inclusive(CONF_DOCKED_TOPIC, "docked"): mqtt.valid_publish_topic, + vol.Inclusive(CONF_DOCKED_TOPIC, "docked"): valid_publish_topic, vol.Inclusive(CONF_ERROR_TEMPLATE, "error"): cv.template, - vol.Inclusive(CONF_ERROR_TOPIC, "error"): mqtt.valid_publish_topic, + vol.Inclusive(CONF_ERROR_TOPIC, "error"): valid_publish_topic, vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All( cv.ensure_list, [cv.string] ), vol.Inclusive(CONF_FAN_SPEED_TEMPLATE, "fan_speed"): cv.template, - vol.Inclusive(CONF_FAN_SPEED_TOPIC, "fan_speed"): mqtt.valid_publish_topic, + vol.Inclusive(CONF_FAN_SPEED_TOPIC, "fan_speed"): valid_publish_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional( CONF_PAYLOAD_CLEAN_SPOT, default=DEFAULT_PAYLOAD_CLEAN_SPOT @@ -135,12 +135,12 @@ PLATFORM_SCHEMA_LEGACY_MODERN = ( vol.Optional( CONF_PAYLOAD_TURN_ON, default=DEFAULT_PAYLOAD_TURN_ON ): cv.string, - vol.Optional(CONF_SEND_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SET_FAN_SPEED_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SEND_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_SET_FAN_SPEED_TOPIC): valid_publish_topic, vol.Optional( CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS ): vol.All(cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]), - vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, } ) diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index 7aa7be07797..3d670780994 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -23,7 +23,7 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from .. import subscription -from ... import mqtt +from ..config import MQTT_BASE_SCHEMA from ..const import ( CONF_COMMAND_TOPIC, CONF_ENCODING, @@ -33,6 +33,7 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema +from ..util import valid_publish_topic from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services @@ -105,7 +106,7 @@ DEFAULT_PAYLOAD_START = "start" DEFAULT_PAYLOAD_PAUSE = "pause" PLATFORM_SCHEMA_STATE_MODERN = ( - mqtt.MQTT_BASE_SCHEMA.extend( + MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All( cv.ensure_list, [cv.string] @@ -123,13 +124,13 @@ PLATFORM_SCHEMA_STATE_MODERN = ( vol.Optional(CONF_PAYLOAD_START, default=DEFAULT_PAYLOAD_START): cv.string, vol.Optional(CONF_PAYLOAD_PAUSE, default=DEFAULT_PAYLOAD_PAUSE): cv.string, vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string, - vol.Optional(CONF_SEND_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SET_FAN_SPEED_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SEND_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_SET_FAN_SPEED_TOPIC): valid_publish_topic, + vol.Optional(CONF_STATE_TOPIC): valid_publish_topic, vol.Optional( CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS ): vol.All(cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]), - vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, } ) @@ -178,7 +179,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): supported_feature_strings, STRING_TO_SERVICE ) self._fan_speed_list = config[CONF_FAN_SPEED_LIST] - self._command_topic = config.get(mqtt.CONF_COMMAND_TOPIC) + self._command_topic = config.get(CONF_COMMAND_TOPIC) self._set_fan_speed_topic = config.get(CONF_SET_FAN_SPEED_TOPIC) self._send_command_topic = config.get(CONF_SEND_COMMAND_TOPIC) diff --git a/homeassistant/components/mqtt_json/device_tracker.py b/homeassistant/components/mqtt_json/device_tracker.py index 505fa3bd809..1d99e6d7b6f 100644 --- a/homeassistant/components/mqtt_json/device_tracker.py +++ b/homeassistant/components/mqtt_json/device_tracker.py @@ -35,7 +35,7 @@ GPS_JSON_PAYLOAD_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) -PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(mqtt.SCHEMA_BASE).extend( +PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(mqtt.config.SCHEMA_BASE).extend( {vol.Required(CONF_DEVICES): {cv.string: mqtt.valid_subscribe_topic}} ) diff --git a/homeassistant/components/mqtt_room/sensor.py b/homeassistant/components/mqtt_room/sensor.py index 8f7455eb998..276695d8edd 100644 --- a/homeassistant/components/mqtt_room/sensor.py +++ b/homeassistant/components/mqtt_room/sensor.py @@ -43,7 +43,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_AWAY_TIMEOUT, default=DEFAULT_AWAY_TIMEOUT): cv.positive_int, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, } -).extend(mqtt.MQTT_RO_PLATFORM_SCHEMA.schema) +).extend(mqtt.config.MQTT_RO_SCHEMA.schema) MQTT_PAYLOAD = vol.Schema( vol.All( diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 7df4cf57e56..7a096a9c404 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Iterable +from concurrent.futures import CancelledError import contextlib from datetime import datetime, timedelta import logging @@ -518,9 +519,16 @@ class Recorder(threading.Thread): def _wait_startup_or_shutdown(self) -> object | None: """Wait for startup or shutdown before starting.""" - return asyncio.run_coroutine_threadsafe( - self._async_wait_for_started(), self.hass.loop - ).result() + try: + return asyncio.run_coroutine_threadsafe( + self._async_wait_for_started(), self.hass.loop + ).result() + except CancelledError as ex: + _LOGGER.warning( + "Recorder startup was externally canceled before it could complete: %s", + ex, + ) + return SHUTDOWN_TASK def run(self) -> None: """Start processing events to save.""" diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index 0ceb013d8c5..835496c2d6e 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -36,7 +36,7 @@ def extract_include_exclude_filter_conf(conf: ConfigType) -> dict[str, Any]: """ return { filter_type: { - matcher: set(conf.get(filter_type, {}).get(matcher, [])) + matcher: set(conf.get(filter_type, {}).get(matcher) or []) for matcher in FITLER_MATCHERS } for filter_type in FILTER_TYPES @@ -88,14 +88,32 @@ class Filters: self.included_domains: Iterable[str] = [] self.included_entity_globs: Iterable[str] = [] + def __repr__(self) -> str: + """Return human readable excludes/includes.""" + return ( + f"" + ) + @property def has_config(self) -> bool: """Determine if there is any filter configuration.""" + return bool(self._have_exclude or self._have_include) + + @property + def _have_exclude(self) -> bool: return bool( self.excluded_entities or self.excluded_domains or self.excluded_entity_globs - or self.included_entities + ) + + @property + def _have_include(self) -> bool: + return bool( + self.included_entities or self.included_domains or self.included_entity_globs ) @@ -103,36 +121,67 @@ class Filters: def _generate_filter_for_columns( self, columns: Iterable[Column], encoder: Callable[[Any], Any] ) -> ClauseList: - includes = [] - if self.included_domains: - includes.append(_domain_matcher(self.included_domains, columns, encoder)) - if self.included_entities: - includes.append(_entity_matcher(self.included_entities, columns, encoder)) - if self.included_entity_globs: - includes.append( - _globs_to_like(self.included_entity_globs, columns, encoder) - ) + """Generate a filter from pre-comuted sets and pattern lists. - excludes = [] - if self.excluded_domains: - excludes.append(_domain_matcher(self.excluded_domains, columns, encoder)) - if self.excluded_entities: - excludes.append(_entity_matcher(self.excluded_entities, columns, encoder)) - if self.excluded_entity_globs: - excludes.append( - _globs_to_like(self.excluded_entity_globs, columns, encoder) - ) + This must match exactly how homeassistant.helpers.entityfilter works. + """ + i_domains = _domain_matcher(self.included_domains, columns, encoder) + i_entities = _entity_matcher(self.included_entities, columns, encoder) + i_entity_globs = _globs_to_like(self.included_entity_globs, columns, encoder) + includes = [i_domains, i_entities, i_entity_globs] - if not includes and not excludes: + e_domains = _domain_matcher(self.excluded_domains, columns, encoder) + e_entities = _entity_matcher(self.excluded_entities, columns, encoder) + e_entity_globs = _globs_to_like(self.excluded_entity_globs, columns, encoder) + excludes = [e_domains, e_entities, e_entity_globs] + + have_exclude = self._have_exclude + have_include = self._have_include + + # Case 1 - no includes or excludes - pass all entities + if not have_include and not have_exclude: return None - if includes and not excludes: + # Case 2 - includes, no excludes - only include specified entities + if have_include and not have_exclude: return or_(*includes).self_group() - if not includes and excludes: + # Case 3 - excludes, no includes - only exclude specified entities + if not have_include and have_exclude: return not_(or_(*excludes).self_group()) - return or_(*includes).self_group() & not_(or_(*excludes).self_group()) + # Case 4 - both includes and excludes specified + # Case 4a - include domain or glob specified + # - if domain is included, pass if entity not excluded + # - if glob is included, pass if entity and domain not excluded + # - if domain and glob are not included, pass if entity is included + # note: if both include domain matches then exclude domains ignored. + # If glob matches then exclude domains and glob checked + if self.included_domains or self.included_entity_globs: + return or_( + (i_domains & ~(e_entities | e_entity_globs)), + ( + ~i_domains + & or_( + (i_entity_globs & ~(or_(*excludes))), + (~i_entity_globs & i_entities), + ) + ), + ).self_group() + + # Case 4b - exclude domain or glob specified, include has no domain or glob + # In this one case the traditional include logic is inverted. Even though an + # include is specified since its only a list of entity IDs its used only to + # expose specific entities excluded by domain or glob. Any entities not + # excluded are then presumed included. Logic is as follows + # - if domain or glob is excluded, pass if entity is included + # - if domain is not excluded, pass if entity not excluded by ID + if self.excluded_domains or self.excluded_entity_globs: + return (not_(or_(*excludes)) | i_entities).self_group() + + # Case 4c - neither include or exclude domain specified + # - Only pass if entity is included. Ignore entity excludes. + return i_entities def states_entity_filter(self) -> ClauseList: """Generate the entity filter query.""" @@ -158,29 +207,32 @@ def _globs_to_like( glob_strs: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] ) -> ClauseList: """Translate glob to sql.""" - return or_( + matchers = [ cast(column, Text()).like( encoder(glob_str).translate(GLOB_TO_SQL_CHARS), escape="\\" ) for glob_str in glob_strs for column in columns - ) + ] + return or_(*matchers) if matchers else or_(False) def _entity_matcher( entity_ids: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] ) -> ClauseList: - return or_( + matchers = [ cast(column, Text()).in_([encoder(entity_id) for entity_id in entity_ids]) for column in columns - ) + ] + return or_(*matchers) if matchers else or_(False) def _domain_matcher( domains: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] ) -> ClauseList: - return or_( + matchers = [ cast(column, Text()).like(encoder(f"{domain}.%")) for domain in domains for column in columns - ) + ] + return or_(*matchers) if matchers else or_(False) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 7e8e97eafd4..49796bd0158 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -237,7 +237,9 @@ def _significant_states_stmt( stmt += _ignore_domains_filter if filters and filters.has_config: entity_filter = filters.states_entity_filter() - stmt += lambda q: q.filter(entity_filter) + stmt = stmt.add_criteria( + lambda q: q.filter(entity_filter), track_on=[filters] + ) stmt += lambda q: q.filter(States.last_updated > start_time) if end_time: @@ -529,7 +531,7 @@ def _get_states_for_all_stmt( stmt += _ignore_domains_filter if filters and filters.has_config: entity_filter = filters.states_entity_filter() - stmt += lambda q: q.filter(entity_filter) + stmt = stmt.add_criteria(lambda q: q.filter(entity_filter), track_on=[filters]) if join_attributes: stmt += lambda q: q.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index bc636d34b10..cc5af684566 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -715,14 +715,13 @@ def _apply_update( # noqa: C901 if engine.dialect.name == SupportedDialect.MYSQL: # Ensure the row format is dynamic or the index # unique will be too large - with session_scope(session=session_maker()) as session: - connection = session.connection() - # This is safe to run multiple times and fast since the table is small - connection.execute( - text( - "ALTER TABLE statistics_meta ENGINE=InnoDB, ROW_FORMAT=DYNAMIC" + with contextlib.suppress(SQLAlchemyError): + with session_scope(session=session_maker()) as session: + connection = session.connection() + # This is safe to run multiple times and fast since the table is small + connection.execute( + text("ALTER TABLE statistics_meta ROW_FORMAT=DYNAMIC") ) - ) try: _create_index( session_maker, "statistics_meta", "ix_statistics_meta_statistic_id" diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 70c816c2af5..8db648f15a8 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -93,6 +93,8 @@ TABLES_TO_CHECK = [ LAST_UPDATED_INDEX = "ix_states_last_updated" ENTITY_ID_LAST_UPDATED_INDEX = "ix_states_entity_id_last_updated" +EVENTS_CONTEXT_ID_INDEX = "ix_events_context_id" +STATES_CONTEXT_ID_INDEX = "ix_states_context_id" EMPTY_JSON_OBJECT = "{}" diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index cd129d82843..f331f980bb4 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -25,7 +25,6 @@ from homeassistant.components.media_player import ( ) from homeassistant.components.media_player.const import ( ATTR_INPUT_SOURCE, - ATTR_MEDIA_ANNOUNCE, ATTR_MEDIA_ENQUEUE, MEDIA_TYPE_ALBUM, MEDIA_TYPE_ARTIST, @@ -544,9 +543,6 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): """ # Use 'replace' as the default enqueue option enqueue = kwargs.get(ATTR_MEDIA_ENQUEUE, MediaPlayerEnqueue.REPLACE) - if kwargs.get(ATTR_MEDIA_ANNOUNCE): - # Temporary workaround until announce support is added - enqueue = MediaPlayerEnqueue.PLAY if spotify.is_spotify_media_type(media_type): media_type = spotify.resolve_spotify_media_type(media_type) diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json index a89934154e9..7fb78a4974b 100644 --- a/homeassistant/components/yolink/manifest.json +++ b/homeassistant/components/yolink/manifest.json @@ -3,7 +3,7 @@ "name": "YoLink", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yolink", - "requirements": ["yolink-api==0.0.5"], + "requirements": ["yolink-api==0.0.6"], "dependencies": ["auth", "application_credentials"], "codeowners": ["@matrixd2"], "iot_class": "cloud_push" diff --git a/homeassistant/const.py b/homeassistant/const.py index 428aa84d5fb..08ab335a33b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0" +PATCH_VERSION: Final = "1" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index ad2539233f4..076b58b5185 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220531.0 +home-assistant-frontend==20220601.0 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 6341528b09a..791875978ef 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -122,7 +122,7 @@ aioasuswrt==1.4.0 aioazuredevops==1.3.5 # homeassistant.components.baf -aiobafi6==0.3.0 +aiobafi6==0.5.0 # homeassistant.components.aws aiobotocore==2.1.0 @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220531.0 +home-assistant-frontend==20220601.0 # homeassistant.components.home_connect homeconnect==0.7.0 @@ -1538,7 +1538,7 @@ pyheos==0.7.2 pyhik==0.3.0 # homeassistant.components.hive -pyhiveapi==0.5.4 +pyhiveapi==0.5.5 # homeassistant.components.homematic pyhomematic==0.1.77 @@ -2486,7 +2486,7 @@ yeelight==0.7.10 yeelightsunflower==0.0.10 # homeassistant.components.yolink -yolink-api==0.0.5 +yolink-api==0.0.6 # homeassistant.components.youless youless-api==0.16 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b5767c1e468..4fa7c37963d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -109,7 +109,7 @@ aioasuswrt==1.4.0 aioazuredevops==1.3.5 # homeassistant.components.baf -aiobafi6==0.3.0 +aiobafi6==0.5.0 # homeassistant.components.aws aiobotocore==2.1.0 @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220531.0 +home-assistant-frontend==20220601.0 # homeassistant.components.home_connect homeconnect==0.7.0 @@ -1029,7 +1029,7 @@ pyhaversion==22.4.1 pyheos==0.7.2 # homeassistant.components.hive -pyhiveapi==0.5.4 +pyhiveapi==0.5.5 # homeassistant.components.homematic pyhomematic==0.1.77 @@ -1638,7 +1638,7 @@ yalexs==1.1.25 yeelight==0.7.10 # homeassistant.components.yolink -yolink-api==0.0.5 +yolink-api==0.0.6 # homeassistant.components.youless youless-api==0.16 diff --git a/rootfs/etc/services.d/home-assistant/finish b/rootfs/etc/services.d/home-assistant/finish index 115d8352618..057957a9c03 100755 --- a/rootfs/etc/services.d/home-assistant/finish +++ b/rootfs/etc/services.d/home-assistant/finish @@ -2,24 +2,24 @@ # ============================================================================== # Take down the S6 supervision tree when Home Assistant fails # ============================================================================== -declare RESTART_EXIT_CODE 100 -declare SIGNAL_EXIT_CODE 256 -declare SIGTERM 15 +declare RESTART_EXIT_CODE=100 +declare SIGNAL_EXIT_CODE=256 +declare SIGTERM=15 declare APP_EXIT_CODE=${1} -declare SYS_EXIT_CODE=${2+x} +declare SIGNAL_NO=${2} declare NEW_EXIT_CODE= -bashio::log.info "Home Assistant Core finish process exit code ${1}" +bashio::log.info "Home Assistant Core finish process exit code ${APP_EXIT_CODE}" if [[ ${APP_EXIT_CODE} -eq ${RESTART_EXIT_CODE} ]]; then exit 0 elif [[ ${APP_EXIT_CODE} -eq ${SIGNAL_EXIT_CODE} ]]; then - bashio::log.info "Home Assistant Core finish process received signal ${APP_EXIT_CODE}" + bashio::log.info "Home Assistant Core finish process received signal ${SIGNAL_NO}" - NEW_EXIT_CODE=$((128 + SYS_EXIT_CODE)) + NEW_EXIT_CODE=$((128 + SIGNAL_NO)) echo ${NEW_EXIT_CODE} > /run/s6-linux-init-container-results/exitcode - if [[ ${NEW_EXIT_CODE} -eq ${SIGTERM} ]]; then + if [[ ${SIGNAL_NO} -eq ${SIGTERM} ]]; then /run/s6/basedir/bin/halt fi else diff --git a/setup.cfg b/setup.cfg index 7f1e739803b..aeafbd53565 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0 +version = 2022.6.1 url = https://www.home-assistant.io/ [options] diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index cbc5e86c37e..9dc7af59a38 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -246,15 +246,11 @@ def test_get_significant_states_exclude(hass_history): def test_get_significant_states_exclude_include_entity(hass_history): """Test significant states when excluding domains and include entities. - We should not get back every thermostat and media player test changes. + We should not get back every thermostat change unless its specifically included """ hass = hass_history zero, four, states = record_states(hass) - del states["media_player.test2"] - del states["media_player.test3"] - del states["thermostat.test"] del states["thermostat.test2"] - del states["script.can_cancel_this_one"] config = history.CONFIG_SCHEMA( { @@ -340,14 +336,12 @@ def test_get_significant_states_include(hass_history): def test_get_significant_states_include_exclude_domain(hass_history): """Test if significant states when excluding and including domains. - We should not get back any changes since we include only the - media_player domain but also exclude it. + We should get back all the media_player domain changes + only since the include wins over the exclude but will + exclude everything else. """ hass = hass_history zero, four, states = record_states(hass) - del states["media_player.test"] - del states["media_player.test2"] - del states["media_player.test3"] del states["thermostat.test"] del states["thermostat.test2"] del states["script.can_cancel_this_one"] @@ -372,7 +366,6 @@ def test_get_significant_states_include_exclude_entity(hass_history): """ hass = hass_history zero, four, states = record_states(hass) - del states["media_player.test"] del states["media_player.test2"] del states["media_player.test3"] del states["thermostat.test"] @@ -394,12 +387,12 @@ def test_get_significant_states_include_exclude_entity(hass_history): def test_get_significant_states_include_exclude(hass_history): """Test if significant states when in/excluding domains and entities. - We should only get back changes of the media_player.test2 entity. + We should get back changes of the media_player.test2, media_player.test3, + and thermostat.test. """ hass = hass_history zero, four, states = record_states(hass) del states["media_player.test"] - del states["thermostat.test"] del states["thermostat.test2"] del states["script.can_cancel_this_one"] diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index b375a8f63c4..f824ee552ca 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -357,7 +357,7 @@ async def test_measure_multiple(hass, recorder_mock): await hass.async_block_till_done() assert hass.states.get("sensor.sensor1").state == "0.5" - assert hass.states.get("sensor.sensor2").state == STATE_UNKNOWN + assert hass.states.get("sensor.sensor2").state == "0.0" assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "50.0" diff --git a/tests/components/hive/test_config_flow.py b/tests/components/hive/test_config_flow.py index bb567b0bdfc..51ceec43ad2 100644 --- a/tests/components/hive/test_config_flow.py +++ b/tests/components/hive/test_config_flow.py @@ -33,6 +33,9 @@ async def test_import_flow(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.device_registration", + return_value=True, ), patch( "homeassistant.components.hive.config_flow.Auth.getDeviceData", return_value=[ @@ -93,6 +96,9 @@ async def test_user_flow(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.device_registration", + return_value=True, ), patch( "homeassistant.components.hive.config_flow.Auth.getDeviceData", return_value=[ @@ -172,6 +178,9 @@ async def test_user_flow_2fa(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.device_registration", + return_value=True, ), patch( "homeassistant.components.hive.config_flow.Auth.getDeviceData", return_value=[ @@ -256,6 +265,9 @@ async def test_reauth_flow(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.device_registration", + return_value=True, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -361,6 +373,9 @@ async def test_user_flow_2fa_send_new_code(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.device_registration", + return_value=True, ), patch( "homeassistant.components.hive.config_flow.Auth.getDeviceData", return_value=[ diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 2903f29f5dc..651a00fb0cf 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -11,7 +11,7 @@ from unittest.mock import Mock, patch import pytest import voluptuous as vol -from homeassistant.components import logbook +from homeassistant.components import logbook, recorder from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED from homeassistant.components.logbook.models import LazyEventPartialState @@ -2037,7 +2037,7 @@ async def test_include_events_domain_glob(hass, hass_client, recorder_mock): _assert_entry(entries[3], name="included", entity_id=entity_id3) -async def test_include_exclude_events(hass, hass_client, recorder_mock): +async def test_include_exclude_events_no_globs(hass, hass_client, recorder_mock): """Test if events are filtered if include and exclude is configured.""" entity_id = "switch.bla" entity_id2 = "sensor.blu" @@ -2082,13 +2082,15 @@ async def test_include_exclude_events(hass, hass_client, recorder_mock): client = await hass_client() entries = await _async_fetch_logbook(client) - assert len(entries) == 4 + assert len(entries) == 6 _assert_entry( entries[0], name="Home Assistant", message="started", domain=ha.DOMAIN ) - _assert_entry(entries[1], name="blu", entity_id=entity_id2, state="10") - _assert_entry(entries[2], name="blu", entity_id=entity_id2, state="20") - _assert_entry(entries[3], name="keep", entity_id=entity_id4, state="10") + _assert_entry(entries[1], name="bla", entity_id=entity_id, state="10") + _assert_entry(entries[2], name="blu", entity_id=entity_id2, state="10") + _assert_entry(entries[3], name="bla", entity_id=entity_id, state="20") + _assert_entry(entries[4], name="blu", entity_id=entity_id2, state="20") + _assert_entry(entries[5], name="keep", entity_id=entity_id4, state="10") async def test_include_exclude_events_with_glob_filters( @@ -2145,13 +2147,15 @@ async def test_include_exclude_events_with_glob_filters( client = await hass_client() entries = await _async_fetch_logbook(client) - assert len(entries) == 4 + assert len(entries) == 6 _assert_entry( entries[0], name="Home Assistant", message="started", domain=ha.DOMAIN ) - _assert_entry(entries[1], name="blu", entity_id=entity_id2, state="10") - _assert_entry(entries[2], name="blu", entity_id=entity_id2, state="20") - _assert_entry(entries[3], name="included", entity_id=entity_id4, state="30") + _assert_entry(entries[1], name="bla", entity_id=entity_id, state="10") + _assert_entry(entries[2], name="blu", entity_id=entity_id2, state="10") + _assert_entry(entries[3], name="bla", entity_id=entity_id, state="20") + _assert_entry(entries[4], name="blu", entity_id=entity_id2, state="20") + _assert_entry(entries[5], name="included", entity_id=entity_id4, state="30") async def test_empty_config(hass, hass_client, recorder_mock): @@ -2796,3 +2800,39 @@ async def test_get_events_with_context_state(hass, hass_ws_client, recorder_mock assert results[3]["context_state"] == "off" assert results[3]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" assert "context_event_type" not in results[3] + + +async def test_logbook_with_empty_config(hass, recorder_mock): + """Test we handle a empty configuration.""" + assert await async_setup_component( + hass, + logbook.DOMAIN, + { + logbook.DOMAIN: {}, + recorder.DOMAIN: {}, + }, + ) + await hass.async_block_till_done() + + +async def test_logbook_with_non_iterable_entity_filter(hass, recorder_mock): + """Test we handle a non-iterable entity filter.""" + assert await async_setup_component( + hass, + logbook.DOMAIN, + { + logbook.DOMAIN: { + CONF_EXCLUDE: { + CONF_ENTITIES: ["light.additional_excluded"], + } + }, + recorder.DOMAIN: { + CONF_EXCLUDE: { + CONF_ENTITIES: None, + CONF_DOMAINS: None, + CONF_ENTITY_GLOBS: None, + } + }, + }, + ) + await hass.async_block_till_done() diff --git a/tests/components/manual_mqtt/test_alarm_control_panel.py b/tests/components/manual_mqtt/test_alarm_control_panel.py index 3572077bd8e..4296f76d741 100644 --- a/tests/components/manual_mqtt/test_alarm_control_panel.py +++ b/tests/components/manual_mqtt/test_alarm_control_panel.py @@ -26,9 +26,9 @@ from tests.components.alarm_control_panel import common CODE = "HELLO_CODE" -async def test_fail_setup_without_state_topic(hass, mqtt_mock): +async def test_fail_setup_without_state_topic(hass, mqtt_mock_entry_with_yaml_config): """Test for failing with no state topic.""" - with assert_setup_component(0) as config: + with assert_setup_component(0, alarm_control_panel.DOMAIN) as config: assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -42,9 +42,9 @@ async def test_fail_setup_without_state_topic(hass, mqtt_mock): assert not config[alarm_control_panel.DOMAIN] -async def test_fail_setup_without_command_topic(hass, mqtt_mock): +async def test_fail_setup_without_command_topic(hass, mqtt_mock_entry_with_yaml_config): """Test failing with no command topic.""" - with assert_setup_component(0): + with assert_setup_component(0, alarm_control_panel.DOMAIN): assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -57,7 +57,7 @@ async def test_fail_setup_without_command_topic(hass, mqtt_mock): ) -async def test_arm_home_no_pending(hass, mqtt_mock): +async def test_arm_home_no_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -86,7 +86,9 @@ async def test_arm_home_no_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME -async def test_arm_home_no_pending_when_code_not_req(hass, mqtt_mock): +async def test_arm_home_no_pending_when_code_not_req( + hass, mqtt_mock_entry_with_yaml_config +): """Test arm home method.""" assert await async_setup_component( hass, @@ -116,7 +118,7 @@ async def test_arm_home_no_pending_when_code_not_req(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME -async def test_arm_home_with_pending(hass, mqtt_mock): +async def test_arm_home_with_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -158,7 +160,7 @@ async def test_arm_home_with_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME -async def test_arm_home_with_invalid_code(hass, mqtt_mock): +async def test_arm_home_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config): """Attempt to arm home without a valid code.""" assert await async_setup_component( hass, @@ -187,7 +189,7 @@ async def test_arm_home_with_invalid_code(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_arm_away_no_pending(hass, mqtt_mock): +async def test_arm_away_no_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -216,7 +218,9 @@ async def test_arm_away_no_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY -async def test_arm_away_no_pending_when_code_not_req(hass, mqtt_mock): +async def test_arm_away_no_pending_when_code_not_req( + hass, mqtt_mock_entry_with_yaml_config +): """Test arm home method.""" assert await async_setup_component( hass, @@ -246,7 +250,7 @@ async def test_arm_away_no_pending_when_code_not_req(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY -async def test_arm_home_with_template_code(hass, mqtt_mock): +async def test_arm_home_with_template_code(hass, mqtt_mock_entry_with_yaml_config): """Attempt to arm with a template-based code.""" assert await async_setup_component( hass, @@ -276,7 +280,7 @@ async def test_arm_home_with_template_code(hass, mqtt_mock): assert state.state == STATE_ALARM_ARMED_HOME -async def test_arm_away_with_pending(hass, mqtt_mock): +async def test_arm_away_with_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -318,7 +322,7 @@ async def test_arm_away_with_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY -async def test_arm_away_with_invalid_code(hass, mqtt_mock): +async def test_arm_away_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config): """Attempt to arm away without a valid code.""" assert await async_setup_component( hass, @@ -347,7 +351,7 @@ async def test_arm_away_with_invalid_code(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_arm_night_no_pending(hass, mqtt_mock): +async def test_arm_night_no_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm night method.""" assert await async_setup_component( hass, @@ -376,7 +380,9 @@ async def test_arm_night_no_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT -async def test_arm_night_no_pending_when_code_not_req(hass, mqtt_mock): +async def test_arm_night_no_pending_when_code_not_req( + hass, mqtt_mock_entry_with_yaml_config +): """Test arm night method.""" assert await async_setup_component( hass, @@ -406,7 +412,7 @@ async def test_arm_night_no_pending_when_code_not_req(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT -async def test_arm_night_with_pending(hass, mqtt_mock): +async def test_arm_night_with_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm night method.""" assert await async_setup_component( hass, @@ -454,7 +460,7 @@ async def test_arm_night_with_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT -async def test_arm_night_with_invalid_code(hass, mqtt_mock): +async def test_arm_night_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config): """Attempt to arm night without a valid code.""" assert await async_setup_component( hass, @@ -483,7 +489,7 @@ async def test_arm_night_with_invalid_code(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_no_pending(hass, mqtt_mock): +async def test_trigger_no_pending(hass, mqtt_mock_entry_with_yaml_config): """Test triggering when no pending submitted method.""" assert await async_setup_component( hass, @@ -521,7 +527,7 @@ async def test_trigger_no_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED -async def test_trigger_with_delay(hass, mqtt_mock): +async def test_trigger_with_delay(hass, mqtt_mock_entry_with_yaml_config): """Test trigger method and switch from pending to triggered.""" assert await async_setup_component( hass, @@ -569,7 +575,7 @@ async def test_trigger_with_delay(hass, mqtt_mock): assert state.state == STATE_ALARM_TRIGGERED -async def test_trigger_zero_trigger_time(hass, mqtt_mock): +async def test_trigger_zero_trigger_time(hass, mqtt_mock_entry_with_yaml_config): """Test disabled trigger.""" assert await async_setup_component( hass, @@ -598,7 +604,9 @@ async def test_trigger_zero_trigger_time(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_zero_trigger_time_with_pending(hass, mqtt_mock): +async def test_trigger_zero_trigger_time_with_pending( + hass, mqtt_mock_entry_with_yaml_config +): """Test disabled trigger.""" assert await async_setup_component( hass, @@ -627,7 +635,7 @@ async def test_trigger_zero_trigger_time_with_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_with_pending(hass, mqtt_mock): +async def test_trigger_with_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -679,7 +687,9 @@ async def test_trigger_with_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_with_disarm_after_trigger(hass, mqtt_mock): +async def test_trigger_with_disarm_after_trigger( + hass, mqtt_mock_entry_with_yaml_config +): """Test disarm after trigger.""" assert await async_setup_component( hass, @@ -718,7 +728,9 @@ async def test_trigger_with_disarm_after_trigger(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_with_zero_specific_trigger_time(hass, mqtt_mock): +async def test_trigger_with_zero_specific_trigger_time( + hass, mqtt_mock_entry_with_yaml_config +): """Test trigger method.""" assert await async_setup_component( hass, @@ -748,7 +760,9 @@ async def test_trigger_with_zero_specific_trigger_time(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_with_unused_zero_specific_trigger_time(hass, mqtt_mock): +async def test_trigger_with_unused_zero_specific_trigger_time( + hass, mqtt_mock_entry_with_yaml_config +): """Test disarm after trigger.""" assert await async_setup_component( hass, @@ -788,7 +802,9 @@ async def test_trigger_with_unused_zero_specific_trigger_time(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_trigger_with_specific_trigger_time(hass, mqtt_mock): +async def test_trigger_with_specific_trigger_time( + hass, mqtt_mock_entry_with_yaml_config +): """Test disarm after trigger.""" assert await async_setup_component( hass, @@ -827,7 +843,9 @@ async def test_trigger_with_specific_trigger_time(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_back_to_back_trigger_with_no_disarm_after_trigger(hass, mqtt_mock): +async def test_back_to_back_trigger_with_no_disarm_after_trigger( + hass, mqtt_mock_entry_with_yaml_config +): """Test no disarm after back to back trigger.""" assert await async_setup_component( hass, @@ -886,7 +904,7 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger(hass, mqtt_mock assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY -async def test_disarm_while_pending_trigger(hass, mqtt_mock): +async def test_disarm_while_pending_trigger(hass, mqtt_mock_entry_with_yaml_config): """Test disarming while pending state.""" assert await async_setup_component( hass, @@ -929,7 +947,9 @@ async def test_disarm_while_pending_trigger(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_disarm_during_trigger_with_invalid_code(hass, mqtt_mock): +async def test_disarm_during_trigger_with_invalid_code( + hass, mqtt_mock_entry_with_yaml_config +): """Test disarming while code is invalid.""" assert await async_setup_component( hass, @@ -973,7 +993,9 @@ async def test_disarm_during_trigger_with_invalid_code(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED -async def test_trigger_with_unused_specific_delay(hass, mqtt_mock): +async def test_trigger_with_unused_specific_delay( + hass, mqtt_mock_entry_with_yaml_config +): """Test trigger method and switch from pending to triggered.""" assert await async_setup_component( hass, @@ -1022,7 +1044,7 @@ async def test_trigger_with_unused_specific_delay(hass, mqtt_mock): assert state.state == STATE_ALARM_TRIGGERED -async def test_trigger_with_specific_delay(hass, mqtt_mock): +async def test_trigger_with_specific_delay(hass, mqtt_mock_entry_with_yaml_config): """Test trigger method and switch from pending to triggered.""" assert await async_setup_component( hass, @@ -1071,7 +1093,7 @@ async def test_trigger_with_specific_delay(hass, mqtt_mock): assert state.state == STATE_ALARM_TRIGGERED -async def test_trigger_with_pending_and_delay(hass, mqtt_mock): +async def test_trigger_with_pending_and_delay(hass, mqtt_mock_entry_with_yaml_config): """Test trigger method and switch from pending to triggered.""" assert await async_setup_component( hass, @@ -1132,7 +1154,9 @@ async def test_trigger_with_pending_and_delay(hass, mqtt_mock): assert state.state == STATE_ALARM_TRIGGERED -async def test_trigger_with_pending_and_specific_delay(hass, mqtt_mock): +async def test_trigger_with_pending_and_specific_delay( + hass, mqtt_mock_entry_with_yaml_config +): """Test trigger method and switch from pending to triggered.""" assert await async_setup_component( hass, @@ -1194,7 +1218,7 @@ async def test_trigger_with_pending_and_specific_delay(hass, mqtt_mock): assert state.state == STATE_ALARM_TRIGGERED -async def test_armed_home_with_specific_pending(hass, mqtt_mock): +async def test_armed_home_with_specific_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -1230,7 +1254,7 @@ async def test_armed_home_with_specific_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME -async def test_armed_away_with_specific_pending(hass, mqtt_mock): +async def test_armed_away_with_specific_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -1266,7 +1290,9 @@ async def test_armed_away_with_specific_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY -async def test_armed_night_with_specific_pending(hass, mqtt_mock): +async def test_armed_night_with_specific_pending( + hass, mqtt_mock_entry_with_yaml_config +): """Test arm home method.""" assert await async_setup_component( hass, @@ -1302,7 +1328,7 @@ async def test_armed_night_with_specific_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT -async def test_trigger_with_specific_pending(hass, mqtt_mock): +async def test_trigger_with_specific_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( hass, @@ -1350,7 +1376,7 @@ async def test_trigger_with_specific_pending(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock): +async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock_entry_with_yaml_config): """Test pending state with and without zero trigger time.""" assert await async_setup_component( hass, @@ -1417,7 +1443,7 @@ async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock): assert state.state == STATE_ALARM_TRIGGERED -async def test_disarm_with_template_code(hass, mqtt_mock): +async def test_disarm_with_template_code(hass, mqtt_mock_entry_with_yaml_config): """Attempt to disarm with a valid or invalid template-based code.""" assert await async_setup_component( hass, @@ -1459,7 +1485,7 @@ async def test_disarm_with_template_code(hass, mqtt_mock): assert state.state == STATE_ALARM_DISARMED -async def test_arm_home_via_command_topic(hass, mqtt_mock): +async def test_arm_home_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): """Test arming home via command topic.""" assert await async_setup_component( hass, @@ -1498,7 +1524,7 @@ async def test_arm_home_via_command_topic(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME -async def test_arm_away_via_command_topic(hass, mqtt_mock): +async def test_arm_away_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): """Test arming away via command topic.""" assert await async_setup_component( hass, @@ -1537,7 +1563,7 @@ async def test_arm_away_via_command_topic(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY -async def test_arm_night_via_command_topic(hass, mqtt_mock): +async def test_arm_night_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): """Test arming night via command topic.""" assert await async_setup_component( hass, @@ -1576,7 +1602,7 @@ async def test_arm_night_via_command_topic(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT -async def test_disarm_pending_via_command_topic(hass, mqtt_mock): +async def test_disarm_pending_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): """Test disarming pending alarm via command topic.""" assert await async_setup_component( hass, @@ -1610,7 +1636,9 @@ async def test_disarm_pending_via_command_topic(hass, mqtt_mock): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED -async def test_state_changes_are_published_to_mqtt(hass, mqtt_mock): +async def test_state_changes_are_published_to_mqtt( + hass, mqtt_mock_entry_with_yaml_config +): """Test publishing of MQTT messages when state changes.""" assert await async_setup_component( hass, @@ -1630,6 +1658,7 @@ async def test_state_changes_are_published_to_mqtt(hass, mqtt_mock): # Component should send disarmed alarm state on startup await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() mqtt_mock.async_publish.assert_called_once_with( "alarm/state", STATE_ALARM_DISARMED, 0, True ) diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index f4d76d5474c..2b013ddf8dd 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -112,9 +112,9 @@ DEFAULT_CONFIG_REMOTE_CODE_TEXT = { } -async def test_fail_setup_without_state_topic(hass, mqtt_mock): +async def test_fail_setup_without_state_topic(hass, mqtt_mock_entry_no_yaml_config): """Test for failing with no state topic.""" - with assert_setup_component(0) as config: + with assert_setup_component(0, alarm_control_panel.DOMAIN) as config: assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -125,12 +125,14 @@ async def test_fail_setup_without_state_topic(hass, mqtt_mock): } }, ) + await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert not config[alarm_control_panel.DOMAIN] -async def test_fail_setup_without_command_topic(hass, mqtt_mock): +async def test_fail_setup_without_command_topic(hass, mqtt_mock_entry_no_yaml_config): """Test failing with no command topic.""" - with assert_setup_component(0): + with assert_setup_component(0, alarm_control_panel.DOMAIN) as config: assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -141,9 +143,12 @@ async def test_fail_setup_without_command_topic(hass, mqtt_mock): } }, ) + await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() + assert not config[alarm_control_panel.DOMAIN] -async def test_update_state_via_state_topic(hass, mqtt_mock): +async def test_update_state_via_state_topic(hass, mqtt_mock_entry_with_yaml_config): """Test updating with via state topic.""" assert await async_setup_component( hass, @@ -151,6 +156,7 @@ async def test_update_state_via_state_topic(hass, mqtt_mock): DEFAULT_CONFIG, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() entity_id = "alarm_control_panel.test" @@ -172,7 +178,9 @@ async def test_update_state_via_state_topic(hass, mqtt_mock): assert hass.states.get(entity_id).state == state -async def test_ignore_update_state_if_unknown_via_state_topic(hass, mqtt_mock): +async def test_ignore_update_state_if_unknown_via_state_topic( + hass, mqtt_mock_entry_with_yaml_config +): """Test ignoring updates via state topic.""" assert await async_setup_component( hass, @@ -180,6 +188,7 @@ async def test_ignore_update_state_if_unknown_via_state_topic(hass, mqtt_mock): DEFAULT_CONFIG, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() entity_id = "alarm_control_panel.test" @@ -201,7 +210,9 @@ async def test_ignore_update_state_if_unknown_via_state_topic(hass, mqtt_mock): (SERVICE_ALARM_TRIGGER, "TRIGGER"), ], ) -async def test_publish_mqtt_no_code(hass, mqtt_mock, service, payload): +async def test_publish_mqtt_no_code( + hass, mqtt_mock_entry_with_yaml_config, service, payload +): """Test publishing of MQTT messages when no code is configured.""" assert await async_setup_component( hass, @@ -209,6 +220,7 @@ async def test_publish_mqtt_no_code(hass, mqtt_mock, service, payload): DEFAULT_CONFIG, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( alarm_control_panel.DOMAIN, @@ -232,7 +244,9 @@ async def test_publish_mqtt_no_code(hass, mqtt_mock, service, payload): (SERVICE_ALARM_TRIGGER, "TRIGGER"), ], ) -async def test_publish_mqtt_with_code(hass, mqtt_mock, service, payload): +async def test_publish_mqtt_with_code( + hass, mqtt_mock_entry_with_yaml_config, service, payload +): """Test publishing of MQTT messages when code is configured.""" assert await async_setup_component( hass, @@ -240,6 +254,7 @@ async def test_publish_mqtt_with_code(hass, mqtt_mock, service, payload): DEFAULT_CONFIG_CODE, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() call_count = mqtt_mock.async_publish.call_count # No code provided, should not publish @@ -282,7 +297,9 @@ async def test_publish_mqtt_with_code(hass, mqtt_mock, service, payload): (SERVICE_ALARM_TRIGGER, "TRIGGER"), ], ) -async def test_publish_mqtt_with_remote_code(hass, mqtt_mock, service, payload): +async def test_publish_mqtt_with_remote_code( + hass, mqtt_mock_entry_with_yaml_config, service, payload +): """Test publishing of MQTT messages when remode code is configured.""" assert await async_setup_component( hass, @@ -290,6 +307,7 @@ async def test_publish_mqtt_with_remote_code(hass, mqtt_mock, service, payload): DEFAULT_CONFIG_REMOTE_CODE, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() call_count = mqtt_mock.async_publish.call_count # No code provided, should not publish @@ -323,7 +341,9 @@ async def test_publish_mqtt_with_remote_code(hass, mqtt_mock, service, payload): (SERVICE_ALARM_TRIGGER, "TRIGGER"), ], ) -async def test_publish_mqtt_with_remote_code_text(hass, mqtt_mock, service, payload): +async def test_publish_mqtt_with_remote_code_text( + hass, mqtt_mock_entry_with_yaml_config, service, payload +): """Test publishing of MQTT messages when remote text code is configured.""" assert await async_setup_component( hass, @@ -331,6 +351,7 @@ async def test_publish_mqtt_with_remote_code_text(hass, mqtt_mock, service, payl DEFAULT_CONFIG_REMOTE_CODE_TEXT, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() call_count = mqtt_mock.async_publish.call_count # No code provided, should not publish @@ -365,7 +386,7 @@ async def test_publish_mqtt_with_remote_code_text(hass, mqtt_mock, service, payl ], ) async def test_publish_mqtt_with_code_required_false( - hass, mqtt_mock, service, payload, disable_code + hass, mqtt_mock_entry_with_yaml_config, service, payload, disable_code ): """Test publishing of MQTT messages when code is configured. @@ -380,6 +401,7 @@ async def test_publish_mqtt_with_code_required_false( config, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() # No code provided, should publish await hass.services.async_call( @@ -412,7 +434,9 @@ async def test_publish_mqtt_with_code_required_false( mqtt_mock.reset_mock() -async def test_disarm_publishes_mqtt_with_template(hass, mqtt_mock): +async def test_disarm_publishes_mqtt_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test publishing of MQTT messages while disarmed. When command_template set to output json @@ -428,6 +452,7 @@ async def test_disarm_publishes_mqtt_with_template(hass, mqtt_mock): config, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_alarm_disarm(hass, "0123") mqtt_mock.async_publish.assert_called_once_with( @@ -435,7 +460,9 @@ async def test_disarm_publishes_mqtt_with_template(hass, mqtt_mock): ) -async def test_update_state_via_state_topic_template(hass, mqtt_mock): +async def test_update_state_via_state_topic_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test updating with template_value via state topic.""" assert await async_setup_component( hass, @@ -456,6 +483,7 @@ async def test_update_state_via_state_topic_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("alarm_control_panel.test") assert state.state == STATE_UNKNOWN @@ -466,13 +494,14 @@ async def test_update_state_via_state_topic_template(hass, mqtt_mock): assert state.state == STATE_ALARM_ARMED_AWAY -async def test_attributes_code_number(hass, mqtt_mock): +async def test_attributes_code_number(hass, mqtt_mock_entry_with_yaml_config): """Test attributes which are not supported by the vacuum.""" config = copy.deepcopy(DEFAULT_CONFIG) config[alarm_control_panel.DOMAIN]["code"] = CODE_NUMBER assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("alarm_control_panel.test") assert ( @@ -481,13 +510,14 @@ async def test_attributes_code_number(hass, mqtt_mock): ) -async def test_attributes_remote_code_number(hass, mqtt_mock): +async def test_attributes_remote_code_number(hass, mqtt_mock_entry_with_yaml_config): """Test attributes which are not supported by the vacuum.""" config = copy.deepcopy(DEFAULT_CONFIG_REMOTE_CODE) config[alarm_control_panel.DOMAIN]["code"] = "REMOTE_CODE" assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("alarm_control_panel.test") assert ( @@ -496,13 +526,14 @@ async def test_attributes_remote_code_number(hass, mqtt_mock): ) -async def test_attributes_code_text(hass, mqtt_mock): +async def test_attributes_code_text(hass, mqtt_mock_entry_with_yaml_config): """Test attributes which are not supported by the vacuum.""" config = copy.deepcopy(DEFAULT_CONFIG) config[alarm_control_panel.DOMAIN]["code"] = CODE_TEXT assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("alarm_control_panel.test") assert ( @@ -511,81 +542,121 @@ async def test_attributes_code_text(hass, mqtt_mock): ) -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG_CODE, ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG_CODE, ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG_CODE, ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG_CODE, ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG, MQTT_ALARM_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one alarm per unique_id.""" config = { alarm_control_panel.DOMAIN: [ @@ -605,18 +676,22 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, alarm_control_panel.DOMAIN, config) - - -async def test_discovery_removal_alarm(hass, mqtt_mock, caplog): - """Test removal of discovered alarm_control_panel.""" - data = json.dumps(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) - await help_test_discovery_removal( - hass, mqtt_mock, caplog, alarm_control_panel.DOMAIN, data + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, alarm_control_panel.DOMAIN, config ) -async def test_discovery_update_alarm_topic_and_template(hass, mqtt_mock, caplog): +async def test_discovery_removal_alarm(hass, mqtt_mock_entry_no_yaml_config, caplog): + """Test removal of discovered alarm_control_panel.""" + data = json.dumps(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, alarm_control_panel.DOMAIN, data + ) + + +async def test_discovery_update_alarm_topic_and_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered alarm_control_panel.""" config1 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) @@ -639,7 +714,7 @@ async def test_discovery_update_alarm_topic_and_template(hass, mqtt_mock, caplog await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, alarm_control_panel.DOMAIN, config1, @@ -649,7 +724,9 @@ async def test_discovery_update_alarm_topic_and_template(hass, mqtt_mock, caplog ) -async def test_discovery_update_alarm_template(hass, mqtt_mock, caplog): +async def test_discovery_update_alarm_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered alarm_control_panel.""" config1 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) @@ -670,7 +747,7 @@ async def test_discovery_update_alarm_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, alarm_control_panel.DOMAIN, config1, @@ -680,7 +757,9 @@ async def test_discovery_update_alarm_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_unchanged_alarm(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_alarm( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered alarm_control_panel.""" config1 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) config1["name"] = "Beer" @@ -690,12 +769,17 @@ async def test_discovery_update_unchanged_alarm(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.alarm_control_panel.MqttAlarm.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, alarm_control_panel.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + alarm_control_panel.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -704,7 +788,12 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ' "command_topic": "test_topic" }' ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, alarm_control_panel.DOMAIN, data1, data2 + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + alarm_control_panel.DOMAIN, + data1, + data2, ) @@ -715,11 +804,13 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ("state_topic", "disarmed"), ], ) -async def test_encoding_subscribable_topics(hass, mqtt_mock, caplog, topic, value): +async def test_encoding_subscribable_topics( + hass, mqtt_mock_entry_with_yaml_config, caplog, topic, value +): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, alarm_control_panel.DOMAIN, DEFAULT_CONFIG[alarm_control_panel.DOMAIN], @@ -728,53 +819,62 @@ async def test_encoding_subscribable_topics(hass, mqtt_mock, caplog, topic, valu ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT alarm control panel device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT alarm control panel device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG, alarm_control_panel.SERVICE_ALARM_DISARM, @@ -807,7 +907,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -823,7 +923,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -837,11 +937,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = alarm_control_panel.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index e4a48b07940..ebb1d78138f 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -62,7 +62,9 @@ DEFAULT_CONFIG = { } -async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, caplog): +async def test_setting_sensor_value_expires_availability_topic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the expiration of the value.""" assert await async_setup_component( hass, @@ -79,6 +81,7 @@ async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNAVAILABLE @@ -89,10 +92,12 @@ async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNAVAILABLE - await expires_helper(hass, mqtt_mock, caplog) + await expires_helper(hass) -async def test_setting_sensor_value_expires(hass, mqtt_mock, caplog): +async def test_setting_sensor_value_expires( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the expiration of the value.""" assert await async_setup_component( hass, @@ -108,15 +113,16 @@ async def test_setting_sensor_value_expires(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() # State should be unavailable since expire_after is defined and > 0 state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNAVAILABLE - await expires_helper(hass, mqtt_mock, caplog) + await expires_helper(hass) -async def expires_helper(hass, mqtt_mock, caplog): +async def expires_helper(hass): """Run the basic expiry code.""" realnow = dt_util.utcnow() now = datetime(realnow.year + 1, 1, 1, 1, tzinfo=dt_util.UTC) @@ -168,9 +174,10 @@ async def expires_helper(hass, mqtt_mock, caplog): async def test_expiration_on_discovery_and_discovery_update_of_binary_sensor( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test that binary_sensor with expire_after set behaves correctly on discovery and discovery update.""" + await mqtt_mock_entry_no_yaml_config() config = { "name": "Test", "state_topic": "test-topic", @@ -247,7 +254,9 @@ async def test_expiration_on_discovery_and_discovery_update_of_binary_sensor( assert state.state == STATE_UNAVAILABLE -async def test_setting_sensor_value_via_mqtt_message(hass, mqtt_mock): +async def test_setting_sensor_value_via_mqtt_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the value via MQTT.""" assert await async_setup_component( hass, @@ -263,6 +272,7 @@ async def test_setting_sensor_value_via_mqtt_message(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") @@ -281,7 +291,9 @@ async def test_setting_sensor_value_via_mqtt_message(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_invalid_sensor_value_via_mqtt_message(hass, mqtt_mock, caplog): +async def test_invalid_sensor_value_via_mqtt_message( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the setting of the value via MQTT.""" assert await async_setup_component( hass, @@ -297,6 +309,7 @@ async def test_invalid_sensor_value_via_mqtt_message(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") @@ -319,7 +332,9 @@ async def test_invalid_sensor_value_via_mqtt_message(hass, mqtt_mock, caplog): assert "No matching payload found for entity" in caplog.text -async def test_setting_sensor_value_via_mqtt_message_and_template(hass, mqtt_mock): +async def test_setting_sensor_value_via_mqtt_message_and_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the value via MQTT.""" assert await async_setup_component( hass, @@ -337,6 +352,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template(hass, mqtt_moc }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNKNOWN @@ -351,7 +367,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template(hass, mqtt_moc async def test_setting_sensor_value_via_mqtt_message_and_template2( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the setting of the value via MQTT.""" assert await async_setup_component( @@ -369,6 +385,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template2( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNKNOWN @@ -388,7 +405,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template2( async def test_setting_sensor_value_via_mqtt_message_and_template_and_raw_state_encoding( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test processing a raw value via MQTT.""" assert await async_setup_component( @@ -407,6 +424,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template_and_raw_state_ }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNKNOWN @@ -421,7 +439,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template_and_raw_state_ async def test_setting_sensor_value_via_mqtt_message_empty_template( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the setting of the value via MQTT.""" assert await async_setup_component( @@ -439,6 +457,7 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") assert state.state == STATE_UNKNOWN @@ -453,7 +472,7 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template( assert state.state == STATE_ON -async def test_valid_device_class(hass, mqtt_mock): +async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of a valid sensor class.""" assert await async_setup_component( hass, @@ -468,12 +487,13 @@ async def test_valid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("binary_sensor.test") assert state.attributes.get("device_class") == "motion" -async def test_invalid_device_class(hass, mqtt_mock): +async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config): """Test the setting of an invalid sensor class.""" assert await async_setup_component( hass, @@ -488,40 +508,43 @@ async def test_invalid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("binary_sensor.test") assert state is None -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_force_update_disabled(hass, mqtt_mock): +async def test_force_update_disabled(hass, mqtt_mock_entry_with_yaml_config): """Test force update option.""" assert await async_setup_component( hass, @@ -537,6 +560,7 @@ async def test_force_update_disabled(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() events = [] @@ -556,7 +580,7 @@ async def test_force_update_disabled(hass, mqtt_mock): assert len(events) == 1 -async def test_force_update_enabled(hass, mqtt_mock): +async def test_force_update_enabled(hass, mqtt_mock_entry_with_yaml_config): """Test force update option.""" assert await async_setup_component( hass, @@ -573,6 +597,7 @@ async def test_force_update_enabled(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() events = [] @@ -592,7 +617,7 @@ async def test_force_update_enabled(hass, mqtt_mock): assert len(events) == 2 -async def test_off_delay(hass, mqtt_mock): +async def test_off_delay(hass, mqtt_mock_entry_with_yaml_config): """Test off_delay option.""" assert await async_setup_component( hass, @@ -610,6 +635,7 @@ async def test_off_delay(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() events = [] @@ -639,42 +665,60 @@ async def test_off_delay(hass, mqtt_mock): assert len(events) == 3 -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + binary_sensor.DOMAIN, + DEFAULT_CONFIG, ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + binary_sensor.DOMAIN, + DEFAULT_CONFIG, ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + binary_sensor.DOMAIN, + DEFAULT_CONFIG, ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one sensor per unique_id.""" config = { binary_sensor.DOMAIN: [ @@ -692,18 +736,24 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, binary_sensor.DOMAIN, config) - - -async def test_discovery_removal_binary_sensor(hass, mqtt_mock, caplog): - """Test removal of discovered binary_sensor.""" - data = json.dumps(DEFAULT_CONFIG[binary_sensor.DOMAIN]) - await help_test_discovery_removal( - hass, mqtt_mock, caplog, binary_sensor.DOMAIN, data + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, config ) -async def test_discovery_update_binary_sensor_topic_template(hass, mqtt_mock, caplog): +async def test_discovery_removal_binary_sensor( + hass, mqtt_mock_entry_no_yaml_config, caplog +): + """Test removal of discovered binary_sensor.""" + data = json.dumps(DEFAULT_CONFIG[binary_sensor.DOMAIN]) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, binary_sensor.DOMAIN, data + ) + + +async def test_discovery_update_binary_sensor_topic_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered binary_sensor.""" config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) @@ -728,7 +778,7 @@ async def test_discovery_update_binary_sensor_topic_template(hass, mqtt_mock, ca await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, binary_sensor.DOMAIN, config1, @@ -738,7 +788,9 @@ async def test_discovery_update_binary_sensor_topic_template(hass, mqtt_mock, ca ) -async def test_discovery_update_binary_sensor_template(hass, mqtt_mock, caplog): +async def test_discovery_update_binary_sensor_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered binary_sensor.""" config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) @@ -761,7 +813,7 @@ async def test_discovery_update_binary_sensor_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, binary_sensor.DOMAIN, config1, @@ -785,12 +837,18 @@ async def test_discovery_update_binary_sensor_template(hass, mqtt_mock, caplog): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, binary_sensor.DOMAIN, DEFAULT_CONFIG[binary_sensor.DOMAIN], @@ -801,7 +859,9 @@ async def test_encoding_subscribable_topics( ) -async def test_discovery_update_unchanged_binary_sensor(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_binary_sensor( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered binary_sensor.""" config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config1["name"] = "Beer" @@ -811,74 +871,90 @@ async def test_discovery_update_unchanged_binary_sensor(hass, mqtt_mock, caplog) "homeassistant.components.mqtt.binary_sensor.MqttBinarySensor.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, binary_sensor.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + binary_sensor.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer",' ' "off_delay": -1 }' data2 = '{ "name": "Milk",' ' "state_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, binary_sensor.DOMAIN, data1, data2 + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + binary_sensor.DOMAIN, + data1, + data2, ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT binary sensor device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT binary sensor device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( - hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG, None + hass, + mqtt_mock_entry_no_yaml_config, + binary_sensor.DOMAIN, + DEFAULT_CONFIG, + None, ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = binary_sensor.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -893,7 +969,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): [("ON", "on", "OFF", "off"), ("OFF", "off", "ON", "on")], ) async def test_cleanup_triggers_and_restoring_state( - hass, mqtt_mock, caplog, tmp_path, freezer, payload1, state1, payload2, state2 + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + tmp_path, + freezer, + payload1, + state1, + payload2, + state2, ): """Test cleanup old triggers at reloading and restoring the state.""" domain = binary_sensor.DOMAIN @@ -914,6 +998,8 @@ async def test_cleanup_triggers_and_restoring_state( {binary_sensor.DOMAIN: [config1, config2]}, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + async_fire_mqtt_message(hass, "test-topic1", payload1) state = hass.states.get("binary_sensor.test1") assert state.state == state1 @@ -925,7 +1011,7 @@ async def test_cleanup_triggers_and_restoring_state( freezer.move_to("2022-02-02 12:01:10+01:00") await help_test_reload_with_config( - hass, caplog, tmp_path, domain, [config1, config2] + hass, caplog, tmp_path, {domain: [config1, config2]} ) assert "Clean up expire after trigger for binary_sensor.test1" in caplog.text assert "Clean up expire after trigger for binary_sensor.test2" not in caplog.text @@ -951,7 +1037,7 @@ async def test_cleanup_triggers_and_restoring_state( async def test_skip_restoring_state_with_over_due_expire_trigger( - hass, mqtt_mock, caplog, freezer + hass, mqtt_mock_entry_with_yaml_config, caplog, freezer ): """Test restoring a state with over due expire timer.""" @@ -973,6 +1059,7 @@ async def test_skip_restoring_state_with_over_due_expire_trigger( ), assert_setup_component(1, domain): assert await async_setup_component(hass, domain, {domain: config3}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert "Skip state recovery after reload for binary_sensor.test3" in caplog.text diff --git a/tests/components/mqtt/test_button.py b/tests/components/mqtt/test_button.py index 941f08e541c..35deccf2bfe 100644 --- a/tests/components/mqtt/test_button.py +++ b/tests/components/mqtt/test_button.py @@ -42,7 +42,7 @@ DEFAULT_CONFIG = { @pytest.mark.freeze_time("2021-11-08 13:31:44+00:00") -async def test_sending_mqtt_commands(hass, mqtt_mock): +async def test_sending_mqtt_commands(hass, mqtt_mock_entry_with_yaml_config): """Test the sending MQTT commands.""" assert await async_setup_component( hass, @@ -59,6 +59,7 @@ async def test_sending_mqtt_commands(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("button.test_button") assert state.state == STATE_UNKNOWN @@ -79,7 +80,7 @@ async def test_sending_mqtt_commands(hass, mqtt_mock): assert state.state == "2021-11-08T13:31:44+00:00" -async def test_command_template(hass, mqtt_mock): +async def test_command_template(hass, mqtt_mock_entry_with_yaml_config): """Test the sending of MQTT commands through a command template.""" assert await async_setup_component( hass, @@ -95,6 +96,7 @@ async def test_command_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("button.test") assert state.state == STATE_UNKNOWN @@ -113,21 +115,23 @@ async def test_command_template(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" config = { button.DOMAIN: { @@ -139,11 +143,17 @@ async def test_default_availability_payload(hass, mqtt_mock): } await help_test_default_availability_payload( - hass, mqtt_mock, button.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + button.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" config = { button.DOMAIN: { @@ -155,53 +165,67 @@ async def test_custom_availability_payload(hass, mqtt_mock): } await help_test_custom_availability_payload( - hass, mqtt_mock, button.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + button.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG, None + hass, mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG, None ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, button.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, button.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, button.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one button per unique_id.""" config = { button.DOMAIN: [ @@ -219,16 +243,20 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, button.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, config + ) -async def test_discovery_removal_button(hass, mqtt_mock, caplog): +async def test_discovery_removal_button(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered button.""" data = '{ "name": "test", "command_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, button.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, button.DOMAIN, data + ) -async def test_discovery_update_button(hass, mqtt_mock, caplog): +async def test_discovery_update_button(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered button.""" config1 = copy.deepcopy(DEFAULT_CONFIG[button.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[button.DOMAIN]) @@ -237,7 +265,7 @@ async def test_discovery_update_button(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, button.DOMAIN, config1, @@ -245,7 +273,9 @@ async def test_discovery_update_button(hass, mqtt_mock, caplog): ) -async def test_discovery_update_unchanged_button(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_button( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered button.""" data1 = ( '{ "name": "Beer",' @@ -256,60 +286,65 @@ async def test_discovery_update_unchanged_button(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.button.MqttButton.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, button.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + button.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk", "command_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, button.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, button.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT button device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT button device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, button.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, button.DOMAIN, DEFAULT_CONFIG, button.SERVICE_PRESS, @@ -318,7 +353,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) -async def test_invalid_device_class(hass, mqtt_mock): +async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config): """Test device_class option with invalid value.""" assert await async_setup_component( hass, @@ -333,12 +368,13 @@ async def test_invalid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("button.test") assert state is None -async def test_valid_device_class(hass, mqtt_mock): +async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config): """Test device_class option with valid values.""" assert await async_setup_component( hass, @@ -366,6 +402,7 @@ async def test_valid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("button.test_1") assert state.attributes["device_class"] == button.ButtonDeviceClass.UPDATE @@ -382,7 +419,14 @@ async def test_valid_device_class(hass, mqtt_mock): ], ) async def test_publishing_with_custom_encoding( - hass, mqtt_mock, caplog, service, topic, parameters, payload, template + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + service, + topic, + parameters, + payload, + template, ): """Test publishing MQTT payload with different encoding.""" domain = button.DOMAIN @@ -390,7 +434,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -402,11 +446,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = button.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index 204103152a7..54d829ce9f9 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -46,7 +46,9 @@ DEFAULT_CONFIG = { } -async def test_run_camera_setup(hass, hass_client_no_auth, mqtt_mock): +async def test_run_camera_setup( + hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config +): """Test that it fetches the given payload.""" topic = "test/camera" await async_setup_component( @@ -55,6 +57,7 @@ async def test_run_camera_setup(hass, hass_client_no_auth, mqtt_mock): {"camera": {"platform": "mqtt", "topic": topic, "name": "Test Camera"}}, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() url = hass.states.get("camera.test_camera").attributes["entity_picture"] @@ -67,7 +70,9 @@ async def test_run_camera_setup(hass, hass_client_no_auth, mqtt_mock): assert body == "beer" -async def test_run_camera_b64_encoded(hass, hass_client_no_auth, mqtt_mock): +async def test_run_camera_b64_encoded( + hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config +): """Test that it fetches the given encoded payload.""" topic = "test/camera" await async_setup_component( @@ -83,6 +88,7 @@ async def test_run_camera_b64_encoded(hass, hass_client_no_auth, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() url = hass.states.get("camera.test_camera").attributes["entity_picture"] @@ -95,77 +101,91 @@ async def test_run_camera_b64_encoded(hass, hass_client_no_auth, mqtt_mock): assert body == "grass" -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG, MQTT_CAMERA_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + camera.DOMAIN, + DEFAULT_CONFIG, + MQTT_CAMERA_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one camera per unique_id.""" config = { camera.DOMAIN: [ @@ -183,94 +203,109 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, camera.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, config + ) -async def test_discovery_removal_camera(hass, mqtt_mock, caplog): +async def test_discovery_removal_camera(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered camera.""" data = json.dumps(DEFAULT_CONFIG[camera.DOMAIN]) - await help_test_discovery_removal(hass, mqtt_mock, caplog, camera.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, data + ) -async def test_discovery_update_camera(hass, mqtt_mock, caplog): +async def test_discovery_update_camera(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered camera.""" config1 = {"name": "Beer", "topic": "test_topic"} config2 = {"name": "Milk", "topic": "test_topic"} await help_test_discovery_update( - hass, mqtt_mock, caplog, camera.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_camera(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_camera( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered camera.""" data1 = '{ "name": "Beer", "topic": "test_topic"}' with patch( "homeassistant.components.mqtt.camera.MqttCamera.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, camera.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + camera.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk", "topic": "test_topic"}' await help_test_discovery_broken( - hass, mqtt_mock, caplog, camera.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT camera device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT camera device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG, ["test_topic"] + hass, + mqtt_mock_entry_with_yaml_config, + camera.DOMAIN, + DEFAULT_CONFIG, + ["test_topic"], ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, camera.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, camera.DOMAIN, DEFAULT_CONFIG, None, @@ -279,11 +314,13 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = camera.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index 98af86248e4..77843cee777 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -106,10 +106,11 @@ DEFAULT_LEGACY_CONFIG = { } -async def test_setup_params(hass, mqtt_mock): +async def test_setup_params(hass, mqtt_mock_entry_with_yaml_config): """Test the initial parameters.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("temperature") == 21 @@ -120,12 +121,15 @@ async def test_setup_params(hass, mqtt_mock): assert state.attributes.get("max_temp") == DEFAULT_MAX_TEMP -async def test_preset_none_in_preset_modes(hass, mqtt_mock, caplog): +async def test_preset_none_in_preset_modes( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test the preset mode payload reset configuration.""" config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN]) config["preset_modes"].append("none") assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert "Invalid config for [climate.mqtt]: not a valid value" in caplog.text state = hass.states.get(ENTITY_CLIMATE) assert state is None @@ -145,21 +149,23 @@ async def test_preset_none_in_preset_modes(hass, mqtt_mock, caplog): ], ) async def test_preset_modes_deprecation_guard( - hass, mqtt_mock, caplog, parameter, config_value + hass, mqtt_mock_entry_no_yaml_config, caplog, parameter, config_value ): """Test the configuration for invalid legacy parameters.""" config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN]) config[parameter] = config_value assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state is None -async def test_supported_features(hass, mqtt_mock): +async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config): """Test the supported_features.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) support = ( @@ -174,10 +180,11 @@ async def test_supported_features(hass, mqtt_mock): assert state.attributes.get("supported_features") == support -async def test_get_hvac_modes(hass, mqtt_mock): +async def test_get_hvac_modes(hass, mqtt_mock_entry_with_yaml_config): """Test that the operation list returns the correct modes.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) modes = state.attributes.get("hvac_modes") @@ -191,13 +198,16 @@ async def test_get_hvac_modes(hass, mqtt_mock): ] == modes -async def test_set_operation_bad_attr_and_state(hass, mqtt_mock, caplog): +async def test_set_operation_bad_attr_and_state( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test setting operation mode without required attribute. Also check the state. """ assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.state == "off" @@ -210,10 +220,11 @@ async def test_set_operation_bad_attr_and_state(hass, mqtt_mock, caplog): assert state.state == "off" -async def test_set_operation(hass, mqtt_mock): +async def test_set_operation(hass, mqtt_mock_entry_with_yaml_config): """Test setting of new operation mode.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.state == "off" @@ -224,12 +235,13 @@ async def test_set_operation(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("mode-topic", "cool", 0, False) -async def test_set_operation_pessimistic(hass, mqtt_mock): +async def test_set_operation_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting operation mode in pessimistic mode.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["mode_state_topic"] = "mode-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.state == "unknown" @@ -247,12 +259,13 @@ async def test_set_operation_pessimistic(hass, mqtt_mock): assert state.state == "cool" -async def test_set_operation_with_power_command(hass, mqtt_mock): +async def test_set_operation_with_power_command(hass, mqtt_mock_entry_with_yaml_config): """Test setting of new operation mode with power command enabled.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["power_command_topic"] = "power-command" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.state == "off" @@ -273,10 +286,11 @@ async def test_set_operation_with_power_command(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_set_fan_mode_bad_attr(hass, mqtt_mock, caplog): +async def test_set_fan_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test setting fan mode without required attribute.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("fan_mode") == "low" @@ -289,12 +303,13 @@ async def test_set_fan_mode_bad_attr(hass, mqtt_mock, caplog): assert state.attributes.get("fan_mode") == "low" -async def test_set_fan_mode_pessimistic(hass, mqtt_mock): +async def test_set_fan_mode_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting of new fan mode in pessimistic mode.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["fan_mode_state_topic"] = "fan-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("fan_mode") is None @@ -312,10 +327,11 @@ async def test_set_fan_mode_pessimistic(hass, mqtt_mock): assert state.attributes.get("fan_mode") == "high" -async def test_set_fan_mode(hass, mqtt_mock): +async def test_set_fan_mode(hass, mqtt_mock_entry_with_yaml_config): """Test setting of new fan mode.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("fan_mode") == "low" @@ -335,13 +351,14 @@ async def test_set_fan_mode(hass, mqtt_mock): ], ) async def test_set_fan_mode_send_if_off( - hass, mqtt_mock, send_if_off, assert_async_publish + hass, mqtt_mock_entry_with_yaml_config, send_if_off, assert_async_publish ): """Test setting of fan mode if the hvac is off.""" config = copy.deepcopy(DEFAULT_CONFIG) config[CLIMATE_DOMAIN].update(send_if_off) assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() assert hass.states.get(ENTITY_CLIMATE) is not None # Turn on HVAC @@ -362,10 +379,11 @@ async def test_set_fan_mode_send_if_off( mqtt_mock.async_publish.assert_has_calls(assert_async_publish) -async def test_set_swing_mode_bad_attr(hass, mqtt_mock, caplog): +async def test_set_swing_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test setting swing mode without required attribute.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("swing_mode") == "off" @@ -378,12 +396,13 @@ async def test_set_swing_mode_bad_attr(hass, mqtt_mock, caplog): assert state.attributes.get("swing_mode") == "off" -async def test_set_swing_pessimistic(hass, mqtt_mock): +async def test_set_swing_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting swing mode in pessimistic mode.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["swing_mode_state_topic"] = "swing-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("swing_mode") is None @@ -401,10 +420,11 @@ async def test_set_swing_pessimistic(hass, mqtt_mock): assert state.attributes.get("swing_mode") == "on" -async def test_set_swing(hass, mqtt_mock): +async def test_set_swing(hass, mqtt_mock_entry_with_yaml_config): """Test setting of new swing mode.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("swing_mode") == "off" @@ -424,13 +444,14 @@ async def test_set_swing(hass, mqtt_mock): ], ) async def test_set_swing_mode_send_if_off( - hass, mqtt_mock, send_if_off, assert_async_publish + hass, mqtt_mock_entry_with_yaml_config, send_if_off, assert_async_publish ): """Test setting of swing mode if the hvac is off.""" config = copy.deepcopy(DEFAULT_CONFIG) config[CLIMATE_DOMAIN].update(send_if_off) assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() assert hass.states.get(ENTITY_CLIMATE) is not None # Turn on HVAC @@ -451,10 +472,11 @@ async def test_set_swing_mode_send_if_off( mqtt_mock.async_publish.assert_has_calls(assert_async_publish) -async def test_set_target_temperature(hass, mqtt_mock): +async def test_set_target_temperature(hass, mqtt_mock_entry_with_yaml_config): """Test setting the target temperature.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("temperature") == 21 @@ -497,13 +519,14 @@ async def test_set_target_temperature(hass, mqtt_mock): ], ) async def test_set_target_temperature_send_if_off( - hass, mqtt_mock, send_if_off, assert_async_publish + hass, mqtt_mock_entry_with_yaml_config, send_if_off, assert_async_publish ): """Test setting of target temperature if the hvac is off.""" config = copy.deepcopy(DEFAULT_CONFIG) config[CLIMATE_DOMAIN].update(send_if_off) assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() assert hass.states.get(ENTITY_CLIMATE) is not None # Turn on HVAC @@ -526,12 +549,15 @@ async def test_set_target_temperature_send_if_off( mqtt_mock.async_publish.assert_has_calls(assert_async_publish) -async def test_set_target_temperature_pessimistic(hass, mqtt_mock): +async def test_set_target_temperature_pessimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting the target temperature.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["temperature_state_topic"] = "temperature-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("temperature") is None @@ -549,10 +575,11 @@ async def test_set_target_temperature_pessimistic(hass, mqtt_mock): assert state.attributes.get("temperature") == 1701 -async def test_set_target_temperature_low_high(hass, mqtt_mock): +async def test_set_target_temperature_low_high(hass, mqtt_mock_entry_with_yaml_config): """Test setting the low/high target temperature.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_set_temperature( hass, target_temp_low=20, target_temp_high=23, entity_id=ENTITY_CLIMATE @@ -564,13 +591,16 @@ async def test_set_target_temperature_low_high(hass, mqtt_mock): mqtt_mock.async_publish.assert_any_call("temperature-high-topic", "23.0", 0, False) -async def test_set_target_temperature_low_highpessimistic(hass, mqtt_mock): +async def test_set_target_temperature_low_highpessimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting the low/high target temperature.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["temperature_low_state_topic"] = "temperature-low-state" config["climate"]["temperature_high_state_topic"] = "temperature-high-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("target_temp_low") is None @@ -601,24 +631,26 @@ async def test_set_target_temperature_low_highpessimistic(hass, mqtt_mock): assert state.attributes.get("target_temp_high") == 1703 -async def test_receive_mqtt_temperature(hass, mqtt_mock): +async def test_receive_mqtt_temperature(hass, mqtt_mock_entry_with_yaml_config): """Test getting the current temperature via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["current_temperature_topic"] = "current_temperature" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "current_temperature", "47") state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("current_temperature") == 47 -async def test_handle_action_received(hass, mqtt_mock): +async def test_handle_action_received(hass, mqtt_mock_entry_with_yaml_config): """Test getting the action received via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["action_topic"] = "action" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() # Cycle through valid modes and also check for wrong input such as "None" (str(None)) async_fire_mqtt_message(hass, "action", "None") @@ -635,11 +667,14 @@ async def test_handle_action_received(hass, mqtt_mock): assert hvac_action == action -async def test_set_preset_mode_optimistic(hass, mqtt_mock, caplog): +async def test_set_preset_mode_optimistic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test setting of the preset mode.""" config = copy.deepcopy(DEFAULT_CONFIG) assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == "none" @@ -680,12 +715,15 @@ async def test_set_preset_mode_optimistic(hass, mqtt_mock, caplog): assert "'invalid' is not a valid preset mode" in caplog.text -async def test_set_preset_mode_pessimistic(hass, mqtt_mock, caplog): +async def test_set_preset_mode_pessimistic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test setting of the preset mode.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["preset_mode_state_topic"] = "preset-mode-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == "none" @@ -725,12 +763,13 @@ async def test_set_preset_mode_pessimistic(hass, mqtt_mock, caplog): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_away_mode_pessimistic(hass, mqtt_mock): +async def test_set_away_mode_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting of the away mode.""" config = copy.deepcopy(DEFAULT_LEGACY_CONFIG) config["climate"]["away_mode_state_topic"] = "away-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == "none" @@ -753,7 +792,7 @@ async def test_set_away_mode_pessimistic(hass, mqtt_mock): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_away_mode(hass, mqtt_mock): +async def test_set_away_mode(hass, mqtt_mock_entry_with_yaml_config): """Test setting of the away mode.""" config = copy.deepcopy(DEFAULT_LEGACY_CONFIG) config["climate"]["payload_on"] = "AN" @@ -761,6 +800,7 @@ async def test_set_away_mode(hass, mqtt_mock): assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == "none" @@ -795,12 +835,13 @@ async def test_set_away_mode(hass, mqtt_mock): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_hold_pessimistic(hass, mqtt_mock): +async def test_set_hold_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting the hold mode in pessimistic mode.""" config = copy.deepcopy(DEFAULT_LEGACY_CONFIG) config["climate"]["hold_state_topic"] = "hold-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("hold_mode") is None @@ -819,10 +860,11 @@ async def test_set_hold_pessimistic(hass, mqtt_mock): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_hold(hass, mqtt_mock): +async def test_set_hold(hass, mqtt_mock_entry_with_yaml_config): """Test setting the hold mode.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_LEGACY_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == "none" @@ -851,10 +893,11 @@ async def test_set_hold(hass, mqtt_mock): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_preset_away(hass, mqtt_mock): +async def test_set_preset_away(hass, mqtt_mock_entry_with_yaml_config): """Test setting the hold mode and away mode.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_LEGACY_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == PRESET_NONE @@ -885,13 +928,14 @@ async def test_set_preset_away(hass, mqtt_mock): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_preset_away_pessimistic(hass, mqtt_mock): +async def test_set_preset_away_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting the hold mode and away mode in pessimistic mode.""" config = copy.deepcopy(DEFAULT_LEGACY_CONFIG) config["climate"]["hold_state_topic"] = "hold-state" config["climate"]["away_mode_state_topic"] = "away-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == PRESET_NONE @@ -936,10 +980,11 @@ async def test_set_preset_away_pessimistic(hass, mqtt_mock): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_preset_mode_twice(hass, mqtt_mock): +async def test_set_preset_mode_twice(hass, mqtt_mock_entry_with_yaml_config): """Test setting of the same mode twice only publishes once.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_LEGACY_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("preset_mode") == "none" @@ -952,12 +997,13 @@ async def test_set_preset_mode_twice(hass, mqtt_mock): assert state.attributes.get("preset_mode") == "hold-on" -async def test_set_aux_pessimistic(hass, mqtt_mock): +async def test_set_aux_pessimistic(hass, mqtt_mock_entry_with_yaml_config): """Test setting of the aux heating in pessimistic mode.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["aux_state_topic"] = "aux-state" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("aux_heat") == "off" @@ -979,10 +1025,11 @@ async def test_set_aux_pessimistic(hass, mqtt_mock): assert state.attributes.get("aux_heat") == "off" -async def test_set_aux(hass, mqtt_mock): +async def test_set_aux(hass, mqtt_mock_entry_with_yaml_config): """Test setting of the aux heating.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("aux_heat") == "off" @@ -998,35 +1045,39 @@ async def test_set_aux(hass, mqtt_mock): assert state.attributes.get("aux_heat") == "off" -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_get_target_temperature_low_high_with_templates(hass, mqtt_mock, caplog): +async def test_get_target_temperature_low_high_with_templates( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test getting temperature high/low with templates.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["temperature_low_state_topic"] = "temperature-state" @@ -1036,6 +1087,7 @@ async def test_get_target_temperature_low_high_with_templates(hass, mqtt_mock, c assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) @@ -1060,7 +1112,7 @@ async def test_get_target_temperature_low_high_with_templates(hass, mqtt_mock, c assert state.attributes.get("target_temp_high") == 1032 -async def test_get_with_templates(hass, mqtt_mock, caplog): +async def test_get_with_templates(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test getting various attributes with templates.""" config = copy.deepcopy(DEFAULT_CONFIG) # By default, just unquote the JSON-strings @@ -1081,6 +1133,7 @@ async def test_get_with_templates(hass, mqtt_mock, caplog): config["climate"]["preset_mode_state_topic"] = "current-preset-mode" assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() # Operation Mode state = hass.states.get(ENTITY_CLIMATE) @@ -1159,7 +1212,9 @@ async def test_get_with_templates(hass, mqtt_mock, caplog): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_get_with_hold_and_away_mode_and_templates(hass, mqtt_mock, caplog): +async def test_get_with_hold_and_away_mode_and_templates( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test getting various for hold and away mode attributes with templates.""" config = copy.deepcopy(DEFAULT_LEGACY_CONFIG) config["climate"]["mode_state_topic"] = "mode-state" @@ -1172,6 +1227,7 @@ async def test_get_with_hold_and_away_mode_and_templates(hass, mqtt_mock, caplog assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() # Operation Mode state = hass.states.get(ENTITY_CLIMATE) @@ -1206,7 +1262,7 @@ async def test_get_with_hold_and_away_mode_and_templates(hass, mqtt_mock, caplog assert state.attributes.get("preset_mode") == "somemode" -async def test_set_and_templates(hass, mqtt_mock, caplog): +async def test_set_and_templates(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test setting various attributes with templates.""" config = copy.deepcopy(DEFAULT_CONFIG) # Create simple templates @@ -1220,6 +1276,7 @@ async def test_set_and_templates(hass, mqtt_mock, caplog): assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() # Fan Mode await common.async_set_fan_mode(hass, "high", ENTITY_CLIMATE) @@ -1284,7 +1341,9 @@ async def test_set_and_templates(hass, mqtt_mock, caplog): # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 -async def test_set_with_away_and_hold_modes_and_templates(hass, mqtt_mock, caplog): +async def test_set_with_away_and_hold_modes_and_templates( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test setting various attributes on hold and away mode with templates.""" config = copy.deepcopy(DEFAULT_LEGACY_CONFIG) # Create simple templates @@ -1292,6 +1351,7 @@ async def test_set_with_away_and_hold_modes_and_templates(hass, mqtt_mock, caplo assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() # Hold Mode await common.async_set_preset_mode(hass, PRESET_ECO, ENTITY_CLIMATE) @@ -1303,13 +1363,14 @@ async def test_set_with_away_and_hold_modes_and_templates(hass, mqtt_mock, caplo assert state.attributes.get("preset_mode") == PRESET_ECO -async def test_min_temp_custom(hass, mqtt_mock): +async def test_min_temp_custom(hass, mqtt_mock_entry_with_yaml_config): """Test a custom min temp.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["min_temp"] = 26 assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) min_temp = state.attributes.get("min_temp") @@ -1318,13 +1379,14 @@ async def test_min_temp_custom(hass, mqtt_mock): assert state.attributes.get("min_temp") == 26 -async def test_max_temp_custom(hass, mqtt_mock): +async def test_max_temp_custom(hass, mqtt_mock_entry_with_yaml_config): """Test a custom max temp.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["max_temp"] = 60 assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) max_temp = state.attributes.get("max_temp") @@ -1333,13 +1395,14 @@ async def test_max_temp_custom(hass, mqtt_mock): assert max_temp == 60 -async def test_temp_step_custom(hass, mqtt_mock): +async def test_temp_step_custom(hass, mqtt_mock_entry_with_yaml_config): """Test a custom temp step.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["temp_step"] = 0.01 assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(ENTITY_CLIMATE) temp_step = state.attributes.get("target_temp_step") @@ -1348,7 +1411,7 @@ async def test_temp_step_custom(hass, mqtt_mock): assert temp_step == 0.01 -async def test_temperature_unit(hass, mqtt_mock): +async def test_temperature_unit(hass, mqtt_mock_entry_with_yaml_config): """Test that setting temperature unit converts temperature values.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["temperature_unit"] = "F" @@ -1356,6 +1419,7 @@ async def test_temperature_unit(hass, mqtt_mock): assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "current_temperature", "77") @@ -1363,49 +1427,61 @@ async def test_temperature_unit(hass, mqtt_mock): assert state.attributes.get("current_temperature") == 25 -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG, MQTT_CLIMATE_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + CLIMATE_DOMAIN, + DEFAULT_CONFIG, + MQTT_CLIMATE_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one climate per unique_id.""" config = { CLIMATE_DOMAIN: [ @@ -1425,7 +1501,9 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, CLIMATE_DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, config + ) @pytest.mark.parametrize( @@ -1449,7 +1527,13 @@ async def test_unique_id(hass, mqtt_mock): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN]) @@ -1460,7 +1544,7 @@ async def test_encoding_subscribable_topics( del config["preset_mode_command_topic"] await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, CLIMATE_DOMAIN, config, @@ -1471,71 +1555,80 @@ async def test_encoding_subscribable_topics( ) -async def test_discovery_removal_climate(hass, mqtt_mock, caplog): +async def test_discovery_removal_climate(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered climate.""" data = json.dumps(DEFAULT_CONFIG[CLIMATE_DOMAIN]) - await help_test_discovery_removal(hass, mqtt_mock, caplog, CLIMATE_DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, data + ) -async def test_discovery_update_climate(hass, mqtt_mock, caplog): +async def test_discovery_update_climate(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered climate.""" config1 = {"name": "Beer"} config2 = {"name": "Milk"} await help_test_discovery_update( - hass, mqtt_mock, caplog, CLIMATE_DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_climate(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_climate( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered climate.""" data1 = '{ "name": "Beer" }' with patch( "homeassistant.components.mqtt.climate.MqttClimate.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, CLIMATE_DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + CLIMATE_DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer", "power_command_topic": "test_topic#" }' data2 = '{ "name": "Milk", "power_command_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, CLIMATE_DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT climate device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT climate device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" config = { CLIMATE_DOMAIN: { @@ -1546,18 +1639,22 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock): } } await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, CLIMATE_DOMAIN, config, ["test-topic", "avty-topic"] + hass, + mqtt_mock_entry_with_yaml_config, + CLIMATE_DOMAIN, + config, + ["test-topic", "avty-topic"], ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, CLIMATE_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" config = { CLIMATE_DOMAIN: { @@ -1569,7 +1666,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): } await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, config, climate.SERVICE_TURN_ON, @@ -1579,10 +1676,11 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) -async def test_precision_default(hass, mqtt_mock): +async def test_precision_default(hass, mqtt_mock_entry_with_yaml_config): """Test that setting precision to tenths works as intended.""" assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_set_temperature( hass, temperature=23.67, entity_id=ENTITY_CLIMATE @@ -1592,12 +1690,13 @@ async def test_precision_default(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_precision_halves(hass, mqtt_mock): +async def test_precision_halves(hass, mqtt_mock_entry_with_yaml_config): """Test that setting precision to halves works as intended.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["precision"] = 0.5 assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_set_temperature( hass, temperature=23.67, entity_id=ENTITY_CLIMATE @@ -1607,12 +1706,13 @@ async def test_precision_halves(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_precision_whole(hass, mqtt_mock): +async def test_precision_whole(hass, mqtt_mock_entry_with_yaml_config): """Test that setting precision to whole works as intended.""" config = copy.deepcopy(DEFAULT_CONFIG) config["climate"]["precision"] = 1.0 assert await async_setup_component(hass, CLIMATE_DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_set_temperature( hass, temperature=23.67, entity_id=ENTITY_CLIMATE @@ -1721,7 +1821,7 @@ async def test_precision_whole(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -1738,7 +1838,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -1750,11 +1850,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = CLIMATE_DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index b5bb5732617..24482129f3d 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -46,10 +46,13 @@ DEFAULT_CONFIG_DEVICE_INFO_MAC = { _SENTINEL = object() -async def help_test_availability_when_connection_lost(hass, mqtt_mock, domain, config): +async def help_test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config, domain, config +): """Test availability after MQTT disconnection.""" assert await async_setup_component(hass, domain, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state != STATE_UNAVAILABLE @@ -62,11 +65,14 @@ async def help_test_availability_when_connection_lost(hass, mqtt_mock, domain, c assert state.state == STATE_UNAVAILABLE -async def help_test_availability_without_topic(hass, mqtt_mock, domain, config): +async def help_test_availability_without_topic( + hass, mqtt_mock_entry_with_yaml_config, domain, config +): """Test availability without defined availability topic.""" assert "availability_topic" not in config[domain] assert await async_setup_component(hass, domain, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state != STATE_UNAVAILABLE @@ -74,7 +80,7 @@ async def help_test_availability_without_topic(hass, mqtt_mock, domain, config): async def help_test_default_availability_payload( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, domain, config, no_assumed_state=False, @@ -94,6 +100,7 @@ async def help_test_default_availability_payload( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state == STATE_UNAVAILABLE @@ -124,7 +131,7 @@ async def help_test_default_availability_payload( async def help_test_default_availability_list_payload( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, domain, config, no_assumed_state=False, @@ -147,6 +154,7 @@ async def help_test_default_availability_list_payload( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state == STATE_UNAVAILABLE @@ -189,7 +197,7 @@ async def help_test_default_availability_list_payload( async def help_test_default_availability_list_payload_all( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, domain, config, no_assumed_state=False, @@ -213,6 +221,7 @@ async def help_test_default_availability_list_payload_all( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state == STATE_UNAVAILABLE @@ -256,7 +265,7 @@ async def help_test_default_availability_list_payload_all( async def help_test_default_availability_list_payload_any( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, domain, config, no_assumed_state=False, @@ -280,6 +289,7 @@ async def help_test_default_availability_list_payload_any( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state == STATE_UNAVAILABLE @@ -318,7 +328,7 @@ async def help_test_default_availability_list_payload_any( async def help_test_default_availability_list_single( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -342,6 +352,7 @@ async def help_test_default_availability_list_single( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state is None @@ -353,7 +364,7 @@ async def help_test_default_availability_list_single( async def help_test_custom_availability_payload( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, domain, config, no_assumed_state=False, @@ -375,6 +386,7 @@ async def help_test_custom_availability_payload( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state.state == STATE_UNAVAILABLE @@ -405,7 +417,7 @@ async def help_test_custom_availability_payload( async def help_test_discovery_update_availability( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, domain, config, no_assumed_state=False, @@ -416,6 +428,7 @@ async def help_test_discovery_update_availability( This is a test helper for the MQTTAvailability mixin. """ + await mqtt_mock_entry_no_yaml_config() # Add availability settings to config config1 = copy.deepcopy(config) config1[domain]["availability_topic"] = "availability-topic1" @@ -484,7 +497,7 @@ async def help_test_discovery_update_availability( async def help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, domain, config + hass, mqtt_mock_entry_with_yaml_config, domain, config ): """Test the setting of attribute via MQTT with JSON payload. @@ -499,6 +512,7 @@ async def help_test_setting_attribute_via_mqtt_json_message( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "attr-topic", '{ "val": "100" }') state = hass.states.get(f"{domain}.test") @@ -507,12 +521,13 @@ async def help_test_setting_attribute_via_mqtt_json_message( async def help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, domain, config, extra_blocked_attributes + hass, mqtt_mock_entry_no_yaml_config, domain, config, extra_blocked_attributes ): """Test the setting of blocked attribute via MQTT with JSON payload. This is a test helper for the MqttAttributes mixin. """ + await mqtt_mock_entry_no_yaml_config() extra_blocked_attributes = extra_blocked_attributes or [] # Add JSON attributes settings to config @@ -534,7 +549,9 @@ async def help_test_setting_blocked_attribute_via_mqtt_json_message( assert state.attributes.get(attr) != val -async def help_test_setting_attribute_with_template(hass, mqtt_mock, domain, config): +async def help_test_setting_attribute_with_template( + hass, mqtt_mock_entry_with_yaml_config, domain, config +): """Test the setting of attribute via MQTT with JSON payload. This is a test helper for the MqttAttributes mixin. @@ -549,6 +566,7 @@ async def help_test_setting_attribute_with_template(hass, mqtt_mock, domain, con config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message( hass, "attr-topic", json.dumps({"Timer1": {"Arm": 0, "Time": "22:18"}}) @@ -560,7 +578,7 @@ async def help_test_setting_attribute_with_template(hass, mqtt_mock, domain, con async def help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, domain, config + hass, mqtt_mock_entry_with_yaml_config, caplog, domain, config ): """Test attributes get extracted from a JSON result. @@ -575,6 +593,7 @@ async def help_test_update_with_json_attrs_not_dict( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "attr-topic", '[ "list", "of", "things"]') state = hass.states.get(f"{domain}.test") @@ -584,7 +603,7 @@ async def help_test_update_with_json_attrs_not_dict( async def help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, domain, config + hass, mqtt_mock_entry_with_yaml_config, caplog, domain, config ): """Test JSON validation of attributes. @@ -599,6 +618,7 @@ async def help_test_update_with_json_attrs_bad_JSON( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "attr-topic", "This is not JSON") @@ -607,11 +627,14 @@ async def help_test_update_with_json_attrs_bad_JSON( assert "Erroneous JSON: This is not JSON" in caplog.text -async def help_test_discovery_update_attr(hass, mqtt_mock, caplog, domain, config): +async def help_test_discovery_update_attr( + hass, mqtt_mock_entry_no_yaml_config, caplog, domain, config +): """Test update of discovered MQTTAttributes. This is a test helper for the MqttAttributes mixin. """ + await mqtt_mock_entry_no_yaml_config() # Add JSON attributes settings to config config1 = copy.deepcopy(config) config1[domain]["json_attributes_topic"] = "attr-topic1" @@ -641,18 +664,22 @@ async def help_test_discovery_update_attr(hass, mqtt_mock, caplog, domain, confi assert state.attributes.get("val") == "75" -async def help_test_unique_id(hass, mqtt_mock, domain, config): +async def help_test_unique_id(hass, mqtt_mock_entry_with_yaml_config, domain, config): """Test unique id option only creates one entity per unique_id.""" assert await async_setup_component(hass, domain, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert len(hass.states.async_entity_ids(domain)) == 1 -async def help_test_discovery_removal(hass, mqtt_mock, caplog, domain, data): +async def help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, domain, data +): """Test removal of discovered component. This is a test helper for the MqttDiscoveryUpdate mixin. """ + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() @@ -669,7 +696,7 @@ async def help_test_discovery_removal(hass, mqtt_mock, caplog, domain, data): async def help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, domain, discovery_config1, @@ -681,6 +708,7 @@ async def help_test_discovery_update( This is a test helper for the MqttDiscoveryUpdate mixin. """ + await mqtt_mock_entry_no_yaml_config() # Add some future configuration to the configurations config1 = copy.deepcopy(discovery_config1) config1["some_future_option_1"] = "future_option_1" @@ -730,12 +758,13 @@ async def help_test_discovery_update( async def help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, domain, data1, discovery_update + hass, mqtt_mock_entry_no_yaml_config, caplog, domain, data1, discovery_update ): """Test update of discovered component without changes. This is a test helper for the MqttDiscoveryUpdate mixin. """ + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) await hass.async_block_till_done() @@ -749,8 +778,11 @@ async def help_test_discovery_update_unchanged( assert not discovery_update.called -async def help_test_discovery_broken(hass, mqtt_mock, caplog, domain, data1, data2): +async def help_test_discovery_broken( + hass, mqtt_mock_entry_no_yaml_config, caplog, domain, data1, data2 +): """Test handling of bad discovery message.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) await hass.async_block_till_done() @@ -769,7 +801,7 @@ async def help_test_discovery_broken(hass, mqtt_mock, caplog, domain, data1, dat async def help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -849,6 +881,7 @@ async def help_test_encoding_subscribable_topics( hass, domain, {domain: [config1, config2, config3]} ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() expected_result = attribute_value or value @@ -899,11 +932,14 @@ async def help_test_encoding_subscribable_topics( pass -async def help_test_entity_device_info_with_identifier(hass, mqtt_mock, domain, config): +async def help_test_entity_device_info_with_identifier( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test device registry integration. This is a test helper for the MqttDiscoveryUpdate mixin. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -926,11 +962,14 @@ async def help_test_entity_device_info_with_identifier(hass, mqtt_mock, domain, assert device.configuration_url == "http://example.com" -async def help_test_entity_device_info_with_connection(hass, mqtt_mock, domain, config): +async def help_test_entity_device_info_with_connection( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test device registry integration. This is a test helper for the MqttDiscoveryUpdate mixin. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_MAC) @@ -955,8 +994,11 @@ async def help_test_entity_device_info_with_connection(hass, mqtt_mock, domain, assert device.configuration_url == "http://example.com" -async def help_test_entity_device_info_remove(hass, mqtt_mock, domain, config): +async def help_test_entity_device_info_remove( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test device registry remove.""" + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -981,11 +1023,14 @@ async def help_test_entity_device_info_remove(hass, mqtt_mock, domain, config): assert not ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique") -async def help_test_entity_device_info_update(hass, mqtt_mock, domain, config): +async def help_test_entity_device_info_update( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test device registry update. This is a test helper for the MqttDiscoveryUpdate mixin. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1012,7 +1057,7 @@ async def help_test_entity_device_info_update(hass, mqtt_mock, domain, config): async def help_test_entity_id_update_subscriptions( - hass, mqtt_mock, domain, config, topics=None + hass, mqtt_mock_entry_with_yaml_config, domain, config, topics=None ): """Test MQTT subscriptions are managed when entity_id is updated.""" # Add unique_id to config @@ -1026,16 +1071,18 @@ async def help_test_entity_id_update_subscriptions( topics = ["avty-topic", "test-topic"] assert len(topics) > 0 registry = mock_registry(hass, {}) + assert await async_setup_component( hass, domain, config, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get(f"{domain}.test") assert state is not None - assert mqtt_mock.async_subscribe.call_count == len(topics) + assert mqtt_mock.async_subscribe.call_count == len(topics) + 3 for topic in topics: mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY) mqtt_mock.async_subscribe.reset_mock() @@ -1053,10 +1100,11 @@ async def help_test_entity_id_update_subscriptions( async def help_test_entity_id_update_discovery_update( - hass, mqtt_mock, domain, config, topic=None + hass, mqtt_mock_entry_no_yaml_config, domain, config, topic=None ): """Test MQTT discovery update after entity_id is updated.""" # Add unique_id to config + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(config) config[domain]["unique_id"] = "TOTALLY_UNIQUE" @@ -1093,11 +1141,14 @@ async def help_test_entity_id_update_discovery_update( assert state.state != STATE_UNAVAILABLE -async def help_test_entity_debug_info(hass, mqtt_mock, domain, config): +async def help_test_entity_debug_info( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test debug_info. This is a test helper for MQTT debug_info. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1127,11 +1178,14 @@ async def help_test_entity_debug_info(hass, mqtt_mock, domain, config): assert len(debug_info_data["triggers"]) == 0 -async def help_test_entity_debug_info_max_messages(hass, mqtt_mock, domain, config): +async def help_test_entity_debug_info_max_messages( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test debug_info message overflow. This is a test helper for MQTT debug_info. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1181,7 +1235,7 @@ async def help_test_entity_debug_info_max_messages(hass, mqtt_mock, domain, conf async def help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, domain, config, service, @@ -1196,6 +1250,7 @@ async def help_test_entity_debug_info_message( This is a test helper for MQTT debug_info. """ # Add device settings to config + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" @@ -1290,11 +1345,14 @@ async def help_test_entity_debug_info_message( assert debug_info_data["entities"][0]["transmitted"] == expected_transmissions -async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config): +async def help_test_entity_debug_info_remove( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test debug_info. This is a test helper for MQTT debug_info. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1333,11 +1391,14 @@ async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config): assert entity_id not in hass.data[debug_info.DATA_MQTT_DEBUG_INFO]["entities"] -async def help_test_entity_debug_info_update_entity_id(hass, mqtt_mock, domain, config): +async def help_test_entity_debug_info_update_entity_id( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test debug_info. This is a test helper for MQTT debug_info. """ + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1389,8 +1450,11 @@ async def help_test_entity_debug_info_update_entity_id(hass, mqtt_mock, domain, ) -async def help_test_entity_disabled_by_default(hass, mqtt_mock, domain, config): +async def help_test_entity_disabled_by_default( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test device registry remove.""" + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1425,8 +1489,11 @@ async def help_test_entity_disabled_by_default(hass, mqtt_mock, domain, config): assert not dev_registry.async_get_device({("mqtt", "helloworld")}) -async def help_test_entity_category(hass, mqtt_mock, domain, config): +async def help_test_entity_category( + hass, mqtt_mock_entry_no_yaml_config, domain, config +): """Test device registry remove.""" + await mqtt_mock_entry_no_yaml_config() # Add device settings to config config = copy.deepcopy(config[domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) @@ -1468,7 +1535,7 @@ async def help_test_entity_category(hass, mqtt_mock, domain, config): async def help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -1519,6 +1586,7 @@ async def help_test_publishing_with_custom_encoding( {domain: setup_config}, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() # 1) test with default encoding await hass.services.async_call( @@ -1583,10 +1651,10 @@ async def help_test_publishing_with_custom_encoding( mqtt_mock.async_publish.reset_mock() -async def help_test_reload_with_config(hass, caplog, tmp_path, domain, config): +async def help_test_reload_with_config(hass, caplog, tmp_path, config): """Test reloading with supplied config.""" new_yaml_config_file = tmp_path / "configuration.yaml" - new_yaml_config = yaml.dump({domain: config}) + new_yaml_config = yaml.dump(config) new_yaml_config_file.write_text(new_yaml_config) assert new_yaml_config_file.read_text() == new_yaml_config @@ -1602,22 +1670,36 @@ async def help_test_reload_with_config(hass, caplog, tmp_path, domain, config): assert "" in caplog.text -async def help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config): +async def help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config +): """Test reloading an MQTT platform.""" # 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_config_3 = copy.deepcopy(config) + old_config_3["name"] = "test_old_3" + old_config_3.pop("platform") + old_config_4 = copy.deepcopy(config) + old_config_4["name"] = "test_old_4" + old_config_4.pop("platform") - assert await async_setup_component( - hass, domain, {domain: [old_config_1, old_config_2]} - ) + old_config = { + domain: [old_config_1, old_config_2], + "mqtt": {domain: [old_config_3, old_config_4]}, + } + + assert await async_setup_component(hass, domain, old_config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get(f"{domain}.test_old_1") assert hass.states.get(f"{domain}.test_old_2") - assert len(hass.states.async_all(domain)) == 2 + assert hass.states.get(f"{domain}.test_old_3") + assert hass.states.get(f"{domain}.test_old_4") + assert len(hass.states.async_all(domain)) == 4 # Create temporary fixture for configuration.yaml based on the supplied config and # test a reload with this new config @@ -1627,16 +1709,31 @@ async def help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config new_config_2["name"] = "test_new_2" new_config_3 = copy.deepcopy(config) new_config_3["name"] = "test_new_3" + new_config_3.pop("platform") + new_config_4 = copy.deepcopy(config) + new_config_4["name"] = "test_new_4" + new_config_4.pop("platform") + new_config_5 = copy.deepcopy(config) + new_config_5["name"] = "test_new_5" + new_config_6 = copy.deepcopy(config) + new_config_6["name"] = "test_new_6" + new_config_6.pop("platform") - await help_test_reload_with_config( - hass, caplog, tmp_path, domain, [new_config_1, new_config_2, new_config_3] - ) + new_config = { + domain: [new_config_1, new_config_2, new_config_5], + "mqtt": {domain: [new_config_3, new_config_4, new_config_6]}, + } - assert len(hass.states.async_all(domain)) == 3 + await help_test_reload_with_config(hass, caplog, tmp_path, new_config) + + assert len(hass.states.async_all(domain)) == 6 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") + assert hass.states.get(f"{domain}.test_new_4") + assert hass.states.get(f"{domain}.test_new_5") + assert hass.states.get(f"{domain}.test_new_6") async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): @@ -1681,9 +1778,10 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): 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] - ) + new_config = { + domain: [new_config_1, new_config_2, new_config_3], + } + await help_test_reload_with_config(hass, caplog, tmp_path, new_config) assert len(hass.states.async_all(domain)) == 3 diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index f0e02ad8a3a..c9784a81f80 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -264,8 +264,9 @@ async def test_hassio_confirm(hass, mock_try_connection_success, mock_finish_set assert len(mock_finish_setup.mock_calls) == 1 -async def test_option_flow(hass, mqtt_mock, mock_try_connection): +async def test_option_flow(hass, mqtt_mock_entry_no_yaml_config, mock_try_connection): """Test config flow options.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() mock_try_connection.return_value = True config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] config_entry.data = { @@ -336,8 +337,11 @@ async def test_option_flow(hass, mqtt_mock, mock_try_connection): assert mqtt_mock.async_connect.call_count == 1 -async def test_disable_birth_will(hass, mqtt_mock, mock_try_connection): +async def test_disable_birth_will( + hass, mqtt_mock_entry_no_yaml_config, mock_try_connection +): """Test disabling birth and will.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() mock_try_connection.return_value = True config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] config_entry.data = { @@ -417,9 +421,10 @@ def get_suggested(schema, key): async def test_option_flow_default_suggested_values( - hass, mqtt_mock, mock_try_connection_success + hass, mqtt_mock_entry_no_yaml_config, mock_try_connection_success ): """Test config flow options has default/suggested values.""" + await mqtt_mock_entry_no_yaml_config() config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] config_entry.data = { mqtt.CONF_BROKER: "test-broker", diff --git a/tests/components/mqtt/test_cover.py b/tests/components/mqtt/test_cover.py index 285af765ab4..5796c12f3cf 100644 --- a/tests/components/mqtt/test_cover.py +++ b/tests/components/mqtt/test_cover.py @@ -12,7 +12,7 @@ from homeassistant.components.cover import ( ATTR_POSITION, ATTR_TILT_POSITION, ) -from homeassistant.components.mqtt import CONF_STATE_TOPIC +from homeassistant.components.mqtt.const import CONF_STATE_TOPIC from homeassistant.components.mqtt.cover import ( CONF_GET_POSITION_TEMPLATE, CONF_GET_POSITION_TOPIC, @@ -83,7 +83,7 @@ DEFAULT_CONFIG = { } -async def test_state_via_state_topic(hass, mqtt_mock): +async def test_state_via_state_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -102,6 +102,7 @@ async def test_state_via_state_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -118,7 +119,9 @@ async def test_state_via_state_topic(hass, mqtt_mock): assert state.state == STATE_OPEN -async def test_opening_and_closing_state_via_custom_state_payload(hass, mqtt_mock): +async def test_opening_and_closing_state_via_custom_state_payload( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling opening and closing state via a custom payload.""" assert await async_setup_component( hass, @@ -139,6 +142,7 @@ async def test_opening_and_closing_state_via_custom_state_payload(hass, mqtt_moc }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -160,7 +164,9 @@ async def test_opening_and_closing_state_via_custom_state_payload(hass, mqtt_moc assert state.state == STATE_CLOSED -async def test_open_closed_state_from_position_optimistic(hass, mqtt_mock): +async def test_open_closed_state_from_position_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the state after setting the position using optimistic mode.""" assert await async_setup_component( hass, @@ -180,6 +186,7 @@ async def test_open_closed_state_from_position_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -207,7 +214,7 @@ async def test_open_closed_state_from_position_optimistic(hass, mqtt_mock): assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_position_via_position_topic(hass, mqtt_mock): +async def test_position_via_position_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -228,6 +235,7 @@ async def test_position_via_position_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -244,7 +252,7 @@ async def test_position_via_position_topic(hass, mqtt_mock): assert state.state == STATE_OPEN -async def test_state_via_template(hass, mqtt_mock): +async def test_state_via_template(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -266,6 +274,7 @@ async def test_state_via_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -281,7 +290,7 @@ async def test_state_via_template(hass, mqtt_mock): assert state.state == STATE_CLOSED -async def test_state_via_template_and_entity_id(hass, mqtt_mock): +async def test_state_via_template_and_entity_id(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -303,6 +312,7 @@ async def test_state_via_template_and_entity_id(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -320,7 +330,9 @@ async def test_state_via_template_and_entity_id(hass, mqtt_mock): assert state.state == STATE_CLOSED -async def test_state_via_template_with_json_value(hass, mqtt_mock, caplog): +async def test_state_via_template_with_json_value( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling state via topic with JSON value.""" assert await async_setup_component( hass, @@ -337,6 +349,7 @@ async def test_state_via_template_with_json_value(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -359,7 +372,9 @@ async def test_state_via_template_with_json_value(hass, mqtt_mock, caplog): ) in caplog.text -async def test_position_via_template_and_entity_id(hass, mqtt_mock): +async def test_position_via_template_and_entity_id( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -381,6 +396,7 @@ async def test_position_via_template_and_entity_id(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -411,7 +427,9 @@ async def test_position_via_template_and_entity_id(hass, mqtt_mock): ({"tilt_command_topic": "abc", "tilt_status_topic": "abc"}, False), ], ) -async def test_optimistic_flag(hass, mqtt_mock, config, assumed_state): +async def test_optimistic_flag( + hass, mqtt_mock_entry_with_yaml_config, config, assumed_state +): """Test assumed_state is set correctly.""" assert await async_setup_component( hass, @@ -419,6 +437,7 @@ async def test_optimistic_flag(hass, mqtt_mock, config, assumed_state): {cover.DOMAIN: {**config, "platform": "mqtt", "name": "test", "qos": 0}}, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -428,7 +447,7 @@ async def test_optimistic_flag(hass, mqtt_mock, config, assumed_state): assert ATTR_ASSUMED_STATE not in state.attributes -async def test_optimistic_state_change(hass, mqtt_mock): +async def test_optimistic_state_change(hass, mqtt_mock_entry_with_yaml_config): """Test changing state optimistically.""" assert await async_setup_component( hass, @@ -443,6 +462,7 @@ async def test_optimistic_state_change(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -484,7 +504,9 @@ async def test_optimistic_state_change(hass, mqtt_mock): assert state.state == STATE_CLOSED -async def test_optimistic_state_change_with_position(hass, mqtt_mock): +async def test_optimistic_state_change_with_position( + hass, mqtt_mock_entry_with_yaml_config +): """Test changing state optimistically.""" assert await async_setup_component( hass, @@ -501,6 +523,7 @@ async def test_optimistic_state_change_with_position(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -547,7 +570,7 @@ async def test_optimistic_state_change_with_position(hass, mqtt_mock): assert state.attributes.get(ATTR_CURRENT_POSITION) == 0 -async def test_send_open_cover_command(hass, mqtt_mock): +async def test_send_open_cover_command(hass, mqtt_mock_entry_with_yaml_config): """Test the sending of open_cover.""" assert await async_setup_component( hass, @@ -563,6 +586,7 @@ async def test_send_open_cover_command(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -576,7 +600,7 @@ async def test_send_open_cover_command(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_send_close_cover_command(hass, mqtt_mock): +async def test_send_close_cover_command(hass, mqtt_mock_entry_with_yaml_config): """Test the sending of close_cover.""" assert await async_setup_component( hass, @@ -592,6 +616,7 @@ async def test_send_close_cover_command(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -605,7 +630,7 @@ async def test_send_close_cover_command(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_send_stop__cover_command(hass, mqtt_mock): +async def test_send_stop__cover_command(hass, mqtt_mock_entry_with_yaml_config): """Test the sending of stop_cover.""" assert await async_setup_component( hass, @@ -621,6 +646,7 @@ async def test_send_stop__cover_command(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -634,7 +660,7 @@ async def test_send_stop__cover_command(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_current_cover_position(hass, mqtt_mock): +async def test_current_cover_position(hass, mqtt_mock_entry_with_yaml_config): """Test the current cover position.""" assert await async_setup_component( hass, @@ -654,6 +680,7 @@ async def test_current_cover_position(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state_attributes_dict = hass.states.get("cover.test").attributes assert ATTR_CURRENT_POSITION not in state_attributes_dict @@ -685,7 +712,7 @@ async def test_current_cover_position(hass, mqtt_mock): assert current_cover_position == 100 -async def test_current_cover_position_inverted(hass, mqtt_mock): +async def test_current_cover_position_inverted(hass, mqtt_mock_entry_with_yaml_config): """Test the current cover position.""" assert await async_setup_component( hass, @@ -705,6 +732,7 @@ async def test_current_cover_position_inverted(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state_attributes_dict = hass.states.get("cover.test").attributes assert ATTR_CURRENT_POSITION not in state_attributes_dict @@ -747,7 +775,7 @@ async def test_current_cover_position_inverted(hass, mqtt_mock): assert hass.states.get("cover.test").state == STATE_CLOSED -async def test_optimistic_position(hass, mqtt_mock): +async def test_optimistic_position(hass, mqtt_mock_entry_no_yaml_config): """Test optimistic position is not supported.""" assert await async_setup_component( hass, @@ -762,12 +790,13 @@ async def test_optimistic_position(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("cover.test") assert state is None -async def test_position_update(hass, mqtt_mock): +async def test_position_update(hass, mqtt_mock_entry_with_yaml_config): """Test cover position update from received MQTT message.""" assert await async_setup_component( hass, @@ -788,6 +817,7 @@ async def test_position_update(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state_attributes_dict = hass.states.get("cover.test").attributes assert ATTR_CURRENT_POSITION not in state_attributes_dict @@ -809,7 +839,7 @@ async def test_position_update(hass, mqtt_mock): [("{{position-1}}", 43, "42"), ("{{100-62}}", 100, "38")], ) async def test_set_position_templated( - hass, mqtt_mock, pos_template, pos_call, pos_message + hass, mqtt_mock_entry_with_yaml_config, pos_template, pos_call, pos_message ): """Test setting cover position via template.""" assert await async_setup_component( @@ -832,6 +862,7 @@ async def test_set_position_templated( }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -845,7 +876,9 @@ async def test_set_position_templated( ) -async def test_set_position_templated_and_attributes(hass, mqtt_mock): +async def test_set_position_templated_and_attributes( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting cover position via template and using entities attributes.""" assert await async_setup_component( hass, @@ -876,6 +909,7 @@ async def test_set_position_templated_and_attributes(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -887,7 +921,7 @@ async def test_set_position_templated_and_attributes(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("set-position-topic", "5", 0, False) -async def test_set_tilt_templated(hass, mqtt_mock): +async def test_set_tilt_templated(hass, mqtt_mock_entry_with_yaml_config): """Test setting cover tilt position via template.""" assert await async_setup_component( hass, @@ -911,6 +945,7 @@ async def test_set_tilt_templated(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -924,7 +959,9 @@ async def test_set_tilt_templated(hass, mqtt_mock): ) -async def test_set_tilt_templated_and_attributes(hass, mqtt_mock): +async def test_set_tilt_templated_and_attributes( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting cover tilt position via template and using entities attributes.""" assert await async_setup_component( hass, @@ -952,6 +989,7 @@ async def test_set_tilt_templated_and_attributes(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1010,7 +1048,7 @@ async def test_set_tilt_templated_and_attributes(hass, mqtt_mock): ) -async def test_set_position_untemplated(hass, mqtt_mock): +async def test_set_position_untemplated(hass, mqtt_mock_entry_with_yaml_config): """Test setting cover position via template.""" assert await async_setup_component( hass, @@ -1029,6 +1067,7 @@ async def test_set_position_untemplated(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1040,7 +1079,9 @@ async def test_set_position_untemplated(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("position-topic", "62", 0, False) -async def test_set_position_untemplated_custom_percentage_range(hass, mqtt_mock): +async def test_set_position_untemplated_custom_percentage_range( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting cover position via template.""" assert await async_setup_component( hass, @@ -1061,6 +1102,7 @@ async def test_set_position_untemplated_custom_percentage_range(hass, mqtt_mock) }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1072,7 +1114,7 @@ async def test_set_position_untemplated_custom_percentage_range(hass, mqtt_mock) mqtt_mock.async_publish.assert_called_once_with("position-topic", "62", 0, False) -async def test_no_command_topic(hass, mqtt_mock): +async def test_no_command_topic(hass, mqtt_mock_entry_with_yaml_config): """Test with no command topic.""" assert await async_setup_component( hass, @@ -1091,11 +1133,12 @@ async def test_no_command_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get("cover.test").attributes["supported_features"] == 240 -async def test_no_payload_close(hass, mqtt_mock): +async def test_no_payload_close(hass, mqtt_mock_entry_with_yaml_config): """Test with no close payload.""" assert await async_setup_component( hass, @@ -1113,11 +1156,12 @@ async def test_no_payload_close(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get("cover.test").attributes["supported_features"] == 9 -async def test_no_payload_open(hass, mqtt_mock): +async def test_no_payload_open(hass, mqtt_mock_entry_with_yaml_config): """Test with no open payload.""" assert await async_setup_component( hass, @@ -1135,11 +1179,12 @@ async def test_no_payload_open(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get("cover.test").attributes["supported_features"] == 10 -async def test_no_payload_stop(hass, mqtt_mock): +async def test_no_payload_stop(hass, mqtt_mock_entry_with_yaml_config): """Test with no stop payload.""" assert await async_setup_component( hass, @@ -1157,11 +1202,12 @@ async def test_no_payload_stop(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get("cover.test").attributes["supported_features"] == 3 -async def test_with_command_topic_and_tilt(hass, mqtt_mock): +async def test_with_command_topic_and_tilt(hass, mqtt_mock_entry_with_yaml_config): """Test with command topic and tilt config.""" assert await async_setup_component( hass, @@ -1181,11 +1227,12 @@ async def test_with_command_topic_and_tilt(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get("cover.test").attributes["supported_features"] == 251 -async def test_tilt_defaults(hass, mqtt_mock): +async def test_tilt_defaults(hass, mqtt_mock_entry_with_yaml_config): """Test the defaults.""" assert await async_setup_component( hass, @@ -1206,6 +1253,7 @@ async def test_tilt_defaults(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state_attributes_dict = hass.states.get("cover.test").attributes assert ATTR_CURRENT_TILT_POSITION in state_attributes_dict @@ -1216,7 +1264,7 @@ async def test_tilt_defaults(hass, mqtt_mock): assert current_cover_position == STATE_UNKNOWN -async def test_tilt_via_invocation_defaults(hass, mqtt_mock): +async def test_tilt_via_invocation_defaults(hass, mqtt_mock_entry_with_yaml_config): """Test tilt defaults on close/open.""" assert await async_setup_component( hass, @@ -1237,6 +1285,7 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1298,7 +1347,7 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", "0", 0, False) -async def test_tilt_given_value(hass, mqtt_mock): +async def test_tilt_given_value(hass, mqtt_mock_entry_with_yaml_config): """Test tilting to a given value.""" assert await async_setup_component( hass, @@ -1321,6 +1370,7 @@ async def test_tilt_given_value(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1386,7 +1436,7 @@ async def test_tilt_given_value(hass, mqtt_mock): ) -async def test_tilt_given_value_optimistic(hass, mqtt_mock): +async def test_tilt_given_value_optimistic(hass, mqtt_mock_entry_with_yaml_config): """Test tilting to a given value.""" assert await async_setup_component( hass, @@ -1410,6 +1460,7 @@ async def test_tilt_given_value_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1462,7 +1513,7 @@ async def test_tilt_given_value_optimistic(hass, mqtt_mock): ) -async def test_tilt_given_value_altered_range(hass, mqtt_mock): +async def test_tilt_given_value_altered_range(hass, mqtt_mock_entry_with_yaml_config): """Test tilting to a given value.""" assert await async_setup_component( hass, @@ -1488,6 +1539,7 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1538,7 +1590,7 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock): ) -async def test_tilt_via_topic(hass, mqtt_mock): +async def test_tilt_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test tilt by updating status via MQTT.""" assert await async_setup_component( hass, @@ -1559,6 +1611,7 @@ async def test_tilt_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "0") @@ -1575,7 +1628,7 @@ async def test_tilt_via_topic(hass, mqtt_mock): assert current_cover_tilt_position == 50 -async def test_tilt_via_topic_template(hass, mqtt_mock): +async def test_tilt_via_topic_template(hass, mqtt_mock_entry_with_yaml_config): """Test tilt by updating status via MQTT and template.""" assert await async_setup_component( hass, @@ -1599,6 +1652,7 @@ async def test_tilt_via_topic_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "99") @@ -1615,7 +1669,9 @@ async def test_tilt_via_topic_template(hass, mqtt_mock): assert current_cover_tilt_position == 50 -async def test_tilt_via_topic_template_json_value(hass, mqtt_mock, caplog): +async def test_tilt_via_topic_template_json_value( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test tilt by updating status via MQTT and template with JSON value.""" assert await async_setup_component( hass, @@ -1639,6 +1695,7 @@ async def test_tilt_via_topic_template_json_value(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", '{"Var1": 9, "Var2": 30}') @@ -1661,7 +1718,7 @@ async def test_tilt_via_topic_template_json_value(hass, mqtt_mock, caplog): ) in caplog.text -async def test_tilt_via_topic_altered_range(hass, mqtt_mock): +async def test_tilt_via_topic_altered_range(hass, mqtt_mock_entry_with_yaml_config): """Test tilt status via MQTT with altered tilt range.""" assert await async_setup_component( hass, @@ -1684,6 +1741,7 @@ async def test_tilt_via_topic_altered_range(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "0") @@ -1707,7 +1765,9 @@ async def test_tilt_via_topic_altered_range(hass, mqtt_mock): assert current_cover_tilt_position == 50 -async def test_tilt_status_out_of_range_warning(hass, caplog, mqtt_mock): +async def test_tilt_status_out_of_range_warning( + hass, caplog, mqtt_mock_entry_with_yaml_config +): """Test tilt status via MQTT tilt out of range warning message.""" assert await async_setup_component( hass, @@ -1730,6 +1790,7 @@ async def test_tilt_status_out_of_range_warning(hass, caplog, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "60") @@ -1738,7 +1799,9 @@ async def test_tilt_status_out_of_range_warning(hass, caplog, mqtt_mock): ) in caplog.text -async def test_tilt_status_not_numeric_warning(hass, caplog, mqtt_mock): +async def test_tilt_status_not_numeric_warning( + hass, caplog, mqtt_mock_entry_with_yaml_config +): """Test tilt status via MQTT tilt not numeric warning message.""" assert await async_setup_component( hass, @@ -1761,13 +1824,16 @@ async def test_tilt_status_not_numeric_warning(hass, caplog, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "abc") assert ("Payload 'abc' is not numeric") in caplog.text -async def test_tilt_via_topic_altered_range_inverted(hass, mqtt_mock): +async def test_tilt_via_topic_altered_range_inverted( + hass, mqtt_mock_entry_with_yaml_config +): """Test tilt status via MQTT with altered tilt range and inverted tilt position.""" assert await async_setup_component( hass, @@ -1790,6 +1856,7 @@ async def test_tilt_via_topic_altered_range_inverted(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "0") @@ -1813,7 +1880,9 @@ async def test_tilt_via_topic_altered_range_inverted(hass, mqtt_mock): assert current_cover_tilt_position == 50 -async def test_tilt_via_topic_template_altered_range(hass, mqtt_mock): +async def test_tilt_via_topic_template_altered_range( + hass, mqtt_mock_entry_with_yaml_config +): """Test tilt status via MQTT and template with altered tilt range.""" assert await async_setup_component( hass, @@ -1839,6 +1908,7 @@ async def test_tilt_via_topic_template_altered_range(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "tilt-status-topic", "99") @@ -1862,7 +1932,7 @@ async def test_tilt_via_topic_template_altered_range(hass, mqtt_mock): assert current_cover_tilt_position == 50 -async def test_tilt_position(hass, mqtt_mock): +async def test_tilt_position(hass, mqtt_mock_entry_with_yaml_config): """Test tilt via method invocation.""" assert await async_setup_component( hass, @@ -1883,6 +1953,7 @@ async def test_tilt_position(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1896,7 +1967,7 @@ async def test_tilt_position(hass, mqtt_mock): ) -async def test_tilt_position_templated(hass, mqtt_mock): +async def test_tilt_position_templated(hass, mqtt_mock_entry_with_yaml_config): """Test tilt position via template.""" assert await async_setup_component( hass, @@ -1918,6 +1989,7 @@ async def test_tilt_position_templated(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1931,7 +2003,7 @@ async def test_tilt_position_templated(hass, mqtt_mock): ) -async def test_tilt_position_altered_range(hass, mqtt_mock): +async def test_tilt_position_altered_range(hass, mqtt_mock_entry_with_yaml_config): """Test tilt via method invocation with altered range.""" assert await async_setup_component( hass, @@ -1956,6 +2028,7 @@ async def test_tilt_position_altered_range(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( cover.DOMAIN, @@ -1969,7 +2042,7 @@ async def test_tilt_position_altered_range(hass, mqtt_mock): ) -async def test_find_percentage_in_range_defaults(hass, mqtt_mock): +async def test_find_percentage_in_range_defaults(hass): """Test find percentage in range with default range.""" mqtt_cover = MqttCover( hass, @@ -2012,7 +2085,7 @@ async def test_find_percentage_in_range_defaults(hass, mqtt_mock): assert mqtt_cover.find_percentage_in_range(44, "cover") == 44 -async def test_find_percentage_in_range_altered(hass, mqtt_mock): +async def test_find_percentage_in_range_altered(hass): """Test find percentage in range with altered range.""" mqtt_cover = MqttCover( hass, @@ -2055,7 +2128,7 @@ async def test_find_percentage_in_range_altered(hass, mqtt_mock): assert mqtt_cover.find_percentage_in_range(120, "cover") == 40 -async def test_find_percentage_in_range_defaults_inverted(hass, mqtt_mock): +async def test_find_percentage_in_range_defaults_inverted(hass): """Test find percentage in range with default range but inverted.""" mqtt_cover = MqttCover( hass, @@ -2098,7 +2171,7 @@ async def test_find_percentage_in_range_defaults_inverted(hass, mqtt_mock): assert mqtt_cover.find_percentage_in_range(44, "cover") == 56 -async def test_find_percentage_in_range_altered_inverted(hass, mqtt_mock): +async def test_find_percentage_in_range_altered_inverted(hass): """Test find percentage in range with altered range and inverted.""" mqtt_cover = MqttCover( hass, @@ -2141,7 +2214,7 @@ async def test_find_percentage_in_range_altered_inverted(hass, mqtt_mock): assert mqtt_cover.find_percentage_in_range(120, "cover") == 60 -async def test_find_in_range_defaults(hass, mqtt_mock): +async def test_find_in_range_defaults(hass): """Test find in range with default range.""" mqtt_cover = MqttCover( hass, @@ -2184,7 +2257,7 @@ async def test_find_in_range_defaults(hass, mqtt_mock): assert mqtt_cover.find_in_range_from_percent(44, "cover") == 44 -async def test_find_in_range_altered(hass, mqtt_mock): +async def test_find_in_range_altered(hass): """Test find in range with altered range.""" mqtt_cover = MqttCover( hass, @@ -2227,7 +2300,7 @@ async def test_find_in_range_altered(hass, mqtt_mock): assert mqtt_cover.find_in_range_from_percent(40, "cover") == 120 -async def test_find_in_range_defaults_inverted(hass, mqtt_mock): +async def test_find_in_range_defaults_inverted(hass): """Test find in range with default range but inverted.""" mqtt_cover = MqttCover( hass, @@ -2270,7 +2343,7 @@ async def test_find_in_range_defaults_inverted(hass, mqtt_mock): assert mqtt_cover.find_in_range_from_percent(56, "cover") == 44 -async def test_find_in_range_altered_inverted(hass, mqtt_mock): +async def test_find_in_range_altered_inverted(hass): """Test find in range with altered range and inverted.""" mqtt_cover = MqttCover( hass, @@ -2313,35 +2386,37 @@ async def test_find_in_range_altered_inverted(hass, mqtt_mock): assert mqtt_cover.find_in_range_from_percent(60, "cover") == 120 -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_valid_device_class(hass, mqtt_mock): +async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of a valid device class.""" assert await async_setup_component( hass, @@ -2356,12 +2431,13 @@ async def test_valid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.attributes.get("device_class") == "garage" -async def test_invalid_device_class(hass, mqtt_mock): +async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config): """Test the setting of an invalid device class.""" assert await async_setup_component( hass, @@ -2376,54 +2452,67 @@ async def test_invalid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("cover.test") assert state is None -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG, MQTT_COVER_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + cover.DOMAIN, + DEFAULT_CONFIG, + MQTT_COVER_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_json(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_json( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique_id option only creates one cover per id.""" config = { cover.DOMAIN: [ @@ -2441,92 +2530,103 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, cover.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, config + ) -async def test_discovery_removal_cover(hass, mqtt_mock, caplog): +async def test_discovery_removal_cover(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered cover.""" data = '{ "name": "test", "command_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, cover.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, cover.DOMAIN, data + ) -async def test_discovery_update_cover(hass, mqtt_mock, caplog): +async def test_discovery_update_cover(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered cover.""" config1 = {"name": "Beer", "command_topic": "test_topic"} config2 = {"name": "Milk", "command_topic": "test_topic"} await help_test_discovery_update( - hass, mqtt_mock, caplog, cover.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, cover.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_cover(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_cover( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered cover.""" data1 = '{ "name": "Beer", "command_topic": "test_topic" }' with patch( "homeassistant.components.mqtt.cover.MqttCover.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, cover.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + cover.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer", "command_topic": "test_topic#" }' data2 = '{ "name": "Milk", "command_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, cover.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, cover.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT cover device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT cover device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, cover.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, cover.DOMAIN, DEFAULT_CONFIG, SERVICE_OPEN_COVER, @@ -2535,7 +2635,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): async def test_state_and_position_topics_state_not_set_via_position_topic( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test state is not set via position topic when both state and position topics are set.""" assert await async_setup_component( @@ -2557,6 +2657,7 @@ async def test_state_and_position_topics_state_not_set_via_position_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -2593,7 +2694,9 @@ async def test_state_and_position_topics_state_not_set_via_position_topic( assert state.state == STATE_CLOSED -async def test_set_state_via_position_using_stopped_state(hass, mqtt_mock): +async def test_set_state_via_position_using_stopped_state( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via position topic using stopped state.""" assert await async_setup_component( hass, @@ -2615,6 +2718,7 @@ async def test_set_state_via_position_using_stopped_state(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("cover.test") assert state.state == STATE_UNKNOWN @@ -2646,7 +2750,9 @@ async def test_set_state_via_position_using_stopped_state(hass, mqtt_mock): assert state.state == STATE_OPEN -async def test_position_via_position_topic_template(hass, mqtt_mock): +async def test_position_via_position_topic_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test position by updating status via position template.""" assert await async_setup_component( hass, @@ -2664,6 +2770,7 @@ async def test_position_via_position_topic_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "99") @@ -2680,7 +2787,9 @@ async def test_position_via_position_topic_template(hass, mqtt_mock): assert current_cover_position_position == 50 -async def test_position_via_position_topic_template_json_value(hass, mqtt_mock, caplog): +async def test_position_via_position_topic_template_json_value( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test position by updating status via position template with a JSON value.""" assert await async_setup_component( hass, @@ -2698,6 +2807,7 @@ async def test_position_via_position_topic_template_json_value(hass, mqtt_mock, }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", '{"Var1": 9, "Var2": 60}') @@ -2720,7 +2830,7 @@ async def test_position_via_position_topic_template_json_value(hass, mqtt_mock, ) in caplog.text -async def test_position_template_with_entity_id(hass, mqtt_mock): +async def test_position_template_with_entity_id(hass, mqtt_mock_entry_with_yaml_config): """Test position by updating status via position template.""" assert await async_setup_component( hass, @@ -2743,6 +2853,7 @@ async def test_position_template_with_entity_id(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "10") @@ -2759,7 +2870,9 @@ async def test_position_template_with_entity_id(hass, mqtt_mock): assert current_cover_position_position == 20 -async def test_position_via_position_topic_template_return_json(hass, mqtt_mock): +async def test_position_via_position_topic_template_return_json( + hass, mqtt_mock_entry_with_yaml_config +): """Test position by updating status via position template and returning json.""" assert await async_setup_component( hass, @@ -2777,6 +2890,7 @@ async def test_position_via_position_topic_template_return_json(hass, mqtt_mock) }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "55") @@ -2787,7 +2901,7 @@ async def test_position_via_position_topic_template_return_json(hass, mqtt_mock) async def test_position_via_position_topic_template_return_json_warning( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_with_yaml_config ): """Test position by updating status via position template returning json without position attribute.""" assert await async_setup_component( @@ -2806,6 +2920,7 @@ async def test_position_via_position_topic_template_return_json_warning( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "55") @@ -2816,7 +2931,7 @@ async def test_position_via_position_topic_template_return_json_warning( async def test_position_and_tilt_via_position_topic_template_return_json( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test position and tilt by updating the position via position template.""" assert await async_setup_component( @@ -2836,6 +2951,7 @@ async def test_position_and_tilt_via_position_topic_template_return_json( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "0") @@ -2857,7 +2973,9 @@ async def test_position_and_tilt_via_position_topic_template_return_json( assert current_cover_position == 99 and current_tilt_position == 49 -async def test_position_via_position_topic_template_all_variables(hass, mqtt_mock): +async def test_position_via_position_topic_template_all_variables( + hass, mqtt_mock_entry_with_yaml_config +): """Test position by updating status via position template.""" assert await async_setup_component( hass, @@ -2886,6 +3004,7 @@ async def test_position_via_position_topic_template_all_variables(hass, mqtt_moc }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "0") @@ -2901,7 +3020,9 @@ async def test_position_via_position_topic_template_all_variables(hass, mqtt_moc assert current_cover_position == 100 -async def test_set_state_via_stopped_state_no_position_topic(hass, mqtt_mock): +async def test_set_state_via_stopped_state_no_position_topic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via stopped state when no position topic.""" assert await async_setup_component( hass, @@ -2923,6 +3044,7 @@ async def test_set_state_via_stopped_state_no_position_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "state-topic", "OPEN") @@ -2951,7 +3073,7 @@ async def test_set_state_via_stopped_state_no_position_topic(hass, mqtt_mock): async def test_position_via_position_topic_template_return_invalid_json( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_with_yaml_config ): """Test position by updating status via position template and returning invalid json.""" assert await async_setup_component( @@ -2970,6 +3092,7 @@ async def test_position_via_position_topic_template_return_invalid_json( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "get-position-topic", "55") @@ -2977,7 +3100,7 @@ async def test_position_via_position_topic_template_return_invalid_json( async def test_set_position_topic_without_get_position_topic_error( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_no_yaml_config ): """Test error when set_position_topic is used without position_topic.""" assert await async_setup_component( @@ -2994,13 +3117,16 @@ async def test_set_position_topic_without_get_position_topic_error( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert ( f"'{CONF_SET_POSITION_TOPIC}' must be set together with '{CONF_GET_POSITION_TOPIC}'." ) in caplog.text -async def test_value_template_without_state_topic_error(hass, caplog, mqtt_mock): +async def test_value_template_without_state_topic_error( + hass, caplog, mqtt_mock_entry_no_yaml_config +): """Test error when value_template is used and state_topic is missing.""" assert await async_setup_component( hass, @@ -3015,13 +3141,16 @@ async def test_value_template_without_state_topic_error(hass, caplog, mqtt_mock) }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert ( f"'{CONF_VALUE_TEMPLATE}' must be set together with '{CONF_STATE_TOPIC}'." ) in caplog.text -async def test_position_template_without_position_topic_error(hass, caplog, mqtt_mock): +async def test_position_template_without_position_topic_error( + hass, caplog, mqtt_mock_entry_no_yaml_config +): """Test error when position_template is used and position_topic is missing.""" assert await async_setup_component( hass, @@ -3036,6 +3165,7 @@ async def test_position_template_without_position_topic_error(hass, caplog, mqtt }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert ( f"'{CONF_GET_POSITION_TEMPLATE}' must be set together with '{CONF_GET_POSITION_TOPIC}'." @@ -3044,7 +3174,7 @@ async def test_position_template_without_position_topic_error(hass, caplog, mqtt async def test_set_position_template_without_set_position_topic( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_no_yaml_config ): """Test error when set_position_template is used and set_position_topic is missing.""" assert await async_setup_component( @@ -3060,6 +3190,7 @@ async def test_set_position_template_without_set_position_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert ( f"'{CONF_SET_POSITION_TEMPLATE}' must be set together with '{CONF_SET_POSITION_TOPIC}'." @@ -3068,7 +3199,7 @@ async def test_set_position_template_without_set_position_topic( async def test_tilt_command_template_without_tilt_command_topic( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_no_yaml_config ): """Test error when tilt_command_template is used and tilt_command_topic is missing.""" assert await async_setup_component( @@ -3084,6 +3215,7 @@ async def test_tilt_command_template_without_tilt_command_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert ( f"'{CONF_TILT_COMMAND_TEMPLATE}' must be set together with '{CONF_TILT_COMMAND_TOPIC}'." @@ -3092,7 +3224,7 @@ async def test_tilt_command_template_without_tilt_command_topic( async def test_tilt_status_template_without_tilt_status_topic_topic( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_no_yaml_config ): """Test error when tilt_status_template is used and tilt_status_topic is missing.""" assert await async_setup_component( @@ -3108,6 +3240,7 @@ async def test_tilt_status_template_without_tilt_status_topic_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert ( f"'{CONF_TILT_STATUS_TEMPLATE}' must be set together with '{CONF_TILT_STATUS_TOPIC}'." @@ -3143,7 +3276,7 @@ async def test_tilt_status_template_without_tilt_status_topic_topic( ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -3158,7 +3291,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -3170,11 +3303,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = cover.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -3194,12 +3329,18 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, cover.DOMAIN, DEFAULT_CONFIG[cover.DOMAIN], diff --git a/tests/components/mqtt/test_device_tracker.py b/tests/components/mqtt/test_device_tracker.py index 020fbad6166..34042105af2 100644 --- a/tests/components/mqtt/test_device_tracker.py +++ b/tests/components/mqtt/test_device_tracker.py @@ -11,7 +11,9 @@ from tests.common import async_fire_mqtt_message # Deprecated in HA Core 2022.6 -async def test_legacy_ensure_device_tracker_platform_validation(hass, mqtt_mock): +async def test_legacy_ensure_device_tracker_platform_validation( + hass, mqtt_mock_entry_with_yaml_config +): """Test if platform validation was done.""" async def mock_setup_scanner(hass, config, see, discovery_info=None): @@ -29,12 +31,17 @@ async def test_legacy_ensure_device_tracker_platform_validation(hass, mqtt_mock) assert await async_setup_component( hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}} ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert mock_sp.call_count == 1 # Deprecated in HA Core 2022.6 -async def test_legacy_new_message(hass, mock_device_tracker_conf, mqtt_mock): +async def test_legacy_new_message( + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config +): """Test new message.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" topic = "/location/paulus" @@ -51,9 +58,10 @@ async def test_legacy_new_message(hass, mock_device_tracker_conf, mqtt_mock): # Deprecated in HA Core 2022.6 async def test_legacy_single_level_wildcard_topic( - hass, mock_device_tracker_conf, mqtt_mock + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config ): """Test single level wildcard topic.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" subscription = "/location/+/paulus" @@ -73,9 +81,10 @@ async def test_legacy_single_level_wildcard_topic( # Deprecated in HA Core 2022.6 async def test_legacy_multi_level_wildcard_topic( - hass, mock_device_tracker_conf, mqtt_mock + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config ): """Test multi level wildcard topic.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" subscription = "/location/#" @@ -95,9 +104,10 @@ async def test_legacy_multi_level_wildcard_topic( # Deprecated in HA Core 2022.6 async def test_legacy_single_level_wildcard_topic_not_matching( - hass, mock_device_tracker_conf, mqtt_mock + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config ): """Test not matching single level wildcard topic.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" subscription = "/location/+/paulus" @@ -117,9 +127,10 @@ async def test_legacy_single_level_wildcard_topic_not_matching( # Deprecated in HA Core 2022.6 async def test_legacy_multi_level_wildcard_topic_not_matching( - hass, mock_device_tracker_conf, mqtt_mock + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config ): """Test not matching multi level wildcard topic.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" subscription = "/location/#" @@ -139,9 +150,10 @@ async def test_legacy_multi_level_wildcard_topic_not_matching( # Deprecated in HA Core 2022.6 async def test_legacy_matching_custom_payload_for_home_and_not_home( - hass, mock_device_tracker_conf, mqtt_mock + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config ): """Test custom payload_home sets state to home and custom payload_not_home sets state to not_home.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" topic = "/location/paulus" @@ -172,9 +184,10 @@ async def test_legacy_matching_custom_payload_for_home_and_not_home( # Deprecated in HA Core 2022.6 async def test_legacy_not_matching_custom_payload_for_home_and_not_home( - hass, mock_device_tracker_conf, mqtt_mock + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config ): """Test not matching payload does not set state to home or not_home.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" topic = "/location/paulus" @@ -202,8 +215,11 @@ async def test_legacy_not_matching_custom_payload_for_home_and_not_home( # Deprecated in HA Core 2022.6 -async def test_legacy_matching_source_type(hass, mock_device_tracker_conf, mqtt_mock): +async def test_legacy_matching_source_type( + hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config +): """Test setting source type.""" + await mqtt_mock_entry_no_yaml_config() dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" topic = "/location/paulus" diff --git a/tests/components/mqtt/test_device_tracker_discovery.py b/tests/components/mqtt/test_device_tracker_discovery.py index f8ee94b58f9..31853ad1dee 100644 --- a/tests/components/mqtt/test_device_tracker_discovery.py +++ b/tests/components/mqtt/test_device_tracker_discovery.py @@ -33,8 +33,9 @@ def entity_reg(hass): return mock_registry(hass) -async def test_discover_device_tracker(hass, mqtt_mock, caplog): +async def test_discover_device_tracker(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test discovering an MQTT device tracker component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -50,8 +51,9 @@ async def test_discover_device_tracker(hass, mqtt_mock, caplog): @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -74,8 +76,11 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): assert state.name == "Beer" -async def test_non_duplicate_device_tracker_discovery(hass, mqtt_mock, caplog): +async def test_non_duplicate_device_tracker_discovery( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test for a non duplicate component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -97,8 +102,9 @@ async def test_non_duplicate_device_tracker_discovery(hass, mqtt_mock, caplog): assert "Component has already been discovered: device_tracker bla" in caplog.text -async def test_device_tracker_removal(hass, mqtt_mock, caplog): +async def test_device_tracker_removal(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of component through empty discovery message.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -114,8 +120,9 @@ async def test_device_tracker_removal(hass, mqtt_mock, caplog): assert state is None -async def test_device_tracker_rediscover(hass, mqtt_mock, caplog): +async def test_device_tracker_rediscover(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test rediscover of removed component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -140,8 +147,11 @@ async def test_device_tracker_rediscover(hass, mqtt_mock, caplog): assert state is not None -async def test_duplicate_device_tracker_removal(hass, mqtt_mock, caplog): +async def test_duplicate_device_tracker_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test for a non duplicate component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -160,8 +170,11 @@ async def test_duplicate_device_tracker_removal(hass, mqtt_mock, caplog): ) -async def test_device_tracker_discovery_update(hass, mqtt_mock, caplog): +async def test_device_tracker_discovery_update( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test for a discovery update event.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -186,10 +199,12 @@ async def test_device_tracker_discovery_update(hass, mqtt_mock, caplog): async def test_cleanup_device_tracker( - hass, hass_ws_client, device_reg, entity_reg, mqtt_mock + hass, hass_ws_client, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config ): """Test discovered device is cleaned up when removed from registry.""" assert await async_setup_component(hass, "config", {}) + await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_no_yaml_config() ws_client = await hass_ws_client(hass) async_fire_mqtt_message( @@ -242,8 +257,11 @@ async def test_cleanup_device_tracker( ) -async def test_setting_device_tracker_value_via_mqtt_message(hass, mqtt_mock, caplog): +async def test_setting_device_tracker_value_via_mqtt_message( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test the setting of the value via MQTT.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -266,9 +284,10 @@ async def test_setting_device_tracker_value_via_mqtt_message(hass, mqtt_mock, ca async def test_setting_device_tracker_value_via_mqtt_message_and_template( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test the setting of the value via MQTT.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -290,9 +309,10 @@ async def test_setting_device_tracker_value_via_mqtt_message_and_template( async def test_setting_device_tracker_value_via_mqtt_message_and_template2( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test the setting of the value via MQTT.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -317,9 +337,10 @@ async def test_setting_device_tracker_value_via_mqtt_message_and_template2( async def test_setting_device_tracker_location_via_mqtt_message( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test the setting of the location via MQTT.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -337,9 +358,10 @@ async def test_setting_device_tracker_location_via_mqtt_message( async def test_setting_device_tracker_location_via_lat_lon_message( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test the setting of the latitude and longitude via MQTT.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", @@ -391,8 +413,14 @@ async def test_setting_device_tracker_location_via_lat_lon_message( assert state.state == STATE_UNKNOWN -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, device_tracker.DOMAIN, DEFAULT_CONFIG, None + hass, + mqtt_mock_entry_no_yaml_config, + device_tracker.DOMAIN, + DEFAULT_CONFIG, + None, ) diff --git a/tests/components/mqtt/test_device_trigger.py b/tests/components/mqtt/test_device_trigger.py index ef5dd22692f..fe08c85a853 100644 --- a/tests/components/mqtt/test_device_trigger.py +++ b/tests/components/mqtt/test_device_trigger.py @@ -39,8 +39,11 @@ def calls(hass): return async_mock_service(hass, "test", "automation") -async def test_get_triggers(hass, device_reg, entity_reg, mqtt_mock): +async def test_get_triggers( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test we get the expected triggers from a discovered mqtt device.""" + await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -70,8 +73,11 @@ async def test_get_triggers(hass, device_reg, entity_reg, mqtt_mock): assert_lists_same(triggers, expected_triggers) -async def test_get_unknown_triggers(hass, device_reg, entity_reg, mqtt_mock): +async def test_get_unknown_triggers( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test we don't get unknown triggers.""" + await mqtt_mock_entry_no_yaml_config() # Discover a sensor (without device triggers) data1 = ( '{ "device":{"identifiers":["0AFFD2"]},' @@ -112,8 +118,11 @@ async def test_get_unknown_triggers(hass, device_reg, entity_reg, mqtt_mock): assert_lists_same(triggers, []) -async def test_get_non_existing_triggers(hass, device_reg, entity_reg, mqtt_mock): +async def test_get_non_existing_triggers( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test getting non existing triggers.""" + await mqtt_mock_entry_no_yaml_config() # Discover a sensor (without device triggers) data1 = ( '{ "device":{"identifiers":["0AFFD2"]},' @@ -131,8 +140,11 @@ async def test_get_non_existing_triggers(hass, device_reg, entity_reg, mqtt_mock @pytest.mark.no_fail_on_log_exception -async def test_discover_bad_triggers(hass, device_reg, entity_reg, mqtt_mock): +async def test_discover_bad_triggers( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test bad discovery message.""" + await mqtt_mock_entry_no_yaml_config() # Test sending bad data data0 = ( '{ "automation_type":"trigger",' @@ -176,8 +188,11 @@ async def test_discover_bad_triggers(hass, device_reg, entity_reg, mqtt_mock): assert_lists_same(triggers, expected_triggers) -async def test_update_remove_triggers(hass, device_reg, entity_reg, mqtt_mock): +async def test_update_remove_triggers( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test triggers can be updated and removed.""" + await mqtt_mock_entry_no_yaml_config() config1 = { "automation_type": "trigger", "device": {"identifiers": ["0AFFD2"]}, @@ -240,8 +255,11 @@ async def test_update_remove_triggers(hass, device_reg, entity_reg, mqtt_mock): assert device_entry is None -async def test_if_fires_on_mqtt_message(hass, device_reg, calls, mqtt_mock): +async def test_if_fires_on_mqtt_message( + hass, device_reg, calls, mqtt_mock_entry_no_yaml_config +): """Test triggers firing.""" + await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -313,8 +331,11 @@ async def test_if_fires_on_mqtt_message(hass, device_reg, calls, mqtt_mock): assert calls[1].data["some"] == "long_press" -async def test_if_fires_on_mqtt_message_template(hass, device_reg, calls, mqtt_mock): +async def test_if_fires_on_mqtt_message_template( + hass, device_reg, calls, mqtt_mock_entry_no_yaml_config +): """Test triggers firing.""" + await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -389,9 +410,10 @@ async def test_if_fires_on_mqtt_message_template(hass, device_reg, calls, mqtt_m async def test_if_fires_on_mqtt_message_late_discover( - hass, device_reg, calls, mqtt_mock + hass, device_reg, calls, mqtt_mock_entry_no_yaml_config ): """Test triggers firing of MQTT device triggers discovered after setup.""" + await mqtt_mock_entry_no_yaml_config() data0 = ( '{ "device":{"identifiers":["0AFFD2"]},' ' "state_topic": "foobar/sensor",' @@ -472,9 +494,10 @@ async def test_if_fires_on_mqtt_message_late_discover( async def test_if_fires_on_mqtt_message_after_update( - hass, device_reg, calls, mqtt_mock + hass, device_reg, calls, mqtt_mock_entry_no_yaml_config ): """Test triggers firing after update.""" + await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -515,6 +538,7 @@ async def test_if_fires_on_mqtt_message_after_update( ] }, ) + await hass.async_block_till_done() # Fake short press. async_fire_mqtt_message(hass, "foobar/triggers/button1", "") @@ -546,8 +570,11 @@ async def test_if_fires_on_mqtt_message_after_update( assert len(calls) == 3 -async def test_no_resubscribe_same_topic(hass, device_reg, mqtt_mock): +async def test_no_resubscribe_same_topic( + hass, device_reg, mqtt_mock_entry_no_yaml_config +): """Test subscription to topics without change.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -589,9 +616,10 @@ async def test_no_resubscribe_same_topic(hass, device_reg, mqtt_mock): async def test_not_fires_on_mqtt_message_after_remove_by_mqtt( - hass, device_reg, calls, mqtt_mock + hass, device_reg, calls, mqtt_mock_entry_no_yaml_config ): """Test triggers not firing after removal.""" + await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -625,6 +653,7 @@ async def test_not_fires_on_mqtt_message_after_remove_by_mqtt( ] }, ) + await hass.async_block_till_done() # Fake short press. async_fire_mqtt_message(hass, "foobar/triggers/button1", "short_press") @@ -649,10 +678,13 @@ async def test_not_fires_on_mqtt_message_after_remove_by_mqtt( async def test_not_fires_on_mqtt_message_after_remove_from_registry( - hass, hass_ws_client, device_reg, calls, mqtt_mock + hass, hass_ws_client, device_reg, calls, mqtt_mock_entry_no_yaml_config ): """Test triggers not firing after removal.""" assert await async_setup_component(hass, "config", {}) + await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() + ws_client = await hass_ws_client(hass) data1 = ( @@ -713,8 +745,9 @@ async def test_not_fires_on_mqtt_message_after_remove_from_registry( assert len(calls) == 1 -async def test_attach_remove(hass, device_reg, mqtt_mock): +async def test_attach_remove(hass, device_reg, mqtt_mock_entry_no_yaml_config): """Test attach and removal of trigger.""" + await mqtt_mock_entry_no_yaml_config() data1 = ( '{ "automation_type":"trigger",' ' "device":{"identifiers":["0AFFD2"]},' @@ -766,8 +799,9 @@ async def test_attach_remove(hass, device_reg, mqtt_mock): assert len(calls) == 1 -async def test_attach_remove_late(hass, device_reg, mqtt_mock): +async def test_attach_remove_late(hass, device_reg, mqtt_mock_entry_no_yaml_config): """Test attach and removal of trigger .""" + await mqtt_mock_entry_no_yaml_config() data0 = ( '{ "device":{"identifiers":["0AFFD2"]},' ' "state_topic": "foobar/sensor",' @@ -827,8 +861,9 @@ async def test_attach_remove_late(hass, device_reg, mqtt_mock): assert len(calls) == 1 -async def test_attach_remove_late2(hass, device_reg, mqtt_mock): +async def test_attach_remove_late2(hass, device_reg, mqtt_mock_entry_no_yaml_config): """Test attach and removal of trigger .""" + await mqtt_mock_entry_no_yaml_config() data0 = ( '{ "device":{"identifiers":["0AFFD2"]},' ' "state_topic": "foobar/sensor",' @@ -882,8 +917,9 @@ async def test_attach_remove_late2(hass, device_reg, mqtt_mock): assert len(calls) == 0 -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT device registry integration.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) data = json.dumps( @@ -915,8 +951,9 @@ async def test_entity_device_info_with_connection(hass, mqtt_mock): assert device.sw_version == "0.1-beta" -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT device registry integration.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) data = json.dumps( @@ -946,8 +983,9 @@ async def test_entity_device_info_with_identifier(hass, mqtt_mock): assert device.sw_version == "0.1-beta" -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) config = { @@ -983,8 +1021,11 @@ async def test_entity_device_info_update(hass, mqtt_mock): assert device.name == "Milk" -async def test_cleanup_trigger(hass, hass_ws_client, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_trigger( + hass, hass_ws_client, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test trigger discovery topic is cleaned when device is removed from registry.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() assert await async_setup_component(hass, "config", {}) ws_client = await hass_ws_client(hass) @@ -1034,8 +1075,11 @@ async def test_cleanup_trigger(hass, hass_ws_client, device_reg, entity_reg, mqt ) -async def test_cleanup_device(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test removal from device registry when trigger is removed.""" + await mqtt_mock_entry_no_yaml_config() config = { "automation_type": "trigger", "topic": "test-topic", @@ -1065,8 +1109,11 @@ async def test_cleanup_device(hass, device_reg, entity_reg, mqtt_mock): assert device_entry is None -async def test_cleanup_device_several_triggers(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device_several_triggers( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test removal from device registry when the last trigger is removed.""" + await mqtt_mock_entry_no_yaml_config() config1 = { "automation_type": "trigger", "topic": "test-topic", @@ -1122,11 +1169,14 @@ async def test_cleanup_device_several_triggers(hass, device_reg, entity_reg, mqt assert device_entry is None -async def test_cleanup_device_with_entity1(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device_with_entity1( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test removal from device registry for device with entity. Trigger removed first, then entity. """ + await mqtt_mock_entry_no_yaml_config() config1 = { "automation_type": "trigger", "topic": "test-topic", @@ -1178,11 +1228,14 @@ async def test_cleanup_device_with_entity1(hass, device_reg, entity_reg, mqtt_mo assert device_entry is None -async def test_cleanup_device_with_entity2(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device_with_entity2( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test removal from device registry for device with entity. Entity removed first, then trigger. """ + await mqtt_mock_entry_no_yaml_config() config1 = { "automation_type": "trigger", "topic": "test-topic", @@ -1234,11 +1287,12 @@ async def test_cleanup_device_with_entity2(hass, device_reg, entity_reg, mqtt_mo assert device_entry is None -async def test_trigger_debug_info(hass, mqtt_mock): +async def test_trigger_debug_info(hass, mqtt_mock_entry_no_yaml_config): """Test debug_info. This is a test helper for MQTT debug_info. """ + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) config1 = { diff --git a/tests/components/mqtt/test_diagnostics.py b/tests/components/mqtt/test_diagnostics.py index bbd42a20c87..65399a22f70 100644 --- a/tests/components/mqtt/test_diagnostics.py +++ b/tests/components/mqtt/test_diagnostics.py @@ -37,8 +37,11 @@ def device_reg(hass): return mock_device_registry(hass) -async def test_entry_diagnostics(hass, device_reg, hass_client, mqtt_mock): +async def test_entry_diagnostics( + hass, device_reg, hass_client, mqtt_mock_entry_no_yaml_config +): """Test config entry diagnostics.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] mqtt_mock.connected = True @@ -154,8 +157,11 @@ async def test_entry_diagnostics(hass, device_reg, hass_client, mqtt_mock): } ], ) -async def test_redact_diagnostics(hass, device_reg, hass_client, mqtt_mock): +async def test_redact_diagnostics( + hass, device_reg, hass_client, mqtt_mock_entry_no_yaml_config +): """Test redacting diagnostics.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() expected_config = dict(default_config) expected_config["password"] = "**REDACTED**" expected_config["username"] = "**REDACTED**" diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 9215ab651b2..df20dc031d0 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -48,8 +48,9 @@ def entity_reg(hass): "mqtt_config", [{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}], ) -async def test_subscribing_config_topic(hass, mqtt_mock): +async def test_subscribing_config_topic(hass, mqtt_mock_entry_no_yaml_config): """Test setting up discovery.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] discovery_topic = "homeassistant" @@ -71,8 +72,9 @@ async def test_subscribing_config_topic(hass, mqtt_mock): ("homeassistant/binary_sensor/rörkrökare/config", True), ], ) -async def test_invalid_topic(hass, mqtt_mock, caplog, topic, log): +async def test_invalid_topic(hass, mqtt_mock_entry_no_yaml_config, caplog, topic, log): """Test sending to invalid topic.""" + await mqtt_mock_entry_no_yaml_config() with patch( "homeassistant.components.mqtt.discovery.async_dispatcher_send" ) as mock_dispatcher_send: @@ -90,8 +92,9 @@ async def test_invalid_topic(hass, mqtt_mock, caplog, topic, log): caplog.clear() -async def test_invalid_json(hass, mqtt_mock, caplog): +async def test_invalid_json(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test sending in invalid JSON.""" + await mqtt_mock_entry_no_yaml_config() with patch( "homeassistant.components.mqtt.discovery.async_dispatcher_send" ) as mock_dispatcher_send: @@ -106,8 +109,9 @@ async def test_invalid_json(hass, mqtt_mock, caplog): assert not mock_dispatcher_send.called -async def test_only_valid_components(hass, mqtt_mock, caplog): +async def test_only_valid_components(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test for a valid component.""" + await mqtt_mock_entry_no_yaml_config() with patch( "homeassistant.components.mqtt.discovery.async_dispatcher_send" ) as mock_dispatcher_send: @@ -127,8 +131,9 @@ async def test_only_valid_components(hass, mqtt_mock, caplog): assert not mock_dispatcher_send.called -async def test_correct_config_discovery(hass, mqtt_mock, caplog): +async def test_correct_config_discovery(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test sending in correct JSON.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/binary_sensor/bla/config", @@ -143,8 +148,9 @@ async def test_correct_config_discovery(hass, mqtt_mock, caplog): assert ("binary_sensor", "bla") in hass.data[ALREADY_DISCOVERED] -async def test_discover_fan(hass, mqtt_mock, caplog): +async def test_discover_fan(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test discovering an MQTT fan.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/fan/bla/config", @@ -159,8 +165,9 @@ async def test_discover_fan(hass, mqtt_mock, caplog): assert ("fan", "bla") in hass.data[ALREADY_DISCOVERED] -async def test_discover_climate(hass, mqtt_mock, caplog): +async def test_discover_climate(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test discovering an MQTT climate component.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "name": "ClimateTest",' ' "current_temperature_topic": "climate/bla/current_temp",' @@ -177,8 +184,11 @@ async def test_discover_climate(hass, mqtt_mock, caplog): assert ("climate", "bla") in hass.data[ALREADY_DISCOVERED] -async def test_discover_alarm_control_panel(hass, mqtt_mock, caplog): +async def test_discover_alarm_control_panel( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test discovering an MQTT alarm control panel component.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "name": "AlarmControlPanelTest",' ' "state_topic": "test_topic",' @@ -341,9 +351,10 @@ async def test_discover_alarm_control_panel(hass, mqtt_mock, caplog): ], ) async def test_discovery_with_object_id( - hass, mqtt_mock, caplog, topic, config, entity_id, name, domain + hass, mqtt_mock_entry_no_yaml_config, caplog, topic, config, entity_id, name, domain ): """Test discovering an MQTT entity with object_id.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message(hass, topic, config) await hass.async_block_till_done() @@ -354,8 +365,9 @@ async def test_discovery_with_object_id( assert (domain, "object bla") in hass.data[ALREADY_DISCOVERED] -async def test_discovery_incl_nodeid(hass, mqtt_mock, caplog): +async def test_discovery_incl_nodeid(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test sending in correct JSON with optional node_id included.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/binary_sensor/my_node_id/bla/config", @@ -370,8 +382,9 @@ async def test_discovery_incl_nodeid(hass, mqtt_mock, caplog): assert ("binary_sensor", "my_node_id bla") in hass.data[ALREADY_DISCOVERED] -async def test_non_duplicate_discovery(hass, mqtt_mock, caplog): +async def test_non_duplicate_discovery(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test for a non duplicate component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/binary_sensor/bla/config", @@ -393,8 +406,9 @@ async def test_non_duplicate_discovery(hass, mqtt_mock, caplog): assert "Component has already been discovered: binary_sensor bla" in caplog.text -async def test_removal(hass, mqtt_mock, caplog): +async def test_removal(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of component through empty discovery message.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/binary_sensor/bla/config", @@ -410,8 +424,9 @@ async def test_removal(hass, mqtt_mock, caplog): assert state is None -async def test_rediscover(hass, mqtt_mock, caplog): +async def test_rediscover(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test rediscover of removed component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/binary_sensor/bla/config", @@ -436,9 +451,9 @@ async def test_rediscover(hass, mqtt_mock, caplog): assert state is not None -async def test_rapid_rediscover(hass, mqtt_mock, caplog): +async def test_rapid_rediscover(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test immediate rediscover of removed component.""" - + await mqtt_mock_entry_no_yaml_config() events = async_capture_events(hass, EVENT_STATE_CHANGED) async_fire_mqtt_message( @@ -485,9 +500,9 @@ async def test_rapid_rediscover(hass, mqtt_mock, caplog): assert events[4].data["old_state"] is None -async def test_rapid_rediscover_unique(hass, mqtt_mock, caplog): +async def test_rapid_rediscover_unique(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test immediate rediscover of removed component.""" - + await mqtt_mock_entry_no_yaml_config() events = [] @ha.callback @@ -544,9 +559,9 @@ async def test_rapid_rediscover_unique(hass, mqtt_mock, caplog): assert events[3].data["old_state"] is None -async def test_rapid_reconfigure(hass, mqtt_mock, caplog): +async def test_rapid_reconfigure(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test immediate reconfigure of added component.""" - + await mqtt_mock_entry_no_yaml_config() events = [] @ha.callback @@ -596,8 +611,9 @@ async def test_rapid_reconfigure(hass, mqtt_mock, caplog): assert events[2].data["new_state"].attributes["friendly_name"] == "Wine" -async def test_duplicate_removal(hass, mqtt_mock, caplog): +async def test_duplicate_removal(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test for a non duplicate component.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, "homeassistant/binary_sensor/bla/config", @@ -614,8 +630,11 @@ async def test_duplicate_removal(hass, mqtt_mock, caplog): assert "Component has already been discovered: binary_sensor bla" not in caplog.text -async def test_cleanup_device(hass, hass_ws_client, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device( + hass, hass_ws_client, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test discvered device is cleaned up when entry removed from device.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() assert await async_setup_component(hass, "config", {}) ws_client = await hass_ws_client(hass) @@ -669,8 +688,11 @@ async def test_cleanup_device(hass, hass_ws_client, device_reg, entity_reg, mqtt ) -async def test_cleanup_device_mqtt(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device_mqtt( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test discvered device is cleaned up when removed through MQTT.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() data = ( '{ "device":{"identifiers":["0AFFD2"]},' ' "state_topic": "foobar/sensor",' @@ -709,10 +731,12 @@ async def test_cleanup_device_mqtt(hass, device_reg, entity_reg, mqtt_mock): async def test_cleanup_device_multiple_config_entries( - hass, hass_ws_client, device_reg, entity_reg, mqtt_mock + hass, hass_ws_client, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config ): """Test discovered device is cleaned up when entry removed from device.""" assert await async_setup_component(hass, "config", {}) + await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_no_yaml_config() ws_client = await hass_ws_client(hass) config_entry = MockConfigEntry(domain="test", data={}) @@ -804,9 +828,10 @@ async def test_cleanup_device_multiple_config_entries( async def test_cleanup_device_multiple_config_entries_mqtt( - hass, device_reg, entity_reg, mqtt_mock + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config ): """Test discovered device is cleaned up when removed through MQTT.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() config_entry = MockConfigEntry(domain="test", data={}) config_entry.add_to_hass(hass) device_entry = device_reg.async_get_or_create( @@ -880,8 +905,9 @@ async def test_cleanup_device_multiple_config_entries_mqtt( mqtt_mock.async_publish.assert_not_called() -async def test_discovery_expansion(hass, mqtt_mock, caplog): +async def test_discovery_expansion(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test expansion of abbreviated discovery payload.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "~": "some/base/topic",' ' "name": "DiscoveryExpansionTest1",' @@ -937,8 +963,9 @@ async def test_discovery_expansion(hass, mqtt_mock, caplog): assert state.state == STATE_UNAVAILABLE -async def test_discovery_expansion_2(hass, mqtt_mock, caplog): +async def test_discovery_expansion_2(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test expansion of abbreviated discovery payload.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "~": "some/base/topic",' ' "name": "DiscoveryExpansionTest1",' @@ -977,8 +1004,9 @@ async def test_discovery_expansion_2(hass, mqtt_mock, caplog): @pytest.mark.no_fail_on_log_exception -async def test_discovery_expansion_3(hass, mqtt_mock, caplog): +async def test_discovery_expansion_3(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test expansion of broken discovery payload.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "~": "some/base/topic",' ' "name": "DiscoveryExpansionTest1",' @@ -1008,9 +1036,10 @@ async def test_discovery_expansion_3(hass, mqtt_mock, caplog): async def test_discovery_expansion_without_encoding_and_value_template_1( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test expansion of raw availability payload with a template as list.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "~": "some/base/topic",' ' "name": "DiscoveryExpansionTest1",' @@ -1056,9 +1085,10 @@ async def test_discovery_expansion_without_encoding_and_value_template_1( async def test_discovery_expansion_without_encoding_and_value_template_2( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_no_yaml_config, caplog ): """Test expansion of raw availability payload with a template directly.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "~": "some/base/topic",' ' "name": "DiscoveryExpansionTest1",' @@ -1133,8 +1163,11 @@ ABBREVIATIONS_WHITE_LIST = [ ] -async def test_missing_discover_abbreviations(hass, mqtt_mock, caplog): +async def test_missing_discover_abbreviations( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Check MQTT platforms for missing abbreviations.""" + await mqtt_mock_entry_no_yaml_config() missing = [] regex = re.compile(r"(CONF_[a-zA-Z\d_]*) *= *[\'\"]([a-zA-Z\d_]*)[\'\"]") for fil in Path(mqtt.__file__).parent.rglob("*.py"): @@ -1157,8 +1190,11 @@ async def test_missing_discover_abbreviations(hass, mqtt_mock, caplog): assert not missing -async def test_no_implicit_state_topic_switch(hass, mqtt_mock, caplog): +async def test_no_implicit_state_topic_switch( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test no implicit state topic for switch.""" + await mqtt_mock_entry_no_yaml_config() data = '{ "name": "Test1",' ' "command_topic": "cmnd"' "}" async_fire_mqtt_message(hass, "homeassistant/switch/bla/config", data) @@ -1187,8 +1223,11 @@ async def test_no_implicit_state_topic_switch(hass, mqtt_mock, caplog): } ], ) -async def test_complex_discovery_topic_prefix(hass, mqtt_mock, caplog): +async def test_complex_discovery_topic_prefix( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Tests handling of discovery topic prefix with multiple slashes.""" + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message( hass, ("my_home/homeassistant/register/binary_sensor/node1/object1/config"), @@ -1204,9 +1243,10 @@ async def test_complex_discovery_topic_prefix(hass, mqtt_mock, caplog): async def test_mqtt_integration_discovery_subscribe_unsubscribe( - hass, mqtt_client_mock, mqtt_mock + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config ): """Check MQTT integration discovery subscribe and unsubscribe.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() mock_entity_platform(hass, "config_flow.comp", None) entry = hass.config_entries.async_entries("mqtt")[0] @@ -1243,8 +1283,11 @@ async def test_mqtt_integration_discovery_subscribe_unsubscribe( assert not mqtt_client_mock.unsubscribe.called -async def test_mqtt_discovery_unsubscribe_once(hass, mqtt_client_mock, mqtt_mock): +async def test_mqtt_discovery_unsubscribe_once( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Check MQTT integration discovery unsubscribe once.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() mock_entity_platform(hass, "config_flow.comp", None) entry = hass.config_entries.async_entries("mqtt")[0] diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 1a533db63c0..145edf5ac7d 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -74,16 +74,25 @@ DEFAULT_CONFIG = { } -async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): +async def test_fail_setup_if_no_command_topic( + hass, caplog, mqtt_mock_entry_no_yaml_config +): """Test if command fails with command topic.""" assert await async_setup_component( hass, fan.DOMAIN, {fan.DOMAIN: {"platform": "mqtt", "name": "test"}} ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert hass.states.get("fan.test") is None + assert ( + "Invalid config for [fan.mqtt]: required key not provided @ data['command_topic']" + in caplog.text + ) -async def test_controlling_state_via_topic(hass, mqtt_mock, caplog): +async def test_controlling_state_via_topic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -120,6 +129,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -202,7 +212,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock, caplog): async def test_controlling_state_via_topic_with_different_speed_range( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the controlling state via topic using an alternate speed range.""" assert await async_setup_component( @@ -241,6 +251,7 @@ async def test_controlling_state_via_topic_with_different_speed_range( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "percentage-state-topic1", "100") state = hass.states.get("fan.test1") @@ -264,7 +275,7 @@ async def test_controlling_state_via_topic_with_different_speed_range( async def test_controlling_state_via_topic_no_percentage_topics( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the controlling state via topic without percentage topics.""" assert await async_setup_component( @@ -289,6 +300,7 @@ async def test_controlling_state_via_topic_no_percentage_topics( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -318,7 +330,9 @@ async def test_controlling_state_via_topic_no_percentage_topics( caplog.clear() -async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, caplog): +async def test_controlling_state_via_topic_and_json_message( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling state via topic and JSON message (percentage mode).""" assert await async_setup_component( hass, @@ -353,6 +367,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -421,7 +436,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap async def test_controlling_state_via_topic_and_json_message_shared_topic( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the controlling state via topic and JSON message using a shared topic.""" assert await async_setup_component( @@ -457,6 +472,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -509,7 +525,9 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( caplog.clear() -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test optimistic mode without state topic.""" assert await async_setup_component( hass, @@ -535,6 +553,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -630,7 +649,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog): assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock): +async def test_sending_mqtt_commands_with_alternate_speed_range( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via topic using an alternate speed range.""" assert await async_setup_component( hass, @@ -668,6 +689,7 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock) }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_set_percentage(hass, "fan.test1", 0) mqtt_mock.async_publish.assert_called_once_with( @@ -734,7 +756,9 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock) assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_and_optimistic_no_legacy(hass, mqtt_mock, caplog): +async def test_sending_mqtt_commands_and_optimistic_no_legacy( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test optimistic mode without state topic without legacy speed command topic.""" assert await async_setup_component( hass, @@ -755,6 +779,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy(hass, mqtt_mock, c }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -862,7 +887,9 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy(hass, mqtt_mock, c await common.async_turn_on(hass, "fan.test", preset_mode="freaking-high") -async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog): +async def test_sending_mqtt_command_templates_( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test optimistic mode without state topic without legacy speed command topic.""" assert await async_setup_component( hass, @@ -888,6 +915,7 @@ async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -1002,7 +1030,7 @@ async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog): async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test optimistic mode without state topic without percentage command topic.""" assert await async_setup_component( @@ -1025,6 +1053,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic( }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -1061,7 +1090,9 @@ async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic( assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, caplog): +async def test_sending_mqtt_commands_and_explicit_optimistic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test optimistic mode with state topic and turn on attributes.""" assert await async_setup_component( hass, @@ -1088,6 +1119,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, ca }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -1305,7 +1337,13 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, ca ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG[fan.DOMAIN]) @@ -1315,7 +1353,7 @@ async def test_encoding_subscribable_topics( config[CONF_OSCILLATION_COMMAND_TOPIC] = "fan/some_oscillation_command_topic" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, fan.DOMAIN, config, @@ -1326,7 +1364,7 @@ async def test_encoding_subscribable_topics( ) -async def test_attributes(hass, mqtt_mock, caplog): +async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test attributes.""" assert await async_setup_component( hass, @@ -1347,6 +1385,7 @@ async def test_attributes(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test") assert state.state == STATE_UNKNOWN @@ -1376,7 +1415,7 @@ async def test_attributes(hass, mqtt_mock, caplog): assert state.attributes.get(fan.ATTR_OSCILLATING) is False -async def test_supported_features(hass, mqtt_mock): +async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config): """Test optimistic mode without state topic.""" assert await async_setup_component( hass, @@ -1498,6 +1537,7 @@ async def test_supported_features(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("fan.test1") assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0 @@ -1548,77 +1588,103 @@ async def test_supported_features(hass, mqtt_mock): assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_PRESET_MODE -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + fan.DOMAIN, + DEFAULT_CONFIG, + True, + "state-topic", + "1", ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + fan.DOMAIN, + DEFAULT_CONFIG, + True, + "state-topic", + "1", ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG, MQTT_FAN_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + fan.DOMAIN, + DEFAULT_CONFIG, + MQTT_FAN_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_json(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_json( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique_id option only creates one fan per id.""" config = { fan.DOMAIN: [ @@ -1638,89 +1704,107 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, fan.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, config + ) -async def test_discovery_removal_fan(hass, mqtt_mock, caplog): +async def test_discovery_removal_fan(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered fan.""" data = '{ "name": "test", "command_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, fan.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, fan.DOMAIN, data + ) -async def test_discovery_update_fan(hass, mqtt_mock, caplog): +async def test_discovery_update_fan(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered fan.""" config1 = {"name": "Beer", "command_topic": "test_topic"} config2 = {"name": "Milk", "command_topic": "test_topic"} await help_test_discovery_update( - hass, mqtt_mock, caplog, fan.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, fan.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_fan(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_fan( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered fan.""" data1 = '{ "name": "Beer", "command_topic": "test_topic" }' with patch( "homeassistant.components.mqtt.fan.MqttFan.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, fan.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + fan.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk", "command_topic": "test_topic" }' - await help_test_discovery_broken(hass, mqtt_mock, caplog, fan.DOMAIN, data1, data2) + + await help_test_discovery_broken( + hass, mqtt_mock_entry_no_yaml_config, caplog, fan.DOMAIN, data1, data2 + ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT fan device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT fan device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, fan.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( - hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG, fan.SERVICE_TURN_ON + hass, + mqtt_mock_entry_no_yaml_config, + fan.DOMAIN, + DEFAULT_CONFIG, + fan.SERVICE_TURN_ON, ) @@ -1766,7 +1850,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -1782,7 +1866,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -1794,11 +1878,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = fan.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_humidifier.py b/tests/components/mqtt/test_humidifier.py index bc00d3afffb..ea9a6edf0e3 100644 --- a/tests/components/mqtt/test_humidifier.py +++ b/tests/components/mqtt/test_humidifier.py @@ -116,7 +116,7 @@ async def async_set_humidity( await hass.services.async_call(DOMAIN, SERVICE_SET_HUMIDITY, data, blocking=True) -async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): +async def test_fail_setup_if_no_command_topic(hass, mqtt_mock_entry_no_yaml_config): """Test if command fails with command topic.""" assert await async_setup_component( hass, @@ -124,10 +124,13 @@ async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): {humidifier.DOMAIN: {"platform": "mqtt", "name": "test"}}, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert hass.states.get("humidifier.test") is None -async def test_controlling_state_via_topic(hass, mqtt_mock, caplog): +async def test_controlling_state_via_topic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -158,6 +161,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -228,7 +232,9 @@ async def test_controlling_state_via_topic(hass, mqtt_mock, caplog): assert state.state == STATE_UNKNOWN -async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, caplog): +async def test_controlling_state_via_topic_and_json_message( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling state via topic and JSON message.""" assert await async_setup_component( hass, @@ -255,6 +261,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -314,7 +321,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap async def test_controlling_state_via_topic_and_json_message_shared_topic( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the controlling state via topic and JSON message using a shared topic.""" assert await async_setup_component( @@ -342,6 +349,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -390,7 +398,9 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( caplog.clear() -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test optimistic mode without state topic.""" assert await async_setup_component( hass, @@ -413,6 +423,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -483,7 +494,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog): assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog): +async def test_sending_mqtt_command_templates_( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Testing command templates with optimistic mode without state topic.""" assert await async_setup_component( hass, @@ -507,6 +520,7 @@ async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -577,7 +591,9 @@ async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog): assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, caplog): +async def test_sending_mqtt_commands_and_explicit_optimistic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test optimistic mode with state topic and turn on attributes.""" assert await async_setup_component( hass, @@ -602,6 +618,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, ca }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -701,7 +718,13 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, ca ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG[humidifier.DOMAIN]) @@ -709,7 +732,7 @@ async def test_encoding_subscribable_topics( config[CONF_MODE_COMMAND_TOPIC] = "humidifier/some_mode_command_topic" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, humidifier.DOMAIN, config, @@ -720,7 +743,7 @@ async def test_encoding_subscribable_topics( ) -async def test_attributes(hass, mqtt_mock, caplog): +async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test attributes.""" assert await async_setup_component( hass, @@ -740,6 +763,7 @@ async def test_attributes(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -765,7 +789,7 @@ async def test_attributes(hass, mqtt_mock, caplog): assert state.attributes.get(humidifier.ATTR_MODE) is None -async def test_invalid_configurations(hass, mqtt_mock, caplog): +async def test_invalid_configurations(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test invalid configurations.""" assert await async_setup_component( hass, @@ -834,6 +858,7 @@ async def test_invalid_configurations(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert hass.states.get("humidifier.test_valid_1") is not None assert hass.states.get("humidifier.test_valid_2") is not None assert hass.states.get("humidifier.test_valid_3") is not None @@ -847,7 +872,7 @@ async def test_invalid_configurations(hass, mqtt_mock, caplog): assert hass.states.get("humidifier.test_invalid_mode_is_reset") is None -async def test_supported_features(hass, mqtt_mock): +async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config): """Test supported features.""" assert await async_setup_component( hass, @@ -896,6 +921,7 @@ async def test_supported_features(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("humidifier.test1") assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0 @@ -916,81 +942,111 @@ async def test_supported_features(hass, mqtt_mock): assert state is None -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + humidifier.DOMAIN, + DEFAULT_CONFIG, + True, + "state-topic", + "1", ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + humidifier.DOMAIN, + DEFAULT_CONFIG, + True, + "state-topic", + "1", ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG, MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, humidifier.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + humidifier.DOMAIN, + DEFAULT_CONFIG, ) -async def test_update_with_json_attrs_bad_json(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_json( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, humidifier.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + humidifier.DOMAIN, + DEFAULT_CONFIG, ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique_id option only creates one fan per id.""" config = { humidifier.DOMAIN: [ @@ -1012,16 +1068,24 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, humidifier.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, config + ) -async def test_discovery_removal_humidifier(hass, mqtt_mock, caplog): +async def test_discovery_removal_humidifier( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test removal of discovered humidifier.""" data = '{ "name": "test", "command_topic": "test_topic", "target_humidity_command_topic": "test-topic2" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, humidifier.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, humidifier.DOMAIN, data + ) -async def test_discovery_update_humidifier(hass, mqtt_mock, caplog): +async def test_discovery_update_humidifier( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered humidifier.""" config1 = { "name": "Beer", @@ -1034,77 +1098,93 @@ async def test_discovery_update_humidifier(hass, mqtt_mock, caplog): "target_humidity_command_topic": "test-topic2", } await help_test_discovery_update( - hass, mqtt_mock, caplog, humidifier.DOMAIN, config1, config2 + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + humidifier.DOMAIN, + config1, + config2, ) -async def test_discovery_update_unchanged_humidifier(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_humidifier( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered humidifier.""" data1 = '{ "name": "Beer", "command_topic": "test_topic", "target_humidity_command_topic": "test-topic2" }' with patch( "homeassistant.components.mqtt.fan.MqttFan.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, humidifier.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + humidifier.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk", "command_topic": "test_topic", "target_humidity_command_topic": "test-topic2" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, humidifier.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, humidifier.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT fan device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT fan device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, humidifier.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( - hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG, humidifier.SERVICE_TURN_ON + hass, + mqtt_mock_entry_no_yaml_config, + humidifier.DOMAIN, + DEFAULT_CONFIG, + humidifier.SERVICE_TURN_ON, ) @@ -1143,7 +1223,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -1159,7 +1239,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -1171,11 +1251,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = humidifier.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 07c39d70df0..861b50d2f71 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -96,22 +96,27 @@ def record_calls(calls): async def test_mqtt_connects_on_home_assistant_mqtt_setup( - hass, mqtt_client_mock, mqtt_mock + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config ): """Test if client is connected after mqtt init on bootstrap.""" + await mqtt_mock_entry_no_yaml_config() assert mqtt_client_mock.connect.call_count == 1 -async def test_mqtt_disconnects_on_home_assistant_stop(hass, mqtt_mock): +async def test_mqtt_disconnects_on_home_assistant_stop( + hass, mqtt_mock_entry_no_yaml_config +): """Test if client stops on HA stop.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() hass.bus.fire(EVENT_HOMEASSISTANT_STOP) await hass.async_block_till_done() await hass.async_block_till_done() assert mqtt_mock.async_disconnect.called -async def test_publish(hass, mqtt_mock): +async def test_publish(hass, mqtt_mock_entry_no_yaml_config): """Test the publish function.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await mqtt.async_publish(hass, "test-topic", "test-payload") await hass.async_block_till_done() assert mqtt_mock.async_publish.called @@ -208,7 +213,7 @@ async def test_command_template_value(hass): assert cmd_tpl.async_render(None, variables=variables) == "beer" -async def test_command_template_variables(hass, mqtt_mock): +async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config): """Test the rendering of enitity_variables.""" topic = "test/select" @@ -232,6 +237,7 @@ async def test_command_template_variables(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("select.test_select") assert state.state == "milk" @@ -291,8 +297,11 @@ async def test_value_template_value(hass): assert val_tpl.async_render_with_possible_json_value('{"id": 4321}') == "4321" -async def test_service_call_without_topic_does_not_publish(hass, mqtt_mock): +async def test_service_call_without_topic_does_not_publish( + hass, mqtt_mock_entry_no_yaml_config +): """Test the service call if topic is missing.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() with pytest.raises(vol.Invalid): await hass.services.async_call( mqtt.DOMAIN, @@ -304,12 +313,13 @@ async def test_service_call_without_topic_does_not_publish(hass, mqtt_mock): async def test_service_call_with_topic_and_topic_template_does_not_publish( - hass, mqtt_mock + hass, mqtt_mock_entry_no_yaml_config ): """Test the service call with topic/topic template. If both 'topic' and 'topic_template' are provided then fail. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() topic = "test/topic" topic_template = "test/{{ 'topic' }}" with pytest.raises(vol.Invalid): @@ -327,9 +337,10 @@ async def test_service_call_with_topic_and_topic_template_does_not_publish( async def test_service_call_with_invalid_topic_template_does_not_publish( - hass, mqtt_mock + hass, mqtt_mock_entry_no_yaml_config ): """Test the service call with a problematic topic template.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await hass.services.async_call( mqtt.DOMAIN, mqtt.SERVICE_PUBLISH, @@ -342,11 +353,14 @@ async def test_service_call_with_invalid_topic_template_does_not_publish( assert not mqtt_mock.async_publish.called -async def test_service_call_with_template_topic_renders_template(hass, mqtt_mock): +async def test_service_call_with_template_topic_renders_template( + hass, mqtt_mock_entry_no_yaml_config +): """Test the service call with rendered topic template. If 'topic_template' is provided and 'topic' is not, then render it. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await hass.services.async_call( mqtt.DOMAIN, mqtt.SERVICE_PUBLISH, @@ -360,11 +374,14 @@ async def test_service_call_with_template_topic_renders_template(hass, mqtt_mock assert mqtt_mock.async_publish.call_args[0][0] == "test/2" -async def test_service_call_with_template_topic_renders_invalid_topic(hass, mqtt_mock): +async def test_service_call_with_template_topic_renders_invalid_topic( + hass, mqtt_mock_entry_no_yaml_config +): """Test the service call with rendered, invalid topic template. If a wildcard topic is rendered, then fail. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await hass.services.async_call( mqtt.DOMAIN, mqtt.SERVICE_PUBLISH, @@ -378,12 +395,13 @@ async def test_service_call_with_template_topic_renders_invalid_topic(hass, mqtt async def test_service_call_with_invalid_rendered_template_topic_doesnt_render_template( - hass, mqtt_mock + hass, mqtt_mock_entry_no_yaml_config ): """Test the service call with unrendered template. If both 'payload' and 'payload_template' are provided then fail. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() payload = "not a template" payload_template = "a template" with pytest.raises(vol.Invalid): @@ -400,11 +418,14 @@ async def test_service_call_with_invalid_rendered_template_topic_doesnt_render_t assert not mqtt_mock.async_publish.called -async def test_service_call_with_template_payload_renders_template(hass, mqtt_mock): +async def test_service_call_with_template_payload_renders_template( + hass, mqtt_mock_entry_no_yaml_config +): """Test the service call with rendered template. If 'payload_template' is provided and 'payload' is not, then render it. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await hass.services.async_call( mqtt.DOMAIN, mqtt.SERVICE_PUBLISH, @@ -429,8 +450,9 @@ async def test_service_call_with_template_payload_renders_template(hass, mqtt_mo mqtt_mock.reset_mock() -async def test_service_call_with_bad_template(hass, mqtt_mock): +async def test_service_call_with_bad_template(hass, mqtt_mock_entry_no_yaml_config): """Test the service call with a bad template does not publish.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await hass.services.async_call( mqtt.DOMAIN, mqtt.SERVICE_PUBLISH, @@ -440,11 +462,14 @@ async def test_service_call_with_bad_template(hass, mqtt_mock): assert not mqtt_mock.async_publish.called -async def test_service_call_with_payload_doesnt_render_template(hass, mqtt_mock): +async def test_service_call_with_payload_doesnt_render_template( + hass, mqtt_mock_entry_no_yaml_config +): """Test the service call with unrendered template. If both 'payload' and 'payload_template' are provided then fail. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() payload = "not a template" payload_template = "a template" with pytest.raises(vol.Invalid): @@ -461,11 +486,14 @@ async def test_service_call_with_payload_doesnt_render_template(hass, mqtt_mock) assert not mqtt_mock.async_publish.called -async def test_service_call_with_ascii_qos_retain_flags(hass, mqtt_mock): +async def test_service_call_with_ascii_qos_retain_flags( + hass, mqtt_mock_entry_no_yaml_config +): """Test the service call with args that can be misinterpreted. Empty payload message and ascii formatted qos and retain flags. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await hass.services.async_call( mqtt.DOMAIN, mqtt.SERVICE_PUBLISH, @@ -665,9 +693,10 @@ def test_entity_device_info_schema(): async def test_receiving_non_utf8_message_gets_logged( - hass, mqtt_mock, calls, record_calls, caplog + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls, caplog ): """Test receiving a non utf8 encoded message.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic", record_calls) async_fire_mqtt_message(hass, "test-topic", b"\x9a") @@ -679,9 +708,10 @@ async def test_receiving_non_utf8_message_gets_logged( async def test_all_subscriptions_run_when_decode_fails( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test all other subscriptions still run when decode fails for one.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic", record_calls, encoding="ascii") await mqtt.async_subscribe(hass, "test-topic", record_calls) @@ -691,8 +721,11 @@ async def test_all_subscriptions_run_when_decode_fails( assert len(calls) == 1 -async def test_subscribe_topic(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_topic( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test the subscription of a topic.""" + await mqtt_mock_entry_no_yaml_config() unsub = await mqtt.async_subscribe(hass, "test-topic", record_calls) async_fire_mqtt_message(hass, "test-topic", "test-payload") @@ -714,8 +747,11 @@ async def test_subscribe_topic(hass, mqtt_mock, calls, record_calls): unsub() -async def test_subscribe_topic_non_async(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_topic_non_async( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test the subscription of a topic using the non-async function.""" + await mqtt_mock_entry_no_yaml_config() unsub = await hass.async_add_executor_job( mqtt.subscribe, hass, "test-topic", record_calls ) @@ -736,14 +772,18 @@ async def test_subscribe_topic_non_async(hass, mqtt_mock, calls, record_calls): assert len(calls) == 1 -async def test_subscribe_bad_topic(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_bad_topic( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test the subscription of a topic.""" + await mqtt_mock_entry_no_yaml_config() with pytest.raises(HomeAssistantError): await mqtt.async_subscribe(hass, 55, record_calls) -async def test_subscribe_deprecated(hass, mqtt_mock): +async def test_subscribe_deprecated(hass, mqtt_mock_entry_no_yaml_config): """Test the subscription of a topic using deprecated callback signature.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() @callback def record_calls(topic, payload, qos): @@ -789,8 +829,9 @@ async def test_subscribe_deprecated(hass, mqtt_mock): assert len(calls) == 1 -async def test_subscribe_deprecated_async(hass, mqtt_mock): +async def test_subscribe_deprecated_async(hass, mqtt_mock_entry_no_yaml_config): """Test the subscription of a topic using deprecated coroutine signature.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() def async_record_calls(topic, payload, qos): """Record calls.""" @@ -835,8 +876,11 @@ async def test_subscribe_deprecated_async(hass, mqtt_mock): assert len(calls) == 1 -async def test_subscribe_topic_not_match(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_topic_not_match( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test if subscribed topic is not a match.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic", record_calls) async_fire_mqtt_message(hass, "another-test-topic", "test-payload") @@ -845,8 +889,11 @@ async def test_subscribe_topic_not_match(hass, mqtt_mock, calls, record_calls): assert len(calls) == 0 -async def test_subscribe_topic_level_wildcard(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_topic_level_wildcard( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic/+/on", record_calls) async_fire_mqtt_message(hass, "test-topic/bier/on", "test-payload") @@ -858,9 +905,10 @@ async def test_subscribe_topic_level_wildcard(hass, mqtt_mock, calls, record_cal async def test_subscribe_topic_level_wildcard_no_subtree_match( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic/+/on", record_calls) async_fire_mqtt_message(hass, "test-topic/bier", "test-payload") @@ -870,9 +918,10 @@ async def test_subscribe_topic_level_wildcard_no_subtree_match( async def test_subscribe_topic_level_wildcard_root_topic_no_subtree_match( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic/#", record_calls) async_fire_mqtt_message(hass, "test-topic-123", "test-payload") @@ -882,9 +931,10 @@ async def test_subscribe_topic_level_wildcard_root_topic_no_subtree_match( async def test_subscribe_topic_subtree_wildcard_subtree_topic( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic/#", record_calls) async_fire_mqtt_message(hass, "test-topic/bier/on", "test-payload") @@ -896,9 +946,10 @@ async def test_subscribe_topic_subtree_wildcard_subtree_topic( async def test_subscribe_topic_subtree_wildcard_root_topic( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic/#", record_calls) async_fire_mqtt_message(hass, "test-topic", "test-payload") @@ -910,9 +961,10 @@ async def test_subscribe_topic_subtree_wildcard_root_topic( async def test_subscribe_topic_subtree_wildcard_no_match( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "test-topic/#", record_calls) async_fire_mqtt_message(hass, "another-test-topic", "test-payload") @@ -922,9 +974,10 @@ async def test_subscribe_topic_subtree_wildcard_no_match( async def test_subscribe_topic_level_wildcard_and_wildcard_root_topic( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "+/test-topic/#", record_calls) async_fire_mqtt_message(hass, "hi/test-topic", "test-payload") @@ -936,9 +989,10 @@ async def test_subscribe_topic_level_wildcard_and_wildcard_root_topic( async def test_subscribe_topic_level_wildcard_and_wildcard_subtree_topic( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "+/test-topic/#", record_calls) async_fire_mqtt_message(hass, "hi/test-topic/here-iam", "test-payload") @@ -950,9 +1004,10 @@ async def test_subscribe_topic_level_wildcard_and_wildcard_subtree_topic( async def test_subscribe_topic_level_wildcard_and_wildcard_level_no_match( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "+/test-topic/#", record_calls) async_fire_mqtt_message(hass, "hi/here-iam/test-topic", "test-payload") @@ -962,9 +1017,10 @@ async def test_subscribe_topic_level_wildcard_and_wildcard_level_no_match( async def test_subscribe_topic_level_wildcard_and_wildcard_no_match( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "+/test-topic/#", record_calls) async_fire_mqtt_message(hass, "hi/another-test-topic", "test-payload") @@ -973,8 +1029,11 @@ async def test_subscribe_topic_level_wildcard_and_wildcard_no_match( assert len(calls) == 0 -async def test_subscribe_topic_sys_root(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_topic_sys_root( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test the subscription of $ root topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "$test-topic/subtree/on", record_calls) async_fire_mqtt_message(hass, "$test-topic/subtree/on", "test-payload") @@ -986,9 +1045,10 @@ async def test_subscribe_topic_sys_root(hass, mqtt_mock, calls, record_calls): async def test_subscribe_topic_sys_root_and_wildcard_topic( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of $ root and wildcard topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "$test-topic/#", record_calls) async_fire_mqtt_message(hass, "$test-topic/some-topic", "test-payload") @@ -1000,9 +1060,10 @@ async def test_subscribe_topic_sys_root_and_wildcard_topic( async def test_subscribe_topic_sys_root_and_wildcard_subtree_topic( - hass, mqtt_mock, calls, record_calls + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls ): """Test the subscription of $ root and wildcard subtree topics.""" + await mqtt_mock_entry_no_yaml_config() await mqtt.async_subscribe(hass, "$test-topic/subtree/#", record_calls) async_fire_mqtt_message(hass, "$test-topic/subtree/some-topic", "test-payload") @@ -1013,8 +1074,11 @@ async def test_subscribe_topic_sys_root_and_wildcard_subtree_topic( assert calls[0][0].payload == "test-payload" -async def test_subscribe_special_characters(hass, mqtt_mock, calls, record_calls): +async def test_subscribe_special_characters( + hass, mqtt_mock_entry_no_yaml_config, calls, record_calls +): """Test the subscription to topics with special characters.""" + await mqtt_mock_entry_no_yaml_config() topic = "/test-topic/$(.)[^]{-}" payload = "p4y.l[]a|> ?" @@ -1027,13 +1091,16 @@ async def test_subscribe_special_characters(hass, mqtt_mock, calls, record_calls assert calls[0][0].payload == payload -async def test_subscribe_same_topic(hass, mqtt_client_mock, mqtt_mock): +async def test_subscribe_same_topic( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """ Test subscring to same topic twice and simulate retained messages. When subscribing to the same topic again, SUBSCRIBE must be sent to the broker again for it to resend any retained messages. """ + mqtt_mock = await mqtt_mock_entry_no_yaml_config() # Fake that the client is connected mqtt_mock().connected = True @@ -1061,9 +1128,10 @@ async def test_subscribe_same_topic(hass, mqtt_client_mock, mqtt_mock): async def test_not_calling_unsubscribe_with_active_subscribers( - hass, mqtt_client_mock, mqtt_mock + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config ): """Test not calling unsubscribe() when other subscribers are active.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() # Fake that the client is connected mqtt_mock().connected = True @@ -1077,8 +1145,9 @@ async def test_not_calling_unsubscribe_with_active_subscribers( assert not mqtt_client_mock.unsubscribe.called -async def test_unsubscribe_race(hass, mqtt_client_mock, mqtt_mock): +async def test_unsubscribe_race(hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config): """Test not calling unsubscribe() when other subscribers are active.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() # Fake that the client is connected mqtt_mock().connected = True @@ -1113,8 +1182,11 @@ async def test_unsubscribe_race(hass, mqtt_client_mock, mqtt_mock): "mqtt_config", [{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}], ) -async def test_restore_subscriptions_on_reconnect(hass, mqtt_client_mock, mqtt_mock): +async def test_restore_subscriptions_on_reconnect( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Test subscriptions are restored on reconnect.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() # Fake that the client is connected mqtt_mock().connected = True @@ -1123,7 +1195,7 @@ async def test_restore_subscriptions_on_reconnect(hass, mqtt_client_mock, mqtt_m assert mqtt_client_mock.subscribe.call_count == 1 mqtt_client_mock.on_disconnect(None, None, 0) - with patch("homeassistant.components.mqtt.DISCOVERY_COOLDOWN", 0): + with patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0): mqtt_client_mock.on_connect(None, None, None, 0) await hass.async_block_till_done() assert mqtt_client_mock.subscribe.call_count == 2 @@ -1134,9 +1206,10 @@ async def test_restore_subscriptions_on_reconnect(hass, mqtt_client_mock, mqtt_m [{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}], ) async def test_restore_all_active_subscriptions_on_reconnect( - hass, mqtt_client_mock, mqtt_mock + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config ): """Test active subscriptions are restored correctly on reconnect.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() # Fake that the client is connected mqtt_mock().connected = True @@ -1157,7 +1230,7 @@ async def test_restore_all_active_subscriptions_on_reconnect( assert mqtt_client_mock.unsubscribe.call_count == 0 mqtt_client_mock.on_disconnect(None, None, 0) - with patch("homeassistant.components.mqtt.DISCOVERY_COOLDOWN", 0): + with patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0): mqtt_client_mock.on_connect(None, None, None, 0) await hass.async_block_till_done() @@ -1176,9 +1249,10 @@ async def test_initial_setup_logs_error(hass, caplog, mqtt_client_mock): async def test_logs_error_if_no_connect_broker( - hass, caplog, mqtt_mock, mqtt_client_mock + hass, caplog, mqtt_mock_entry_no_yaml_config, mqtt_client_mock ): """Test for setup failure if connection to broker is missing.""" + await mqtt_mock_entry_no_yaml_config() # test with rc = 3 -> broker unavailable mqtt_client_mock.on_connect(mqtt_client_mock, None, None, 3) await hass.async_block_till_done() @@ -1188,9 +1262,12 @@ async def test_logs_error_if_no_connect_broker( ) -@patch("homeassistant.components.mqtt.TIMEOUT_ACK", 0.3) -async def test_handle_mqtt_on_callback(hass, caplog, mqtt_mock, mqtt_client_mock): +@patch("homeassistant.components.mqtt.client.TIMEOUT_ACK", 0.3) +async def test_handle_mqtt_on_callback( + hass, caplog, mqtt_mock_entry_no_yaml_config, mqtt_client_mock +): """Test receiving an ACK callback before waiting for it.""" + await mqtt_mock_entry_no_yaml_config() # Simulate an ACK for mid == 1, this will call mqtt_mock._mqtt_handle_mid(mid) mqtt_client_mock.on_publish(mqtt_client_mock, None, 1) await hass.async_block_till_done() @@ -1224,8 +1301,11 @@ async def test_publish_error(hass, caplog): assert "Failed to connect to MQTT server: Out of memory." in caplog.text -async def test_handle_message_callback(hass, caplog, mqtt_mock, mqtt_client_mock): +async def test_handle_message_callback( + hass, caplog, mqtt_mock_entry_no_yaml_config, mqtt_client_mock +): """Test for handling an incoming message callback.""" + await mqtt_mock_entry_no_yaml_config() msg = ReceiveMessage("some-topic", b"test-payload", 0, False) mqtt_client_mock.on_connect(mqtt_client_mock, None, None, 0) await mqtt.async_subscribe(hass, "some-topic", lambda *args: 0) @@ -1331,7 +1411,7 @@ async def test_setup_mqtt_client_protocol(hass): """Test MQTT client protocol setup.""" entry = MockConfigEntry( domain=mqtt.DOMAIN, - data={mqtt.CONF_BROKER: "test-broker", mqtt.CONF_PROTOCOL: "3.1"}, + data={mqtt.CONF_BROKER: "test-broker", mqtt.config.CONF_PROTOCOL: "3.1"}, ) with patch("paho.mqtt.client.Client") as mock_client: mock_client.on_connect(return_value=0) @@ -1341,7 +1421,7 @@ async def test_setup_mqtt_client_protocol(hass): assert mock_client.call_args[1]["protocol"] == 3 -@patch("homeassistant.components.mqtt.TIMEOUT_ACK", 0.2) +@patch("homeassistant.components.mqtt.client.TIMEOUT_ACK", 0.2) async def test_handle_mqtt_timeout_on_callback(hass, caplog): """Test publish without receiving an ACK callback.""" mid = 0 @@ -1478,15 +1558,18 @@ async def test_setup_without_tls_config_uses_tlsv1_under_python36(hass): } ], ) -async def test_custom_birth_message(hass, mqtt_client_mock, mqtt_mock): +async def test_custom_birth_message( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Test sending birth message.""" + await mqtt_mock_entry_no_yaml_config() birth = asyncio.Event() async def wait_birth(topic, payload, qos): """Handle birth message.""" birth.set() - with patch("homeassistant.components.mqtt.DISCOVERY_COOLDOWN", 0.1): + with patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.1): await mqtt.async_subscribe(hass, "birth", wait_birth) mqtt_client_mock.on_connect(None, None, 0, 0) await hass.async_block_till_done() @@ -1508,15 +1591,18 @@ async def test_custom_birth_message(hass, mqtt_client_mock, mqtt_mock): } ], ) -async def test_default_birth_message(hass, mqtt_client_mock, mqtt_mock): +async def test_default_birth_message( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Test sending birth message.""" + await mqtt_mock_entry_no_yaml_config() birth = asyncio.Event() async def wait_birth(topic, payload, qos): """Handle birth message.""" birth.set() - with patch("homeassistant.components.mqtt.DISCOVERY_COOLDOWN", 0.1): + with patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.1): await mqtt.async_subscribe(hass, "homeassistant/status", wait_birth) mqtt_client_mock.on_connect(None, None, 0, 0) await hass.async_block_till_done() @@ -1530,9 +1616,10 @@ async def test_default_birth_message(hass, mqtt_client_mock, mqtt_mock): "mqtt_config", [{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: {}}], ) -async def test_no_birth_message(hass, mqtt_client_mock, mqtt_mock): +async def test_no_birth_message(hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config): """Test disabling birth message.""" - with patch("homeassistant.components.mqtt.DISCOVERY_COOLDOWN", 0.1): + await mqtt_mock_entry_no_yaml_config() + with patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.1): mqtt_client_mock.on_connect(None, None, 0, 0) await hass.async_block_till_done() await asyncio.sleep(0.2) @@ -1553,8 +1640,12 @@ async def test_no_birth_message(hass, mqtt_client_mock, mqtt_mock): } ], ) -async def test_delayed_birth_message(hass, mqtt_client_mock, mqtt_config, mqtt_mock): +async def test_delayed_birth_message( + hass, mqtt_client_mock, mqtt_config, mqtt_mock_entry_no_yaml_config +): """Test sending birth message does not happen until Home Assistant starts.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() + hass.state = CoreState.starting birth = asyncio.Event() @@ -1580,7 +1671,7 @@ async def test_delayed_birth_message(hass, mqtt_client_mock, mqtt_config, mqtt_m """Handle birth message.""" birth.set() - with patch("homeassistant.components.mqtt.DISCOVERY_COOLDOWN", 0.1): + with patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.1): await mqtt.async_subscribe(hass, "homeassistant/status", wait_birth) mqtt_client_mock.on_connect(None, None, 0, 0) await hass.async_block_till_done() @@ -1610,15 +1701,23 @@ async def test_delayed_birth_message(hass, mqtt_client_mock, mqtt_config, mqtt_m } ], ) -async def test_custom_will_message(hass, mqtt_client_mock, mqtt_mock): +async def test_custom_will_message( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Test will message.""" + await mqtt_mock_entry_no_yaml_config() + mqtt_client_mock.will_set.assert_called_with( topic="death", payload="death", qos=0, retain=False ) -async def test_default_will_message(hass, mqtt_client_mock, mqtt_mock): +async def test_default_will_message( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Test will message.""" + await mqtt_mock_entry_no_yaml_config() + mqtt_client_mock.will_set.assert_called_with( topic="homeassistant/status", payload="offline", qos=0, retain=False ) @@ -1628,8 +1727,10 @@ async def test_default_will_message(hass, mqtt_client_mock, mqtt_mock): "mqtt_config", [{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_WILL_MESSAGE: {}}], ) -async def test_no_will_message(hass, mqtt_client_mock, mqtt_mock): +async def test_no_will_message(hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config): """Test will message.""" + await mqtt_mock_entry_no_yaml_config() + mqtt_client_mock.will_set.assert_not_called() @@ -1643,8 +1744,12 @@ async def test_no_will_message(hass, mqtt_client_mock, mqtt_mock): } ], ) -async def test_mqtt_subscribes_topics_on_connect(hass, mqtt_client_mock, mqtt_mock): +async def test_mqtt_subscribes_topics_on_connect( + hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config +): """Test subscription to topic on connect.""" + await mqtt_mock_entry_no_yaml_config() + await mqtt.async_subscribe(hass, "topic/test", None) await mqtt.async_subscribe(hass, "home/sensor", None, 2) await mqtt.async_subscribe(hass, "still/pending", None) @@ -1662,7 +1767,9 @@ async def test_mqtt_subscribes_topics_on_connect(hass, mqtt_client_mock, mqtt_mo assert calls == expected -async def test_setup_entry_with_config_override(hass, device_reg, mqtt_client_mock): +async def test_setup_entry_with_config_override( + hass, device_reg, mqtt_mock_entry_with_yaml_config +): """Test if the MQTT component loads with no config and config entry can be setup.""" data = ( '{ "device":{"identifiers":["0AFFD2"]},' @@ -1672,6 +1779,8 @@ async def test_setup_entry_with_config_override(hass, device_reg, mqtt_client_mo # mqtt present in yaml config assert await async_setup_component(hass, mqtt.DOMAIN, {}) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() # User sets up a config entry entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) @@ -1734,8 +1843,11 @@ async def test_fail_no_broker(hass, device_reg, mqtt_client_mock, caplog): @pytest.mark.no_fail_on_log_exception -async def test_message_callback_exception_gets_logged(hass, caplog, mqtt_mock): +async def test_message_callback_exception_gets_logged( + hass, caplog, mqtt_mock_entry_no_yaml_config +): """Test exception raised by message handler.""" + await mqtt_mock_entry_no_yaml_config() @callback def bad_handler(*args): @@ -1752,8 +1864,11 @@ async def test_message_callback_exception_gets_logged(hass, caplog, mqtt_mock): ) -async def test_mqtt_ws_subscription(hass, hass_ws_client, mqtt_mock): +async def test_mqtt_ws_subscription( + hass, hass_ws_client, mqtt_mock_entry_no_yaml_config +): """Test MQTT websocket subscription.""" + await mqtt_mock_entry_no_yaml_config() client = await hass_ws_client(hass) await client.send_json({"id": 5, "type": "mqtt/subscribe", "topic": "test-topic"}) response = await client.receive_json() @@ -1782,9 +1897,10 @@ async def test_mqtt_ws_subscription(hass, hass_ws_client, mqtt_mock): async def test_mqtt_ws_subscription_not_admin( - hass, hass_ws_client, mqtt_mock, hass_read_only_access_token + hass, hass_ws_client, mqtt_mock_entry_no_yaml_config, hass_read_only_access_token ): """Test MQTT websocket user is not admin.""" + await mqtt_mock_entry_no_yaml_config() client = await hass_ws_client(hass, access_token=hass_read_only_access_token) await client.send_json({"id": 5, "type": "mqtt/subscribe", "topic": "test-topic"}) response = await client.receive_json() @@ -1793,8 +1909,9 @@ async def test_mqtt_ws_subscription_not_admin( assert response["error"]["message"] == "Unauthorized" -async def test_dump_service(hass, mqtt_mock): +async def test_dump_service(hass, mqtt_mock_entry_no_yaml_config): """Test that we can dump a topic.""" + await mqtt_mock_entry_no_yaml_config() mopen = mock_open() await hass.services.async_call( @@ -1814,10 +1931,12 @@ async def test_dump_service(hass, mqtt_mock): async def test_mqtt_ws_remove_discovered_device( - hass, device_reg, entity_reg, hass_ws_client, mqtt_mock + hass, device_reg, entity_reg, hass_ws_client, mqtt_mock_entry_no_yaml_config ): """Test MQTT websocket device removal.""" assert await async_setup_component(hass, "config", {}) + await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() data = ( '{ "device":{"identifiers":["0AFFD2"]},' @@ -1851,9 +1970,10 @@ async def test_mqtt_ws_remove_discovered_device( async def test_mqtt_ws_get_device_debug_info( - hass, device_reg, hass_ws_client, mqtt_mock + hass, device_reg, hass_ws_client, mqtt_mock_entry_no_yaml_config ): """Test MQTT websocket device debug info.""" + await mqtt_mock_entry_no_yaml_config() config_sensor = { "device": {"identifiers": ["0AFFD2"]}, "platform": "mqtt", @@ -1913,9 +2033,10 @@ async def test_mqtt_ws_get_device_debug_info( async def test_mqtt_ws_get_device_debug_info_binary( - hass, device_reg, hass_ws_client, mqtt_mock + hass, device_reg, hass_ws_client, mqtt_mock_entry_no_yaml_config ): """Test MQTT websocket device debug info.""" + await mqtt_mock_entry_no_yaml_config() config = { "device": {"identifiers": ["0AFFD2"]}, "platform": "mqtt", @@ -1975,8 +2096,9 @@ async def test_mqtt_ws_get_device_debug_info_binary( assert response["result"] == expected_result -async def test_debug_info_multiple_devices(hass, mqtt_mock): +async def test_debug_info_multiple_devices(hass, mqtt_mock_entry_no_yaml_config): """Test we get correct debug_info when multiple devices are present.""" + await mqtt_mock_entry_no_yaml_config() devices = [ { "domain": "sensor", @@ -2054,8 +2176,11 @@ async def test_debug_info_multiple_devices(hass, mqtt_mock): assert discovery_data["payload"] == d["config"] -async def test_debug_info_multiple_entities_triggers(hass, mqtt_mock): +async def test_debug_info_multiple_entities_triggers( + hass, mqtt_mock_entry_no_yaml_config +): """Test we get correct debug_info for a device with multiple entities and triggers.""" + await mqtt_mock_entry_no_yaml_config() config = [ { "domain": "sensor", @@ -2137,8 +2262,11 @@ async def test_debug_info_multiple_entities_triggers(hass, mqtt_mock): } in discovery_data -async def test_debug_info_non_mqtt(hass, device_reg, entity_reg, mqtt_mock): +async def test_debug_info_non_mqtt( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test we get empty debug_info for a device with non MQTT entities.""" + await mqtt_mock_entry_no_yaml_config() DOMAIN = "sensor" platform = getattr(hass.components, f"test.{DOMAIN}") platform.init() @@ -2164,8 +2292,9 @@ async def test_debug_info_non_mqtt(hass, device_reg, entity_reg, mqtt_mock): assert len(debug_info_data["triggers"]) == 0 -async def test_debug_info_wildcard(hass, mqtt_mock): +async def test_debug_info_wildcard(hass, mqtt_mock_entry_no_yaml_config): """Test debug info.""" + await mqtt_mock_entry_no_yaml_config() config = { "device": {"identifiers": ["helloworld"]}, "platform": "mqtt", @@ -2210,8 +2339,9 @@ async def test_debug_info_wildcard(hass, mqtt_mock): } in debug_info_data["entities"][0]["subscriptions"] -async def test_debug_info_filter_same(hass, mqtt_mock): +async def test_debug_info_filter_same(hass, mqtt_mock_entry_no_yaml_config): """Test debug info removes messages with same timestamp.""" + await mqtt_mock_entry_no_yaml_config() config = { "device": {"identifiers": ["helloworld"]}, "platform": "mqtt", @@ -2268,8 +2398,9 @@ async def test_debug_info_filter_same(hass, mqtt_mock): } == debug_info_data["entities"][0]["subscriptions"][0] -async def test_debug_info_same_topic(hass, mqtt_mock): +async def test_debug_info_same_topic(hass, mqtt_mock_entry_no_yaml_config): """Test debug info.""" + await mqtt_mock_entry_no_yaml_config() config = { "device": {"identifiers": ["helloworld"]}, "platform": "mqtt", @@ -2320,8 +2451,9 @@ async def test_debug_info_same_topic(hass, mqtt_mock): async_fire_mqtt_message(hass, "sensor/status", "123", qos=0, retain=False) -async def test_debug_info_qos_retain(hass, mqtt_mock): +async def test_debug_info_qos_retain(hass, mqtt_mock_entry_no_yaml_config): """Test debug info.""" + await mqtt_mock_entry_no_yaml_config() config = { "device": {"identifiers": ["helloworld"]}, "platform": "mqtt", @@ -2377,8 +2509,10 @@ async def test_debug_info_qos_retain(hass, mqtt_mock): } in debug_info_data["entities"][0]["subscriptions"][0]["messages"] -async def test_publish_json_from_template(hass, mqtt_mock): +async def test_publish_json_from_template(hass, mqtt_mock_entry_no_yaml_config): """Test the publishing of call to services.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() + test_str = "{'valid': 'python', 'invalid': 'json'}" test_str_tpl = "{'valid': '{{ \"python\" }}', 'invalid': 'json'}" @@ -2424,8 +2558,11 @@ async def test_publish_json_from_template(hass, mqtt_mock): assert mqtt_mock.async_publish.call_args[0][1] == test_str -async def test_subscribe_connection_status(hass, mqtt_mock, mqtt_client_mock): +async def test_subscribe_connection_status( + hass, mqtt_mock_entry_no_yaml_config, mqtt_client_mock +): """Test connextion status subscription.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() mqtt_connected_calls = [] @callback @@ -2459,7 +2596,9 @@ async def test_subscribe_connection_status(hass, mqtt_mock, mqtt_client_mock): assert mqtt_connected_calls[1] is False -async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog): +async def test_one_deprecation_warning_per_platform( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test a deprecation warning is is logged once per platform.""" platform = "light" config = {"platform": "mqtt", "command_topic": "test-topic"} @@ -2469,6 +2608,7 @@ async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog): config2["name"] = "test2" await async_setup_component(hass, platform, {platform: [config1, config2]}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() count = 0 for record in caplog.records: if record.levelname == "WARNING" and ( diff --git a/tests/components/mqtt/test_legacy_vacuum.py b/tests/components/mqtt/test_legacy_vacuum.py index f451079e0f0..43b6e839904 100644 --- a/tests/components/mqtt/test_legacy_vacuum.py +++ b/tests/components/mqtt/test_legacy_vacuum.py @@ -6,7 +6,7 @@ from unittest.mock import patch import pytest from homeassistant.components import vacuum -from homeassistant.components.mqtt import CONF_COMMAND_TOPIC +from homeassistant.components.mqtt.const import CONF_COMMAND_TOPIC from homeassistant.components.mqtt.vacuum import schema_legacy as mqttvacuum from homeassistant.components.mqtt.vacuum.schema import services_to_strings from homeassistant.components.mqtt.vacuum.schema_legacy import ( @@ -89,12 +89,13 @@ DEFAULT_CONFIG = { DEFAULT_CONFIG_2 = {vacuum.DOMAIN: {"platform": "mqtt", "name": "test"}} -async def test_default_supported_features(hass, mqtt_mock): +async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config): """Test that the correct supported features.""" assert await async_setup_component( hass, vacuum.DOMAIN, {vacuum.DOMAIN: DEFAULT_CONFIG} ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() entity = hass.states.get("vacuum.mqtttest") entity_features = entity.attributes.get(mqttvacuum.CONF_SUPPORTED_FEATURES, 0) assert sorted(services_to_strings(entity_features, SERVICE_TO_STRING)) == sorted( @@ -110,7 +111,7 @@ async def test_default_supported_features(hass, mqtt_mock): ) -async def test_all_commands(hass, mqtt_mock): +async def test_all_commands(hass, mqtt_mock_entry_with_yaml_config): """Test simple commands to the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -119,6 +120,7 @@ async def test_all_commands(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_turn_on(hass, "vacuum.mqtttest") mqtt_mock.async_publish.assert_called_once_with( @@ -189,7 +191,9 @@ async def test_all_commands(hass, mqtt_mock): } -async def test_commands_without_supported_features(hass, mqtt_mock): +async def test_commands_without_supported_features( + hass, mqtt_mock_entry_with_yaml_config +): """Test commands which are not supported by the vacuum.""" config = deepcopy(DEFAULT_CONFIG) services = mqttvacuum.STRING_TO_SERVICE["status"] @@ -199,6 +203,7 @@ async def test_commands_without_supported_features(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await common.async_turn_on(hass, "vacuum.mqtttest") mqtt_mock.async_publish.assert_not_called() @@ -237,7 +242,9 @@ async def test_commands_without_supported_features(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_attributes_without_supported_features(hass, mqtt_mock): +async def test_attributes_without_supported_features( + hass, mqtt_mock_entry_with_yaml_config +): """Test attributes which are not supported by the vacuum.""" config = deepcopy(DEFAULT_CONFIG) services = mqttvacuum.STRING_TO_SERVICE["turn_on"] @@ -247,6 +254,7 @@ async def test_attributes_without_supported_features(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "battery_level": 54, @@ -264,7 +272,7 @@ async def test_attributes_without_supported_features(hass, mqtt_mock): assert state.attributes.get(ATTR_FAN_SPEED_LIST) is None -async def test_status(hass, mqtt_mock): +async def test_status(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -273,6 +281,7 @@ async def test_status(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "battery_level": 54, @@ -304,7 +313,7 @@ async def test_status(hass, mqtt_mock): assert state.attributes.get(ATTR_FAN_SPEED) == "min" -async def test_status_battery(hass, mqtt_mock): +async def test_status_battery(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -313,6 +322,7 @@ async def test_status_battery(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "battery_level": 54 @@ -322,7 +332,7 @@ async def test_status_battery(hass, mqtt_mock): assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-50" -async def test_status_cleaning(hass, mqtt_mock): +async def test_status_cleaning(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -331,6 +341,7 @@ async def test_status_cleaning(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "cleaning": true @@ -340,7 +351,7 @@ async def test_status_cleaning(hass, mqtt_mock): assert state.state == STATE_ON -async def test_status_docked(hass, mqtt_mock): +async def test_status_docked(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -349,6 +360,7 @@ async def test_status_docked(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "docked": true @@ -358,7 +370,7 @@ async def test_status_docked(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_status_charging(hass, mqtt_mock): +async def test_status_charging(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -367,6 +379,7 @@ async def test_status_charging(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "charging": true @@ -376,7 +389,7 @@ async def test_status_charging(hass, mqtt_mock): assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-outline" -async def test_status_fan_speed(hass, mqtt_mock): +async def test_status_fan_speed(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -385,6 +398,7 @@ async def test_status_fan_speed(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "fan_speed": "max" @@ -394,7 +408,7 @@ async def test_status_fan_speed(hass, mqtt_mock): assert state.attributes.get(ATTR_FAN_SPEED) == "max" -async def test_status_fan_speed_list(hass, mqtt_mock): +async def test_status_fan_speed_list(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -403,12 +417,13 @@ async def test_status_fan_speed_list(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state.attributes.get(ATTR_FAN_SPEED_LIST) == ["min", "medium", "high", "max"] -async def test_status_no_fan_speed_list(hass, mqtt_mock): +async def test_status_no_fan_speed_list(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum. If the vacuum doesn't support fan speed, fan speed list should be None. @@ -421,12 +436,13 @@ async def test_status_no_fan_speed_list(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state.attributes.get(ATTR_FAN_SPEED_LIST) is None -async def test_status_error(hass, mqtt_mock): +async def test_status_error(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -435,6 +451,7 @@ async def test_status_error(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "error": "Error1" @@ -451,7 +468,7 @@ async def test_status_error(hass, mqtt_mock): assert state.attributes.get(ATTR_STATUS) == "Stopped" -async def test_battery_template(hass, mqtt_mock): +async def test_battery_template(hass, mqtt_mock_entry_with_yaml_config): """Test that you can use non-default templates for battery_level.""" config = deepcopy(DEFAULT_CONFIG) config.update( @@ -466,6 +483,7 @@ async def test_battery_template(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "retroroomba/battery_level", "54") state = hass.states.get("vacuum.mqtttest") @@ -473,7 +491,7 @@ async def test_battery_template(hass, mqtt_mock): assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-50" -async def test_status_invalid_json(hass, mqtt_mock): +async def test_status_invalid_json(hass, mqtt_mock_entry_with_yaml_config): """Test to make sure nothing breaks if the vacuum sends bad JSON.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -482,6 +500,7 @@ async def test_status_invalid_json(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "vacuum/state", '{"asdfasas false}') state = hass.states.get("vacuum.mqtttest") @@ -489,153 +508,169 @@ async def test_status_invalid_json(hass, mqtt_mock): assert state.attributes.get(ATTR_STATUS) == "Stopped" -async def test_missing_battery_template(hass, mqtt_mock): +async def test_missing_battery_template(hass, mqtt_mock_entry_no_yaml_config): """Test to make sure missing template is not allowed.""" config = deepcopy(DEFAULT_CONFIG) config.pop(mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state is None -async def test_missing_charging_template(hass, mqtt_mock): +async def test_missing_charging_template(hass, mqtt_mock_entry_no_yaml_config): """Test to make sure missing template is not allowed.""" config = deepcopy(DEFAULT_CONFIG) config.pop(mqttvacuum.CONF_CHARGING_TEMPLATE) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state is None -async def test_missing_cleaning_template(hass, mqtt_mock): +async def test_missing_cleaning_template(hass, mqtt_mock_entry_no_yaml_config): """Test to make sure missing template is not allowed.""" config = deepcopy(DEFAULT_CONFIG) config.pop(mqttvacuum.CONF_CLEANING_TEMPLATE) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state is None -async def test_missing_docked_template(hass, mqtt_mock): +async def test_missing_docked_template(hass, mqtt_mock_entry_no_yaml_config): """Test to make sure missing template is not allowed.""" config = deepcopy(DEFAULT_CONFIG) config.pop(mqttvacuum.CONF_DOCKED_TEMPLATE) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state is None -async def test_missing_error_template(hass, mqtt_mock): +async def test_missing_error_template(hass, mqtt_mock_entry_no_yaml_config): """Test to make sure missing template is not allowed.""" config = deepcopy(DEFAULT_CONFIG) config.pop(mqttvacuum.CONF_ERROR_TEMPLATE) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state is None -async def test_missing_fan_speed_template(hass, mqtt_mock): +async def test_missing_fan_speed_template(hass, mqtt_mock_entry_no_yaml_config): """Test to make sure missing template is not allowed.""" config = deepcopy(DEFAULT_CONFIG) config.pop(mqttvacuum.CONF_FAN_SPEED_TEMPLATE) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state is None -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2, MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one vacuum per unique_id.""" config = { vacuum.DOMAIN: [ @@ -653,74 +688,85 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, vacuum.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, config + ) -async def test_discovery_removal_vacuum(hass, mqtt_mock, caplog): +async def test_discovery_removal_vacuum(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered vacuum.""" data = json.dumps(DEFAULT_CONFIG_2[vacuum.DOMAIN]) - await help_test_discovery_removal(hass, mqtt_mock, caplog, vacuum.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, data + ) -async def test_discovery_update_vacuum(hass, mqtt_mock, caplog): +async def test_discovery_update_vacuum(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered vacuum.""" config1 = {"name": "Beer", "command_topic": "test_topic"} config2 = {"name": "Milk", "command_topic": "test_topic"} await help_test_discovery_update( - hass, mqtt_mock, caplog, vacuum.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_vacuum(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_vacuum( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered vacuum.""" data1 = '{ "name": "Beer", "command_topic": "test_topic" }' with patch( "homeassistant.components.mqtt.vacuum.schema_legacy.MqttVacuum.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, vacuum.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + vacuum.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer",' ' "command_topic": "test_topic#" }' data2 = '{ "name": "Milk",' ' "command_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, vacuum.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT vacuum device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT vacuum device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" config = { vacuum.DOMAIN: { @@ -733,18 +779,22 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock): } } await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, vacuum.DOMAIN, config, ["test-topic", "avty-topic"] + hass, + mqtt_mock_entry_with_yaml_config, + vacuum.DOMAIN, + config, + ["test-topic", "avty-topic"], ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" config = { vacuum.DOMAIN: { @@ -757,7 +807,11 @@ async def test_entity_debug_info_message(hass, mqtt_mock): } } await help_test_entity_debug_info_message( - hass, mqtt_mock, vacuum.DOMAIN, config, vacuum.SERVICE_TURN_ON + hass, + mqtt_mock_entry_no_yaml_config, + vacuum.DOMAIN, + config, + vacuum.SERVICE_TURN_ON, ) @@ -803,7 +857,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -824,7 +878,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -836,11 +890,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = vacuum.DOMAIN config = DEFAULT_CONFIG - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -872,7 +928,13 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" config = deepcopy(DEFAULT_CONFIG) @@ -892,7 +954,7 @@ async def test_encoding_subscribable_topics( await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, config, diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 957178da14f..08d5432ba27 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -251,16 +251,17 @@ DEFAULT_CONFIG = { } -async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): +async def test_fail_setup_if_no_command_topic(hass, mqtt_mock_entry_no_yaml_config): """Test if command fails with command topic.""" assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {"platform": "mqtt", "name": "test"}} ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert hass.states.get("light.test") is None -async def test_legacy_rgb_white_light(hass, mqtt_mock): +async def test_legacy_rgb_white_light(hass, mqtt_mock_entry_with_yaml_config): """Test legacy RGB + white light flags brightness support.""" assert await async_setup_component( hass, @@ -276,6 +277,7 @@ async def test_legacy_rgb_white_light(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") expected_features = ( @@ -286,7 +288,9 @@ async def test_legacy_rgb_white_light(hass, mqtt_mock): assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == ["hs", "rgbw"] -async def test_no_color_brightness_color_temp_hs_white_xy_if_no_topics(hass, mqtt_mock): +async def test_no_color_brightness_color_temp_hs_white_xy_if_no_topics( + hass, mqtt_mock_entry_with_yaml_config +): """Test if there is no color and brightness if no topic.""" assert await async_setup_component( hass, @@ -301,6 +305,7 @@ async def test_no_color_brightness_color_temp_hs_white_xy_if_no_topics(hass, mqt }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -343,7 +348,9 @@ async def test_no_color_brightness_color_temp_hs_white_xy_if_no_topics(hass, mqt assert state.state == STATE_UNKNOWN -async def test_legacy_controlling_state_via_topic(hass, mqtt_mock): +async def test_legacy_controlling_state_via_topic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling of the state via topic for legacy light (white_value).""" config = { light.DOMAIN: { @@ -374,6 +381,7 @@ async def test_legacy_controlling_state_via_topic(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -472,7 +480,7 @@ async def test_legacy_controlling_state_via_topic(hass, mqtt_mock): assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_controlling_state_via_topic(hass, mqtt_mock): +async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling of the state via topic.""" config = { light.DOMAIN: { @@ -505,6 +513,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -593,7 +602,9 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_legacy_invalid_state_via_topic(hass, mqtt_mock, caplog): +async def test_legacy_invalid_state_via_topic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test handling of empty data via topic.""" config = { light.DOMAIN: { @@ -623,6 +634,7 @@ async def test_legacy_invalid_state_via_topic(hass, mqtt_mock, caplog): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -709,7 +721,7 @@ async def test_legacy_invalid_state_via_topic(hass, mqtt_mock, caplog): assert light_state.attributes["white_value"] == 255 -async def test_invalid_state_via_topic(hass, mqtt_mock, caplog): +async def test_invalid_state_via_topic(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test handling of empty data via topic.""" config = { light.DOMAIN: { @@ -742,6 +754,7 @@ async def test_invalid_state_via_topic(hass, mqtt_mock, caplog): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -843,7 +856,7 @@ async def test_invalid_state_via_topic(hass, mqtt_mock, caplog): assert light_state.attributes["color_temp"] == 153 -async def test_brightness_controlling_scale(hass, mqtt_mock): +async def test_brightness_controlling_scale(hass, mqtt_mock_entry_with_yaml_config): """Test the brightness controlling scale.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -865,6 +878,7 @@ async def test_brightness_controlling_scale(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -890,7 +904,9 @@ async def test_brightness_controlling_scale(hass, mqtt_mock): assert light_state.attributes["brightness"] == 255 -async def test_brightness_from_rgb_controlling_scale(hass, mqtt_mock): +async def test_brightness_from_rgb_controlling_scale( + hass, mqtt_mock_entry_with_yaml_config +): """Test the brightness controlling scale.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -911,6 +927,7 @@ async def test_brightness_from_rgb_controlling_scale(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -929,7 +946,9 @@ async def test_brightness_from_rgb_controlling_scale(hass, mqtt_mock): assert state.attributes.get("brightness") == 127 -async def test_legacy_white_value_controlling_scale(hass, mqtt_mock): +async def test_legacy_white_value_controlling_scale( + hass, mqtt_mock_entry_with_yaml_config +): """Test the white_value controlling scale.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -951,6 +970,7 @@ async def test_legacy_white_value_controlling_scale(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -976,7 +996,9 @@ async def test_legacy_white_value_controlling_scale(hass, mqtt_mock): assert light_state.attributes["white_value"] == 255 -async def test_legacy_controlling_state_via_topic_with_templates(hass, mqtt_mock): +async def test_legacy_controlling_state_via_topic_with_templates( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the state with a template.""" config = { light.DOMAIN: { @@ -1011,6 +1033,7 @@ async def test_legacy_controlling_state_via_topic_with_templates(hass, mqtt_mock assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1065,7 +1088,9 @@ async def test_legacy_controlling_state_via_topic_with_templates(hass, mqtt_mock assert state.state == STATE_UNKNOWN -async def test_controlling_state_via_topic_with_templates(hass, mqtt_mock): +async def test_controlling_state_via_topic_with_templates( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the state with a template.""" config = { light.DOMAIN: { @@ -1104,6 +1129,7 @@ async def test_controlling_state_via_topic_with_templates(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1165,7 +1191,9 @@ async def test_controlling_state_via_topic_with_templates(hass, mqtt_mock): assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_legacy_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_legacy_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of command in optimistic mode.""" config = { light.DOMAIN: { @@ -1204,6 +1232,7 @@ async def test_legacy_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): ), assert_setup_component(1, light.DOMAIN): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_ON @@ -1295,7 +1324,9 @@ async def test_legacy_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert state.attributes["color_temp"] == 125 -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of command in optimistic mode.""" config = { light.DOMAIN: { @@ -1334,6 +1365,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): ), assert_setup_component(1, light.DOMAIN): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_ON @@ -1484,7 +1516,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_sending_mqtt_rgb_command_with_template(hass, mqtt_mock): +async def test_sending_mqtt_rgb_command_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of RGB command with template.""" config = { light.DOMAIN: { @@ -1502,6 +1536,7 @@ async def test_sending_mqtt_rgb_command_with_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1521,7 +1556,9 @@ async def test_sending_mqtt_rgb_command_with_template(hass, mqtt_mock): assert state.attributes["rgb_color"] == (255, 128, 64) -async def test_sending_mqtt_rgbw_command_with_template(hass, mqtt_mock): +async def test_sending_mqtt_rgbw_command_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of RGBW command with template.""" config = { light.DOMAIN: { @@ -1539,6 +1576,7 @@ async def test_sending_mqtt_rgbw_command_with_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1558,7 +1596,9 @@ async def test_sending_mqtt_rgbw_command_with_template(hass, mqtt_mock): assert state.attributes["rgbw_color"] == (255, 128, 64, 32) -async def test_sending_mqtt_rgbww_command_with_template(hass, mqtt_mock): +async def test_sending_mqtt_rgbww_command_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of RGBWW command with template.""" config = { light.DOMAIN: { @@ -1576,6 +1616,7 @@ async def test_sending_mqtt_rgbww_command_with_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1595,7 +1636,9 @@ async def test_sending_mqtt_rgbww_command_with_template(hass, mqtt_mock): assert state.attributes["rgbww_color"] == (255, 128, 64, 32, 16) -async def test_sending_mqtt_color_temp_command_with_template(hass, mqtt_mock): +async def test_sending_mqtt_color_temp_command_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of Color Temp command with template.""" config = { light.DOMAIN: { @@ -1612,6 +1655,7 @@ async def test_sending_mqtt_color_temp_command_with_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1631,7 +1675,7 @@ async def test_sending_mqtt_color_temp_command_with_template(hass, mqtt_mock): assert state.attributes["color_temp"] == 100 -async def test_on_command_first(hass, mqtt_mock): +async def test_on_command_first(hass, mqtt_mock_entry_with_yaml_config): """Test on command being sent before brightness.""" config = { light.DOMAIN: { @@ -1645,6 +1689,7 @@ async def test_on_command_first(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1667,7 +1712,7 @@ async def test_on_command_first(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False) -async def test_on_command_last(hass, mqtt_mock): +async def test_on_command_last(hass, mqtt_mock_entry_with_yaml_config): """Test on command being sent after brightness.""" config = { light.DOMAIN: { @@ -1680,6 +1725,7 @@ async def test_on_command_last(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1702,7 +1748,7 @@ async def test_on_command_last(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False) -async def test_on_command_brightness(hass, mqtt_mock): +async def test_on_command_brightness(hass, mqtt_mock_entry_with_yaml_config): """Test on command being sent as only brightness.""" config = { light.DOMAIN: { @@ -1717,6 +1763,7 @@ async def test_on_command_brightness(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1757,7 +1804,7 @@ async def test_on_command_brightness(hass, mqtt_mock): ) -async def test_on_command_brightness_scaled(hass, mqtt_mock): +async def test_on_command_brightness_scaled(hass, mqtt_mock_entry_with_yaml_config): """Test brightness scale.""" config = { light.DOMAIN: { @@ -1773,6 +1820,7 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1827,7 +1875,7 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock): ) -async def test_legacy_on_command_rgb(hass, mqtt_mock): +async def test_legacy_on_command_rgb(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGB brightness mode.""" config = { light.DOMAIN: { @@ -1841,6 +1889,7 @@ async def test_legacy_on_command_rgb(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1918,7 +1967,7 @@ async def test_legacy_on_command_rgb(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_on_command_rgb(hass, mqtt_mock): +async def test_on_command_rgb(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGB brightness mode.""" config = { light.DOMAIN: { @@ -1931,6 +1980,7 @@ async def test_on_command_rgb(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2008,7 +2058,7 @@ async def test_on_command_rgb(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_on_command_rgbw(hass, mqtt_mock): +async def test_on_command_rgbw(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGBW brightness mode.""" config = { light.DOMAIN: { @@ -2021,6 +2071,7 @@ async def test_on_command_rgbw(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2098,7 +2149,7 @@ async def test_on_command_rgbw(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_on_command_rgbww(hass, mqtt_mock): +async def test_on_command_rgbww(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGBWW brightness mode.""" config = { light.DOMAIN: { @@ -2111,6 +2162,7 @@ async def test_on_command_rgbww(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2188,7 +2240,7 @@ async def test_on_command_rgbww(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_on_command_rgb_template(hass, mqtt_mock): +async def test_on_command_rgb_template(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGB brightness mode with RGB template.""" config = { light.DOMAIN: { @@ -2202,6 +2254,7 @@ async def test_on_command_rgb_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2225,7 +2278,7 @@ async def test_on_command_rgb_template(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False) -async def test_on_command_rgbw_template(hass, mqtt_mock): +async def test_on_command_rgbw_template(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGBW brightness mode with RGBW template.""" config = { light.DOMAIN: { @@ -2239,6 +2292,7 @@ async def test_on_command_rgbw_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2261,7 +2315,7 @@ async def test_on_command_rgbw_template(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False) -async def test_on_command_rgbww_template(hass, mqtt_mock): +async def test_on_command_rgbww_template(hass, mqtt_mock_entry_with_yaml_config): """Test on command in RGBWW brightness mode with RGBWW template.""" config = { light.DOMAIN: { @@ -2275,6 +2329,7 @@ async def test_on_command_rgbww_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2298,7 +2353,7 @@ async def test_on_command_rgbww_template(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False) -async def test_on_command_white(hass, mqtt_mock): +async def test_on_command_white(hass, mqtt_mock_entry_with_yaml_config): """Test sending commands for RGB + white light.""" config = { light.DOMAIN: { @@ -2324,6 +2379,7 @@ async def test_on_command_white(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2375,7 +2431,7 @@ async def test_on_command_white(hass, mqtt_mock): ) -async def test_explicit_color_mode(hass, mqtt_mock): +async def test_explicit_color_mode(hass, mqtt_mock_entry_with_yaml_config): """Test explicit color mode over mqtt.""" config = { light.DOMAIN: { @@ -2409,6 +2465,7 @@ async def test_explicit_color_mode(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2525,7 +2582,7 @@ async def test_explicit_color_mode(hass, mqtt_mock): assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_explicit_color_mode_templated(hass, mqtt_mock): +async def test_explicit_color_mode_templated(hass, mqtt_mock_entry_with_yaml_config): """Test templated explicit color mode over mqtt.""" config = { light.DOMAIN: { @@ -2550,6 +2607,7 @@ async def test_explicit_color_mode_templated(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2606,7 +2664,7 @@ async def test_explicit_color_mode_templated(hass, mqtt_mock): assert light_state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_white_state_update(hass, mqtt_mock): +async def test_white_state_update(hass, mqtt_mock_entry_with_yaml_config): """Test state updates for RGB + white light.""" config = { light.DOMAIN: { @@ -2636,6 +2694,7 @@ async def test_white_state_update(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2670,7 +2729,7 @@ async def test_white_state_update(hass, mqtt_mock): assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes -async def test_effect(hass, mqtt_mock): +async def test_effect(hass, mqtt_mock_entry_with_yaml_config): """Test effect.""" config = { light.DOMAIN: { @@ -2684,6 +2743,7 @@ async def test_effect(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -2707,77 +2767,91 @@ async def test_effect(hass, mqtt_mock): mqtt_mock.async_publish.assert_called_once_with("test_light/set", "OFF", 0, False) -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG, MQTT_LIGHT_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, + MQTT_LIGHT_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one light per unique_id.""" config = { light.DOMAIN: [ @@ -2797,21 +2871,26 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, light.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config + ) -async def test_discovery_removal_light(hass, mqtt_mock, caplog): +async def test_discovery_removal_light(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered light.""" data = ( '{ "name": "test",' ' "state_topic": "test_topic",' ' "command_topic": "test_topic" }' ) - await help_test_discovery_removal(hass, mqtt_mock, caplog, light.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, data + ) -async def test_discovery_deprecated(hass, mqtt_mock, caplog): +async def test_discovery_deprecated(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test discovery of mqtt light with deprecated platform option.""" + await mqtt_mock_entry_no_yaml_config() data = ( '{ "name": "Beer",' ' "platform": "mqtt",' ' "command_topic": "test_topic"}' ) @@ -2822,7 +2901,9 @@ async def test_discovery_deprecated(hass, mqtt_mock, caplog): assert state.name == "Beer" -async def test_discovery_update_light_topic_and_template(hass, mqtt_mock, caplog): +async def test_discovery_update_light_topic_and_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered light.""" config1 = { "name": "Beer", @@ -3073,7 +3154,7 @@ async def test_discovery_update_light_topic_and_template(hass, mqtt_mock, caplog await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, config1, @@ -3083,7 +3164,9 @@ async def test_discovery_update_light_topic_and_template(hass, mqtt_mock, caplog ) -async def test_discovery_update_light_template(hass, mqtt_mock, caplog): +async def test_discovery_update_light_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered light.""" config1 = { "name": "Beer", @@ -3292,7 +3375,7 @@ async def test_discovery_update_light_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, config1, @@ -3302,7 +3385,9 @@ async def test_discovery_update_light_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_light( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered light.""" data1 = ( '{ "name": "Beer",' @@ -3313,12 +3398,17 @@ async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.light.schema_basic.MqttLight.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, light.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -3327,60 +3417,64 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ' "command_topic": "test_topic" }' ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, light.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT light device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT light device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG, light.SERVICE_TURN_ON + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, + light.SERVICE_TURN_ON, ) -async def test_max_mireds(hass, mqtt_mock): +async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config): """Test setting min_mireds and max_mireds.""" config = { light.DOMAIN: { @@ -3394,6 +3488,7 @@ async def test_max_mireds(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.attributes.get("min_mireds") == 153 @@ -3488,7 +3583,7 @@ async def test_max_mireds(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -3508,7 +3603,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -3522,11 +3617,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = light.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -3568,7 +3665,14 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value, init_payload + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, + init_payload, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN]) @@ -3587,7 +3691,7 @@ async def test_encoding_subscribable_topics( await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, config, @@ -3599,7 +3703,9 @@ async def test_encoding_subscribable_topics( ) -async def test_sending_mqtt_brightness_command_with_template(hass, mqtt_mock): +async def test_sending_mqtt_brightness_command_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of Brightness command with template.""" config = { light.DOMAIN: { @@ -3616,6 +3722,7 @@ async def test_sending_mqtt_brightness_command_with_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -3635,7 +3742,9 @@ async def test_sending_mqtt_brightness_command_with_template(hass, mqtt_mock): assert state.attributes["brightness"] == 100 -async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock): +async def test_sending_mqtt_effect_command_with_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of Effect command with template.""" config = { light.DOMAIN: { @@ -3654,6 +3763,7 @@ async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 962bf534370..c24c5e87937 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -162,7 +162,7 @@ class JsonValidator: return json.loads(self.jsondata) == json.loads(other) -async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): +async def test_fail_setup_if_no_command_topic(hass, mqtt_mock_entry_no_yaml_config): """Test if setup fails with no command topic.""" assert await async_setup_component( hass, @@ -170,11 +170,14 @@ async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): {light.DOMAIN: {"platform": "mqtt", "schema": "json", "name": "test"}}, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert hass.states.get("light.test") is None @pytest.mark.parametrize("deprecated", ("color_temp", "hs", "rgb", "white_value", "xy")) -async def test_fail_setup_if_color_mode_deprecated(hass, mqtt_mock, deprecated): +async def test_fail_setup_if_color_mode_deprecated( + hass, mqtt_mock_entry_no_yaml_config, deprecated +): """Test if setup fails if color mode is combined with deprecated config keys.""" supported_color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"] @@ -196,6 +199,7 @@ async def test_fail_setup_if_color_mode_deprecated(hass, mqtt_mock, deprecated): config, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert hass.states.get("light.test") is None @@ -203,7 +207,7 @@ async def test_fail_setup_if_color_mode_deprecated(hass, mqtt_mock, deprecated): "supported_color_modes", [["onoff", "rgb"], ["brightness", "rgb"], ["unknown"]] ) async def test_fail_setup_if_color_modes_invalid( - hass, mqtt_mock, supported_color_modes + hass, mqtt_mock_entry_no_yaml_config, supported_color_modes ): """Test if setup fails if supported color modes is invalid.""" config = { @@ -223,10 +227,11 @@ async def test_fail_setup_if_color_modes_invalid( config, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert hass.states.get("light.test") is None -async def test_rgb_light(hass, mqtt_mock): +async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): """Test RGB light flags brightness support.""" assert await async_setup_component( hass, @@ -242,6 +247,7 @@ async def test_rgb_light(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") expected_features = ( @@ -253,7 +259,9 @@ async def test_rgb_light(hass, mqtt_mock): assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features -async def test_no_color_brightness_color_temp_white_val_if_no_topics(hass, mqtt_mock): +async def test_no_color_brightness_color_temp_white_val_if_no_topics( + hass, mqtt_mock_entry_with_yaml_config +): """Test for no RGB, brightness, color temp, effect, white val or XY.""" assert await async_setup_component( hass, @@ -269,6 +277,7 @@ async def test_no_color_brightness_color_temp_white_val_if_no_topics(hass, mqtt_ }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -305,7 +314,7 @@ async def test_no_color_brightness_color_temp_white_val_if_no_topics(hass, mqtt_ assert state.state == STATE_UNKNOWN -async def test_controlling_state_via_topic(hass, mqtt_mock): +async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling of the state via topic.""" assert await async_setup_component( hass, @@ -329,6 +338,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -434,7 +444,9 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): assert light_state.attributes.get("white_value") == 155 -async def test_controlling_state_via_topic2(hass, mqtt_mock, caplog): +async def test_controlling_state_via_topic2( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling of the state via topic for a light supporting color mode.""" supported_color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"] @@ -457,6 +469,7 @@ async def test_controlling_state_via_topic2(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -602,7 +615,9 @@ async def test_controlling_state_via_topic2(hass, mqtt_mock, caplog): assert "Invalid or incomplete color value received" in caplog.text -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of command in optimistic mode.""" fake_state = ha.State( "light.test", @@ -641,6 +656,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_ON @@ -745,7 +761,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert state.attributes["xy_color"] == (0.611, 0.375) -async def test_sending_mqtt_commands_and_optimistic2(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic2( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of command in optimistic mode for a light supporting color mode.""" supported_color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"] fake_state = ha.State( @@ -783,6 +801,7 @@ async def test_sending_mqtt_commands_and_optimistic2(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_ON @@ -953,7 +972,7 @@ async def test_sending_mqtt_commands_and_optimistic2(hass, mqtt_mock): mqtt_mock.async_publish.reset_mock() -async def test_sending_hs_color(hass, mqtt_mock): +async def test_sending_hs_color(hass, mqtt_mock_entry_with_yaml_config): """Test light.turn_on with hs color sends hs color parameters.""" assert await async_setup_component( hass, @@ -971,6 +990,7 @@ async def test_sending_hs_color(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1018,7 +1038,7 @@ async def test_sending_hs_color(hass, mqtt_mock): ) -async def test_sending_rgb_color_no_brightness(hass, mqtt_mock): +async def test_sending_rgb_color_no_brightness(hass, mqtt_mock_entry_with_yaml_config): """Test light.turn_on with hs color sends rgb color parameters.""" assert await async_setup_component( hass, @@ -1034,6 +1054,7 @@ async def test_sending_rgb_color_no_brightness(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1071,7 +1092,7 @@ async def test_sending_rgb_color_no_brightness(hass, mqtt_mock): ) -async def test_sending_rgb_color_no_brightness2(hass, mqtt_mock): +async def test_sending_rgb_color_no_brightness2(hass, mqtt_mock_entry_with_yaml_config): """Test light.turn_on with hs color sends rgb color parameters.""" supported_color_modes = ["rgb", "rgbw", "rgbww"] assert await async_setup_component( @@ -1089,6 +1110,7 @@ async def test_sending_rgb_color_no_brightness2(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1148,7 +1170,9 @@ async def test_sending_rgb_color_no_brightness2(hass, mqtt_mock): ) -async def test_sending_rgb_color_with_brightness(hass, mqtt_mock): +async def test_sending_rgb_color_with_brightness( + hass, mqtt_mock_entry_with_yaml_config +): """Test light.turn_on with hs color sends rgb color parameters.""" assert await async_setup_component( hass, @@ -1166,6 +1190,7 @@ async def test_sending_rgb_color_with_brightness(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1218,7 +1243,9 @@ async def test_sending_rgb_color_with_brightness(hass, mqtt_mock): ) -async def test_sending_rgb_color_with_scaled_brightness(hass, mqtt_mock): +async def test_sending_rgb_color_with_scaled_brightness( + hass, mqtt_mock_entry_with_yaml_config +): """Test light.turn_on with hs color sends rgb color parameters.""" assert await async_setup_component( hass, @@ -1237,6 +1264,7 @@ async def test_sending_rgb_color_with_scaled_brightness(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1289,7 +1317,7 @@ async def test_sending_rgb_color_with_scaled_brightness(hass, mqtt_mock): ) -async def test_sending_xy_color(hass, mqtt_mock): +async def test_sending_xy_color(hass, mqtt_mock_entry_with_yaml_config): """Test light.turn_on with hs color sends xy color parameters.""" assert await async_setup_component( hass, @@ -1307,6 +1335,7 @@ async def test_sending_xy_color(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1353,7 +1382,7 @@ async def test_sending_xy_color(hass, mqtt_mock): ) -async def test_effect(hass, mqtt_mock): +async def test_effect(hass, mqtt_mock_entry_with_yaml_config): """Test for effect being sent when included.""" assert await async_setup_component( hass, @@ -1370,6 +1399,7 @@ async def test_effect(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1415,7 +1445,7 @@ async def test_effect(hass, mqtt_mock): assert state.attributes.get("effect") == "colorloop" -async def test_flash_short_and_long(hass, mqtt_mock): +async def test_flash_short_and_long(hass, mqtt_mock_entry_with_yaml_config): """Test for flash length being sent when included.""" assert await async_setup_component( hass, @@ -1433,6 +1463,7 @@ async def test_flash_short_and_long(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1476,7 +1507,7 @@ async def test_flash_short_and_long(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_transition(hass, mqtt_mock): +async def test_transition(hass, mqtt_mock_entry_with_yaml_config): """Test for transition time being sent when included.""" assert await async_setup_component( hass, @@ -1492,6 +1523,7 @@ async def test_transition(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1522,7 +1554,7 @@ async def test_transition(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_brightness_scale(hass, mqtt_mock): +async def test_brightness_scale(hass, mqtt_mock_entry_with_yaml_config): """Test for brightness scaling.""" assert await async_setup_component( hass, @@ -1540,6 +1572,7 @@ async def test_brightness_scale(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1563,7 +1596,7 @@ async def test_brightness_scale(hass, mqtt_mock): assert state.attributes.get("brightness") == 255 -async def test_invalid_values(hass, mqtt_mock): +async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): """Test that invalid color/brightness/white/etc. values are ignored.""" assert await async_setup_component( hass, @@ -1584,6 +1617,7 @@ async def test_invalid_values(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -1700,77 +1734,95 @@ async def test_invalid_values(hass, mqtt_mock): assert state.attributes.get("color_temp") == 100 -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG, MQTT_LIGHT_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, + MQTT_LIGHT_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one light per unique_id.""" config = { light.DOMAIN: [ @@ -1792,16 +1844,24 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, light.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config + ) -async def test_discovery_removal(hass, mqtt_mock, caplog): +async def test_discovery_removal(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered mqtt_json lights.""" data = '{ "name": "test",' ' "schema": "json",' ' "command_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, light.DOMAIN, data) + await help_test_discovery_removal( + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + data, + ) -async def test_discovery_update_light(hass, mqtt_mock, caplog): +async def test_discovery_update_light(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered light.""" config1 = { "name": "Beer", @@ -1816,11 +1876,18 @@ async def test_discovery_update_light(hass, mqtt_mock, caplog): "command_topic": "test_topic", } await help_test_discovery_update( - hass, mqtt_mock, caplog, light.DOMAIN, config1, config2 + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + config1, + config2, ) -async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_light( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered light.""" data1 = ( '{ "name": "Beer",' @@ -1832,12 +1899,17 @@ async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.light.schema_json.MqttLightJson.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, light.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -1847,57 +1919,80 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ' "command_topic": "test_topic" }' ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, light.DOMAIN, data1, data2 + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + data1, + data2, ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT light device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT light device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_with_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG, light.SERVICE_TURN_ON, @@ -1906,7 +2001,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) -async def test_max_mireds(hass, mqtt_mock): +async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config): """Test setting min_mireds and max_mireds.""" config = { light.DOMAIN: { @@ -1921,6 +2016,7 @@ async def test_max_mireds(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.attributes.get("min_mireds") == 153 @@ -1952,7 +2048,7 @@ async def test_max_mireds(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -1972,7 +2068,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -1986,11 +2082,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = light.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -2013,7 +2111,14 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value, init_payload + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, + init_payload, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN]) @@ -2028,7 +2133,7 @@ async def test_encoding_subscribable_topics( ] await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, config, diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index a88fc094f6d..0d4b95e9152 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -90,69 +90,53 @@ DEFAULT_CONFIG = { } -async def test_setup_fails(hass, mqtt_mock): +@pytest.mark.parametrize( + "test_config", + [ + ({"platform": "mqtt", "schema": "template", "name": "test"},), + ( + { + "platform": "mqtt", + "schema": "template", + "name": "test", + "command_topic": "test_topic", + }, + ), + ( + { + "platform": "mqtt", + "schema": "template", + "name": "test", + "command_topic": "test_topic", + "command_on_template": "on", + }, + ), + ( + { + "platform": "mqtt", + "schema": "template", + "name": "test", + "command_topic": "test_topic", + "command_off_template": "off", + }, + ), + ], +) +async def test_setup_fails(hass, mqtt_mock_entry_no_yaml_config, test_config): """Test that setup fails with missing required configuration items.""" - with assert_setup_component(0, light.DOMAIN): + with assert_setup_component(0, light.DOMAIN) as setup_config: assert await async_setup_component( hass, light.DOMAIN, - {light.DOMAIN: {"platform": "mqtt", "schema": "template", "name": "test"}}, - ) - await hass.async_block_till_done() - assert hass.states.get("light.test") is None - - with assert_setup_component(0, light.DOMAIN): - assert await async_setup_component( - hass, - light.DOMAIN, - { - light.DOMAIN: { - "platform": "mqtt", - "schema": "template", - "name": "test", - "command_topic": "test_topic", - } - }, - ) - await hass.async_block_till_done() - assert hass.states.get("light.test") is None - - with assert_setup_component(0, light.DOMAIN): - assert await async_setup_component( - hass, - light.DOMAIN, - { - light.DOMAIN: { - "platform": "mqtt", - "schema": "template", - "name": "test", - "command_topic": "test_topic", - "command_on_template": "on", - } - }, - ) - await hass.async_block_till_done() - assert hass.states.get("light.test") is None - - with assert_setup_component(0, light.DOMAIN): - assert await async_setup_component( - hass, - light.DOMAIN, - { - light.DOMAIN: { - "platform": "mqtt", - "schema": "template", - "name": "test", - "command_topic": "test_topic", - "command_off_template": "off", - } - }, + {light.DOMAIN: test_config}, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() + assert not setup_config[light.DOMAIN] assert hass.states.get("light.test") is None -async def test_rgb_light(hass, mqtt_mock): +async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): """Test RGB light flags brightness support.""" assert await async_setup_component( hass, @@ -172,6 +156,7 @@ async def test_rgb_light(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -184,7 +169,7 @@ async def test_rgb_light(hass, mqtt_mock): assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features -async def test_state_change_via_topic(hass, mqtt_mock): +async def test_state_change_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test state change via topic.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -210,6 +195,7 @@ async def test_state_change_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -240,7 +226,7 @@ async def test_state_change_via_topic(hass, mqtt_mock): async def test_state_brightness_color_effect_temp_white_change_via_topic( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test state, bri, color, effect, color temp, white val change.""" with assert_setup_component(1, light.DOMAIN): @@ -276,6 +262,7 @@ async def test_state_brightness_color_effect_temp_white_change_via_topic( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -340,7 +327,9 @@ async def test_state_brightness_color_effect_temp_white_change_via_topic( assert light_state.attributes.get("effect") == "rainbow" -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending of command in optimistic mode.""" fake_state = ha.State( "light.test", @@ -391,6 +380,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_ON @@ -495,7 +485,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): async def test_sending_mqtt_commands_non_optimistic_brightness_template( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test the sending of command in optimistic mode.""" with assert_setup_component(1, light.DOMAIN): @@ -532,6 +522,7 @@ async def test_sending_mqtt_commands_non_optimistic_brightness_template( }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -626,7 +617,7 @@ async def test_sending_mqtt_commands_non_optimistic_brightness_template( state = hass.states.get("light.test") -async def test_effect(hass, mqtt_mock): +async def test_effect(hass, mqtt_mock_entry_with_yaml_config): """Test effect sent over MQTT in optimistic mode.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -646,6 +637,7 @@ async def test_effect(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -678,7 +670,7 @@ async def test_effect(hass, mqtt_mock): assert state.attributes.get("effect") == "colorloop" -async def test_flash(hass, mqtt_mock): +async def test_flash(hass, mqtt_mock_entry_with_yaml_config): """Test flash sent over MQTT in optimistic mode.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -697,6 +689,7 @@ async def test_flash(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -726,7 +719,7 @@ async def test_flash(hass, mqtt_mock): assert state.state == STATE_ON -async def test_transition(hass, mqtt_mock): +async def test_transition(hass, mqtt_mock_entry_with_yaml_config): """Test for transition time being sent when included.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -745,6 +738,7 @@ async def test_transition(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -767,7 +761,7 @@ async def test_transition(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_invalid_values(hass, mqtt_mock): +async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config): """Test that invalid values are ignored.""" with assert_setup_component(1, light.DOMAIN): assert await async_setup_component( @@ -801,6 +795,7 @@ async def test_invalid_values(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.state == STATE_UNKNOWN @@ -867,77 +862,91 @@ async def test_invalid_values(hass, mqtt_mock): assert state.attributes.get("effect") == "rainbow" -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG, MQTT_LIGHT_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + DEFAULT_CONFIG, + MQTT_LIGHT_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one light per unique_id.""" config = { light.DOMAIN: [ @@ -961,10 +970,12 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, light.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config + ) -async def test_discovery_removal(hass, mqtt_mock, caplog): +async def test_discovery_removal(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered mqtt_json lights.""" data = ( '{ "name": "test",' @@ -973,10 +984,12 @@ async def test_discovery_removal(hass, mqtt_mock, caplog): ' "command_on_template": "on",' ' "command_off_template": "off"}' ) - await help_test_discovery_removal(hass, mqtt_mock, caplog, light.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, data + ) -async def test_discovery_update_light(hass, mqtt_mock, caplog): +async def test_discovery_update_light(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered light.""" config1 = { "name": "Beer", @@ -995,11 +1008,13 @@ async def test_discovery_update_light(hass, mqtt_mock, caplog): "command_off_template": "off", } await help_test_discovery_update( - hass, mqtt_mock, caplog, light.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_light( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered light.""" data1 = ( '{ "name": "Beer",' @@ -1013,12 +1028,17 @@ async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.light.schema_template.MqttLightTemplate.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, light.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + light.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -1030,53 +1050,53 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ' "command_off_template": "off"}' ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, light.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT light device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT light device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" config = { light.DOMAIN: { @@ -1090,11 +1110,15 @@ async def test_entity_debug_info_message(hass, mqtt_mock): } } await help_test_entity_debug_info_message( - hass, mqtt_mock, light.DOMAIN, config, light.SERVICE_TURN_ON + hass, + mqtt_mock_entry_no_yaml_config, + light.DOMAIN, + config, + light.SERVICE_TURN_ON, ) -async def test_max_mireds(hass, mqtt_mock): +async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config): """Test setting min_mireds and max_mireds.""" config = { light.DOMAIN: { @@ -1111,6 +1135,7 @@ async def test_max_mireds(hass, mqtt_mock): assert await async_setup_component(hass, light.DOMAIN, config) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("light.test") assert state.attributes.get("min_mireds") == 153 @@ -1142,7 +1167,7 @@ async def test_max_mireds(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -1162,7 +1187,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -1176,11 +1201,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = light.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -1197,14 +1224,21 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value, init_payload + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, + init_payload, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN]) config["state_template"] = "{{ value }}" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, config, diff --git a/tests/components/mqtt/test_lock.py b/tests/components/mqtt/test_lock.py index ef752ef8749..b48557efc8f 100644 --- a/tests/components/mqtt/test_lock.py +++ b/tests/components/mqtt/test_lock.py @@ -58,7 +58,7 @@ DEFAULT_CONFIG = { } -async def test_controlling_state_via_topic(hass, mqtt_mock): +async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -77,6 +77,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -94,7 +95,9 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): assert state.state is STATE_UNLOCKED -async def test_controlling_non_default_state_via_topic(hass, mqtt_mock): +async def test_controlling_non_default_state_via_topic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -113,6 +116,7 @@ async def test_controlling_non_default_state_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -129,7 +133,9 @@ async def test_controlling_non_default_state_via_topic(hass, mqtt_mock): assert state.state is STATE_UNLOCKED -async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): +async def test_controlling_state_via_topic_and_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via topic and JSON message.""" assert await async_setup_component( hass, @@ -149,6 +155,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -165,7 +172,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): async def test_controlling_non_default_state_via_topic_and_json_message( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test the controlling state via topic and JSON message.""" assert await async_setup_component( @@ -186,6 +193,7 @@ async def test_controlling_non_default_state_via_topic_and_json_message( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -201,7 +209,9 @@ async def test_controlling_non_default_state_via_topic_and_json_message( assert state.state is STATE_UNLOCKED -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test optimistic mode without state topic.""" assert await async_setup_component( hass, @@ -219,6 +229,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -245,7 +256,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_explicit_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test optimistic mode without state topic.""" assert await async_setup_component( hass, @@ -265,6 +278,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -291,7 +305,9 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock): assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_support_open_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_support_open_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test open function of the lock without state topic.""" assert await async_setup_component( hass, @@ -310,6 +326,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(hass, mqtt_mock }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -348,7 +365,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(hass, mqtt_mock async def test_sending_mqtt_commands_support_open_and_explicit_optimistic( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test open function of the lock without state topic.""" assert await async_setup_component( @@ -370,6 +387,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic( }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("lock.test") assert state.state is STATE_UNLOCKED @@ -407,77 +425,91 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic( assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG, MQTT_LOCK_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + LOCK_DOMAIN, + DEFAULT_CONFIG, + MQTT_LOCK_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_json(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_json( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one lock per unique_id.""" config = { LOCK_DOMAIN: [ @@ -497,16 +529,20 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, LOCK_DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, config + ) -async def test_discovery_removal_lock(hass, mqtt_mock, caplog): +async def test_discovery_removal_lock(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered lock.""" data = '{ "name": "test",' ' "command_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, LOCK_DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, data + ) -async def test_discovery_update_lock(hass, mqtt_mock, caplog): +async def test_discovery_update_lock(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered lock.""" config1 = { "name": "Beer", @@ -521,11 +557,13 @@ async def test_discovery_update_lock(hass, mqtt_mock, caplog): "availability_topic": "availability_topic2", } await help_test_discovery_update( - hass, mqtt_mock, caplog, LOCK_DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_lock(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_lock( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered lock.""" data1 = ( '{ "name": "Beer",' @@ -536,65 +574,72 @@ async def test_discovery_update_unchanged_lock(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.lock.MqttLock.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, LOCK_DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + LOCK_DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk",' ' "command_topic": "test_topic" }' - await help_test_discovery_broken(hass, mqtt_mock, caplog, LOCK_DOMAIN, data1, data2) + await help_test_discovery_broken( + hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, data1, data2 + ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT lock device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT lock device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG, SERVICE_LOCK, @@ -616,7 +661,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -630,7 +675,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -642,11 +687,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = LOCK_DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -663,12 +710,18 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG[LOCK_DOMAIN], diff --git a/tests/components/mqtt/test_number.py b/tests/components/mqtt/test_number.py index 4eb8fdec351..a49b6de198d 100644 --- a/tests/components/mqtt/test_number.py +++ b/tests/components/mqtt/test_number.py @@ -64,7 +64,7 @@ DEFAULT_CONFIG = { } -async def test_run_number_setup(hass, mqtt_mock): +async def test_run_number_setup(hass, mqtt_mock_entry_with_yaml_config): """Test that it fetches the given payload.""" topic = "test/number" await async_setup_component( @@ -82,6 +82,7 @@ async def test_run_number_setup(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, "10") @@ -108,7 +109,7 @@ async def test_run_number_setup(hass, mqtt_mock): assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "my unit" -async def test_value_template(hass, mqtt_mock): +async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): """Test that it fetches the given payload with a template.""" topic = "test/number" await async_setup_component( @@ -125,6 +126,7 @@ async def test_value_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, '{"val":10}') @@ -148,7 +150,7 @@ async def test_value_template(hass, mqtt_mock): assert state.state == "unknown" -async def test_run_number_service_optimistic(hass, mqtt_mock): +async def test_run_number_service_optimistic(hass, mqtt_mock_entry_with_yaml_config): """Test that set_value service works in optimistic mode.""" topic = "test/number" @@ -170,6 +172,7 @@ async def test_run_number_service_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("number.test_number") assert state.state == "3" @@ -215,7 +218,9 @@ async def test_run_number_service_optimistic(hass, mqtt_mock): assert state.state == "42.1" -async def test_run_number_service_optimistic_with_command_template(hass, mqtt_mock): +async def test_run_number_service_optimistic_with_command_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test that set_value service works in optimistic mode and with a command_template.""" topic = "test/number" @@ -238,6 +243,7 @@ async def test_run_number_service_optimistic_with_command_template(hass, mqtt_mo }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("number.test_number") assert state.state == "3" @@ -285,7 +291,7 @@ async def test_run_number_service_optimistic_with_command_template(hass, mqtt_mo assert state.state == "42.1" -async def test_run_number_service(hass, mqtt_mock): +async def test_run_number_service(hass, mqtt_mock_entry_with_yaml_config): """Test that set_value service works in non optimistic mode.""" cmd_topic = "test/number/set" state_topic = "test/number" @@ -303,6 +309,7 @@ async def test_run_number_service(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, state_topic, "32") state = hass.states.get("number.test_number") @@ -319,7 +326,9 @@ async def test_run_number_service(hass, mqtt_mock): assert state.state == "32" -async def test_run_number_service_with_command_template(hass, mqtt_mock): +async def test_run_number_service_with_command_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test that set_value service works in non optimistic mode and with a command_template.""" cmd_topic = "test/number/set" state_topic = "test/number" @@ -338,6 +347,7 @@ async def test_run_number_service_with_command_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, state_topic, "32") state = hass.states.get("number.test_number") @@ -356,77 +366,91 @@ async def test_run_number_service_with_command_template(hass, mqtt_mock): assert state.state == "32" -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG, MQTT_NUMBER_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + number.DOMAIN, + DEFAULT_CONFIG, + MQTT_NUMBER_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, number.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, number.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one number per unique_id.""" config = { number.DOMAIN: [ @@ -446,16 +470,20 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, number.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, config + ) -async def test_discovery_removal_number(hass, mqtt_mock, caplog): +async def test_discovery_removal_number(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered number.""" data = json.dumps(DEFAULT_CONFIG[number.DOMAIN]) - await help_test_discovery_removal(hass, mqtt_mock, caplog, number.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, data + ) -async def test_discovery_update_number(hass, mqtt_mock, caplog): +async def test_discovery_update_number(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered number.""" config1 = { "name": "Beer", @@ -469,11 +497,13 @@ async def test_discovery_update_number(hass, mqtt_mock, caplog): } await help_test_discovery_update( - hass, mqtt_mock, caplog, number.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_number(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_number( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered number.""" data1 = ( '{ "name": "Beer", "state_topic": "test-topic", "command_topic": "test-topic"}' @@ -482,12 +512,17 @@ async def test_discovery_update_unchanged_number(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.number.MqttNumber.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, number.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + number.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -495,57 +530,57 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, number.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT number device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT number device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, number.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, number.DOMAIN, DEFAULT_CONFIG, SERVICE_SET_VALUE, @@ -555,7 +590,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) -async def test_min_max_step_attributes(hass, mqtt_mock): +async def test_min_max_step_attributes(hass, mqtt_mock_entry_with_yaml_config): """Test min/max/step attributes.""" topic = "test/number" await async_setup_component( @@ -574,6 +609,7 @@ async def test_min_max_step_attributes(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("number.test_number") assert state.attributes.get(ATTR_MIN) == 5 @@ -581,7 +617,7 @@ async def test_min_max_step_attributes(hass, mqtt_mock): assert state.attributes.get(ATTR_STEP) == 20 -async def test_invalid_min_max_attributes(hass, caplog, mqtt_mock): +async def test_invalid_min_max_attributes(hass, caplog, mqtt_mock_entry_no_yaml_config): """Test invalid min/max attributes.""" topic = "test/number" await async_setup_component( @@ -599,11 +635,14 @@ async def test_invalid_min_max_attributes(hass, caplog, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() assert f"'{CONF_MAX}' must be > '{CONF_MIN}'" in caplog.text -async def test_mqtt_payload_not_a_number_warning(hass, caplog, mqtt_mock): +async def test_mqtt_payload_not_a_number_warning( + hass, caplog, mqtt_mock_entry_with_yaml_config +): """Test warning for MQTT payload which is not a number.""" topic = "test/number" await async_setup_component( @@ -619,6 +658,7 @@ async def test_mqtt_payload_not_a_number_warning(hass, caplog, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, "not_a_number") @@ -627,7 +667,9 @@ async def test_mqtt_payload_not_a_number_warning(hass, caplog, mqtt_mock): assert "Payload 'not_a_number' is not a Number" in caplog.text -async def test_mqtt_payload_out_of_range_error(hass, caplog, mqtt_mock): +async def test_mqtt_payload_out_of_range_error( + hass, caplog, mqtt_mock_entry_with_yaml_config +): """Test error when MQTT payload is out of min/max range.""" topic = "test/number" await async_setup_component( @@ -645,6 +687,7 @@ async def test_mqtt_payload_out_of_range_error(hass, caplog, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, "115.5") @@ -669,7 +712,7 @@ async def test_mqtt_payload_out_of_range_error(hass, caplog, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -683,7 +726,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -695,11 +738,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = number.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -717,12 +762,18 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, "number", DEFAULT_CONFIG["number"], diff --git a/tests/components/mqtt/test_scene.py b/tests/components/mqtt/test_scene.py index 15bbd3964e6..eb5cb94df2d 100644 --- a/tests/components/mqtt/test_scene.py +++ b/tests/components/mqtt/test_scene.py @@ -34,7 +34,7 @@ DEFAULT_CONFIG = { } -async def test_sending_mqtt_commands(hass, mqtt_mock): +async def test_sending_mqtt_commands(hass, mqtt_mock_entry_with_yaml_config): """Test the sending MQTT commands.""" fake_state = ha.State("scene.test", STATE_UNKNOWN) @@ -55,6 +55,7 @@ async def test_sending_mqtt_commands(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("scene.test") assert state.state == STATE_UNKNOWN @@ -67,21 +68,23 @@ async def test_sending_mqtt_commands(hass, mqtt_mock): ) -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, scene.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, scene.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, scene.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, scene.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" config = { scene.DOMAIN: { @@ -93,11 +96,17 @@ async def test_default_availability_payload(hass, mqtt_mock): } await help_test_default_availability_payload( - hass, mqtt_mock, scene.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + scene.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" config = { scene.DOMAIN: { @@ -109,11 +118,17 @@ async def test_custom_availability_payload(hass, mqtt_mock): } await help_test_custom_availability_payload( - hass, mqtt_mock, scene.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + scene.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one scene per unique_id.""" config = { scene.DOMAIN: [ @@ -131,16 +146,20 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, scene.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, scene.DOMAIN, config + ) -async def test_discovery_removal_scene(hass, mqtt_mock, caplog): +async def test_discovery_removal_scene(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered scene.""" data = '{ "name": "test",' ' "command_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, scene.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, scene.DOMAIN, data + ) -async def test_discovery_update_payload(hass, mqtt_mock, caplog): +async def test_discovery_update_payload(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered scene.""" config1 = copy.deepcopy(DEFAULT_CONFIG[scene.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[scene.DOMAIN]) @@ -151,7 +170,7 @@ async def test_discovery_update_payload(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, scene.DOMAIN, config1, @@ -159,32 +178,41 @@ async def test_discovery_update_payload(hass, mqtt_mock, caplog): ) -async def test_discovery_update_unchanged_scene(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_scene( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered scene.""" data1 = '{ "name": "Beer",' ' "command_topic": "test_topic" }' with patch( "homeassistant.components.mqtt.scene.MqttScene.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, scene.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + scene.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk",' ' "command_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, scene.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, scene.DOMAIN, data1, data2 ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = scene.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): diff --git a/tests/components/mqtt/test_select.py b/tests/components/mqtt/test_select.py index cf5abf55854..888dd301018 100644 --- a/tests/components/mqtt/test_select.py +++ b/tests/components/mqtt/test_select.py @@ -59,7 +59,7 @@ DEFAULT_CONFIG = { } -async def test_run_select_setup(hass, mqtt_mock): +async def test_run_select_setup(hass, mqtt_mock_entry_with_yaml_config): """Test that it fetches the given payload.""" topic = "test/select" await async_setup_component( @@ -76,6 +76,7 @@ async def test_run_select_setup(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, "milk") @@ -92,7 +93,7 @@ async def test_run_select_setup(hass, mqtt_mock): assert state.state == "beer" -async def test_value_template(hass, mqtt_mock): +async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): """Test that it fetches the given payload with a template.""" topic = "test/select" await async_setup_component( @@ -110,6 +111,7 @@ async def test_value_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, '{"val":"milk"}') @@ -133,7 +135,7 @@ async def test_value_template(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_run_select_service_optimistic(hass, mqtt_mock): +async def test_run_select_service_optimistic(hass, mqtt_mock_entry_with_yaml_config): """Test that set_value service works in optimistic mode.""" topic = "test/select" @@ -156,6 +158,7 @@ async def test_run_select_service_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("select.test_select") assert state.state == "milk" @@ -174,7 +177,9 @@ async def test_run_select_service_optimistic(hass, mqtt_mock): assert state.state == "beer" -async def test_run_select_service_optimistic_with_command_template(hass, mqtt_mock): +async def test_run_select_service_optimistic_with_command_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test that set_value service works in optimistic mode and with a command_template.""" topic = "test/select" @@ -198,6 +203,7 @@ async def test_run_select_service_optimistic_with_command_template(hass, mqtt_mo }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("select.test_select") assert state.state == "milk" @@ -218,7 +224,7 @@ async def test_run_select_service_optimistic_with_command_template(hass, mqtt_mo assert state.state == "beer" -async def test_run_select_service(hass, mqtt_mock): +async def test_run_select_service(hass, mqtt_mock_entry_with_yaml_config): """Test that set_value service works in non optimistic mode.""" cmd_topic = "test/select/set" state_topic = "test/select" @@ -237,6 +243,7 @@ async def test_run_select_service(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, state_topic, "beer") state = hass.states.get("select.test_select") @@ -253,7 +260,9 @@ async def test_run_select_service(hass, mqtt_mock): assert state.state == "beer" -async def test_run_select_service_with_command_template(hass, mqtt_mock): +async def test_run_select_service_with_command_template( + hass, mqtt_mock_entry_with_yaml_config +): """Test that set_value service works in non optimistic mode and with a command_template.""" cmd_topic = "test/select/set" state_topic = "test/select" @@ -273,6 +282,7 @@ async def test_run_select_service_with_command_template(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, state_topic, "beer") state = hass.states.get("select.test_select") @@ -289,77 +299,91 @@ async def test_run_select_service_with_command_template(hass, mqtt_mock): ) -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG, MQTT_SELECT_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + select.DOMAIN, + DEFAULT_CONFIG, + MQTT_SELECT_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, select.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, select.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one select per unique_id.""" config = { select.DOMAIN: [ @@ -381,16 +405,20 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, select.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, config + ) -async def test_discovery_removal_select(hass, mqtt_mock, caplog): +async def test_discovery_removal_select(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered select.""" data = json.dumps(DEFAULT_CONFIG[select.DOMAIN]) - await help_test_discovery_removal(hass, mqtt_mock, caplog, select.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, data + ) -async def test_discovery_update_select(hass, mqtt_mock, caplog): +async def test_discovery_update_select(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered select.""" config1 = { "name": "Beer", @@ -406,79 +434,86 @@ async def test_discovery_update_select(hass, mqtt_mock, caplog): } await help_test_discovery_update( - hass, mqtt_mock, caplog, select.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_select(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_select( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered select.""" data1 = '{ "name": "Beer", "state_topic": "test-topic", "command_topic": "test-topic", "options": ["milk", "beer"]}' with patch( "homeassistant.components.mqtt.select.MqttSelect.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, select.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + select.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = '{ "name": "Milk", "state_topic": "test-topic", "command_topic": "test-topic", "options": ["milk", "beer"]}' await help_test_discovery_broken( - hass, mqtt_mock, caplog, select.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT select device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT select device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, select.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, select.DOMAIN, DEFAULT_CONFIG, select.SERVICE_SELECT_OPTION, @@ -489,7 +524,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): @pytest.mark.parametrize("options", [["milk", "beer"], ["milk"], []]) -async def test_options_attributes(hass, mqtt_mock, options): +async def test_options_attributes(hass, mqtt_mock_entry_with_yaml_config, options): """Test options attribute.""" topic = "test/select" await async_setup_component( @@ -506,12 +541,15 @@ async def test_options_attributes(hass, mqtt_mock, options): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("select.test_select") assert state.attributes.get(ATTR_OPTIONS) == options -async def test_mqtt_payload_not_an_option_warning(hass, caplog, mqtt_mock): +async def test_mqtt_payload_not_an_option_warning( + hass, caplog, mqtt_mock_entry_with_yaml_config +): """Test warning for MQTT payload which is not a valid option.""" topic = "test/select" await async_setup_component( @@ -528,6 +566,7 @@ async def test_mqtt_payload_not_an_option_warning(hass, caplog, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, topic, "öl") @@ -552,7 +591,14 @@ async def test_mqtt_payload_not_an_option_warning(hass, caplog, mqtt_mock): ], ) async def test_publishing_with_custom_encoding( - hass, mqtt_mock, caplog, service, topic, parameters, payload, template + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + service, + topic, + parameters, + payload, + template, ): """Test publishing MQTT payload with different encoding.""" domain = select.DOMAIN @@ -561,7 +607,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -573,11 +619,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = select.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -595,14 +643,20 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" config = copy.deepcopy(DEFAULT_CONFIG["select"]) config["options"] = ["milk", "beer"] await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, "select", config, diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index befb5785cdd..7081ae45993 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -72,7 +72,9 @@ DEFAULT_CONFIG = { } -async def test_setting_sensor_value_via_mqtt_message(hass, mqtt_mock): +async def test_setting_sensor_value_via_mqtt_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the value via MQTT.""" assert await async_setup_component( hass, @@ -87,6 +89,7 @@ async def test_setting_sensor_value_via_mqtt_message(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "test-topic", "100") state = hass.states.get("sensor.test") @@ -122,7 +125,13 @@ async def test_setting_sensor_value_via_mqtt_message(hass, mqtt_mock): ], ) async def test_setting_sensor_native_value_handling_via_mqtt_message( - hass, mqtt_mock, caplog, device_class, native_value, state_value, log + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + device_class, + native_value, + state_value, + log, ): """Test the setting of the value via MQTT.""" assert await async_setup_component( @@ -138,6 +147,7 @@ async def test_setting_sensor_native_value_handling_via_mqtt_message( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "test-topic", native_value) state = hass.states.get("sensor.test") @@ -147,7 +157,9 @@ async def test_setting_sensor_native_value_handling_via_mqtt_message( assert log == ("Invalid state message" in caplog.text) -async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, caplog): +async def test_setting_sensor_value_expires_availability_topic( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the expiration of the value.""" assert await async_setup_component( hass, @@ -164,6 +176,7 @@ async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("sensor.test") assert state.state == STATE_UNAVAILABLE @@ -174,10 +187,12 @@ async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, state = hass.states.get("sensor.test") assert state.state == STATE_UNAVAILABLE - await expires_helper(hass, mqtt_mock, caplog) + await expires_helper(hass, caplog) -async def test_setting_sensor_value_expires(hass, mqtt_mock, caplog): +async def test_setting_sensor_value_expires( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the expiration of the value.""" assert await async_setup_component( hass, @@ -194,15 +209,16 @@ async def test_setting_sensor_value_expires(hass, mqtt_mock, caplog): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() # State should be unavailable since expire_after is defined and > 0 state = hass.states.get("sensor.test") assert state.state == STATE_UNAVAILABLE - await expires_helper(hass, mqtt_mock, caplog) + await expires_helper(hass, caplog) -async def expires_helper(hass, mqtt_mock, caplog): +async def expires_helper(hass, caplog): """Run the basic expiry code.""" realnow = dt_util.utcnow() now = datetime(realnow.year + 1, 1, 1, 1, tzinfo=dt_util.UTC) @@ -253,7 +269,9 @@ async def expires_helper(hass, mqtt_mock, caplog): assert state.state == STATE_UNAVAILABLE -async def test_setting_sensor_value_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_sensor_value_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the value via MQTT with JSON payload.""" assert await async_setup_component( hass, @@ -269,6 +287,7 @@ async def test_setting_sensor_value_via_mqtt_json_message(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "test-topic", '{ "val": "100" }') state = hass.states.get("sensor.test") @@ -277,7 +296,7 @@ async def test_setting_sensor_value_via_mqtt_json_message(hass, mqtt_mock): async def test_setting_sensor_value_via_mqtt_json_message_and_default_current_state( - hass, mqtt_mock + hass, mqtt_mock_entry_with_yaml_config ): """Test the setting of the value via MQTT with fall back to current state.""" assert await async_setup_component( @@ -294,6 +313,7 @@ async def test_setting_sensor_value_via_mqtt_json_message_and_default_current_st }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message( hass, "test-topic", '{ "val": "valcontent", "par": "parcontent" }' @@ -308,7 +328,9 @@ async def test_setting_sensor_value_via_mqtt_json_message_and_default_current_st assert state.state == "valcontent-parcontent" -async def test_setting_sensor_last_reset_via_mqtt_message(hass, mqtt_mock, caplog): +async def test_setting_sensor_last_reset_via_mqtt_message( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the setting of the last_reset property via MQTT.""" assert await async_setup_component( hass, @@ -325,6 +347,7 @@ async def test_setting_sensor_last_reset_via_mqtt_message(hass, mqtt_mock, caplo }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "last-reset-topic", "2020-01-02 08:11:00") state = hass.states.get("sensor.test") @@ -338,7 +361,7 @@ async def test_setting_sensor_last_reset_via_mqtt_message(hass, mqtt_mock, caplo @pytest.mark.parametrize("datestring", ["2020-21-02 08:11:00", "Hello there!"]) async def test_setting_sensor_bad_last_reset_via_mqtt_message( - hass, caplog, datestring, mqtt_mock + hass, caplog, datestring, mqtt_mock_entry_with_yaml_config ): """Test the setting of the last_reset property via MQTT.""" assert await async_setup_component( @@ -356,6 +379,7 @@ async def test_setting_sensor_bad_last_reset_via_mqtt_message( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "last-reset-topic", datestring) state = hass.states.get("sensor.test") @@ -364,7 +388,7 @@ async def test_setting_sensor_bad_last_reset_via_mqtt_message( async def test_setting_sensor_empty_last_reset_via_mqtt_message( - hass, caplog, mqtt_mock + hass, caplog, mqtt_mock_entry_with_yaml_config ): """Test the setting of the last_reset property via MQTT.""" assert await async_setup_component( @@ -382,6 +406,7 @@ async def test_setting_sensor_empty_last_reset_via_mqtt_message( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "last-reset-topic", "") state = hass.states.get("sensor.test") @@ -389,7 +414,9 @@ async def test_setting_sensor_empty_last_reset_via_mqtt_message( assert "Ignoring empty last_reset message" in caplog.text -async def test_setting_sensor_last_reset_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_sensor_last_reset_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of the value via MQTT with JSON payload.""" assert await async_setup_component( hass, @@ -407,6 +434,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message( hass, "last-reset-topic", '{ "last_reset": "2020-01-02 08:11:00" }' @@ -417,7 +445,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message(hass, mqtt_mock): @pytest.mark.parametrize("extra", [{}, {"last_reset_topic": "test-topic"}]) async def test_setting_sensor_last_reset_via_mqtt_json_message_2( - hass, mqtt_mock, caplog, extra + hass, mqtt_mock_entry_with_yaml_config, caplog, extra ): """Test the setting of the value via MQTT with JSON payload.""" assert await async_setup_component( @@ -439,6 +467,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message_2( }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message( hass, @@ -455,7 +484,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message_2( ) -async def test_force_update_disabled(hass, mqtt_mock): +async def test_force_update_disabled(hass, mqtt_mock_entry_with_yaml_config): """Test force update option.""" assert await async_setup_component( hass, @@ -470,6 +499,7 @@ async def test_force_update_disabled(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() events = [] @@ -488,7 +518,7 @@ async def test_force_update_disabled(hass, mqtt_mock): assert len(events) == 1 -async def test_force_update_enabled(hass, mqtt_mock): +async def test_force_update_enabled(hass, mqtt_mock_entry_with_yaml_config): """Test force update option.""" assert await async_setup_component( hass, @@ -504,6 +534,7 @@ async def test_force_update_enabled(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() events = [] @@ -522,70 +553,80 @@ async def test_force_update_enabled(hass, mqtt_mock): assert len(events) == 2 -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_list_payload(hass, mqtt_mock): +async def test_default_availability_list_payload( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability by default payload with defined topic.""" await help_test_default_availability_list_payload( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_list_payload_all(hass, mqtt_mock): +async def test_default_availability_list_payload_all( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability by default payload with defined topic.""" await help_test_default_availability_list_payload_all( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_list_payload_any(hass, mqtt_mock): +async def test_default_availability_list_payload_any( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability by default payload with defined topic.""" await help_test_default_availability_list_payload_any( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_list_single(hass, mqtt_mock, caplog): +async def test_default_availability_list_single( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test availability list and availability_topic are mutually exclusive.""" await help_test_default_availability_list_single( - hass, mqtt_mock, caplog, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_availability(hass, mqtt_mock): +async def test_discovery_update_availability(hass, mqtt_mock_entry_no_yaml_config): """Test availability discovery update.""" await help_test_discovery_update_availability( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_invalid_device_class(hass, mqtt_mock): +async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config): """Test device_class option with invalid value.""" assert await async_setup_component( hass, @@ -600,12 +641,13 @@ async def test_invalid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("sensor.test") assert state is None -async def test_valid_device_class(hass, mqtt_mock): +async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config): """Test device_class option with valid values.""" assert await async_setup_component( hass, @@ -623,6 +665,7 @@ async def test_valid_device_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("sensor.test_1") assert state.attributes["device_class"] == "temperature" @@ -630,7 +673,7 @@ async def test_valid_device_class(hass, mqtt_mock): assert "device_class" not in state.attributes -async def test_invalid_state_class(hass, mqtt_mock): +async def test_invalid_state_class(hass, mqtt_mock_entry_no_yaml_config): """Test state_class option with invalid value.""" assert await async_setup_component( hass, @@ -645,12 +688,13 @@ async def test_invalid_state_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("sensor.test") assert state is None -async def test_valid_state_class(hass, mqtt_mock): +async def test_valid_state_class(hass, mqtt_mock_entry_with_yaml_config): """Test state_class option with valid values.""" assert await async_setup_component( hass, @@ -668,6 +712,7 @@ async def test_valid_state_class(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("sensor.test_1") assert state.attributes["state_class"] == "measurement" @@ -675,49 +720,61 @@ async def test_valid_state_class(hass, mqtt_mock): assert "state_class" not in state.attributes -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG, MQTT_SENSOR_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + sensor.DOMAIN, + DEFAULT_CONFIG, + MQTT_SENSOR_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one sensor per unique_id.""" config = { sensor.DOMAIN: [ @@ -735,16 +792,22 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, sensor.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, config + ) -async def test_discovery_removal_sensor(hass, mqtt_mock, caplog): +async def test_discovery_removal_sensor(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered sensor.""" data = '{ "name": "test", "state_topic": "test_topic" }' - await help_test_discovery_removal(hass, mqtt_mock, caplog, sensor.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, data + ) -async def test_discovery_update_sensor_topic_template(hass, mqtt_mock, caplog): +async def test_discovery_update_sensor_topic_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered sensor.""" config = {"name": "test", "state_topic": "test_topic"} config1 = copy.deepcopy(config) @@ -767,7 +830,7 @@ async def test_discovery_update_sensor_topic_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, config1, @@ -777,7 +840,9 @@ async def test_discovery_update_sensor_topic_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_sensor_template(hass, mqtt_mock, caplog): +async def test_discovery_update_sensor_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered sensor.""" config = {"name": "test", "state_topic": "test_topic"} config1 = copy.deepcopy(config) @@ -798,7 +863,7 @@ async def test_discovery_update_sensor_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, config1, @@ -808,71 +873,79 @@ async def test_discovery_update_sensor_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_unchanged_sensor(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_sensor( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered sensor.""" data1 = '{ "name": "Beer", "state_topic": "test_topic" }' with patch( "homeassistant.components.mqtt.sensor.MqttSensor.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, sensor.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + sensor.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer", "state_topic": "test_topic#" }' data2 = '{ "name": "Milk", "state_topic": "test_topic" }' await help_test_discovery_broken( - hass, mqtt_mock, caplog, sensor.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_hub(hass, mqtt_mock): +async def test_entity_device_info_with_hub(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor device registry integration.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) hub = registry.async_get_or_create( config_entry_id="123", @@ -899,53 +972,57 @@ async def test_entity_device_info_with_hub(hass, mqtt_mock): assert device.via_device_id == hub.id -async def test_entity_debug_info(hass, mqtt_mock): +async def test_entity_debug_info(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor debug info.""" - await help_test_entity_debug_info(hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG) + await help_test_entity_debug_info( + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG + ) -async def test_entity_debug_info_max_messages(hass, mqtt_mock): +async def test_entity_debug_info_max_messages(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor debug info.""" await help_test_entity_debug_info_max_messages( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG, None + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG, None ) -async def test_entity_debug_info_remove(hass, mqtt_mock): +async def test_entity_debug_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor debug info.""" await help_test_entity_debug_info_remove( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_update_entity_id(hass, mqtt_mock): +async def test_entity_debug_info_update_entity_id(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT sensor debug info.""" await help_test_entity_debug_info_update_entity_id( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_disabled_by_default(hass, mqtt_mock): +async def test_entity_disabled_by_default(hass, mqtt_mock_entry_no_yaml_config): """Test entity disabled by default.""" await help_test_entity_disabled_by_default( - hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG ) @pytest.mark.no_fail_on_log_exception -async def test_entity_category(hass, mqtt_mock): +async def test_entity_category(hass, mqtt_mock_entry_no_yaml_config): """Test entity category.""" - await help_test_entity_category(hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG) + await help_test_entity_category( + hass, mqtt_mock_entry_no_yaml_config, sensor.DOMAIN, DEFAULT_CONFIG + ) -async def test_value_template_with_entity_id(hass, mqtt_mock): +async def test_value_template_with_entity_id(hass, mqtt_mock_entry_with_yaml_config): """Test the access to attributes in value_template via the entity_id.""" assert await async_setup_component( hass, @@ -966,6 +1043,7 @@ async def test_value_template_with_entity_id(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "test-topic", "100") state = hass.states.get("sensor.test") @@ -973,11 +1051,13 @@ async def test_value_template_with_entity_id(hass, mqtt_mock): assert state.state == "101" -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = sensor.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -988,7 +1068,7 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): async def test_cleanup_triggers_and_restoring_state( - hass, mqtt_mock, caplog, tmp_path, freezer + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, freezer ): """Test cleanup old triggers at reloading and restoring the state.""" domain = sensor.DOMAIN @@ -1014,6 +1094,7 @@ async def test_cleanup_triggers_and_restoring_state( {domain: [config1, config2]}, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "test-topic1", "100") state = hass.states.get("sensor.test1") assert state.state == "38" # 100 °F -> 38 °C @@ -1025,7 +1106,7 @@ async def test_cleanup_triggers_and_restoring_state( freezer.move_to("2022-02-02 12:01:10+01:00") await help_test_reload_with_config( - hass, caplog, tmp_path, domain, [config1, config2] + hass, caplog, tmp_path, {domain: [config1, config2]} ) await hass.async_block_till_done() @@ -1053,7 +1134,7 @@ async def test_cleanup_triggers_and_restoring_state( async def test_skip_restoring_state_with_over_due_expire_trigger( - hass, mqtt_mock, caplog, freezer + hass, mqtt_mock_entry_with_yaml_config, caplog, freezer ): """Test restoring a state with over due expire timer.""" @@ -1081,6 +1162,7 @@ async def test_skip_restoring_state_with_over_due_expire_trigger( ): assert await async_setup_component(hass, domain, {domain: config3}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() assert "Skip state recovery after reload for sensor.test3" in caplog.text @@ -1092,12 +1174,18 @@ async def test_skip_restoring_state_with_over_due_expire_trigger( ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG[sensor.DOMAIN], diff --git a/tests/components/mqtt/test_siren.py b/tests/components/mqtt/test_siren.py index 197ed34b7e4..2db2060c133 100644 --- a/tests/components/mqtt/test_siren.py +++ b/tests/components/mqtt/test_siren.py @@ -70,7 +70,7 @@ async def async_turn_off(hass, entity_id=ENTITY_MATCH_ALL) -> None: await hass.services.async_call(siren.DOMAIN, SERVICE_TURN_OFF, data, blocking=True) -async def test_controlling_state_via_topic(hass, mqtt_mock): +async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -87,6 +87,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("siren.test") assert state.state == STATE_UNKNOWN @@ -103,7 +104,9 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending MQTT commands in optimistic mode.""" assert await async_setup_component( hass, @@ -120,6 +123,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("siren.test") assert state.state == STATE_OFF @@ -143,7 +147,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, caplog): +async def test_controlling_state_via_topic_and_json_message( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test the controlling state via topic and JSON message.""" assert await async_setup_component( hass, @@ -161,6 +167,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("siren.test") assert state.state == STATE_UNKNOWN @@ -181,7 +188,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap async def test_controlling_state_and_attributes_with_json_message_without_template( - hass, mqtt_mock, caplog + hass, mqtt_mock_entry_with_yaml_config, caplog ): """Test the controlling state via topic and JSON message without a value template.""" assert await async_setup_component( @@ -200,6 +207,7 @@ async def test_controlling_state_and_attributes_with_json_message_without_templa }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("siren.test") assert state.state == STATE_UNKNOWN @@ -262,7 +270,9 @@ async def test_controlling_state_and_attributes_with_json_message_without_templa ) -async def test_filtering_not_supported_attributes_optimistic(hass, mqtt_mock): +async def test_filtering_not_supported_attributes_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting attributes with support flags optimistic.""" config = { "platform": "mqtt", @@ -285,6 +295,7 @@ async def test_filtering_not_supported_attributes_optimistic(hass, mqtt_mock): {siren.DOMAIN: [config1, config2, config3]}, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state1 = hass.states.get("siren.test1") assert state1.state == STATE_OFF @@ -345,7 +356,9 @@ async def test_filtering_not_supported_attributes_optimistic(hass, mqtt_mock): assert state3.attributes.get(siren.ATTR_VOLUME_LEVEL) == 0.88 -async def test_filtering_not_supported_attributes_via_state(hass, mqtt_mock): +async def test_filtering_not_supported_attributes_via_state( + hass, mqtt_mock_entry_with_yaml_config +): """Test setting attributes with support flags via state.""" config = { "platform": "mqtt", @@ -371,6 +384,7 @@ async def test_filtering_not_supported_attributes_via_state(hass, mqtt_mock): {siren.DOMAIN: [config1, config2, config3]}, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state1 = hass.states.get("siren.test1") assert state1.state == STATE_UNKNOWN @@ -422,21 +436,23 @@ async def test_filtering_not_supported_attributes_via_state(hass, mqtt_mock): assert state3.attributes.get(siren.ATTR_VOLUME_LEVEL) == 0.88 -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" config = { siren.DOMAIN: { @@ -450,11 +466,17 @@ async def test_default_availability_payload(hass, mqtt_mock): } await help_test_default_availability_payload( - hass, mqtt_mock, siren.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + siren.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" config = { siren.DOMAIN: { @@ -468,11 +490,17 @@ async def test_custom_availability_payload(hass, mqtt_mock): } await help_test_custom_availability_payload( - hass, mqtt_mock, siren.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + siren.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_custom_state_payload(hass, mqtt_mock): +async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config): """Test the state payload.""" assert await async_setup_component( hass, @@ -491,6 +519,7 @@ async def test_custom_state_payload(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("siren.test") assert state.state == STATE_UNKNOWN @@ -507,49 +536,57 @@ async def test_custom_state_payload(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG, {} + hass, mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG, {} ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one siren per unique_id.""" config = { siren.DOMAIN: [ @@ -569,20 +606,26 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, siren.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, config + ) -async def test_discovery_removal_siren(hass, mqtt_mock, caplog): +async def test_discovery_removal_siren(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered siren.""" data = ( '{ "name": "test",' ' "state_topic": "test_topic",' ' "command_topic": "test_topic" }' ) - await help_test_discovery_removal(hass, mqtt_mock, caplog, siren.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, siren.DOMAIN, data + ) -async def test_discovery_update_siren_topic_template(hass, mqtt_mock, caplog): +async def test_discovery_update_siren_topic_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered siren.""" config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) @@ -607,7 +650,7 @@ async def test_discovery_update_siren_topic_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, siren.DOMAIN, config1, @@ -617,7 +660,9 @@ async def test_discovery_update_siren_topic_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_siren_template(hass, mqtt_mock, caplog): +async def test_discovery_update_siren_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered siren.""" config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) @@ -640,7 +685,7 @@ async def test_discovery_update_siren_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, siren.DOMAIN, config1, @@ -650,7 +695,7 @@ async def test_discovery_update_siren_template(hass, mqtt_mock, caplog): ) -async def test_command_templates(hass, mqtt_mock, caplog): +async def test_command_templates(hass, mqtt_mock_entry_with_yaml_config, caplog): """Test siren with command templates optimistic.""" config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config1["name"] = "Beer" @@ -669,6 +714,7 @@ async def test_command_templates(hass, mqtt_mock, caplog): {siren.DOMAIN: [config1, config2]}, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state1 = hass.states.get("siren.beer") assert state1.state == STATE_OFF @@ -729,7 +775,9 @@ async def test_command_templates(hass, mqtt_mock, caplog): mqtt_mock.reset_mock() -async def test_discovery_update_unchanged_siren(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_siren( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered siren.""" data1 = ( '{ "name": "Beer",' @@ -741,12 +789,17 @@ async def test_discovery_update_unchanged_siren(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.siren.MqttSiren.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, siren.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + siren.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -755,57 +808,57 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ' "command_topic": "test_topic" }' ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, siren.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, siren.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT siren device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT siren device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, siren.DOMAIN, DEFAULT_CONFIG, siren.SERVICE_TURN_ON, @@ -834,7 +887,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -849,7 +902,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -861,11 +914,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = siren.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -882,12 +937,18 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG[siren.DOMAIN], diff --git a/tests/components/mqtt/test_state_vacuum.py b/tests/components/mqtt/test_state_vacuum.py index 3f752f1b528..f20a881dda1 100644 --- a/tests/components/mqtt/test_state_vacuum.py +++ b/tests/components/mqtt/test_state_vacuum.py @@ -6,7 +6,7 @@ from unittest.mock import patch import pytest from homeassistant.components import vacuum -from homeassistant.components.mqtt import CONF_COMMAND_TOPIC, CONF_STATE_TOPIC +from homeassistant.components.mqtt.const import CONF_COMMAND_TOPIC, CONF_STATE_TOPIC from homeassistant.components.mqtt.vacuum import CONF_SCHEMA, schema_state as mqttvacuum from homeassistant.components.mqtt.vacuum.const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from homeassistant.components.mqtt.vacuum.schema import services_to_strings @@ -87,12 +87,13 @@ DEFAULT_CONFIG_2 = { } -async def test_default_supported_features(hass, mqtt_mock): +async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config): """Test that the correct supported features.""" assert await async_setup_component( hass, vacuum.DOMAIN, {vacuum.DOMAIN: DEFAULT_CONFIG} ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() entity = hass.states.get("vacuum.mqtttest") entity_features = entity.attributes.get(mqttvacuum.CONF_SUPPORTED_FEATURES, 0) assert sorted(services_to_strings(entity_features, SERVICE_TO_STRING)) == sorted( @@ -100,7 +101,7 @@ async def test_default_supported_features(hass, mqtt_mock): ) -async def test_all_commands(hass, mqtt_mock): +async def test_all_commands(hass, mqtt_mock_entry_with_yaml_config): """Test simple commands send to the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -109,6 +110,7 @@ async def test_all_commands(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( DOMAIN, SERVICE_START, {"entity_id": ENTITY_MATCH_ALL}, blocking=True @@ -171,7 +173,9 @@ async def test_all_commands(hass, mqtt_mock): } -async def test_commands_without_supported_features(hass, mqtt_mock): +async def test_commands_without_supported_features( + hass, mqtt_mock_entry_with_yaml_config +): """Test commands which are not supported by the vacuum.""" config = deepcopy(DEFAULT_CONFIG) services = mqttvacuum.STRING_TO_SERVICE["status"] @@ -181,6 +185,7 @@ async def test_commands_without_supported_features(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() await hass.services.async_call( DOMAIN, SERVICE_START, {"entity_id": ENTITY_MATCH_ALL}, blocking=True @@ -228,7 +233,7 @@ async def test_commands_without_supported_features(hass, mqtt_mock): mqtt_mock.async_publish.assert_not_called() -async def test_status(hass, mqtt_mock): +async def test_status(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -237,6 +242,7 @@ async def test_status(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state.state == STATE_UNKNOWN @@ -272,7 +278,7 @@ async def test_status(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_no_fan_vacuum(hass, mqtt_mock): +async def test_no_fan_vacuum(hass, mqtt_mock_entry_with_yaml_config): """Test status updates from the vacuum when fan is not supported.""" config = deepcopy(DEFAULT_CONFIG) del config[mqttvacuum.CONF_FAN_SPEED_LIST] @@ -282,6 +288,7 @@ async def test_no_fan_vacuum(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() message = """{ "battery_level": 54, @@ -323,7 +330,7 @@ async def test_no_fan_vacuum(hass, mqtt_mock): @pytest.mark.no_fail_on_log_exception -async def test_status_invalid_json(hass, mqtt_mock): +async def test_status_invalid_json(hass, mqtt_mock_entry_with_yaml_config): """Test to make sure nothing breaks if the vacuum sends bad JSON.""" config = deepcopy(DEFAULT_CONFIG) config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( @@ -332,83 +339,98 @@ async def test_status_invalid_json(hass, mqtt_mock): assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() async_fire_mqtt_message(hass, "vacuum/state", '{"asdfasas false}') state = hass.states.get("vacuum.mqtttest") assert state.state == STATE_UNKNOWN -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" await help_test_default_availability_payload( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2, MQTT_VACUUM_ATTRIBUTES_BLOCKED + hass, + mqtt_mock_entry_no_yaml_config, + vacuum.DOMAIN, + DEFAULT_CONFIG_2, + MQTT_VACUUM_ATTRIBUTES_BLOCKED, ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_update_with_json_attrs_bad_json(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_json( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one vacuum per unique_id.""" config = { vacuum.DOMAIN: [ @@ -428,92 +450,103 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, vacuum.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, config + ) -async def test_discovery_removal_vacuum(hass, mqtt_mock, caplog): +async def test_discovery_removal_vacuum(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered vacuum.""" data = '{ "schema": "state", "name": "test", "command_topic": "test_topic"}' - await help_test_discovery_removal(hass, mqtt_mock, caplog, vacuum.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, data + ) -async def test_discovery_update_vacuum(hass, mqtt_mock, caplog): +async def test_discovery_update_vacuum(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered vacuum.""" config1 = {"schema": "state", "name": "Beer", "command_topic": "test_topic"} config2 = {"schema": "state", "name": "Milk", "command_topic": "test_topic"} await help_test_discovery_update( - hass, mqtt_mock, caplog, vacuum.DOMAIN, config1, config2 + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, config1, config2 ) -async def test_discovery_update_unchanged_vacuum(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_vacuum( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered vacuum.""" data1 = '{ "schema": "state", "name": "Beer", "command_topic": "test_topic"}' with patch( "homeassistant.components.mqtt.vacuum.schema_state.MqttStateVacuum.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, vacuum.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + vacuum.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "schema": "state", "name": "Beer", "command_topic": "test_topic#"}' data2 = '{ "schema": "state", "name": "Milk", "command_topic": "test_topic"}' await help_test_discovery_broken( - hass, mqtt_mock, caplog, vacuum.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT vacuum device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT vacuum device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2 + hass, mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2 ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, vacuum.DOMAIN, DEFAULT_CONFIG_2, vacuum.SERVICE_START, @@ -564,7 +597,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -590,7 +623,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -602,11 +635,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = vacuum.DOMAIN config = DEFAULT_CONFIG - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -634,12 +669,18 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG, diff --git a/tests/components/mqtt/test_subscription.py b/tests/components/mqtt/test_subscription.py index e2ffc602ddd..7c1663b9c09 100644 --- a/tests/components/mqtt/test_subscription.py +++ b/tests/components/mqtt/test_subscription.py @@ -11,8 +11,9 @@ from homeassistant.core import callback from tests.common import async_fire_mqtt_message -async def test_subscribe_topics(hass, mqtt_mock, caplog): +async def test_subscribe_topics(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test subscription to topics.""" + await mqtt_mock_entry_no_yaml_config() calls1 = [] @callback @@ -59,8 +60,9 @@ async def test_subscribe_topics(hass, mqtt_mock, caplog): assert len(calls2) == 1 -async def test_modify_topics(hass, mqtt_mock, caplog): +async def test_modify_topics(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test modification of topics.""" + await mqtt_mock_entry_no_yaml_config() calls1 = [] @callback @@ -121,8 +123,9 @@ async def test_modify_topics(hass, mqtt_mock, caplog): assert len(calls2) == 1 -async def test_qos_encoding_default(hass, mqtt_mock, caplog): +async def test_qos_encoding_default(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test default qos and encoding.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() @callback def msg_callback(*args): @@ -136,11 +139,12 @@ async def test_qos_encoding_default(hass, mqtt_mock, caplog): {"test_topic1": {"topic": "test-topic1", "msg_callback": msg_callback}}, ) await async_subscribe_topics(hass, sub_state) - mqtt_mock.async_subscribe.assert_called_once_with("test-topic1", ANY, 0, "utf-8") + mqtt_mock.async_subscribe.assert_called_with("test-topic1", ANY, 0, "utf-8") -async def test_qos_encoding_custom(hass, mqtt_mock, caplog): +async def test_qos_encoding_custom(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test custom qos and encoding.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() @callback def msg_callback(*args): @@ -161,11 +165,12 @@ async def test_qos_encoding_custom(hass, mqtt_mock, caplog): }, ) await async_subscribe_topics(hass, sub_state) - mqtt_mock.async_subscribe.assert_called_once_with("test-topic1", ANY, 1, "utf-16") + mqtt_mock.async_subscribe.assert_called_with("test-topic1", ANY, 1, "utf-16") -async def test_no_change(hass, mqtt_mock, caplog): +async def test_no_change(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test subscription to topics without change.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() calls = [] diff --git a/tests/components/mqtt/test_switch.py b/tests/components/mqtt/test_switch.py index 699f0de87f0..b217bf40c22 100644 --- a/tests/components/mqtt/test_switch.py +++ b/tests/components/mqtt/test_switch.py @@ -53,7 +53,7 @@ DEFAULT_CONFIG = { } -async def test_controlling_state_via_topic(hass, mqtt_mock): +async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_config): """Test the controlling state via topic.""" assert await async_setup_component( hass, @@ -71,6 +71,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("switch.test") assert state.state == STATE_UNKNOWN @@ -93,7 +94,9 @@ async def test_controlling_state_via_topic(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the sending MQTT commands in optimistic mode.""" fake_state = ha.State("switch.test", "on") @@ -116,6 +119,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() state = hass.states.get("switch.test") assert state.state == STATE_ON @@ -139,7 +143,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_sending_inital_state_and_optimistic(hass, mqtt_mock): +async def test_sending_inital_state_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): """Test the initial state in optimistic mode.""" assert await async_setup_component( hass, @@ -153,13 +159,16 @@ async def test_sending_inital_state_and_optimistic(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("switch.test") assert state.state == STATE_UNKNOWN assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): +async def test_controlling_state_via_topic_and_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the controlling state via topic and JSON message.""" assert await async_setup_component( hass, @@ -177,6 +186,7 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("switch.test") assert state.state == STATE_UNKNOWN @@ -197,21 +207,23 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): assert state.state == STATE_UNKNOWN -async def test_availability_when_connection_lost(hass, mqtt_mock): +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_availability_without_topic(hass, mqtt_mock): +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): """Test availability without defined availability topic.""" await help_test_availability_without_topic( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_default_availability_payload(hass, mqtt_mock): +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by default payload with defined topic.""" config = { switch.DOMAIN: { @@ -225,11 +237,17 @@ async def test_default_availability_payload(hass, mqtt_mock): } await help_test_default_availability_payload( - hass, mqtt_mock, switch.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + switch.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_custom_availability_payload(hass, mqtt_mock): +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): """Test availability by custom payload with defined topic.""" config = { switch.DOMAIN: { @@ -243,11 +261,17 @@ async def test_custom_availability_payload(hass, mqtt_mock): } await help_test_custom_availability_payload( - hass, mqtt_mock, switch.DOMAIN, config, True, "state-topic", "1" + hass, + mqtt_mock_entry_with_yaml_config, + switch.DOMAIN, + config, + True, + "state-topic", + "1", ) -async def test_custom_state_payload(hass, mqtt_mock): +async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config): """Test the state payload.""" assert await async_setup_component( hass, @@ -266,6 +290,7 @@ async def test_custom_state_payload(hass, mqtt_mock): }, ) await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() state = hass.states.get("switch.test") assert state.state == STATE_UNKNOWN @@ -282,49 +307,57 @@ async def test_custom_state_payload(hass, mqtt_mock): assert state.state == STATE_OFF -async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_via_mqtt_json_message( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_setting_blocked_attribute_via_mqtt_json_message(hass, mqtt_mock): +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_blocked_attribute_via_mqtt_json_message( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG, {} + hass, mqtt_mock_entry_no_yaml_config, switch.DOMAIN, DEFAULT_CONFIG, {} ) -async def test_setting_attribute_with_template(hass, mqtt_mock): +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): """Test the setting of attribute via MQTT with JSON payload.""" await help_test_setting_attribute_with_template( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_not_dict( - hass, mqtt_mock, caplog, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_update_with_json_attrs_bad_JSON(hass, mqtt_mock, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass, mqtt_mock_entry_with_yaml_config, caplog +): """Test attributes get extracted from a JSON result.""" await help_test_update_with_json_attrs_bad_JSON( - hass, mqtt_mock, caplog, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_discovery_update_attr(hass, mqtt_mock, caplog): +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test update of discovered MQTTAttributes.""" await help_test_discovery_update_attr( - hass, mqtt_mock, caplog, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_unique_id(hass, mqtt_mock): +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): """Test unique id option only creates one switch per unique_id.""" config = { switch.DOMAIN: [ @@ -344,20 +377,26 @@ async def test_unique_id(hass, mqtt_mock): }, ] } - await help_test_unique_id(hass, mqtt_mock, switch.DOMAIN, config) + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, config + ) -async def test_discovery_removal_switch(hass, mqtt_mock, caplog): +async def test_discovery_removal_switch(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test removal of discovered switch.""" data = ( '{ "name": "test",' ' "state_topic": "test_topic",' ' "command_topic": "test_topic" }' ) - await help_test_discovery_removal(hass, mqtt_mock, caplog, switch.DOMAIN, data) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, switch.DOMAIN, data + ) -async def test_discovery_update_switch_topic_template(hass, mqtt_mock, caplog): +async def test_discovery_update_switch_topic_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered switch.""" config1 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) @@ -382,7 +421,7 @@ async def test_discovery_update_switch_topic_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, switch.DOMAIN, config1, @@ -392,7 +431,9 @@ async def test_discovery_update_switch_topic_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_switch_template(hass, mqtt_mock, caplog): +async def test_discovery_update_switch_template( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered switch.""" config1 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) @@ -415,7 +456,7 @@ async def test_discovery_update_switch_template(hass, mqtt_mock, caplog): await help_test_discovery_update( hass, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, caplog, switch.DOMAIN, config1, @@ -425,7 +466,9 @@ async def test_discovery_update_switch_template(hass, mqtt_mock, caplog): ) -async def test_discovery_update_unchanged_switch(hass, mqtt_mock, caplog): +async def test_discovery_update_unchanged_switch( + hass, mqtt_mock_entry_no_yaml_config, caplog +): """Test update of discovered switch.""" data1 = ( '{ "name": "Beer",' @@ -437,12 +480,17 @@ async def test_discovery_update_unchanged_switch(hass, mqtt_mock, caplog): "homeassistant.components.mqtt.switch.MqttSwitch.discovery_update" ) as discovery_update: await help_test_discovery_update_unchanged( - hass, mqtt_mock, caplog, switch.DOMAIN, data1, discovery_update + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + switch.DOMAIN, + data1, + discovery_update, ) @pytest.mark.no_fail_on_log_exception -async def test_discovery_broken(hass, mqtt_mock, caplog): +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer" }' data2 = ( @@ -451,56 +499,60 @@ async def test_discovery_broken(hass, mqtt_mock, caplog): ' "command_topic": "test_topic" }' ) await help_test_discovery_broken( - hass, mqtt_mock, caplog, switch.DOMAIN, data1, data2 + hass, mqtt_mock_entry_no_yaml_config, caplog, switch.DOMAIN, data1, data2 ) -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT switch device registry integration.""" await help_test_entity_device_info_with_connection( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT switch device registry integration.""" await help_test_entity_device_info_with_identifier( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" await help_test_entity_device_info_update( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_device_info_remove(hass, mqtt_mock): +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): """Test device registry remove.""" await help_test_entity_device_info_remove( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_subscriptions(hass, mqtt_mock): +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): """Test MQTT subscriptions are managed when entity_id is updated.""" await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_id_update_discovery_update(hass, mqtt_mock): +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT discovery update when entity_id is updated.""" await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG + hass, mqtt_mock_entry_no_yaml_config, switch.DOMAIN, DEFAULT_CONFIG ) -async def test_entity_debug_info_message(hass, mqtt_mock): +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT debug info.""" await help_test_entity_debug_info_message( - hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG, switch.SERVICE_TURN_ON + hass, + mqtt_mock_entry_no_yaml_config, + switch.DOMAIN, + DEFAULT_CONFIG, + switch.SERVICE_TURN_ON, ) @@ -525,7 +577,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock): ) async def test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, service, topic, @@ -539,7 +591,7 @@ async def test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, domain, config, @@ -551,11 +603,13 @@ async def test_publishing_with_custom_encoding( ) -async def test_reloadable(hass, mqtt_mock, caplog, tmp_path): +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): """Test reloading the MQTT platform.""" domain = switch.DOMAIN config = DEFAULT_CONFIG[domain] - await help_test_reloadable(hass, mqtt_mock, caplog, tmp_path, domain, config) + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): @@ -572,12 +626,18 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): ], ) async def test_encoding_subscribable_topics( - hass, mqtt_mock, caplog, topic, value, attribute, attribute_value + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, ): """Test handling of incoming encoded payload.""" await help_test_encoding_subscribable_topics( hass, - mqtt_mock, + mqtt_mock_entry_with_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG[switch.DOMAIN], diff --git a/tests/components/mqtt/test_tag.py b/tests/components/mqtt/test_tag.py index 99e2fffc085..09be31011f2 100644 --- a/tests/components/mqtt/test_tag.py +++ b/tests/components/mqtt/test_tag.py @@ -62,8 +62,11 @@ def tag_mock(): @pytest.mark.no_fail_on_log_exception -async def test_discover_bad_tag(hass, device_reg, entity_reg, mqtt_mock, tag_mock): +async def test_discover_bad_tag( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config, tag_mock +): """Test bad discovery message.""" + await mqtt_mock_entry_no_yaml_config() config1 = copy.deepcopy(DEFAULT_CONFIG_DEVICE) # Test sending bad data @@ -84,9 +87,10 @@ async def test_discover_bad_tag(hass, device_reg, entity_reg, mqtt_mock, tag_moc async def test_if_fires_on_mqtt_message_with_device( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning, with device.""" + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG_DEVICE) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -100,9 +104,10 @@ async def test_if_fires_on_mqtt_message_with_device( async def test_if_fires_on_mqtt_message_without_device( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning, without device.""" + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -115,9 +120,10 @@ async def test_if_fires_on_mqtt_message_without_device( async def test_if_fires_on_mqtt_message_with_template( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning, with device.""" + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG_JSON) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -130,8 +136,9 @@ async def test_if_fires_on_mqtt_message_with_template( tag_mock.assert_called_once_with(ANY, DEFAULT_TAG_ID, device_entry.id) -async def test_strip_tag_id(hass, device_reg, mqtt_mock, tag_mock): +async def test_strip_tag_id(hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock): """Test strip whitespace from tag_id.""" + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -144,9 +151,10 @@ async def test_strip_tag_id(hass, device_reg, mqtt_mock, tag_mock): async def test_if_fires_on_mqtt_message_after_update_with_device( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning after update.""" + await mqtt_mock_entry_no_yaml_config() config1 = copy.deepcopy(DEFAULT_CONFIG_DEVICE) config1["some_future_option_1"] = "future_option_1" config2 = copy.deepcopy(DEFAULT_CONFIG_DEVICE) @@ -190,9 +198,10 @@ async def test_if_fires_on_mqtt_message_after_update_with_device( async def test_if_fires_on_mqtt_message_after_update_without_device( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning after update.""" + await mqtt_mock_entry_no_yaml_config() config1 = copy.deepcopy(DEFAULT_CONFIG) config2 = copy.deepcopy(DEFAULT_CONFIG) config2["topic"] = "foobar/tag_scanned2" @@ -233,9 +242,10 @@ async def test_if_fires_on_mqtt_message_after_update_without_device( async def test_if_fires_on_mqtt_message_after_update_with_template( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning after update.""" + await mqtt_mock_entry_no_yaml_config() config1 = copy.deepcopy(DEFAULT_CONFIG_JSON) config2 = copy.deepcopy(DEFAULT_CONFIG_JSON) config2["value_template"] = "{{ value_json.RDM6300.UID }}" @@ -277,8 +287,11 @@ async def test_if_fires_on_mqtt_message_after_update_with_template( tag_mock.assert_called_once_with(ANY, DEFAULT_TAG_ID, device_entry.id) -async def test_no_resubscribe_same_topic(hass, device_reg, mqtt_mock): +async def test_no_resubscribe_same_topic( + hass, device_reg, mqtt_mock_entry_no_yaml_config +): """Test subscription to topics without change.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG_DEVICE) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -292,9 +305,10 @@ async def test_no_resubscribe_same_topic(hass, device_reg, mqtt_mock): async def test_not_fires_on_mqtt_message_after_remove_by_mqtt_with_device( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning after removal.""" + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG_DEVICE) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -325,9 +339,10 @@ async def test_not_fires_on_mqtt_message_after_remove_by_mqtt_with_device( async def test_not_fires_on_mqtt_message_after_remove_by_mqtt_without_device( - hass, device_reg, mqtt_mock, tag_mock + hass, device_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test tag scanning not firing after removal.""" + await mqtt_mock_entry_no_yaml_config() config = copy.deepcopy(DEFAULT_CONFIG) async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -360,11 +375,13 @@ async def test_not_fires_on_mqtt_message_after_remove_from_registry( hass, hass_ws_client, device_reg, - mqtt_mock, + mqtt_mock_entry_no_yaml_config, tag_mock, ): """Test tag scanning after removal.""" assert await async_setup_component(hass, "config", {}) + await hass.async_block_till_done() + await mqtt_mock_entry_no_yaml_config() ws_client = await hass_ws_client(hass) config = copy.deepcopy(DEFAULT_CONFIG_DEVICE) @@ -397,8 +414,9 @@ async def test_not_fires_on_mqtt_message_after_remove_from_registry( tag_mock.assert_not_called() -async def test_entity_device_info_with_connection(hass, mqtt_mock): +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT device registry integration.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) data = json.dumps( @@ -427,8 +445,9 @@ async def test_entity_device_info_with_connection(hass, mqtt_mock): assert device.sw_version == "0.1-beta" -async def test_entity_device_info_with_identifier(hass, mqtt_mock): +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): """Test MQTT device registry integration.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) data = json.dumps( @@ -455,8 +474,9 @@ async def test_entity_device_info_with_identifier(hass, mqtt_mock): assert device.sw_version == "0.1-beta" -async def test_entity_device_info_update(hass, mqtt_mock): +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): """Test device registry update.""" + await mqtt_mock_entry_no_yaml_config() registry = dr.async_get(hass) config = { @@ -489,9 +509,13 @@ async def test_entity_device_info_update(hass, mqtt_mock): assert device.name == "Milk" -async def test_cleanup_tag(hass, hass_ws_client, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_tag( + hass, hass_ws_client, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test tag discovery topic is cleaned when device is removed from registry.""" assert await async_setup_component(hass, "config", {}) + await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_no_yaml_config() ws_client = await hass_ws_client(hass) mqtt_entry = hass.config_entries.async_entries("mqtt")[0] @@ -566,8 +590,11 @@ async def test_cleanup_tag(hass, hass_ws_client, device_reg, entity_reg, mqtt_mo ) -async def test_cleanup_device(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test removal from device registry when tag is removed.""" + await mqtt_mock_entry_no_yaml_config() config = { "topic": "test-topic", "device": {"identifiers": ["helloworld"]}, @@ -590,9 +617,10 @@ async def test_cleanup_device(hass, device_reg, entity_reg, mqtt_mock): async def test_cleanup_device_several_tags( - hass, device_reg, entity_reg, mqtt_mock, tag_mock + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config, tag_mock ): """Test removal from device registry when the last tag is removed.""" + await mqtt_mock_entry_no_yaml_config() config1 = { "topic": "test-topic1", "device": {"identifiers": ["helloworld"]}, @@ -634,12 +662,13 @@ async def test_cleanup_device_several_tags( async def test_cleanup_device_with_entity_and_trigger_1( - hass, device_reg, entity_reg, mqtt_mock + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config ): """Test removal from device registry for device with tag, entity and trigger. Tag removed first, then trigger and entity. """ + await mqtt_mock_entry_no_yaml_config() config1 = { "topic": "test-topic", "device": {"identifiers": ["helloworld"]}, @@ -697,11 +726,14 @@ async def test_cleanup_device_with_entity_and_trigger_1( assert device_entry is None -async def test_cleanup_device_with_entity2(hass, device_reg, entity_reg, mqtt_mock): +async def test_cleanup_device_with_entity2( + hass, device_reg, entity_reg, mqtt_mock_entry_no_yaml_config +): """Test removal from device registry for device with tag, entity and trigger. Trigger and entity removed first, then tag. """ + await mqtt_mock_entry_no_yaml_config() config1 = { "topic": "test-topic", "device": {"identifiers": ["helloworld"]}, diff --git a/tests/components/mqtt/test_trigger.py b/tests/components/mqtt/test_trigger.py index c2c77dcddd8..a4079558c34 100644 --- a/tests/components/mqtt/test_trigger.py +++ b/tests/components/mqtt/test_trigger.py @@ -18,9 +18,10 @@ def calls(hass): @pytest.fixture(autouse=True) -def setup_comp(hass, mqtt_mock): +async def setup_comp(hass, mqtt_mock_entry_no_yaml_config): """Initialize components.""" mock_component(hass, "group") + return await mqtt_mock_entry_no_yaml_config() async def test_if_fires_on_topic_match(hass, calls): @@ -213,7 +214,7 @@ async def test_if_not_fires_on_topic_but_no_payload_match(hass, calls): assert len(calls) == 0 -async def test_encoding_default(hass, calls, mqtt_mock): +async def test_encoding_default(hass, calls, setup_comp): """Test default encoding.""" assert await async_setup_component( hass, @@ -226,10 +227,10 @@ async def test_encoding_default(hass, calls, mqtt_mock): }, ) - mqtt_mock.async_subscribe.assert_called_once_with("test-topic", ANY, 0, "utf-8") + setup_comp.async_subscribe.assert_called_with("test-topic", ANY, 0, "utf-8") -async def test_encoding_custom(hass, calls, mqtt_mock): +async def test_encoding_custom(hass, calls, setup_comp): """Test default encoding.""" assert await async_setup_component( hass, @@ -242,4 +243,4 @@ async def test_encoding_custom(hass, calls, mqtt_mock): }, ) - mqtt_mock.async_subscribe.assert_called_once_with("test-topic", ANY, 0, None) + setup_comp.async_subscribe.assert_called_with("test-topic", ANY, 0, None) diff --git a/tests/components/mqtt_json/test_device_tracker.py b/tests/components/mqtt_json/test_device_tracker.py index d17484cc5e9..b0cba664250 100644 --- a/tests/components/mqtt_json/test_device_tracker.py +++ b/tests/components/mqtt_json/test_device_tracker.py @@ -26,7 +26,7 @@ LOCATION_MESSAGE_INCOMPLETE = {"longitude": 2.0} @pytest.fixture(autouse=True) -def setup_comp(hass, mqtt_mock): +async def setup_comp(hass, mqtt_mock_entry_with_yaml_config): """Initialize components.""" yaml_devices = hass.config.path(YAML_DEVICES) yield diff --git a/tests/components/mqtt_room/test_sensor.py b/tests/components/mqtt_room/test_sensor.py index c3b8704c754..b17a2bed457 100644 --- a/tests/components/mqtt_room/test_sensor.py +++ b/tests/components/mqtt_room/test_sensor.py @@ -47,7 +47,7 @@ async def assert_distance(hass, distance): assert state.attributes.get("distance") == distance -async def test_room_update(hass, mqtt_mock): +async def test_room_update(hass, mqtt_mock_entry_with_yaml_config): """Test the updating between rooms.""" assert await async_setup_component( hass, diff --git a/tests/components/recorder/test_filters.py b/tests/components/recorder/test_filters.py index fa80df6e345..5c0afa10f9d 100644 --- a/tests/components/recorder/test_filters.py +++ b/tests/components/recorder/test_filters.py @@ -12,6 +12,13 @@ from homeassistant.helpers.entityfilter import ( CONF_INCLUDE, ) +EMPTY_INCLUDE_FILTER = { + CONF_INCLUDE: { + CONF_DOMAINS: None, + CONF_ENTITIES: None, + CONF_ENTITY_GLOBS: None, + } +} SIMPLE_INCLUDE_FILTER = { CONF_INCLUDE: { CONF_DOMAINS: ["homeassistant"], @@ -87,6 +94,19 @@ def test_extract_include_exclude_filter_conf(): assert SIMPLE_INCLUDE_EXCLUDE_FILTER[CONF_EXCLUDE][CONF_ENTITIES] != { "cover.altered" } + empty_include_filter = extract_include_exclude_filter_conf(EMPTY_INCLUDE_FILTER) + assert empty_include_filter == { + CONF_EXCLUDE: { + CONF_DOMAINS: set(), + CONF_ENTITIES: set(), + CONF_ENTITY_GLOBS: set(), + }, + CONF_INCLUDE: { + CONF_DOMAINS: set(), + CONF_ENTITIES: set(), + CONF_ENTITY_GLOBS: set(), + }, + } def test_merge_include_exclude_filters(): diff --git a/tests/components/recorder/test_filters_with_entityfilter.py b/tests/components/recorder/test_filters_with_entityfilter.py new file mode 100644 index 00000000000..0758d6fdc95 --- /dev/null +++ b/tests/components/recorder/test_filters_with_entityfilter.py @@ -0,0 +1,516 @@ +"""The tests for the recorder filter matching the EntityFilter component.""" +import json + +from sqlalchemy import select +from sqlalchemy.engine.row import Row + +from homeassistant.components.recorder import get_instance +from homeassistant.components.recorder.filters import ( + Filters, + extract_include_exclude_filter_conf, + sqlalchemy_filter_from_include_exclude_conf, +) +from homeassistant.components.recorder.models import EventData, States +from homeassistant.components.recorder.util import session_scope +from homeassistant.const import ATTR_ENTITY_ID, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entityfilter import ( + CONF_DOMAINS, + CONF_ENTITIES, + CONF_ENTITY_GLOBS, + CONF_EXCLUDE, + CONF_INCLUDE, + convert_include_exclude_filter, +) + +from .common import async_wait_recording_done + + +async def _async_get_states_and_events_with_filter( + hass: HomeAssistant, sqlalchemy_filter: Filters, entity_ids: set[str] +) -> tuple[list[Row], list[Row]]: + """Get states from the database based on a filter.""" + for entity_id in entity_ids: + hass.states.async_set(entity_id, STATE_ON) + hass.bus.async_fire("any", {ATTR_ENTITY_ID: entity_id}) + + await async_wait_recording_done(hass) + + def _get_states_with_session(): + with session_scope(hass=hass) as session: + return session.execute( + select(States.entity_id).filter( + sqlalchemy_filter.states_entity_filter() + ) + ).all() + + filtered_states_entity_ids = { + row[0] + for row in await get_instance(hass).async_add_executor_job( + _get_states_with_session + ) + } + + def _get_events_with_session(): + with session_scope(hass=hass) as session: + return session.execute( + select(EventData.shared_data).filter( + sqlalchemy_filter.events_entity_filter() + ) + ).all() + + filtered_events_entity_ids = set() + for row in await get_instance(hass).async_add_executor_job( + _get_events_with_session + ): + event_data = json.loads(row[0]) + if ATTR_ENTITY_ID not in event_data: + continue + filtered_events_entity_ids.add(json.loads(row[0])[ATTR_ENTITY_ID]) + + return filtered_states_entity_ids, filtered_events_entity_ids + + +async def test_included_and_excluded_simple_case_no_domains(hass, recorder_mock): + """Test filters with included and excluded without domains.""" + filter_accept = {"sensor.kitchen4", "switch.kitchen"} + filter_reject = { + "light.any", + "switch.other", + "cover.any", + "sensor.weather5", + "light.kitchen", + } + conf = { + CONF_INCLUDE: { + CONF_ENTITY_GLOBS: ["sensor.kitchen*"], + CONF_ENTITIES: ["switch.kitchen"], + }, + CONF_EXCLUDE: { + CONF_ENTITY_GLOBS: ["sensor.weather*"], + CONF_ENTITIES: ["light.kitchen"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + assert not entity_filter.explicitly_included("light.any") + assert not entity_filter.explicitly_included("switch.other") + assert entity_filter.explicitly_included("sensor.kitchen4") + assert entity_filter.explicitly_included("switch.kitchen") + + assert not entity_filter.explicitly_excluded("light.any") + assert not entity_filter.explicitly_excluded("switch.other") + assert entity_filter.explicitly_excluded("sensor.weather5") + assert entity_filter.explicitly_excluded("light.kitchen") + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_included_and_excluded_simple_case_no_globs(hass, recorder_mock): + """Test filters with included and excluded without globs.""" + filter_accept = {"switch.bla", "sensor.blu", "sensor.keep"} + filter_reject = {"sensor.bli"} + conf = { + CONF_INCLUDE: { + CONF_DOMAINS: ["sensor", "homeassistant"], + CONF_ENTITIES: ["switch.bla"], + }, + CONF_EXCLUDE: { + CONF_DOMAINS: ["switch"], + CONF_ENTITIES: ["sensor.bli"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_included_and_excluded_simple_case_without_underscores( + hass, recorder_mock +): + """Test filters with included and excluded without underscores.""" + filter_accept = {"light.any", "sensor.kitchen4", "switch.kitchen"} + filter_reject = {"switch.other", "cover.any", "sensor.weather5", "light.kitchen"} + conf = { + CONF_INCLUDE: { + CONF_DOMAINS: ["light"], + CONF_ENTITY_GLOBS: ["sensor.kitchen*"], + CONF_ENTITIES: ["switch.kitchen"], + }, + CONF_EXCLUDE: { + CONF_DOMAINS: ["cover"], + CONF_ENTITY_GLOBS: ["sensor.weather*"], + CONF_ENTITIES: ["light.kitchen"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + assert not entity_filter.explicitly_included("light.any") + assert not entity_filter.explicitly_included("switch.other") + assert entity_filter.explicitly_included("sensor.kitchen4") + assert entity_filter.explicitly_included("switch.kitchen") + + assert not entity_filter.explicitly_excluded("light.any") + assert not entity_filter.explicitly_excluded("switch.other") + assert entity_filter.explicitly_excluded("sensor.weather5") + assert entity_filter.explicitly_excluded("light.kitchen") + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_included_and_excluded_simple_case_with_underscores(hass, recorder_mock): + """Test filters with included and excluded with underscores.""" + filter_accept = {"light.any", "sensor.kitchen_4", "switch.kitchen"} + filter_reject = {"switch.other", "cover.any", "sensor.weather_5", "light.kitchen"} + conf = { + CONF_INCLUDE: { + CONF_DOMAINS: ["light"], + CONF_ENTITY_GLOBS: ["sensor.kitchen_*"], + CONF_ENTITIES: ["switch.kitchen"], + }, + CONF_EXCLUDE: { + CONF_DOMAINS: ["cover"], + CONF_ENTITY_GLOBS: ["sensor.weather_*"], + CONF_ENTITIES: ["light.kitchen"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + assert not entity_filter.explicitly_included("light.any") + assert not entity_filter.explicitly_included("switch.other") + assert entity_filter.explicitly_included("sensor.kitchen_4") + assert entity_filter.explicitly_included("switch.kitchen") + + assert not entity_filter.explicitly_excluded("light.any") + assert not entity_filter.explicitly_excluded("switch.other") + assert entity_filter.explicitly_excluded("sensor.weather_5") + assert entity_filter.explicitly_excluded("light.kitchen") + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_included_and_excluded_complex_case(hass, recorder_mock): + """Test filters with included and excluded with a complex filter.""" + filter_accept = {"light.any", "sensor.kitchen_4", "switch.kitchen"} + filter_reject = { + "camera.one", + "notify.any", + "automation.update_readme", + "automation.update_utilities_cost", + "binary_sensor.iss", + } + conf = { + CONF_INCLUDE: { + CONF_ENTITIES: ["group.trackers"], + }, + CONF_EXCLUDE: { + CONF_ENTITIES: [ + "automation.update_readme", + "automation.update_utilities_cost", + "binary_sensor.iss", + ], + CONF_DOMAINS: [ + "camera", + "group", + "media_player", + "notify", + "scene", + "sun", + "zone", + ], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_included_entities_and_excluded_domain(hass, recorder_mock): + """Test filters with included entities and excluded domain.""" + filter_accept = { + "media_player.test", + "media_player.test3", + "thermostat.test", + "zone.home", + "script.can_cancel_this_one", + } + filter_reject = { + "thermostat.test2", + } + conf = { + CONF_INCLUDE: { + CONF_ENTITIES: ["media_player.test", "thermostat.test"], + }, + CONF_EXCLUDE: { + CONF_DOMAINS: ["thermostat"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_same_domain_included_excluded(hass, recorder_mock): + """Test filters with the same domain included and excluded.""" + filter_accept = { + "media_player.test", + "media_player.test3", + } + filter_reject = { + "thermostat.test2", + "thermostat.test", + "zone.home", + "script.can_cancel_this_one", + } + conf = { + CONF_INCLUDE: { + CONF_DOMAINS: ["media_player"], + }, + CONF_EXCLUDE: { + CONF_DOMAINS: ["media_player"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_same_entity_included_excluded(hass, recorder_mock): + """Test filters with the same entity included and excluded.""" + filter_accept = { + "media_player.test", + } + filter_reject = { + "media_player.test3", + "thermostat.test2", + "thermostat.test", + "zone.home", + "script.can_cancel_this_one", + } + conf = { + CONF_INCLUDE: { + CONF_ENTITIES: ["media_player.test"], + }, + CONF_EXCLUDE: { + CONF_ENTITIES: ["media_player.test"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) + + +async def test_same_entity_included_excluded_include_domain_wins(hass, recorder_mock): + """Test filters with domain and entities and the include domain wins.""" + filter_accept = { + "media_player.test2", + "media_player.test3", + "thermostat.test", + } + filter_reject = { + "thermostat.test2", + "zone.home", + "script.can_cancel_this_one", + } + conf = { + CONF_INCLUDE: { + CONF_DOMAINS: ["media_player"], + CONF_ENTITIES: ["thermostat.test"], + }, + CONF_EXCLUDE: { + CONF_DOMAINS: ["thermostat"], + CONF_ENTITIES: ["media_player.test"], + }, + } + + extracted_filter = extract_include_exclude_filter_conf(conf) + entity_filter = convert_include_exclude_filter(extracted_filter) + sqlalchemy_filter = sqlalchemy_filter_from_include_exclude_conf(extracted_filter) + assert sqlalchemy_filter is not None + + for entity_id in filter_accept: + assert entity_filter(entity_id) is True + + for entity_id in filter_reject: + assert entity_filter(entity_id) is False + + ( + filtered_states_entity_ids, + filtered_events_entity_ids, + ) = await _async_get_states_and_events_with_filter( + hass, sqlalchemy_filter, filter_accept | filter_reject + ) + + assert filtered_states_entity_ids == filter_accept + assert not filtered_states_entity_ids.intersection(filter_reject) + + assert filtered_events_entity_ids == filter_accept + assert not filtered_events_entity_ids.intersection(filter_reject) diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 41c4428ee5e..87dbce3ba3b 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -118,6 +118,26 @@ async def test_shutdown_before_startup_finishes( assert run_info.end is not None +async def test_canceled_before_startup_finishes( + hass: HomeAssistant, + async_setup_recorder_instance: SetupRecorderInstanceT, + caplog: pytest.LogCaptureFixture, +): + """Test recorder shuts down when its startup future is canceled out from under it.""" + hass.state = CoreState.not_running + await async_setup_recorder_instance(hass) + instance = get_instance(hass) + await instance.async_db_ready + instance._hass_started.cancel() + with patch.object(instance, "engine"): + await hass.async_block_till_done() + await hass.async_add_executor_job(instance.join) + assert ( + "Recorder startup was externally canceled before it could complete" + in caplog.text + ) + + async def test_shutdown_closes_connections(hass, recorder_mock): """Test shutdown closes connections.""" diff --git a/tests/components/tasmota/test_discovery.py b/tests/components/tasmota/test_discovery.py index 0b7e3726482..a97b877d819 100644 --- a/tests/components/tasmota/test_discovery.py +++ b/tests/components/tasmota/test_discovery.py @@ -1,7 +1,7 @@ """The tests for the MQTT discovery.""" import copy import json -from unittest.mock import patch +from unittest.mock import ANY, patch from homeassistant.components.tasmota.const import DEFAULT_PREFIX from homeassistant.components.tasmota.discovery import ALREADY_DISCOVERED @@ -19,9 +19,7 @@ async def test_subscribing_config_topic(hass, mqtt_mock, setup_tasmota): discovery_topic = DEFAULT_PREFIX assert mqtt_mock.async_subscribe.called - call_args = mqtt_mock.async_subscribe.mock_calls[0][1] - assert call_args[0] == discovery_topic + "/#" - assert call_args[2] == 0 + mqtt_mock.async_subscribe.assert_any_call(discovery_topic + "/#", ANY, 0, "utf-8") async def test_future_discovery_message(hass, mqtt_mock, caplog): diff --git a/tests/components/yolink/test_config_flow.py b/tests/components/yolink/test_config_flow.py index 5d6bb8fd727..e224bc3e1d2 100644 --- a/tests/components/yolink/test_config_flow.py +++ b/tests/components/yolink/test_config_flow.py @@ -3,6 +3,8 @@ import asyncio from http import HTTPStatus from unittest.mock import patch +from yolink.const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN + from homeassistant import config_entries, data_entry_flow, setup from homeassistant.components import application_credentials from homeassistant.core import HomeAssistant @@ -12,11 +14,7 @@ from tests.common import MockConfigEntry CLIENT_ID = "12345" CLIENT_SECRET = "6789" -YOLINK_HOST = "api.yosmart.com" -YOLINK_HTTP_HOST = f"http://{YOLINK_HOST}" DOMAIN = "yolink" -OAUTH2_AUTHORIZE = f"{YOLINK_HTTP_HOST}/oauth/v2/authorization.htm" -OAUTH2_TOKEN = f"{YOLINK_HTTP_HOST}/open/yolink/token" async def test_abort_if_no_configuration(hass): diff --git a/tests/conftest.py b/tests/conftest.py index 8ea6e114e9a..97b1a959d2c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import AsyncGenerator +from contextlib import asynccontextmanager import functools import logging import ssl @@ -547,8 +548,19 @@ def mqtt_client_mock(hass): @pytest.fixture -async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): +async def mqtt_mock( + hass, + mqtt_client_mock, + mqtt_config, + mqtt_mock_entry_no_yaml_config, +): """Fixture to mock MQTT component.""" + return await mqtt_mock_entry_no_yaml_config() + + +@asynccontextmanager +async def _mqtt_mock_entry(hass, mqtt_client_mock, mqtt_config): + """Fixture to mock a delayed setup of the MQTT config entry.""" if mqtt_config is None: mqtt_config = {mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: {}} @@ -557,29 +569,79 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): entry = MockConfigEntry( data=mqtt_config, domain=mqtt.DOMAIN, - title="Tasmota", + title="MQTT", ) - entry.add_to_hass(hass) - # Do not forward the entry setup to the components here - with patch("homeassistant.components.mqtt.PLATFORMS", []): - assert await hass.config_entries.async_setup(entry.entry_id) + + real_mqtt = mqtt.MQTT + real_mqtt_instance = None + mock_mqtt_instance = None + + async def _setup_mqtt_entry(setup_entry): + """Set up the MQTT config entry.""" + assert await setup_entry(hass, entry) + + # Assert that MQTT is setup + assert real_mqtt_instance is not None, "MQTT was not setup correctly" + mock_mqtt_instance.conf = real_mqtt_instance.conf # For diagnostics + mock_mqtt_instance._mqttc = mqtt_client_mock + + # connected set to True to get a more realistic behavior when subscribing + mock_mqtt_instance.connected = True + + hass.helpers.dispatcher.async_dispatcher_send(mqtt.MQTT_CONNECTED) await hass.async_block_till_done() - mqtt_component_mock = MagicMock( - return_value=hass.data["mqtt"], - spec_set=hass.data["mqtt"], - wraps=hass.data["mqtt"], - ) - mqtt_component_mock.conf = hass.data["mqtt"].conf # For diagnostics - mqtt_component_mock._mqttc = mqtt_client_mock - # connected set to True to get a more realistics behavior when subscribing - hass.data["mqtt"].connected = True + return mock_mqtt_instance - hass.data["mqtt"] = mqtt_component_mock - component = hass.data["mqtt"] - component.reset_mock() - return component + def create_mock_mqtt(*args, **kwargs): + """Create a mock based on mqtt.MQTT.""" + nonlocal mock_mqtt_instance + nonlocal real_mqtt_instance + real_mqtt_instance = real_mqtt(*args, **kwargs) + mock_mqtt_instance = MagicMock( + return_value=real_mqtt_instance, + spec_set=real_mqtt_instance, + wraps=real_mqtt_instance, + ) + return mock_mqtt_instance + + with patch("homeassistant.components.mqtt.MQTT", side_effect=create_mock_mqtt): + yield _setup_mqtt_entry + + +@pytest.fixture +async def mqtt_mock_entry_no_yaml_config(hass, mqtt_client_mock, mqtt_config): + """Set up an MQTT config entry without MQTT yaml config.""" + + async def _async_setup_config_entry(hass, entry): + """Help set up the config entry.""" + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + return True + + async def _setup_mqtt_entry(): + """Set up the MQTT config entry.""" + return await mqtt_mock_entry(_async_setup_config_entry) + + async with _mqtt_mock_entry(hass, mqtt_client_mock, mqtt_config) as mqtt_mock_entry: + yield _setup_mqtt_entry + + +@pytest.fixture +async def mqtt_mock_entry_with_yaml_config(hass, mqtt_client_mock, mqtt_config): + """Set up an MQTT config entry with MQTT yaml config.""" + + async def _async_do_not_setup_config_entry(hass, entry): + """Do nothing.""" + return True + + async def _setup_mqtt_entry(): + """Set up the MQTT config entry.""" + return await mqtt_mock_entry(_async_do_not_setup_config_entry) + + async with _mqtt_mock_entry(hass, mqtt_client_mock, mqtt_config) as mqtt_mock_entry: + yield _setup_mqtt_entry @pytest.fixture(autouse=True)