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_WILL_MESSAGE,
CONF_WS_HEADERS, CONF_WS_HEADERS,
CONF_WS_PATH, CONF_WS_PATH,
CONFIG_ENTRY_MINOR_VERSION,
CONFIG_ENTRY_VERSION,
DEFAULT_DISCOVERY, DEFAULT_DISCOVERY,
DEFAULT_ENCODING, DEFAULT_ENCODING,
DEFAULT_PREFIX, DEFAULT_PREFIX,
@ -76,6 +78,7 @@ from .const import ( # noqa: F401
DEFAULT_RETAIN, DEFAULT_RETAIN,
DOMAIN, DOMAIN,
ENTITY_PLATFORMS, ENTITY_PLATFORMS,
ENTRY_OPTION_FIELDS,
MQTT_CONNECTION_STATE, MQTT_CONNECTION_STATE,
TEMPLATE_ERRORS, TEMPLATE_ERRORS,
) )
@ -282,15 +285,45 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True 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: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Load a config entry.""" """Load a config entry."""
conf: dict[str, Any]
mqtt_data: MqttData mqtt_data: MqttData
async def _setup_client() -> tuple[MqttData, dict[str, Any]]: async def _setup_client() -> tuple[MqttData, dict[str, Any]]:
"""Set up the MQTT client.""" """Set up the MQTT client."""
# Fetch configuration # Fetch configuration
conf = dict(entry.data) conf = dict(entry.data | entry.options)
hass_config = await conf_util.async_hass_config_yaml(hass) hass_config = await conf_util.async_hass_config_yaml(hass)
mqtt_yaml = CONFIG_SCHEMA(hass_config).get(DOMAIN, []) mqtt_yaml = CONFIG_SCHEMA(hass_config).get(DOMAIN, [])
await async_create_certificate_temp_files(hass, conf) await async_create_certificate_temp_files(hass, conf)

View File

@ -76,6 +76,8 @@ from .const import (
CONF_WILL_MESSAGE, CONF_WILL_MESSAGE,
CONF_WS_HEADERS, CONF_WS_HEADERS,
CONF_WS_PATH, CONF_WS_PATH,
CONFIG_ENTRY_MINOR_VERSION,
CONFIG_ENTRY_VERSION,
DEFAULT_BIRTH, DEFAULT_BIRTH,
DEFAULT_DISCOVERY, DEFAULT_DISCOVERY,
DEFAULT_ENCODING, DEFAULT_ENCODING,
@ -205,7 +207,9 @@ def update_password_from_user_input(
class FlowHandler(ConfigFlow, domain=DOMAIN): class FlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow.""" """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 _hassio_discovery: dict[str, Any] | None = None
_addon_manager: AddonManager _addon_manager: AddonManager
@ -496,7 +500,6 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
reconfigure_entry, reconfigure_entry,
data=validated_user_input, data=validated_user_input,
) )
validated_user_input[CONF_DISCOVERY] = DEFAULT_DISCOVERY
return self.async_create_entry( return self.async_create_entry(
title=validated_user_input[CONF_BROKER], title=validated_user_input[CONF_BROKER],
data=validated_user_input, data=validated_user_input,
@ -564,58 +567,17 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
class MQTTOptionsFlowHandler(OptionsFlow): class MQTTOptionsFlowHandler(OptionsFlow):
"""Handle MQTT options.""" """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: async def async_step_init(self, user_input: None = None) -> ConfigFlowResult:
"""Manage the MQTT options.""" """Manage the MQTT options."""
return await self.async_step_broker() return await self.async_step_options()
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,
)
async def async_step_options( async def async_step_options(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Manage the MQTT options.""" """Manage the MQTT options."""
errors = {} 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 bad_input: bool = False
def _birth_will(birt_or_will: str) -> dict[str, Any]: def _birth_will(birt_or_will: str) -> dict[str, Any]:
@ -674,26 +636,18 @@ class MQTTOptionsFlowHandler(OptionsFlow):
options_config[CONF_WILL_MESSAGE] = {} options_config[CONF_WILL_MESSAGE] = {}
if not bad_input: if not bad_input:
updated_config = {} return self.async_create_entry(data=options_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={})
birth = { birth = {
**DEFAULT_BIRTH, **DEFAULT_BIRTH,
**current_config.get(CONF_BIRTH_MESSAGE, {}), **options_config.get(CONF_BIRTH_MESSAGE, {}),
} }
will = { will = {
**DEFAULT_WILL, **DEFAULT_WILL,
**current_config.get(CONF_WILL_MESSAGE, {}), **options_config.get(CONF_WILL_MESSAGE, {}),
} }
discovery = current_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY) discovery = options_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY)
discovery_prefix = current_config.get(CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX) discovery_prefix = options_config.get(CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX)
# build form # build form
fields: OrderedDict[vol.Marker, Any] = OrderedDict() fields: OrderedDict[vol.Marker, Any] = OrderedDict()
@ -706,8 +660,8 @@ class MQTTOptionsFlowHandler(OptionsFlow):
fields[ fields[
vol.Optional( vol.Optional(
"birth_enable", "birth_enable",
default=CONF_BIRTH_MESSAGE not in current_config default=CONF_BIRTH_MESSAGE not in options_config
or current_config[CONF_BIRTH_MESSAGE] != {}, or options_config[CONF_BIRTH_MESSAGE] != {},
) )
] = BOOLEAN_SELECTOR ] = BOOLEAN_SELECTOR
fields[ fields[
@ -729,8 +683,8 @@ class MQTTOptionsFlowHandler(OptionsFlow):
fields[ fields[
vol.Optional( vol.Optional(
"will_enable", "will_enable",
default=CONF_WILL_MESSAGE not in current_config default=CONF_WILL_MESSAGE not in options_config
or current_config[CONF_WILL_MESSAGE] != {}, or options_config[CONF_WILL_MESSAGE] != {},
) )
] = BOOLEAN_SELECTOR ] = BOOLEAN_SELECTOR
fields[ fields[

View File

@ -4,7 +4,7 @@ import logging
import jinja2 import jinja2
from homeassistant.const import CONF_PAYLOAD, Platform from homeassistant.const import CONF_DISCOVERY, CONF_PAYLOAD, Platform
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
ATTR_DISCOVERY_HASH = "discovery_hash" ATTR_DISCOVERY_HASH = "discovery_hash"
@ -163,6 +163,20 @@ MQTT_CONNECTION_STATE = "mqtt_connection_state"
PAYLOAD_EMPTY_JSON = "{}" PAYLOAD_EMPTY_JSON = "{}"
PAYLOAD_NONE = "None" 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 = [ ENTITY_PLATFORMS = [
Platform.ALARM_CONTROL_PANEL, Platform.ALARM_CONTROL_PANEL,
Platform.BINARY_SENSOR, Platform.BINARY_SENSOR,

View File

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

View File

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

View File

@ -18,7 +18,6 @@ from tests.common import MockConfigEntry
from tests.typing import MqttMockPahoClient from tests.typing import MqttMockPahoClient
ENTRY_DEFAULT_BIRTH_MESSAGE = { ENTRY_DEFAULT_BIRTH_MESSAGE = {
mqtt.CONF_BROKER: "mock-broker",
mqtt.CONF_BIRTH_MESSAGE: { mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "homeassistant/status", mqtt.ATTR_TOPIC: "homeassistant/status",
mqtt.ATTR_PAYLOAD: "online", mqtt.ATTR_PAYLOAD: "online",
@ -77,6 +76,7 @@ def mock_debouncer(hass: HomeAssistant) -> Generator[asyncio.Event]:
async def setup_with_birth_msg_client_mock( async def setup_with_birth_msg_client_mock(
hass: HomeAssistant, hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any] | None, mqtt_config_entry_data: dict[str, Any] | None,
mqtt_config_entry_options: dict[str, Any] | None,
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> AsyncGenerator[MqttMockPahoClient]: ) -> AsyncGenerator[MqttMockPahoClient]:
"""Test sending birth message.""" """Test sending birth message."""
@ -89,6 +89,9 @@ async def setup_with_birth_msg_client_mock(
entry = MockConfigEntry( entry = MockConfigEntry(
domain=mqtt.DOMAIN, domain=mqtt.DOMAIN,
data=mqtt_config_entry_data or {mqtt.CONF_BROKER: "test-broker"}, 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) entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN) 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_BROKER: "test-broker",
mqtt.CONF_DISCOVERY: False, mqtt.CONF_DISCOVERY: False,
}, },
version=mqtt.CONFIG_ENTRY_VERSION,
minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id) 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) 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( async def test_publish(
hass: HomeAssistant, setup_with_birth_msg_client_mock: MqttMockPahoClient hass: HomeAssistant, setup_with_birth_msg_client_mock: MqttMockPahoClient
) -> None: ) -> None:
@ -1022,8 +1024,8 @@ async def test_unsubscribe_race(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", ("mqtt_config_entry_data", "mqtt_config_entry_options"),
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}], [({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
) )
async def test_restore_subscriptions_on_reconnect( async def test_restore_subscriptions_on_reconnect(
hass: HomeAssistant, hass: HomeAssistant,
@ -1059,8 +1061,8 @@ async def test_restore_subscriptions_on_reconnect(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", ("mqtt_config_entry_data", "mqtt_config_entry_options"),
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}], [({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
) )
async def test_restore_all_active_subscriptions_on_reconnect( async def test_restore_all_active_subscriptions_on_reconnect(
hass: HomeAssistant, hass: HomeAssistant,
@ -1100,8 +1102,8 @@ async def test_restore_all_active_subscriptions_on_reconnect(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", ("mqtt_config_entry_data", "mqtt_config_entry_options"),
[{mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_DISCOVERY: False}], [({mqtt.CONF_BROKER: "mock-broker"}, {mqtt.CONF_DISCOVERY: False})],
) )
async def test_subscribed_at_highest_qos( async def test_subscribed_at_highest_qos(
hass: HomeAssistant, hass: HomeAssistant,
@ -1136,7 +1138,12 @@ async def test_initial_setup_logs_error(
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test for setup failure if initial client connection fails.""" """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) entry.add_to_hass(hass)
mqtt_client_mock.connect.side_effect = MagicMock(return_value=1) mqtt_client_mock.connect.side_effect = MagicMock(return_value=1)
try: try:
@ -1239,7 +1246,12 @@ async def test_publish_error(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None: ) -> None:
"""Test publish error.""" """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) entry.add_to_hass(hass)
# simulate an Out of memory error # simulate an Out of memory error
@ -1381,7 +1393,10 @@ async def test_handle_mqtt_timeout_on_callback(
) )
entry = MockConfigEntry( 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) 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 hass: HomeAssistant, caplog: pytest.LogCaptureFixture, exception: Exception
) -> None: ) -> None:
"""Test for setup failure if connection to broker is missing.""" """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) entry.add_to_hass(hass)
with patch( with patch(
@ -1495,17 +1515,19 @@ async def test_tls_version(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", ("mqtt_config_entry_data", "mqtt_config_entry_options"),
[ [
{ (
mqtt.CONF_BROKER: "mock-broker", {mqtt.CONF_BROKER: "mock-broker"},
mqtt.CONF_BIRTH_MESSAGE: { {
mqtt.ATTR_TOPIC: "birth", mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_PAYLOAD: "birth", mqtt.ATTR_TOPIC: "birth",
mqtt.ATTR_QOS: 0, mqtt.ATTR_PAYLOAD: "birth",
mqtt.ATTR_RETAIN: False, mqtt.ATTR_QOS: 0,
mqtt.ATTR_RETAIN: False,
}
}, },
} )
], ],
) )
@patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0) @patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0)
@ -1515,11 +1537,18 @@ async def test_custom_birth_message(
hass: HomeAssistant, hass: HomeAssistant,
mock_debouncer: asyncio.Event, mock_debouncer: asyncio.Event,
mqtt_config_entry_data: dict[str, Any], mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test sending birth message.""" """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) entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN) hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
@ -1533,7 +1562,7 @@ async def test_custom_birth_message(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", "mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE], [ENTRY_DEFAULT_BIRTH_MESSAGE],
) )
async def test_default_birth_message( async def test_default_birth_message(
@ -1548,8 +1577,8 @@ async def test_default_birth_message(
@pytest.mark.parametrize( @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.CONF_BROKER: "mock-broker"}, {mqtt.CONF_BIRTH_MESSAGE: {}})],
) )
@patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0) @patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0)
@patch("homeassistant.components.mqtt.client.DISCOVERY_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, record_calls: MessageCallbackType,
mock_debouncer: asyncio.Event, mock_debouncer: asyncio.Event,
mqtt_config_entry_data: dict[str, Any], mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test disabling birth message.""" """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) entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN) hass.config.components.add(mqtt.DOMAIN)
mock_debouncer.clear() mock_debouncer.clear()
@ -1582,20 +1618,27 @@ async def test_no_birth_message(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", ("mqtt_config_entry_data", "mqtt_config_entry_options"),
[ENTRY_DEFAULT_BIRTH_MESSAGE], [({mqtt.CONF_BROKER: "mock-broker"}, ENTRY_DEFAULT_BIRTH_MESSAGE)],
) )
@patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.2) @patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.2)
async def test_delayed_birth_message( async def test_delayed_birth_message(
hass: HomeAssistant, hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any], mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test sending birth message does not happen until Home Assistant starts.""" """Test sending birth message does not happen until Home Assistant starts."""
hass.set_state(CoreState.starting) hass.set_state(CoreState.starting)
await hass.async_block_till_done() await hass.async_block_till_done()
birth = asyncio.Event() 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) entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN) hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
@ -1619,7 +1662,7 @@ async def test_delayed_birth_message(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", "mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE], [ENTRY_DEFAULT_BIRTH_MESSAGE],
) )
async def test_subscription_done_when_birth_message_is_sent( 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( @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.CONF_BROKER: "mock-broker",
mqtt.ATTR_TOPIC: "death",
mqtt.ATTR_PAYLOAD: "death",
mqtt.ATTR_QOS: 0,
mqtt.ATTR_RETAIN: False,
}, },
} {
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( async def test_custom_will_message(
hass: HomeAssistant, hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any], mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test will message.""" """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) entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN) hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
@ -1678,16 +1732,23 @@ async def test_default_will_message(
@pytest.mark.parametrize( @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.CONF_BROKER: "mock-broker"}, {mqtt.CONF_WILL_MESSAGE: {}})],
) )
async def test_no_will_message( async def test_no_will_message(
hass: HomeAssistant, hass: HomeAssistant,
mqtt_config_entry_data: dict[str, Any], mqtt_config_entry_data: dict[str, Any],
mqtt_config_entry_options: dict[str, Any],
mqtt_client_mock: MqttMockPahoClient, mqtt_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test will message.""" """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) entry.add_to_hass(hass)
hass.config.components.add(mqtt.DOMAIN) hass.config.components.add(mqtt.DOMAIN)
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
@ -1697,7 +1758,7 @@ async def test_no_will_message(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", "mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE | {mqtt.CONF_DISCOVERY: False}], [ENTRY_DEFAULT_BIRTH_MESSAGE | {mqtt.CONF_DISCOVERY: False}],
) )
async def test_mqtt_subscribes_topics_on_connect( 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 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( async def test_mqtt_subscribes_wildcard_topics_in_correct_order(
hass: HomeAssistant, hass: HomeAssistant,
mock_debouncer: asyncio.Event, mock_debouncer: asyncio.Event,
@ -1789,7 +1850,7 @@ async def test_mqtt_subscribes_wildcard_topics_in_correct_order(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mqtt_config_entry_data", "mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE | {mqtt.CONF_DISCOVERY: False}], [ENTRY_DEFAULT_BIRTH_MESSAGE | {mqtt.CONF_DISCOVERY: False}],
) )
async def test_mqtt_discovery_not_subscribes_when_disabled( 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( @pytest.mark.parametrize(
"mqtt_config_entry_data", "mqtt_config_entry_options",
[ENTRY_DEFAULT_BIRTH_MESSAGE], [ENTRY_DEFAULT_BIRTH_MESSAGE],
) )
async def test_mqtt_subscribes_in_single_call( 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_SUBSCRIBES_PER_CALL", 2)
@patch("homeassistant.components.mqtt.client.MAX_UNSUBSCRIBES_PER_CALL", 2) @patch("homeassistant.components.mqtt.client.MAX_UNSUBSCRIBES_PER_CALL", 2)
async def test_mqtt_subscribes_and_unsubscribes_in_chunks( 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]}, mqtt.DOMAIN: {domain: [old_config_1, old_config_2]},
} }
# Start the MQTT entry with the old config # 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) entry.add_to_hass(hass)
mqtt_client_mock.connect.return_value = 0 mqtt_client_mock.connect.return_value = 0
with patch("homeassistant.config.load_yaml_config_file", return_value=old_config): 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_CERT = b"## mock client certificate file ##"
MOCK_CLIENT_KEY = b"## mock key 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) @pytest.fixture(autouse=True)
def mock_finish_setup() -> Generator[MagicMock]: def mock_finish_setup() -> Generator[MagicMock]:
@ -243,8 +265,10 @@ async def test_user_connection_works(
assert result["result"].data == { assert result["result"].data == {
"broker": "127.0.0.1", "broker": "127.0.0.1",
"port": 1883, "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 # Check we tried the connection
assert len(mock_try_connection.mock_calls) == 1 assert len(mock_try_connection.mock_calls) == 1
# Check config entry got setup # Check config entry got setup
@ -283,7 +307,6 @@ async def test_user_connection_works_with_supervisor(
assert result["result"].data == { assert result["result"].data == {
"broker": "127.0.0.1", "broker": "127.0.0.1",
"port": 1883, "port": 1883,
"discovery": True,
} }
# Check we tried the connection # Check we tried the connection
assert len(mock_try_connection.mock_calls) == 1 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["type"] is FlowResultType.CREATE_ENTRY
assert result["result"].data == { assert result["result"].data == {
"broker": "another-broker", "broker": "another-broker",
"discovery": True,
"port": 2345, "port": 2345,
"protocol": "5", "protocol": "5",
} }
@ -383,14 +405,12 @@ async def test_manual_config_set(
assert result["result"].data == { assert result["result"].data == {
"broker": "127.0.0.1", "broker": "127.0.0.1",
"port": 1883, "port": 1883,
"discovery": True,
} }
# Check we tried the connection, with precedence for config entry settings # Check we tried the connection, with precedence for config entry settings
mock_try_connection.assert_called_once_with( mock_try_connection.assert_called_once_with(
{ {
"broker": "127.0.0.1", "broker": "127.0.0.1",
"port": 1883, "port": 1883,
"discovery": True,
}, },
) )
# Check config entry got setup # Check config entry got setup
@ -401,7 +421,11 @@ async def test_manual_config_set(
async def test_user_single_instance(hass: HomeAssistant) -> None: async def test_user_single_instance(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow.""" """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( result = await hass.config_entries.flow.async_init(
"mqtt", context={"source": config_entries.SOURCE_USER} "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: async def test_hassio_already_configured(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow.""" """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( result = await hass.config_entries.flow.async_init(
"mqtt", context={"source": config_entries.SOURCE_HASSIO} "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: async def test_hassio_ignored(hass: HomeAssistant) -> None:
"""Test we supervisor discovered instance can be ignored.""" """Test we supervisor discovered instance can be ignored."""
MockConfigEntry( 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) ).add_to_hass(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -934,43 +965,19 @@ async def test_addon_not_installed_failures(
async def test_option_flow( async def test_option_flow(
hass: HomeAssistant, hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator, mqtt_mock_entry: MqttMockHAClientGenerator,
mock_try_connection: MagicMock,
) -> None: ) -> None:
"""Test config flow options.""" """Test config flow options."""
with patch( with patch(
"homeassistant.config.async_hass_config_yaml", AsyncMock(return_value={}) "homeassistant.config.async_hass_config_yaml", AsyncMock(return_value={})
) as yaml_mock: ) as yaml_mock:
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] 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) result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM 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" assert result["step_id"] == "options"
await hass.async_block_till_done() await hass.async_block_till_done()
assert mqtt_mock.async_connect.call_count == 0
yaml_mock.reset_mock() yaml_mock.reset_mock()
@ -992,12 +999,10 @@ async def test_option_flow(
}, },
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {} await hass.async_block_till_done()
assert config_entry.data == { await hass.async_block_till_done(wait_background_tasks=True)
mqtt.CONF_BROKER: "another-broker", assert config_entry.data == {mqtt.CONF_BROKER: "mock-broker"}
CONF_PORT: 2345, assert config_entry.options == {
CONF_USERNAME: "user",
CONF_PASSWORD: "pass",
mqtt.CONF_DISCOVERY: True, mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant", mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
mqtt.CONF_BIRTH_MESSAGE: { mqtt.CONF_BIRTH_MESSAGE: {
@ -1015,8 +1020,7 @@ async def test_option_flow(
} }
await hass.async_block_till_done() 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 assert yaml_mock.await_count
@ -1071,7 +1075,7 @@ async def test_bad_certificate(
test_input.pop(mqtt.CONF_CLIENT_KEY) test_input.pop(mqtt.CONF_CLIENT_KEY)
mqtt_mock = await mqtt_mock_entry() 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 # Add at least one advanced option to get the full form
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -1088,11 +1092,11 @@ async def test_bad_certificate(
mqtt_mock.async_connect.reset_mock() 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["type"] is FlowResultType.FORM
assert result["step_id"] == "broker" assert result["step_id"] == "broker"
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_BROKER: "another-broker", mqtt.CONF_BROKER: "another-broker",
@ -1109,14 +1113,14 @@ async def test_bad_certificate(
test_input["set_ca_cert"] = set_ca_cert test_input["set_ca_cert"] = set_ca_cert
test_input["tls_insecure"] = tls_insecure 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"], result["flow_id"],
user_input=test_input, user_input=test_input,
) )
if test_error is not None: if test_error is not None:
assert result["errors"]["base"] == test_error assert result["errors"]["base"] == test_error
return return
assert result["errors"] == {} assert "errors" not in result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1148,7 +1152,7 @@ async def test_keepalive_validation(
mqtt_mock = await mqtt_mock_entry() mqtt_mock = await mqtt_mock_entry()
mock_try_connection.return_value = True 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 # Add at least one advanced option to get the full form
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -1161,22 +1165,23 @@ async def test_keepalive_validation(
mqtt_mock.async_connect.reset_mock() 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["type"] is FlowResultType.FORM
assert result["step_id"] == "broker" assert result["step_id"] == "broker"
if error: if error:
with pytest.raises(vol.Invalid): with pytest.raises(vol.Invalid):
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input=test_input, user_input=test_input,
) )
return return
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input=test_input, user_input=test_input,
) )
assert not result["errors"] assert "errors" not in result
assert result["reason"] == "reconfigure_successful"
async def test_disable_birth_will( async def test_disable_birth_will(
@ -1186,7 +1191,7 @@ async def test_disable_birth_will(
mock_reload_after_entry_update: MagicMock, mock_reload_after_entry_update: MagicMock,
) -> None: ) -> None:
"""Test disabling birth and will.""" """Test disabling birth and will."""
mqtt_mock = await mqtt_mock_entry() await mqtt_mock_entry()
mock_try_connection.return_value = True mock_try_connection.return_value = True
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
@ -1199,26 +1204,10 @@ async def test_disable_birth_will(
await hass.async_block_till_done() await hass.async_block_till_done()
mock_reload_after_entry_update.reset_mock() 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) result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM 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" assert result["step_id"] == "options"
await hass.async_block_till_done() await hass.async_block_till_done()
assert mqtt_mock.async_connect.call_count == 0
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
@ -1238,12 +1227,14 @@ async def test_disable_birth_will(
}, },
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {} assert result["data"] == {
assert config_entry.data == { "birth_message": {},
mqtt.CONF_BROKER: "another-broker", "discovery": True,
CONF_PORT: 2345, "discovery_prefix": "homeassistant",
CONF_USERNAME: "user", "will_message": {},
CONF_PASSWORD: "pass", }
assert config_entry.data == {mqtt.CONF_BROKER: "test-broker", CONF_PORT: 1234}
assert config_entry.options == {
mqtt.CONF_DISCOVERY: True, mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant", mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
mqtt.CONF_BIRTH_MESSAGE: {}, mqtt.CONF_BIRTH_MESSAGE: {},
@ -1270,6 +1261,8 @@ async def test_invalid_discovery_prefix(
data={ data={
mqtt.CONF_BROKER: "test-broker", mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234, CONF_PORT: 1234,
},
options={
mqtt.CONF_DISCOVERY: True, mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant", 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) result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM 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" assert result["step_id"] == "options"
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1308,6 +1291,8 @@ async def test_invalid_discovery_prefix(
assert config_entry.data == { assert config_entry.data == {
mqtt.CONF_BROKER: "test-broker", mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234, CONF_PORT: 1234,
}
assert config_entry.options == {
mqtt.CONF_DISCOVERY: True, mqtt.CONF_DISCOVERY: True,
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant", mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
} }
@ -1356,6 +1341,8 @@ async def test_option_flow_default_suggested_values(
CONF_PORT: 1234, CONF_PORT: 1234,
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
},
options={
mqtt.CONF_DISCOVERY: True, mqtt.CONF_DISCOVERY: True,
mqtt.CONF_BIRTH_MESSAGE: { mqtt.CONF_BIRTH_MESSAGE: {
mqtt.ATTR_TOPIC: "ha_state/online", 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 # Test default/suggested values from config
result = await hass.config_entries.options.async_init(config_entry.entry_id) result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM 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" assert result["step_id"] == "options"
defaults = { defaults = {
mqtt.CONF_DISCOVERY: True,
"birth_qos": 1, "birth_qos": 1,
"birth_retain": True, "birth_retain": True,
"will_qos": 2, "will_qos": 2,
@ -1421,7 +1384,6 @@ async def test_option_flow_default_suggested_values(
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_DISCOVERY: False,
"birth_topic": "ha_state/onl1ne", "birth_topic": "ha_state/onl1ne",
"birth_payload": "onl1ne", "birth_payload": "onl1ne",
"birth_qos": 2, "birth_qos": 2,
@ -1437,28 +1399,8 @@ async def test_option_flow_default_suggested_values(
# Test updated default/suggested values from config # Test updated default/suggested values from config
result = await hass.config_entries.options.async_init(config_entry.entry_id) result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM 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" assert result["step_id"] == "options"
defaults = { defaults = {
mqtt.CONF_DISCOVERY: False,
"birth_qos": 2, "birth_qos": 2,
"birth_retain": False, "birth_retain": False,
"will_qos": 1, "will_qos": 1,
@ -1478,7 +1420,6 @@ async def test_option_flow_default_suggested_values(
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_DISCOVERY: True,
"birth_topic": "ha_state/onl1ne", "birth_topic": "ha_state/onl1ne",
"birth_payload": "onl1ne", "birth_payload": "onl1ne",
"birth_qos": 2, "birth_qos": 2,
@ -1496,7 +1437,8 @@ async def test_option_flow_default_suggested_values(
@pytest.mark.parametrize( @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") @pytest.mark.usefixtures("mock_reload_after_entry_update")
async def test_skipping_advanced_options( async def test_skipping_advanced_options(
@ -1504,41 +1446,35 @@ async def test_skipping_advanced_options(
mqtt_mock_entry: MqttMockHAClientGenerator, mqtt_mock_entry: MqttMockHAClientGenerator,
mock_try_connection: MagicMock, mock_try_connection: MagicMock,
advanced_options: bool, advanced_options: bool,
step_id: str, flow_result: FlowResultType,
) -> None: ) -> None:
"""Test advanced options option.""" """Test advanced options option."""
test_input = { test_input = {
mqtt.CONF_BROKER: "another-broker", mqtt.CONF_BROKER: "another-broker",
CONF_PORT: 2345, CONF_PORT: 2345,
"advanced_options": advanced_options,
} }
if advanced_options:
test_input["advanced_options"] = True
mqtt_mock = await mqtt_mock_entry() mqtt_mock = await mqtt_mock_entry()
mock_try_connection.return_value = True 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]
# Initiate with a basic setup
hass.config_entries.async_update_entry(
config_entry,
data={
mqtt.CONF_BROKER: "test-broker",
CONF_PORT: 1234,
},
)
mqtt_mock.async_connect.reset_mock() mqtt_mock.async_connect.reset_mock()
result = await hass.config_entries.options.async_init( result = await config_entry.start_reconfigure_flow(
config_entry.entry_id, context={"show_advanced_options": True} hass, show_advanced_options=advanced_options
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker" 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"], result["flow_id"],
user_input=test_input, user_input=test_input,
) )
assert result["step_id"] == step_id assert result["type"] is flow_result
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1582,7 +1518,12 @@ async def test_step_reauth(
"""Test that the reauth step works.""" """Test that the reauth step works."""
# Prepare the config entry # 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) config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id) 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" addon_info["hostname"] = "core-mosquitto"
# Prepare the config entry # 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) config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id) 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" addon_info["hostname"] = "core-mosquitto"
# Prepare the config entry # 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) config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id) 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() 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 hass: HomeAssistant, mock_try_connection_time_out: MagicMock
) -> None: ) -> None:
"""Test if connection cannot be made.""" """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) config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -1775,11 +1730,11 @@ async def test_options_user_connection_fails(
CONF_PORT: 1234, 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 assert result["type"] is FlowResultType.FORM
mock_try_connection_time_out.reset_mock() 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"], result["flow_id"],
user_input={mqtt.CONF_BROKER: "bad-broker", CONF_PORT: 2345}, 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 hass: HomeAssistant, mock_try_connection: MqttMockPahoClient
) -> None: ) -> None:
"""Test bad birth message.""" """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) config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -1813,13 +1772,6 @@ async def test_options_bad_birth_message_fails(
mock_try_connection.return_value = True mock_try_connection.return_value = True
result = await hass.config_entries.options.async_init(config_entry.entry_id) 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["type"] is FlowResultType.FORM
assert result["step_id"] == "options" assert result["step_id"] == "options"
@ -1841,7 +1793,11 @@ async def test_options_bad_will_message_fails(
hass: HomeAssistant, mock_try_connection: MagicMock hass: HomeAssistant, mock_try_connection: MagicMock
) -> None: ) -> None:
"""Test bad will message.""" """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) config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -1854,13 +1810,6 @@ async def test_options_bad_will_message_fails(
mock_try_connection.return_value = True mock_try_connection.return_value = True
result = await hass.config_entries.options.async_init(config_entry.entry_id) 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["type"] is FlowResultType.FORM
assert result["step_id"] == "options" 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") @pytest.mark.usefixtures("mock_ssl_context", "mock_process_uploaded_file")
async def test_try_connection_with_advanced_parameters( async def test_try_connection_with_advanced_parameters(
hass: HomeAssistant, mock_try_connection_success: MqttMockPahoClient hass: HomeAssistant, mock_try_connection_success: MqttMockPahoClient
) -> None: ) -> None:
"""Test config flow with advanced parameters from config.""" """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) config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -1920,7 +1870,7 @@ async def test_try_connection_with_advanced_parameters(
) )
# Test default/suggested values from config # 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["type"] is FlowResultType.FORM
assert result["step_id"] == "broker" assert result["step_id"] == "broker"
defaults = { defaults = {
@ -1944,9 +1894,8 @@ async def test_try_connection_with_advanced_parameters(
assert get_suggested(result["data_schema"].schema, k) == v assert get_suggested(result["data_schema"].schema, k) == v
# test we can change username and password # 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() 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"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_BROKER: "another-broker", mqtt.CONF_BROKER: "another-broker",
@ -1961,9 +1910,8 @@ async def test_try_connection_with_advanced_parameters(
mqtt.CONF_WS_HEADERS: '{"h3": "v3"}', mqtt.CONF_WS_HEADERS: '{"h3": "v3"}',
}, },
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.ABORT
assert result["errors"] == {} assert result["reason"] == "reconfigure_successful"
assert result["step_id"] == "options"
await hass.async_block_till_done() await hass.async_block_till_done()
# check if the username and password was set from config flow and not from configuration.yaml # 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", "/new/path",
{"h3": "v3"}, {"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() await hass.async_block_till_done()
@ -2005,7 +1947,11 @@ async def test_setup_with_advanced_settings(
"""Test config flow setup with advanced parameters.""" """Test config flow setup with advanced parameters."""
file_id = mock_process_uploaded_file.file_id 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) config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -2017,15 +1963,13 @@ async def test_setup_with_advanced_settings(
mock_try_connection.return_value = True mock_try_connection.return_value = True
result = await hass.config_entries.options.async_init( result = await config_entry.start_reconfigure_flow(hass, show_advanced_options=True)
config_entry.entry_id, context={"show_advanced_options": True}
)
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "broker" assert result["step_id"] == "broker"
assert result["data_schema"].schema["advanced_options"] assert result["data_schema"].schema["advanced_options"]
# first iteration, basic settings # first iteration, basic settings
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_BROKER: "test-broker", 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 assert mqtt.CONF_CLIENT_KEY not in result["data_schema"].schema
# second iteration, advanced settings with request for client cert # 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"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_BROKER: "test-broker", 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] assert result["data_schema"].schema[mqtt.CONF_WS_HEADERS]
# third iteration, advanced settings with client cert and key set and bad json payload # 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"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_BROKER: "test-broker", 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 # fourth iteration, advanced settings with client cert and key set
# and correct json payload for ws_headers # 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"], result["flow_id"],
user_input={ user_input={
mqtt.CONF_BROKER: "test-broker", mqtt.CONF_BROKER: "test-broker",
@ -2124,17 +2068,8 @@ async def test_setup_with_advanced_settings(
}, },
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.ABORT
assert result["step_id"] == "options" assert result["reason"] == "reconfigure_successful"
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
# Check config entry result # Check config entry result
assert config_entry.data == { assert config_entry.data == {
@ -2153,8 +2088,6 @@ async def test_setup_with_advanced_settings(
"header_2": "content_header_2", "header_2": "content_header_2",
}, },
mqtt.CONF_CERTIFICATE: "auto", 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 hass: HomeAssistant, mock_try_connection: MagicMock
) -> None: ) -> None:
"""Test reconfiguration flow changing websockets transport settings.""" """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) config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
@ -2254,3 +2191,95 @@ async def test_reconfigure_flow_form(
mqtt.CONF_WS_PATH: "/some_new_path", mqtt.CONF_WS_PATH: "/some_new_path",
} }
await hass.async_block_till_done(wait_background_tasks=True) 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 from tests.typing import ClientSessionGenerator, MqttMockHAClientGenerator
default_config = { default_entry_data = {
"birth_message": {},
"broker": "mock-broker", "broker": "mock-broker",
} }
default_entry_options = {
"birth_message": {},
}
async def test_entry_diagnostics( 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) == { assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"connected": True, "connected": True,
"devices": [], "devices": [],
"mqtt_config": default_config, "mqtt_config": {"data": default_entry_data, "options": default_entry_options},
"mqtt_debug_info": {"entities": [], "triggers": []}, "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) == { assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"connected": True, "connected": True,
"devices": [expected_device], "devices": [expected_device],
"mqtt_config": default_config, "mqtt_config": {"data": default_entry_data, "options": default_entry_options},
"mqtt_debug_info": expected_debug_info, "mqtt_debug_info": expected_debug_info,
} }
@ -132,20 +134,24 @@ async def test_entry_diagnostics(
) == { ) == {
"connected": True, "connected": True,
"device": expected_device, "device": expected_device,
"mqtt_config": default_config, "mqtt_config": {"data": default_entry_data, "options": default_entry_options},
"mqtt_debug_info": expected_debug_info, "mqtt_debug_info": expected_debug_info,
} }
@pytest.mark.parametrize( @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.CONF_BROKER: "mock-broker",
CONF_PASSWORD: "hunter2", CONF_PASSWORD: "hunter2",
CONF_USERNAME: "my_user", CONF_USERNAME: "my_user",
} },
{
mqtt.CONF_BIRTH_MESSAGE: {},
},
)
], ],
) )
async def test_redact_diagnostics( async def test_redact_diagnostics(
@ -157,9 +163,12 @@ async def test_redact_diagnostics(
) -> None: ) -> None:
"""Test redacting diagnostics.""" """Test redacting diagnostics."""
mqtt_mock = await mqtt_mock_entry() mqtt_mock = await mqtt_mock_entry()
expected_config = dict(default_config) expected_config = {
expected_config["password"] = "**REDACTED**" "data": dict(default_entry_data),
expected_config["username"] = "**REDACTED**" "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] config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
mqtt_mock.connected = True mqtt_mock.connected = True

View File

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

View File

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

View File

@ -313,7 +313,12 @@ async def test_default_entity_and_device_name(
hass.set_state(CoreState.starting) hass.set_state(CoreState.starting)
await hass.async_block_till_done() 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) entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)

View File

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

View File

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