Refactor the MQTT option and reconfigure flow (#133342)

* Move entry options to entry.options en remove broker setup from mqtt option flow

* UPdate diagnostics to export both entry data and options

* Parameterize entry options directly not depending on migration

* Update tests to use v2 entry and add separate migration test

* use start_reconfigure_flow helper

* Update quality scale comment

* Do minor entry upgrade, and do not force to upgrade entry

* Ensure options are read from older entries

* Add comment

* Follow up on code review

* Assert config entry version checking the broker connection

* Update comment
pull/127280/head^2
Jan Bouwhuis 2025-01-13 19:00:18 +01:00 committed by GitHub
parent b84a4dc120
commit b93aa760c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 533 additions and 371 deletions

View File

@ -69,6 +69,8 @@ from .const import ( # noqa: F401
CONF_WILL_MESSAGE,
CONF_WS_HEADERS,
CONF_WS_PATH,
CONFIG_ENTRY_MINOR_VERSION,
CONFIG_ENTRY_VERSION,
DEFAULT_DISCOVERY,
DEFAULT_ENCODING,
DEFAULT_PREFIX,
@ -76,6 +78,7 @@ from .const import ( # noqa: F401
DEFAULT_RETAIN,
DOMAIN,
ENTITY_PLATFORMS,
ENTRY_OPTION_FIELDS,
MQTT_CONNECTION_STATE,
TEMPLATE_ERRORS,
)
@ -282,15 +285,45 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate the options from config entry data."""
_LOGGER.debug("Migrating from version %s:%s", entry.version, entry.minor_version)
data: dict[str, Any] = dict(entry.data)
options: dict[str, Any] = dict(entry.options)
if entry.version > 1:
# This means the user has downgraded from a future version
return False
if entry.version == 1 and entry.minor_version < 2:
# Can be removed when config entry is bumped to version 2.1
# with HA Core 2026.1.0. Read support for version 2.1 is expected before 2026.1
# From 2026.1 we will write version 2.1
for key in ENTRY_OPTION_FIELDS:
if key not in data:
continue
options[key] = data.pop(key)
hass.config_entries.async_update_entry(
entry,
data=data,
options=options,
version=CONFIG_ENTRY_VERSION,
minor_version=CONFIG_ENTRY_MINOR_VERSION,
)
_LOGGER.debug(
"Migration to version %s:%s successful", entry.version, entry.minor_version
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Load a config entry."""
conf: dict[str, Any]
mqtt_data: MqttData
async def _setup_client() -> tuple[MqttData, dict[str, Any]]:
"""Set up the MQTT client."""
# Fetch configuration
conf = dict(entry.data)
conf = dict(entry.data | entry.options)
hass_config = await conf_util.async_hass_config_yaml(hass)
mqtt_yaml = CONFIG_SCHEMA(hass_config).get(DOMAIN, [])
await async_create_certificate_temp_files(hass, conf)

View File

@ -76,6 +76,8 @@ from .const import (
CONF_WILL_MESSAGE,
CONF_WS_HEADERS,
CONF_WS_PATH,
CONFIG_ENTRY_MINOR_VERSION,
CONFIG_ENTRY_VERSION,
DEFAULT_BIRTH,
DEFAULT_DISCOVERY,
DEFAULT_ENCODING,
@ -205,7 +207,9 @@ def update_password_from_user_input(
class FlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
VERSION = 1
# Can be bumped to version 2.1 with HA Core 2026.1.0
VERSION = CONFIG_ENTRY_VERSION # 1
MINOR_VERSION = CONFIG_ENTRY_MINOR_VERSION # 2
_hassio_discovery: dict[str, Any] | None = None
_addon_manager: AddonManager
@ -496,7 +500,6 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
reconfigure_entry,
data=validated_user_input,
)
validated_user_input[CONF_DISCOVERY] = DEFAULT_DISCOVERY
return self.async_create_entry(
title=validated_user_input[CONF_BROKER],
data=validated_user_input,
@ -564,58 +567,17 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
class MQTTOptionsFlowHandler(OptionsFlow):
"""Handle MQTT options."""
def __init__(self) -> None:
"""Initialize MQTT options flow."""
self.broker_config: dict[str, Any] = {}
async def async_step_init(self, user_input: None = None) -> ConfigFlowResult:
"""Manage the MQTT options."""
return await self.async_step_broker()
async def async_step_broker(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the MQTT broker configuration."""
errors: dict[str, str] = {}
fields: OrderedDict[Any, Any] = OrderedDict()
validated_user_input: dict[str, Any] = {}
if await async_get_broker_settings(
self,
fields,
self.config_entry.data,
user_input,
validated_user_input,
errors,
):
self.broker_config.update(
update_password_from_user_input(
self.config_entry.data.get(CONF_PASSWORD), validated_user_input
),
)
can_connect = await self.hass.async_add_executor_job(
try_connection,
self.broker_config,
)
if can_connect:
return await self.async_step_options()
errors["base"] = "cannot_connect"
return self.async_show_form(
step_id="broker",
data_schema=vol.Schema(fields),
errors=errors,
last_step=False,
)
return await self.async_step_options()
async def async_step_options(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the MQTT options."""
errors = {}
current_config = self.config_entry.data
options_config: dict[str, Any] = {}
options_config: dict[str, Any] = dict(self.config_entry.options)
bad_input: bool = False
def _birth_will(birt_or_will: str) -> dict[str, Any]:
@ -674,26 +636,18 @@ class MQTTOptionsFlowHandler(OptionsFlow):
options_config[CONF_WILL_MESSAGE] = {}
if not bad_input:
updated_config = {}
updated_config.update(self.broker_config)
updated_config.update(options_config)
self.hass.config_entries.async_update_entry(
self.config_entry,
data=updated_config,
title=str(self.broker_config[CONF_BROKER]),
)
return self.async_create_entry(title="", data={})
return self.async_create_entry(data=options_config)
birth = {
**DEFAULT_BIRTH,
**current_config.get(CONF_BIRTH_MESSAGE, {}),
**options_config.get(CONF_BIRTH_MESSAGE, {}),
}
will = {
**DEFAULT_WILL,
**current_config.get(CONF_WILL_MESSAGE, {}),
**options_config.get(CONF_WILL_MESSAGE, {}),
}
discovery = current_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY)
discovery_prefix = current_config.get(CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX)
discovery = options_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY)
discovery_prefix = options_config.get(CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX)
# build form
fields: OrderedDict[vol.Marker, Any] = OrderedDict()
@ -706,8 +660,8 @@ class MQTTOptionsFlowHandler(OptionsFlow):
fields[
vol.Optional(
"birth_enable",
default=CONF_BIRTH_MESSAGE not in current_config
or current_config[CONF_BIRTH_MESSAGE] != {},
default=CONF_BIRTH_MESSAGE not in options_config
or options_config[CONF_BIRTH_MESSAGE] != {},
)
] = BOOLEAN_SELECTOR
fields[
@ -729,8 +683,8 @@ class MQTTOptionsFlowHandler(OptionsFlow):
fields[
vol.Optional(
"will_enable",
default=CONF_WILL_MESSAGE not in current_config
or current_config[CONF_WILL_MESSAGE] != {},
default=CONF_WILL_MESSAGE not in options_config
or options_config[CONF_WILL_MESSAGE] != {},
)
] = BOOLEAN_SELECTOR
fields[

View File

@ -4,7 +4,7 @@ import logging
import jinja2
from homeassistant.const import CONF_PAYLOAD, Platform
from homeassistant.const import CONF_DISCOVERY, CONF_PAYLOAD, Platform
from homeassistant.exceptions import TemplateError
ATTR_DISCOVERY_HASH = "discovery_hash"
@ -163,6 +163,20 @@ MQTT_CONNECTION_STATE = "mqtt_connection_state"
PAYLOAD_EMPTY_JSON = "{}"
PAYLOAD_NONE = "None"
CONFIG_ENTRY_VERSION = 1
CONFIG_ENTRY_MINOR_VERSION = 2
# Split mqtt entry data and options
# Can be removed when config entry is bumped to version 2.1
# with HA Core 2026.1.0. Read support for version 2.1 is expected before 2026.1
# From 2026.1 we will write version 2.1
ENTRY_OPTION_FIELDS = (
CONF_DISCOVERY,
CONF_DISCOVERY_PREFIX,
"birth_message",
"will_message",
)
ENTITY_PLATFORMS = [
Platform.ALARM_CONTROL_PANEL,
Platform.BINARY_SENSOR,

View File

@ -2,7 +2,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from typing import Any
from homeassistant.components import device_tracker
from homeassistant.components.diagnostics import async_redact_data
@ -18,7 +18,6 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntry
from . import debug_info, is_connected
from .models import DATA_MQTT
REDACT_CONFIG = {CONF_PASSWORD, CONF_USERNAME}
REDACT_STATE_DEVICE_TRACKER = {ATTR_LATITUDE, ATTR_LONGITUDE}
@ -45,11 +44,10 @@ def _async_get_diagnostics(
device: DeviceEntry | None = None,
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
mqtt_instance = hass.data[DATA_MQTT].client
if TYPE_CHECKING:
assert mqtt_instance is not None
redacted_config = async_redact_data(mqtt_instance.conf, REDACT_CONFIG)
redacted_config = {
"data": async_redact_data(dict(entry.data), REDACT_CONFIG),
"options": dict(entry.options),
}
data = {
"connected": is_connected(hass),

View File

@ -89,10 +89,7 @@ rules:
comment: >
This is not possible because the integrations generates entities
based on a user supplied config or discovery.
reconfiguration-flow:
status: done
comment: >
This integration can also be reconfigured via options flow.
reconfiguration-flow: done
dynamic-devices:
status: done
comment: |

View File

@ -18,7 +18,6 @@ from tests.common import MockConfigEntry
from tests.typing import MqttMockPahoClient
ENTRY_DEFAULT_BIRTH_MESSAGE = {
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "homeassistant/status",
mqtt.ATTR_PAYLOAD: "online",
@ -77,6 +76,7 @@ def mock_debouncer(hass: HomeAssistant) -> Generator[asyncio.Event]:
async def setup_with_birth_msg_client_mock(
hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any] | None,
mqtt_config_entry_options: dict[str, Any] | None,
mqtt_client_mock: MqttMockPahoClient,
) -> AsyncGenerator[MqttMockPahoClient]:
"""Test sending birth message."""
@ -89,6 +89,9 @@ async def setup_with_birth_msg_client_mock(
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=mqtt_config_entry_data or {mqtt.CONF_BROKER: "test-broker"},
options=mqtt_config_entry_options or {},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)

View File

@ -105,6 +105,8 @@ async def test_mqtt_await_ack_at_disconnect(hass: HomeAssistant) -> None:
mqtt.CONF_BROKER: "test-broker",
mqtt.CONF_DISCOVERY: False,
},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
@ -132,7 +134,7 @@ async def test_mqtt_await_ack_at_disconnect(hass: HomeAssistant) -> None:
await hass.async_block_till_done(wait_background_tasks=True)
@pytest.mark.parametrize("mqtt_config_entry_data", [ENTRY_DEFAULT_BIRTH_MESSAGE])
@pytest.mark.parametrize("mqtt_config_entry_options", [ENTRY_DEFAULT_BIRTH_MESSAGE])
async def test_publish(
hass: HomeAssistant, setup_with_birth_msg_client_mock: MqttMockPahoClient
) -> None:
@ -1022,8 +1024,8 @@ async def test_unsubscribe_race(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
)
async def test_restore_subscriptions_on_reconnect(
hass: HomeAssistant,
@ -1059,8 +1061,8 @@ async def test_restore_subscriptions_on_reconnect(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
)
async def test_restore_all_active_subscriptions_on_reconnect(
hass: HomeAssistant,
@ -1100,8 +1102,8 @@ async def test_restore_all_active_subscriptions_on_reconnect(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
)
async def test_subscribed_at_highest_qos(
hass: HomeAssistant,
@ -1136,7 +1138,12 @@ async def test_initial_setup_logs_error(
mqtt_client_mock: MqttMockPahoClient,
) -> None:
"""Test for setup failure if initial client connection fails."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
mqtt_client_mock.connect.side_effect = MagicMock(return_value=1)
try:
@ -1239,7 +1246,12 @@ async def test_publish_error(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test publish error."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
# simulate an Out of memory error
@ -1381,7 +1393,10 @@ async def test_handle_mqtt_timeout_on_callback(
)
entry = MockConfigEntry(
domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -1414,7 +1429,12 @@ async def test_setup_raises_config_entry_not_ready_if_no_connect_broker(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, exception: Exception
) -> None:
"""Test for setup failure if connection to broker is missing."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
with patch(
@ -1495,17 +1515,19 @@ async def test_tls_version(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[
{
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "birth",
mqtt.ATTR_PAYLOAD: "birth",
mqtt.ATTR_QOS: 0,
mqtt.ATTR_RETAIN: False,
(
{mqtt.CONF_BROKER: "mock-broker"},
{
mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "birth",
mqtt.ATTR_PAYLOAD: "birth",
mqtt.ATTR_QOS: 0,
mqtt.ATTR_RETAIN: False,
}
},
}
)
],
)
@patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0)
@ -1515,11 +1537,18 @@ async def test_custom_birth_message(
hass: HomeAssistant,
mock_debouncer: asyncio.Event,
mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient,
) -> None:
"""Test sending birth message."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=mqtt_config_entry_data)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=mqtt_config_entry_data,
options=mqtt_config_entry_options,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id)
@ -1533,7 +1562,7 @@ async def test_custom_birth_message(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
"mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE],
)
async def test_default_birth_message(
@ -1548,8 +1577,8 @@ async def test_default_birth_message(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: {}}],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_BIRTH_MESSAGE: {}})],
)
@patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0)
@patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.0)
@ -1559,10 +1588,17 @@ async def test_no_birth_message(
record_calls: MessageCallbackType,
mock_debouncer: asyncio.Event,
mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient,
) -> None:
"""Test disabling birth message."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=mqtt_config_entry_data)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=mqtt_config_entry_data,
options=mqtt_config_entry_options,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
mock_debouncer.clear()
@ -1582,20 +1618,27 @@ async def test_no_birth_message(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[ENTRY_DEFAULT_BIRTH_MESSAGE],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, ENTRY_DEFAULT_BIRTH_MESSAGE)],
)
@patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.2)
async def test_delayed_birth_message(
hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient,
) -> None:
"""Test sending birth message does not happen until Home Assistant starts."""
hass.set_state(CoreState.starting)
await hass.async_block_till_done()
birth = asyncio.Event()
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=mqtt_config_entry_data)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=mqtt_config_entry_data,
options=mqtt_config_entry_options,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id)
@ -1619,7 +1662,7 @@ async def test_delayed_birth_message(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
"mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE],
)
async def test_subscription_done_when_birth_message_is_sent(
@ -1637,26 +1680,37 @@ async def test_subscription_done_when_birth_message_is_sent(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[
{
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_WILL_MESSAGE: {
mqtt.ATTR_TOPIC: "death",
mqtt.ATTR_PAYLOAD: "death",
mqtt.ATTR_QOS: 0,
mqtt.ATTR_RETAIN: False,
(
{
mqtt.CONF_BROKER: "mock-broker",
},
}
{
mqtt.CONF_WILL_MESSAGE: {
mqtt.ATTR_TOPIC: "death",
mqtt.ATTR_PAYLOAD: "death",
mqtt.ATTR_QOS: 0,
mqtt.ATTR_RETAIN: False,
},
},
)
],
)
async def test_custom_will_message(
hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient,
) -> None:
"""Test will message."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=mqtt_config_entry_data)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=mqtt_config_entry_data,
options=mqtt_config_entry_options,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id)
@ -1678,16 +1732,23 @@ async def test_default_will_message(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_WILL_MESSAGE: {}}],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_WILL_MESSAGE: {}})],
)
async def test_no_will_message(
hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient,
) -> None:
"""Test will message."""
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=mqtt_config_entry_data)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=mqtt_config_entry_data,
options=mqtt_config_entry_options,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id)
@ -1697,7 +1758,7 @@ async def test_no_will_message(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
"mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE | {mqtt.CONF_DISCOVERY: False}],
)
async def test_mqtt_subscribes_topics_on_connect(
@ -1730,7 +1791,7 @@ async def test_mqtt_subscribes_topics_on_connect(
assert ("still/pending", 1) in subscribe_calls
@pytest.mark.parametrize("mqtt_config_entry_data", [ENTRY_DEFAULT_BIRTH_MESSAGE])
@pytest.mark.parametrize("mqtt_config_entry_options", [ENTRY_DEFAULT_BIRTH_MESSAGE])
async def test_mqtt_subscribes_wildcard_topics_in_correct_order(
hass: HomeAssistant,
mock_debouncer: asyncio.Event,
@ -1789,7 +1850,7 @@ async def test_mqtt_subscribes_wildcard_topics_in_correct_order(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
"mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE | {mqtt.CONF_DISCOVERY: False}],
)
async def test_mqtt_discovery_not_subscribes_when_disabled(
@ -1822,7 +1883,7 @@ async def test_mqtt_discovery_not_subscribes_when_disabled(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
"mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE],
)
async def test_mqtt_subscribes_in_single_call(
@ -1848,7 +1909,7 @@ async def test_mqtt_subscribes_in_single_call(
]
@pytest.mark.parametrize("mqtt_config_entry_data", [ENTRY_DEFAULT_BIRTH_MESSAGE])
@pytest.mark.parametrize("mqtt_config_entry_options", [ENTRY_DEFAULT_BIRTH_MESSAGE])
@patch("homeassistant.components.mqtt.client.MAX_SUBSCRIBES_PER_CALL", 2)
@patch("homeassistant.components.mqtt.client.MAX_UNSUBSCRIBES_PER_CALL", 2)
async def test_mqtt_subscribes_and_unsubscribes_in_chunks(

View File

@ -1887,7 +1887,12 @@ async def help_test_reloadable(
mqtt.DOMAIN: {domain: [old_config_1, old_config_2]},
}
# Start the MQTT entry with the old config
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
mqtt_client_mock.connect.return_value = 0
with patch("homeassistant.config.load_yaml_config_file", return_value=old_config):

View File

@ -43,6 +43,28 @@ ADD_ON_DISCOVERY_INFO = {
MOCK_CLIENT_CERT = b"## mock client certificate file ##"
MOCK_CLIENT_KEY = b"## mock key file ##"
MOCK_ENTRY_DATA = {
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
}
MOCK_ENTRY_OPTIONS = {
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "ha_state/online",
mqtt.ATTR_PAYLOAD: "online",
mqtt.ATTR_QOS: 1,
mqtt.ATTR_RETAIN: True,
},
mqtt.CONF_WILL_MESSAGE: {
mqtt.ATTR_TOPIC: "ha_state/offline",
mqtt.ATTR_PAYLOAD: "offline",
mqtt.ATTR_QOS: 2,
mqtt.ATTR_RETAIN: False,
},
}
@pytest.fixture(autouse=True)
def mock_finish_setup() -> Generator[MagicMock]:
@ -243,8 +265,10 @@ async def test_user_connection_works(
assert result["result"].data == {
"broker": "127.0.0.1",
"port": 1883,
"discovery": True,
}
# Check we have the latest Config Entry version
assert result["result"].version == 1
assert result["result"].minor_version == 2
# Check we tried the connection
assert len(mock_try_connection.mock_calls) == 1
# Check config entry got setup
@ -283,7 +307,6 @@ async def test_user_connection_works_with_supervisor(
assert result["result"].data == {
"broker": "127.0.0.1",
"port": 1883,
"discovery": True,
}
# Check we tried the connection
assert len(mock_try_connection.mock_calls) == 1
@ -324,7 +347,6 @@ async def test_user_v5_connection_works(
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["result"].data == {
"broker": "another-broker",
"discovery": True,
"port": 2345,
"protocol": "5",
}
@ -383,14 +405,12 @@ async def test_manual_config_set(
assert result["result"].data == {
"broker": "127.0.0.1",
"port": 1883,
"discovery": True,
}
# Check we tried the connection, with precedence for config entry settings
mock_try_connection.assert_called_once_with(
{
"broker": "127.0.0.1",
"port": 1883,
"discovery": True,
},
)
# Check config entry got setup
@ -401,7 +421,11 @@ async def test_manual_config_set(
async def test_user_single_instance(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow."""
MockConfigEntry(domain="mqtt").add_to_hass(hass)
MockConfigEntry(
domain="mqtt",
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
"mqtt", context={"source": config_entries.SOURCE_USER}
@ -412,7 +436,11 @@ async def test_user_single_instance(hass: HomeAssistant) -> None:
async def test_hassio_already_configured(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow."""
MockConfigEntry(domain="mqtt").add_to_hass(hass)
MockConfigEntry(
domain="mqtt",
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
"mqtt", context={"source": config_entries.SOURCE_HASSIO}
@ -424,7 +452,10 @@ async def test_hassio_already_configured(hass: HomeAssistant) -> None:
async def test_hassio_ignored(hass: HomeAssistant) -> None:
"""Test we supervisor discovered instance can be ignored."""
MockConfigEntry(
domain=mqtt.DOMAIN, source=config_entries.SOURCE_IGNORE
domain=mqtt.DOMAIN,
source=config_entries.SOURCE_IGNORE,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
@ -934,43 +965,19 @@ async def test_addon_not_installed_failures(
async def test_option_flow(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
mock_try_connection: MagicMock,
) -> None:
"""Test config flow options."""
with patch(
"homeassistant.config.async_hass_config_yaml", AsyncMock(return_value={})
) as yaml_mock:
mqtt_mock = await mqtt_mock_entry()
mock_try_connection.return_value = True
await mqtt_mock_entry()
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
hass.config_entries.async_update_entry(
config_entry,
data={
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
},
)
mqtt_mock.async_connect.reset_mock()
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
await hass.async_block_till_done()
assert mqtt_mock.async_connect.call_count == 0
yaml_mock.reset_mock()
@ -992,12 +999,10 @@ async def test_option_flow(
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {}
assert config_entry.data == {
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
assert config_entry.data == {mqtt.CONF_BROKER: "mock-broker"}
assert config_entry.options == {
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
mqtt.CONF_BIRTH_MESSAGE: {
@ -1015,8 +1020,7 @@ async def test_option_flow(
}
await hass.async_block_till_done()
assert config_entry.title == "another-broker"
# assert that the entry was reloaded with the new config
# assert that the entry was reloaded with the new config
assert yaml_mock.await_count
@ -1071,7 +1075,7 @@ async def test_bad_certificate(
test_input.pop(mqtt.CONF_CLIENT_KEY)
mqtt_mock = await mqtt_mock_entry()
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
# Add at least one advanced option to get the full form
hass.config_entries.async_update_entry(
config_entry,
@ -1088,11 +1092,11 @@ async def test_bad_certificate(
mqtt_mock.async_connect.reset_mock()
result = await hass.config_entries.options.async_init(config_entry.entry_id)
result = await config_entry.start_reconfigure_flow(hass, show_advanced_options=True)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "another-broker",
@ -1109,14 +1113,14 @@ async def test_bad_certificate(
test_input["set_ca_cert"] = set_ca_cert
test_input["tls_insecure"] = tls_insecure
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=test_input,
)
if test_error is not None:
assert result["errors"]["base"] == test_error
return
assert result["errors"] == {}
assert "errors" not in result
@pytest.mark.parametrize(
@ -1148,7 +1152,7 @@ async def test_keepalive_validation(
mqtt_mock = await mqtt_mock_entry()
mock_try_connection.return_value = True
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
# Add at least one advanced option to get the full form
hass.config_entries.async_update_entry(
config_entry,
@ -1161,22 +1165,23 @@ async def test_keepalive_validation(
mqtt_mock.async_connect.reset_mock()
result = await hass.config_entries.options.async_init(config_entry.entry_id)
result = await config_entry.start_reconfigure_flow(hass, show_advanced_options=True)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
if error:
with pytest.raises(vol.Invalid):
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=test_input,
)
return
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=test_input,
)
assert not result["errors"]
assert "errors" not in result
assert result["reason"] == "reconfigure_successful"
async def test_disable_birth_will(
@ -1186,7 +1191,7 @@ async def test_disable_birth_will(
mock_reload_after_entry_update: MagicMock,
) -> None:
"""Test disabling birth and will."""
mqtt_mock = await mqtt_mock_entry()
await mqtt_mock_entry()
mock_try_connection.return_value = True
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
hass.config_entries.async_update_entry(
@ -1199,26 +1204,10 @@ async def test_disable_birth_will(
await hass.async_block_till_done()
mock_reload_after_entry_update.reset_mock()
mqtt_mock.async_connect.reset_mock()
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
await hass.async_block_till_done()
assert mqtt_mock.async_connect.call_count == 0
result = await hass.config_entries.options.async_configure(
result["flow_id"],
@ -1238,12 +1227,14 @@ async def test_disable_birth_will(
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {}
assert config_entry.data == {
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
assert result["data"] == {
"birth_message": {},
"discovery": True,
"discovery_prefix": "homeassistant",
"will_message": {},
}
assert config_entry.data == {mqtt.CONF_BROKER: "test-broker", CONF_PORT: 1234}
assert config_entry.options == {
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
mqtt.CONF_BIRTH_MESSAGE: {},
@ -1270,6 +1261,8 @@ async def test_invalid_discovery_prefix(
data={
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
},
options={
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
},
@ -1280,16 +1273,6 @@ async def test_invalid_discovery_prefix(
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
await hass.async_block_till_done()
@ -1308,6 +1291,8 @@ async def test_invalid_discovery_prefix(
assert config_entry.data == {
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
}
assert config_entry.options == {
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
}
@ -1356,6 +1341,8 @@ async def test_option_flow_default_suggested_values(
CONF_PORT: 1234,
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
},
options={
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "ha_state/online",
@ -1371,37 +1358,13 @@ async def test_option_flow_default_suggested_values(
},
},
)
await hass.async_block_till_done()
# Test default/suggested values from config
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
defaults = {
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
}
suggested = {
CONF_USERNAME: "user",
CONF_PASSWORD: PWD_NOT_CHANGED,
}
for key, value in defaults.items():
assert get_default(result["data_schema"].schema, key) == value
for key, value in suggested.items():
assert get_suggested(result["data_schema"].schema, key) == value
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
CONF_USERNAME: "us3r",
CONF_PASSWORD: "p4ss",
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
defaults = {
mqtt.CONF_DISCOVERY: True,
"birth_qos": 1,
"birth_retain": True,
"will_qos": 2,
@ -1421,7 +1384,6 @@ async def test_option_flow_default_suggested_values(
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_DISCOVERY: False,
"birth_topic": "ha_state/onl1ne",
"birth_payload": "onl1ne",
"birth_qos": 2,
@ -1437,28 +1399,8 @@ async def test_option_flow_default_suggested_values(
# Test updated default/suggested values from config
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
defaults = {
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
}
suggested = {
CONF_USERNAME: "us3r",
CONF_PASSWORD: PWD_NOT_CHANGED,
}
for key, value in defaults.items():
assert get_default(result["data_schema"].schema, key) == value
for key, value in suggested.items():
assert get_suggested(result["data_schema"].schema, key) == value
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={mqtt.CONF_BROKER: "another-broker", CONF_PORT: 2345},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
defaults = {
mqtt.CONF_DISCOVERY: False,
"birth_qos": 2,
"birth_retain": False,
"will_qos": 1,
@ -1478,7 +1420,6 @@ async def test_option_flow_default_suggested_values(
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_DISCOVERY: True,
"birth_topic": "ha_state/onl1ne",
"birth_payload": "onl1ne",
"birth_qos": 2,
@ -1496,7 +1437,8 @@ async def test_option_flow_default_suggested_values(
@pytest.mark.parametrize(
("advanced_options", "step_id"), [(False, "options"), (True, "broker")]
("advanced_options", "flow_result"),
[(False, FlowResultType.ABORT), (True, FlowResultType.FORM)],
)
@pytest.mark.usefixtures("mock_reload_after_entry_update")
async def test_skipping_advanced_options(
@ -1504,41 +1446,35 @@ async def test_skipping_advanced_options(
mqtt_mock_entry: MqttMockHAClientGenerator,
mock_try_connection: MagicMock,
advanced_options: bool,
step_id: str,
flow_result: FlowResultType,
) -> None:
"""Test advanced options option."""
test_input = {
mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345,
"advanced_options": advanced_options,
}
if advanced_options:
test_input["advanced_options"] = True
mqtt_mock = await mqtt_mock_entry()
mock_try_connection.return_value = True
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
# Initiate with a basic setup
hass.config_entries.async_update_entry(
config_entry,
data={
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
},
)
config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
mqtt_mock.async_connect.reset_mock()
result = await hass.config_entries.options.async_init(
config_entry.entry_id, context={"show_advanced_options": True}
result = await config_entry.start_reconfigure_flow(
hass, show_advanced_options=advanced_options
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
result = await hass.config_entries.options.async_configure(
assert ("advanced_options" in result["data_schema"].schema) == advanced_options
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=test_input,
)
assert result["step_id"] == step_id
assert result["type"] is flow_result
@pytest.mark.parametrize(
@ -1582,7 +1518,12 @@ async def test_step_reauth(
"""Test that the reauth step works."""
# Prepare the config entry
config_entry = MockConfigEntry(domain=mqtt.DOMAIN, data=test_input)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=test_input,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -1658,7 +1599,12 @@ async def test_step_hassio_reauth(
addon_info["hostname"] = "core-mosquitto"
# Prepare the config entry
config_entry = MockConfigEntry(domain=mqtt.DOMAIN, data=entry_data)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=entry_data,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -1740,7 +1686,12 @@ async def test_step_hassio_reauth_no_discovery_info(
addon_info["hostname"] = "core-mosquitto"
# Prepare the config entry
config_entry = MockConfigEntry(domain=mqtt.DOMAIN, data=entry_data)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data=entry_data,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -1762,11 +1713,15 @@ async def test_step_hassio_reauth_no_discovery_info(
mock_try_connection.assert_not_called()
async def test_options_user_connection_fails(
async def test_reconfigure_user_connection_fails(
hass: HomeAssistant, mock_try_connection_time_out: MagicMock
) -> None:
"""Test if connection cannot be made."""
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
@ -1775,11 +1730,11 @@ async def test_options_user_connection_fails(
CONF_PORT: 1234,
},
)
result = await hass.config_entries.options.async_init(config_entry.entry_id)
result = await config_entry.start_reconfigure_flow(hass, show_advanced_options=True)
assert result["type"] is FlowResultType.FORM
mock_try_connection_time_out.reset_mock()
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={mqtt.CONF_BROKER: "bad-broker", CONF_PORT: 2345},
)
@ -1800,7 +1755,11 @@ async def test_options_bad_birth_message_fails(
hass: HomeAssistant, mock_try_connection: MqttMockPahoClient
) -> None:
"""Test bad birth message."""
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
@ -1813,13 +1772,6 @@ async def test_options_bad_birth_message_fails(
mock_try_connection.return_value = True
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={mqtt.CONF_BROKER: "another-broker", CONF_PORT: 2345},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
@ -1841,7 +1793,11 @@ async def test_options_bad_will_message_fails(
hass: HomeAssistant, mock_try_connection: MagicMock
) -> None:
"""Test bad will message."""
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
@ -1854,13 +1810,6 @@ async def test_options_bad_will_message_fails(
mock_try_connection.return_value = True
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={mqtt.CONF_BROKER: "another-broker", CONF_PORT: 2345},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
@ -1878,15 +1827,16 @@ async def test_options_bad_will_message_fails(
}
@pytest.mark.parametrize(
"hass_config", [{"mqtt": {"sensor": [{"state_topic": "some-topic"}]}}]
)
@pytest.mark.usefixtures("mock_ssl_context", "mock_process_uploaded_file")
async def test_try_connection_with_advanced_parameters(
hass: HomeAssistant, mock_try_connection_success: MqttMockPahoClient
) -> None:
"""Test config flow with advanced parameters from config."""
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
@ -1920,7 +1870,7 @@ async def test_try_connection_with_advanced_parameters(
)
# Test default/suggested values from config
result = await hass.config_entries.options.async_init(config_entry.entry_id)
result = await config_entry.start_reconfigure_flow(hass, show_advanced_options=True)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
defaults = {
@ -1944,9 +1894,8 @@ async def test_try_connection_with_advanced_parameters(
assert get_suggested(result["data_schema"].schema, k) == v
# test we can change username and password
# as it was configured as auto in configuration.yaml is is migrated now
mock_try_connection_success.reset_mock()
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "another-broker",
@ -1961,9 +1910,8 @@ async def test_try_connection_with_advanced_parameters(
mqtt.CONF_WS_HEADERS: '{"h3": "v3"}',
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
assert result["step_id"] == "options"
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
await hass.async_block_till_done()
# check if the username and password was set from config flow and not from configuration.yaml
@ -1987,12 +1935,6 @@ async def test_try_connection_with_advanced_parameters(
"/new/path",
{"h3": "v3"},
)
# Accept default option
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
await hass.async_block_till_done()
@ -2005,7 +1947,11 @@ async def test_setup_with_advanced_settings(
"""Test config flow setup with advanced parameters."""
file_id = mock_process_uploaded_file.file_id
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
@ -2017,15 +1963,13 @@ async def test_setup_with_advanced_settings(
mock_try_connection.return_value = True
result = await hass.config_entries.options.async_init(
config_entry.entry_id, context={"show_advanced_options": True}
)
result = await config_entry.start_reconfigure_flow(hass, show_advanced_options=True)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker"
assert result["data_schema"].schema["advanced_options"]
# first iteration, basic settings
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "test-broker",
@ -2049,7 +1993,7 @@ async def test_setup_with_advanced_settings(
assert mqtt.CONF_CLIENT_KEY not in result["data_schema"].schema
# second iteration, advanced settings with request for client cert
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "test-broker",
@ -2080,7 +2024,7 @@ async def test_setup_with_advanced_settings(
assert result["data_schema"].schema[mqtt.CONF_WS_HEADERS]
# third iteration, advanced settings with client cert and key set and bad json payload
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "test-broker",
@ -2105,7 +2049,7 @@ async def test_setup_with_advanced_settings(
# fourth iteration, advanced settings with client cert and key set
# and correct json payload for ws_headers
result = await hass.config_entries.options.async_configure(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_BROKER: "test-broker",
@ -2124,17 +2068,8 @@ async def test_setup_with_advanced_settings(
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "options"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
# Check config entry result
assert config_entry.data == {
@ -2153,8 +2088,6 @@ async def test_setup_with_advanced_settings(
"header_2": "content_header_2",
},
mqtt.CONF_CERTIFICATE: "auto",
mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test",
}
@ -2163,7 +2096,11 @@ async def test_change_websockets_transport_to_tcp(
hass: HomeAssistant, mock_try_connection: MagicMock
) -> None:
"""Test reconfiguration flow changing websockets transport settings."""
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
config_entry = MockConfigEntry(
domain=mqtt.DOMAIN,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
@ -2254,3 +2191,95 @@ async def test_reconfigure_flow_form(
mqtt.CONF_WS_PATH: "/some_new_path",
}
await hass.async_block_till_done(wait_background_tasks=True)
@pytest.mark.parametrize(
(
"version",
"minor_version",
"data",
"options",
"expected_version",
"expected_minor_version",
),
[
(1, 1, MOCK_ENTRY_DATA | MOCK_ENTRY_OPTIONS, {}, 1, 2),
(1, 2, MOCK_ENTRY_DATA, MOCK_ENTRY_OPTIONS, 1, 2),
(1, 3, MOCK_ENTRY_DATA, MOCK_ENTRY_OPTIONS, 1, 3),
],
)
@pytest.mark.usefixtures("mock_reload_after_entry_update")
async def test_migrate_config_entry(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
version: int,
minor_version: int,
data: dict[str, Any],
options: dict[str, Any],
expected_version: int,
expected_minor_version: int,
) -> None:
"""Test migrating a config entry."""
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
# Mock to a migratable or compatbible config entry version
hass.config_entries.async_update_entry(
config_entry,
data=data,
options=options,
version=version,
minor_version=minor_version,
)
await hass.async_block_till_done()
# Start MQTT
await mqtt_mock_entry()
await hass.async_block_till_done()
assert (
config_entry.data | config_entry.options == MOCK_ENTRY_DATA | MOCK_ENTRY_OPTIONS
)
assert config_entry.version == expected_version
assert config_entry.minor_version == expected_minor_version
@pytest.mark.parametrize(
(
"version",
"minor_version",
"data",
"options",
"expected_version",
"expected_minor_version",
),
[
(2, 1, MOCK_ENTRY_DATA, MOCK_ENTRY_OPTIONS, 2, 1),
],
)
@pytest.mark.usefixtures("mock_reload_after_entry_update")
async def test_migrate_of_incompatible_config_entry(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
version: int,
minor_version: int,
data: dict[str, Any],
options: dict[str, Any],
expected_version: int,
expected_minor_version: int,
) -> None:
"""Test migrating a config entry."""
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
# Mock an incompatible config entry version
hass.config_entries.async_update_entry(
config_entry,
data=data,
options=options,
version=version,
minor_version=minor_version,
)
await hass.async_block_till_done()
assert config_entry.version == expected_version
assert config_entry.minor_version == expected_minor_version
# Try to start MQTT with incompatible config entry
with pytest.raises(AssertionError):
await mqtt_mock_entry()
assert config_entry.state is config_entries.ConfigEntryState.MIGRATION_ERROR

View File

@ -17,10 +17,12 @@ from tests.components.diagnostics import (
)
from tests.typing import ClientSessionGenerator, MqttMockHAClientGenerator
default_config = {
"birth_message": {},
default_entry_data = {
"broker": "mock-broker",
}
default_entry_options = {
"birth_message": {},
}
async def test_entry_diagnostics(
@ -38,7 +40,7 @@ async def test_entry_diagnostics(
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"connected": True,
"devices": [],
"mqtt_config": default_config,
"mqtt_config": {"data": default_entry_data, "options": default_entry_options},
"mqtt_debug_info": {"entities": [], "triggers": []},
}
@ -123,7 +125,7 @@ async def test_entry_diagnostics(
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"connected": True,
"devices": [expected_device],
"mqtt_config": default_config,
"mqtt_config": {"data": default_entry_data, "options": default_entry_options},
"mqtt_debug_info": expected_debug_info,
}
@ -132,20 +134,24 @@ async def test_entry_diagnostics(
) == {
"connected": True,
"device": expected_device,
"mqtt_config": default_config,
"mqtt_config": {"data": default_entry_data, "options": default_entry_options},
"mqtt_debug_info": expected_debug_info,
}
@pytest.mark.parametrize(
"mqtt_config_entry_data",
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[
{
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_BIRTH_MESSAGE: {},
CONF_PASSWORD: "hunter2",
CONF_USERNAME: "my_user",
}
(
{
mqtt.CONF_BROKER: "mock-broker",
CONF_PASSWORD: "hunter2",
CONF_USERNAME: "my_user",
},
{
mqtt.CONF_BIRTH_MESSAGE: {},
},
)
],
)
async def test_redact_diagnostics(
@ -157,9 +163,12 @@ async def test_redact_diagnostics(
) -> None:
"""Test redacting diagnostics."""
mqtt_mock = await mqtt_mock_entry()
expected_config = dict(default_config)
expected_config["password"] = "**REDACTED**"
expected_config["username"] = "**REDACTED**"
expected_config = {
"data": dict(default_entry_data),
"options": dict(default_entry_options),
}
expected_config["data"]["password"] = "**REDACTED**"
expected_config["data"]["username"] = "**REDACTED**"
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
mqtt_mock.connected = True

View File

@ -195,8 +195,8 @@ async def mock_mqtt_flow(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}],
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
)
async def test_subscribing_config_topic(
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
@ -1946,7 +1946,12 @@ async def test_cleanup_device_multiple_config_entries(
mqtt_mock = await mqtt_mock_entry()
ws_client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain="test", data={})
config_entry = MockConfigEntry(
domain="test",
data={},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
@ -2042,7 +2047,12 @@ async def test_cleanup_device_multiple_config_entries_mqtt(
) -> None:
"""Test discovered device is cleaned up when removed through MQTT."""
mqtt_mock = await mqtt_mock_entry()
config_entry = MockConfigEntry(domain="test", data={})
config_entry = MockConfigEntry(
domain="test",
data={},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
config_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
@ -2437,12 +2447,14 @@ async def test_no_implicit_state_topic_switch(
@pytest.mark.parametrize(
"mqtt_config_entry_data",
("mqtt_config_entry_data", "mqtt_config_entry_options"),
[
{
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_DISCOVERY_PREFIX: "my_home/homeassistant/register",
}
(
{mqtt.CONF_BROKER: "mock-broker"},
{
mqtt.CONF_DISCOVERY_PREFIX: "my_home/homeassistant/register",
},
)
],
)
async def test_complex_discovery_topic_prefix(
@ -2497,7 +2509,13 @@ async def test_mqtt_integration_discovery_flow_fitering_on_redundant_payload(
"""Handle birth message."""
birth.set()
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=ENTRY_DEFAULT_BIRTH_MESSAGE)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "mock-broker"},
options=ENTRY_DEFAULT_BIRTH_MESSAGE,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
with (
patch(
@ -2562,7 +2580,13 @@ async def test_mqtt_discovery_flow_starts_once(
"""Handle birth message."""
birth.set()
entry = MockConfigEntry(domain=mqtt.DOMAIN, data=ENTRY_DEFAULT_BIRTH_MESSAGE)
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "mock-broker"},
options=ENTRY_DEFAULT_BIRTH_MESSAGE,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
with (

View File

@ -695,7 +695,12 @@ async def test_reload_entry_with_restored_subscriptions(
) -> None:
"""Test reloading the config entry with with subscriptions restored."""
# Setup the MQTT entry
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
with patch("homeassistant.config.load_yaml_config_file", return_value={}):
@ -800,7 +805,10 @@ async def test_default_entry_setting_are_applied(
# Config entry data is incomplete but valid according the schema
entry = MockConfigEntry(
domain=mqtt.DOMAIN, data={"broker": "test-broker", "port": 1234}
domain=mqtt.DOMAIN,
data={"broker": "test-broker", "port": 1234},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN)
@ -1614,6 +1622,8 @@ async def test_unload_config_entry(
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "test-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)

View File

@ -313,7 +313,12 @@ async def test_default_entity_and_device_name(
hass.set_state(CoreState.starting)
await hass.async_block_till_done()
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "mock-broker"})
entry = MockConfigEntry(
domain=mqtt.DOMAIN,
data={mqtt.CONF_BROKER: "mock-broker"},
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)

View File

@ -231,6 +231,8 @@ async def test_waiting_for_client_not_loaded(
domain=mqtt.DOMAIN,
data={"broker": "test-broker"},
state=ConfigEntryState.NOT_LOADED,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -286,6 +288,8 @@ async def test_waiting_for_client_entry_fails(
domain=mqtt.DOMAIN,
data={"broker": "test-broker"},
state=ConfigEntryState.NOT_LOADED,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -314,6 +318,8 @@ async def test_waiting_for_client_setup_fails(
domain=mqtt.DOMAIN,
data={"broker": "test-broker"},
state=ConfigEntryState.NOT_LOADED,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -341,6 +347,8 @@ async def test_waiting_for_client_timeout(
domain=mqtt.DOMAIN,
data={"broker": "test-broker"},
state=ConfigEntryState.NOT_LOADED,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -360,6 +368,8 @@ async def test_waiting_for_client_with_disabled_entry(
domain=mqtt.DOMAIN,
data={"broker": "test-broker"},
state=ConfigEntryState.NOT_LOADED,
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
)
entry.add_to_hass(hass)

View File

@ -924,7 +924,13 @@ def fail_on_log_exception(
@pytest.fixture
def mqtt_config_entry_data() -> dict[str, Any] | None:
"""Fixture to allow overriding MQTT config."""
"""Fixture to allow overriding MQTT entry data."""
return None
@pytest.fixture
def mqtt_config_entry_options() -> dict[str, Any] | None:
"""Fixture to allow overriding MQTT entry options."""
return None
@ -1001,6 +1007,7 @@ async def mqtt_mock(
mock_hass_config: None,
mqtt_client_mock: MqttMockPahoClient,
mqtt_config_entry_data: dict[str, Any] | None,
mqtt_config_entry_options: dict[str, Any] | None,
mqtt_mock_entry: MqttMockHAClientGenerator,
) -> AsyncGenerator[MqttMockHAClient]:
"""Fixture to mock MQTT component."""
@ -1012,6 +1019,7 @@ async def _mqtt_mock_entry(
hass: HomeAssistant,
mqtt_client_mock: MqttMockPahoClient,
mqtt_config_entry_data: dict[str, Any] | None,
mqtt_config_entry_options: dict[str, Any] | None,
) -> AsyncGenerator[MqttMockHAClientGenerator]:
"""Fixture to mock a delayed setup of the MQTT config entry."""
# Local import to avoid processing MQTT modules when running a testcase
@ -1019,17 +1027,19 @@ async def _mqtt_mock_entry(
from homeassistant.components import mqtt # pylint: disable=import-outside-toplevel
if mqtt_config_entry_data is None:
mqtt_config_entry_data = {
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_BIRTH_MESSAGE: {},
}
mqtt_config_entry_data = {mqtt.CONF_BROKER: "mock-broker"}
if mqtt_config_entry_options is None:
mqtt_config_entry_options = {mqtt.CONF_BIRTH_MESSAGE: {}}
await hass.async_block_till_done()
entry = MockConfigEntry(
data=mqtt_config_entry_data,
options=mqtt_config_entry_options,
domain=mqtt.DOMAIN,
title="MQTT",
version=1,
minor_version=2,
)
entry.add_to_hass(hass)
@ -1045,7 +1055,6 @@ async def _mqtt_mock_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
@ -1140,6 +1149,7 @@ async def mqtt_mock_entry(
hass: HomeAssistant,
mqtt_client_mock: MqttMockPahoClient,
mqtt_config_entry_data: dict[str, Any] | None,
mqtt_config_entry_options: dict[str, Any] | None,
) -> AsyncGenerator[MqttMockHAClientGenerator]:
"""Set up an MQTT config entry."""
@ -1156,7 +1166,7 @@ async def mqtt_mock_entry(
return await mqtt_mock_entry(_async_setup_config_entry)
async with _mqtt_mock_entry(
hass, mqtt_client_mock, mqtt_config_entry_data
hass, mqtt_client_mock, mqtt_config_entry_data, mqtt_config_entry_options
) as mqtt_mock_entry:
yield _setup_mqtt_entry