Remove dependency on async_setup from mqtt integration (#87987)
* Remove async_setup from mqtt integration * Final update common tests * Related tests init * Related tests diagnostics * Related tests config_flow * Cleanup and correct test * Keep websockets_api commands in async_setup
This commit is contained in:
parent
5e03272821
commit
14ffda9758
7 changed files with 153 additions and 428 deletions
|
@ -10,7 +10,7 @@ from typing import Any, cast
|
||||||
import jinja2
|
import jinja2
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config as conf_util, config_entries
|
from homeassistant import config as conf_util
|
||||||
from homeassistant.components import websocket_api
|
from homeassistant.components import websocket_api
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -25,16 +25,10 @@ from homeassistant.const import (
|
||||||
)
|
)
|
||||||
from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback
|
from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback
|
||||||
from homeassistant.exceptions import TemplateError, Unauthorized
|
from homeassistant.exceptions import TemplateError, Unauthorized
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import config_validation as cv, event, template
|
||||||
config_validation as cv,
|
|
||||||
discovery_flow,
|
|
||||||
event,
|
|
||||||
template,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.device_registry import DeviceEntry
|
from homeassistant.helpers.device_registry import DeviceEntry
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import async_get_platforms
|
from homeassistant.helpers.entity_platform import async_get_platforms
|
||||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
|
||||||
from homeassistant.helpers.reload import (
|
from homeassistant.helpers.reload import (
|
||||||
async_integration_yaml_config,
|
async_integration_yaml_config,
|
||||||
async_reload_integration_platforms,
|
async_reload_integration_platforms,
|
||||||
|
@ -52,11 +46,9 @@ from .client import ( # noqa: F401
|
||||||
subscribe,
|
subscribe,
|
||||||
)
|
)
|
||||||
from .config_integration import (
|
from .config_integration import (
|
||||||
CONFIG_SCHEMA_BASE,
|
|
||||||
CONFIG_SCHEMA_ENTRY,
|
CONFIG_SCHEMA_ENTRY,
|
||||||
DEFAULT_VALUES,
|
DEFAULT_VALUES,
|
||||||
DEPRECATED_CERTIFICATE_CONFIG_KEYS,
|
PLATFORM_CONFIG_SCHEMA_BASE,
|
||||||
DEPRECATED_CONFIG_KEYS,
|
|
||||||
)
|
)
|
||||||
from .const import ( # noqa: F401
|
from .const import ( # noqa: F401
|
||||||
ATTR_PAYLOAD,
|
ATTR_PAYLOAD,
|
||||||
|
@ -99,7 +91,6 @@ from .models import ( # noqa: F401
|
||||||
from .util import (
|
from .util import (
|
||||||
async_create_certificate_temp_files,
|
async_create_certificate_temp_files,
|
||||||
get_mqtt_data,
|
get_mqtt_data,
|
||||||
migrate_certificate_file_to_content,
|
|
||||||
mqtt_config_entry_enabled,
|
mqtt_config_entry_enabled,
|
||||||
valid_publish_topic,
|
valid_publish_topic,
|
||||||
valid_qos_schema,
|
valid_qos_schema,
|
||||||
|
@ -146,22 +137,22 @@ CONFIG_ENTRY_CONFIG_KEYS = [
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.All(
|
DOMAIN: vol.All(
|
||||||
cv.deprecated(CONF_BIRTH_MESSAGE), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_BIRTH_MESSAGE), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_BROKER), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_BROKER), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_CERTIFICATE), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_CERTIFICATE), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_CLIENT_ID), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_CLIENT_ID), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_CLIENT_CERT), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_CLIENT_CERT), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_CLIENT_KEY), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_CLIENT_KEY), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_DISCOVERY), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_DISCOVERY), # Removed in HA Core 2022.3
|
||||||
cv.deprecated(CONF_DISCOVERY_PREFIX), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_DISCOVERY_PREFIX), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_KEEPALIVE), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_KEEPALIVE), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_PASSWORD), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_PASSWORD), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_PORT), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_PORT), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_PROTOCOL), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_PROTOCOL), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_TLS_INSECURE), # Deprecated in HA Core 2022.11
|
cv.removed(CONF_TLS_INSECURE), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_USERNAME), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_USERNAME), # Removed in HA Core 2023.4
|
||||||
cv.deprecated(CONF_WILL_MESSAGE), # Deprecated in HA Core 2022.3
|
cv.removed(CONF_WILL_MESSAGE), # Removed in HA Core 2023.4
|
||||||
CONFIG_SCHEMA_BASE,
|
PLATFORM_CONFIG_SCHEMA_BASE,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
@ -197,34 +188,8 @@ async def _async_setup_discovery(
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up the MQTT protocol service."""
|
"""Set up the MQTT protocol service."""
|
||||||
mqtt_data = get_mqtt_data(hass, True)
|
|
||||||
|
|
||||||
conf: ConfigType | None = config.get(DOMAIN)
|
|
||||||
|
|
||||||
websocket_api.async_register_command(hass, websocket_subscribe)
|
websocket_api.async_register_command(hass, websocket_subscribe)
|
||||||
websocket_api.async_register_command(hass, websocket_mqtt_info)
|
websocket_api.async_register_command(hass, websocket_mqtt_info)
|
||||||
|
|
||||||
if conf:
|
|
||||||
conf = dict(conf)
|
|
||||||
mqtt_data.config = conf
|
|
||||||
|
|
||||||
if (mqtt_entry_status := mqtt_config_entry_enabled(hass)) is None:
|
|
||||||
# Create an import flow if the user has yaml configured entities etc.
|
|
||||||
# but no broker configuration. Note: The intention is not for this to
|
|
||||||
# import broker configuration from YAML because that has been deprecated.
|
|
||||||
discovery_flow.async_create_flow(
|
|
||||||
hass,
|
|
||||||
DOMAIN,
|
|
||||||
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
|
|
||||||
data={},
|
|
||||||
)
|
|
||||||
mqtt_data.reload_needed = True
|
|
||||||
elif mqtt_entry_status is False:
|
|
||||||
_LOGGER.info(
|
|
||||||
"MQTT will be not available until the config entry is enabled",
|
|
||||||
)
|
|
||||||
mqtt_data.reload_needed = True
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -247,30 +212,15 @@ def _filter_entry_config(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
hass.config_entries.async_update_entry(entry, data=filtered_data)
|
hass.config_entries.async_update_entry(entry, data=filtered_data)
|
||||||
|
|
||||||
|
|
||||||
async def _async_merge_basic_config(
|
async def _async_auto_mend_config(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, yaml_config: dict[str, Any]
|
hass: HomeAssistant, entry: ConfigEntry, yaml_config: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Merge basic options in configuration.yaml config with config entry.
|
"""Mends config fetched from config entry and adds missing values.
|
||||||
|
|
||||||
This mends incomplete migration from old version of HA Core.
|
This mends incomplete migration from old version of HA Core.
|
||||||
"""
|
"""
|
||||||
entry_updated = False
|
entry_updated = False
|
||||||
entry_config = {**entry.data}
|
entry_config = {**entry.data}
|
||||||
for key in DEPRECATED_CERTIFICATE_CONFIG_KEYS:
|
|
||||||
if key in yaml_config and key not in entry_config:
|
|
||||||
if (
|
|
||||||
content := await hass.async_add_executor_job(
|
|
||||||
migrate_certificate_file_to_content, yaml_config[key]
|
|
||||||
)
|
|
||||||
) is not None:
|
|
||||||
entry_config[key] = content
|
|
||||||
entry_updated = True
|
|
||||||
|
|
||||||
for key in DEPRECATED_CONFIG_KEYS:
|
|
||||||
if key in yaml_config and key not in entry_config:
|
|
||||||
entry_config[key] = yaml_config[key]
|
|
||||||
entry_updated = True
|
|
||||||
|
|
||||||
for key in MANDATORY_DEFAULT_VALUES:
|
for key in MANDATORY_DEFAULT_VALUES:
|
||||||
if key not in entry_config:
|
if key not in entry_config:
|
||||||
entry_config[key] = DEFAULT_VALUES[key]
|
entry_config[key] = DEFAULT_VALUES[key]
|
||||||
|
@ -298,17 +248,16 @@ async def _async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -
|
||||||
async def async_fetch_config(
|
async def async_fetch_config(
|
||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: ConfigEntry
|
||||||
) -> dict[str, Any] | None:
|
) -> dict[str, Any] | None:
|
||||||
"""Fetch fresh MQTT yaml config from the hass config when (re)loading the entry."""
|
"""Fetch fresh MQTT yaml config from the hass config."""
|
||||||
mqtt_data = get_mqtt_data(hass)
|
mqtt_data = get_mqtt_data(hass)
|
||||||
if mqtt_data.reload_entry:
|
hass_config = await conf_util.async_hass_config_yaml(hass)
|
||||||
hass_config = await conf_util.async_hass_config_yaml(hass)
|
mqtt_data.config = PLATFORM_CONFIG_SCHEMA_BASE(hass_config.get(DOMAIN, {}))
|
||||||
mqtt_data.config = CONFIG_SCHEMA_BASE(hass_config.get(DOMAIN, {}))
|
|
||||||
|
|
||||||
# Remove unknown keys from config entry data
|
# Remove unknown keys from config entry data
|
||||||
_filter_entry_config(hass, entry)
|
_filter_entry_config(hass, entry)
|
||||||
|
|
||||||
# Merge basic configuration, and add missing defaults for basic options
|
# Add missing defaults to migrate older config entries
|
||||||
await _async_merge_basic_config(hass, entry, mqtt_data.config or {})
|
await _async_auto_mend_config(hass, entry, mqtt_data.config or {})
|
||||||
# Bail out if broker setting is missing
|
# Bail out if broker setting is missing
|
||||||
if CONF_BROKER not in entry.data:
|
if CONF_BROKER not in entry.data:
|
||||||
_LOGGER.error("MQTT broker is not configured, please configure it")
|
_LOGGER.error("MQTT broker is not configured, please configure it")
|
||||||
|
@ -319,37 +268,6 @@ async def async_fetch_config(
|
||||||
if (conf := mqtt_data.config) is None:
|
if (conf := mqtt_data.config) is None:
|
||||||
conf = CONFIG_SCHEMA_ENTRY(dict(entry.data))
|
conf = CONFIG_SCHEMA_ENTRY(dict(entry.data))
|
||||||
|
|
||||||
# User has configuration.yaml config, warn about config entry overrides
|
|
||||||
elif any(key in conf for key in entry.data):
|
|
||||||
shared_keys = conf.keys() & entry.data.keys()
|
|
||||||
override = {k: entry.data[k] for k in shared_keys if conf[k] != entry.data[k]}
|
|
||||||
if CONF_PASSWORD in override:
|
|
||||||
override[CONF_PASSWORD] = "********"
|
|
||||||
if CONF_CLIENT_KEY in override:
|
|
||||||
override[CONF_CLIENT_KEY] = "-----PRIVATE KEY-----"
|
|
||||||
if override:
|
|
||||||
_LOGGER.warning(
|
|
||||||
(
|
|
||||||
"Deprecated configuration settings found in configuration.yaml. "
|
|
||||||
"These settings from your configuration entry will override: %s"
|
|
||||||
),
|
|
||||||
override,
|
|
||||||
)
|
|
||||||
# Register a repair issue
|
|
||||||
async_create_issue(
|
|
||||||
hass,
|
|
||||||
DOMAIN,
|
|
||||||
"deprecated_yaml_broker_settings",
|
|
||||||
breaks_in_ha_version="2023.4.0", # Warning first added in 2022.11.0
|
|
||||||
is_fixable=False,
|
|
||||||
severity=IssueSeverity.WARNING,
|
|
||||||
translation_key="deprecated_yaml_broker_settings",
|
|
||||||
translation_placeholders={
|
|
||||||
"more_info_url": "https://www.home-assistant.io/integrations/mqtt/",
|
|
||||||
"deprecated_settings": str(shared_keys)[1:-1],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Merge advanced configuration values from configuration.yaml
|
# Merge advanced configuration values from configuration.yaml
|
||||||
conf = _merge_extended_config(entry, conf)
|
conf = _merge_extended_config(entry, conf)
|
||||||
return conf
|
return conf
|
||||||
|
@ -359,10 +277,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Load a config entry."""
|
"""Load a config entry."""
|
||||||
mqtt_data = get_mqtt_data(hass, True)
|
mqtt_data = get_mqtt_data(hass, True)
|
||||||
|
|
||||||
# Merge basic configuration, and add missing defaults for basic options
|
# Fetch configuration and add missing defaults for basic options
|
||||||
if (conf := await async_fetch_config(hass, entry)) is None:
|
if (conf := await async_fetch_config(hass, entry)) is None:
|
||||||
# Bail out
|
# Bail out
|
||||||
return False
|
return False
|
||||||
|
|
||||||
await async_create_certificate_temp_files(hass, dict(entry.data))
|
await async_create_certificate_temp_files(hass, dict(entry.data))
|
||||||
mqtt_data.client = MQTT(hass, entry, conf)
|
mqtt_data.client = MQTT(hass, entry, conf)
|
||||||
# Restore saved subscriptions
|
# Restore saved subscriptions
|
||||||
|
@ -480,6 +399,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
async def _reload_config(call: ServiceCall) -> None:
|
async def _reload_config(call: ServiceCall) -> None:
|
||||||
"""Reload the platforms."""
|
"""Reload the platforms."""
|
||||||
|
# Fetch updated manual configured items and validate
|
||||||
|
config_yaml = await async_integration_yaml_config(hass, DOMAIN) or {}
|
||||||
|
mqtt_data.updated_config = config_yaml.get(DOMAIN, {})
|
||||||
|
|
||||||
# Reload the modern yaml platforms
|
# Reload the modern yaml platforms
|
||||||
mqtt_platforms = async_get_platforms(hass, DOMAIN)
|
mqtt_platforms = async_get_platforms(hass, DOMAIN)
|
||||||
tasks = [
|
tasks = [
|
||||||
|
@ -493,8 +416,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
]
|
]
|
||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
config_yaml = await async_integration_yaml_config(hass, DOMAIN) or {}
|
|
||||||
mqtt_data.updated_config = config_yaml.get(DOMAIN, {})
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*(
|
*(
|
||||||
[
|
[
|
||||||
|
|
|
@ -588,7 +588,7 @@ async def async_get_broker_settings(
|
||||||
current_user = user_input_basic.get(CONF_USERNAME)
|
current_user = user_input_basic.get(CONF_USERNAME)
|
||||||
current_pass = user_input_basic.get(CONF_PASSWORD)
|
current_pass = user_input_basic.get(CONF_PASSWORD)
|
||||||
else:
|
else:
|
||||||
# Get default settings from entry or yaml (if any)
|
# Get default settings from entry (if any)
|
||||||
current_broker = current_config.get(CONF_BROKER)
|
current_broker = current_config.get(CONF_BROKER)
|
||||||
current_port = current_config.get(CONF_PORT, DEFAULT_PORT)
|
current_port = current_config.get(CONF_PORT, DEFAULT_PORT)
|
||||||
current_user = current_config.get(CONF_USERNAME)
|
current_user = current_config.get(CONF_USERNAME)
|
||||||
|
|
|
@ -247,7 +247,7 @@ def warn_for_legacy_schema(domain: str) -> Callable[[ConfigType], ConfigType]:
|
||||||
(
|
(
|
||||||
"Manually configured MQTT %s(s) found under platform key '%s', "
|
"Manually configured MQTT %s(s) found under platform key '%s', "
|
||||||
"please move to the mqtt integration key, see "
|
"please move to the mqtt integration key, see "
|
||||||
"https://www.home-assistant.io/integrations/%s.mqtt/#new_format"
|
"https://www.home-assistant.io/integrations/%s.mqtt/"
|
||||||
),
|
),
|
||||||
domain,
|
domain,
|
||||||
domain,
|
domain,
|
||||||
|
|
|
@ -36,7 +36,6 @@ from homeassistant.helpers import (
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.setup import async_setup_component
|
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_mqtt_message
|
from tests.common import MockConfigEntry, async_fire_mqtt_message
|
||||||
from tests.typing import MqttMockHAClient, MqttMockHAClientGenerator, MqttMockPahoClient
|
from tests.typing import MqttMockHAClient, MqttMockHAClientGenerator, MqttMockPahoClient
|
||||||
|
@ -109,14 +108,15 @@ async def help_setup_component(
|
||||||
item += 1
|
item += 1
|
||||||
topic = f"homeassistant/{domain}/item_{item}/config"
|
topic = f"homeassistant/{domain}/item_{item}/config"
|
||||||
async_fire_mqtt_message(hass, topic, json.dumps(comp))
|
async_fire_mqtt_message(hass, topic, json.dumps(comp))
|
||||||
|
await hass.async_block_till_done()
|
||||||
else:
|
else:
|
||||||
await async_setup_component(
|
entry = MockConfigEntry(
|
||||||
hass,
|
domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}
|
||||||
mqtt.DOMAIN,
|
|
||||||
config,
|
|
||||||
)
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
with patch("homeassistant.config.load_yaml_config_file", return_value=config):
|
||||||
|
await entry.async_setup(hass)
|
||||||
mqtt_mock = None
|
mqtt_mock = None
|
||||||
await hass.async_block_till_done()
|
|
||||||
return mqtt_mock
|
return mqtt_mock
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ async def help_test_default_availability_payload(
|
||||||
|
|
||||||
async def help_test_default_availability_list_payload(
|
async def help_test_default_availability_list_payload(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mqtt_mock_entry_with_no_config: MqttMockHAClientGenerator,
|
mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator,
|
||||||
domain: str,
|
domain: str,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
no_assumed_state: bool = False,
|
no_assumed_state: bool = False,
|
||||||
|
@ -243,7 +243,7 @@ async def help_test_default_availability_list_payload(
|
||||||
{"topic": "availability-topic1"},
|
{"topic": "availability-topic1"},
|
||||||
{"topic": "availability-topic2"},
|
{"topic": "availability-topic2"},
|
||||||
]
|
]
|
||||||
await help_setup_component(hass, mqtt_mock_entry_with_no_config, domain, config)
|
await help_setup_component(hass, mqtt_mock_entry_no_yaml_config, domain, config)
|
||||||
|
|
||||||
state = hass.states.get(f"{domain}.test")
|
state = hass.states.get(f"{domain}.test")
|
||||||
assert state and state.state == STATE_UNAVAILABLE
|
assert state and state.state == STATE_UNAVAILABLE
|
||||||
|
@ -1169,7 +1169,7 @@ async def help_test_entity_id_update_subscriptions(
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
mqtt_mock = await help_setup_component(
|
mqtt_mock = await help_setup_component(
|
||||||
hass, mqtt_mock_entry_no_yaml_config, domain, config, True
|
hass, mqtt_mock_entry_no_yaml_config, domain, config, use_discovery=True
|
||||||
)
|
)
|
||||||
assert mqtt_mock is not None
|
assert mqtt_mock is not None
|
||||||
|
|
||||||
|
@ -1796,27 +1796,6 @@ async def help_test_reload_with_config(
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
async def help_test_entry_reload_with_new_config(
|
|
||||||
hass: HomeAssistant, tmp_path: Path, new_config: ConfigType
|
|
||||||
) -> None:
|
|
||||||
"""Test reloading with supplied config."""
|
|
||||||
mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
||||||
assert mqtt_config_entry.state is ConfigEntryState.LOADED
|
|
||||||
new_yaml_config_file = tmp_path / "configuration.yaml"
|
|
||||||
new_yaml_config = yaml.dump(new_config)
|
|
||||||
new_yaml_config_file.write_text(new_yaml_config)
|
|
||||||
assert new_yaml_config_file.read_text() == new_yaml_config
|
|
||||||
|
|
||||||
with patch.object(
|
|
||||||
module_hass_config, "YAML_CONFIG_FILE", new_yaml_config_file
|
|
||||||
), patch("paho.mqtt.client.Client") as mock_client:
|
|
||||||
mock_client().connect = lambda *args: 0
|
|
||||||
# reload the config entry
|
|
||||||
assert await hass.config_entries.async_reload(mqtt_config_entry.entry_id)
|
|
||||||
assert mqtt_config_entry.state is ConfigEntryState.LOADED
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
|
|
||||||
async def help_test_reloadable(
|
async def help_test_reloadable(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mqtt_client_mock: MqttMockPahoClient,
|
mqtt_client_mock: MqttMockPahoClient,
|
||||||
|
@ -1839,10 +1818,8 @@ async def help_test_reloadable(
|
||||||
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
|
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
mqtt_client_mock.connect.return_value = 0
|
mqtt_client_mock.connect.return_value = 0
|
||||||
# We should call await mqtt.async_setup_entry(hass, entry) when async_setup
|
with patch("homeassistant.config.load_yaml_config_file", return_value=old_config):
|
||||||
# is removed (this is planned with #87987). Until then we set up the mqtt component
|
await entry.async_setup(hass)
|
||||||
# to test reload after the async_setup setup has set the initial config
|
|
||||||
await help_setup_component(hass, None, domain, old_config, use_discovery=False)
|
|
||||||
|
|
||||||
assert hass.states.get(f"{domain}.test_old_1")
|
assert hass.states.get(f"{domain}.test_old_1")
|
||||||
assert hass.states.get(f"{domain}.test_old_2")
|
assert hass.states.get(f"{domain}.test_old_2")
|
||||||
|
@ -1860,15 +1837,15 @@ async def help_test_reloadable(
|
||||||
new_config = {
|
new_config = {
|
||||||
mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]},
|
mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]},
|
||||||
}
|
}
|
||||||
module_hass_config.load_yaml_config_file.return_value = new_config
|
with patch("homeassistant.config.load_yaml_config_file", return_value=new_config):
|
||||||
# Reload the mqtt entry with the new config
|
# Reload the mqtt entry with the new config
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"mqtt",
|
"mqtt",
|
||||||
SERVICE_RELOAD,
|
SERVICE_RELOAD,
|
||||||
{},
|
{},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all(domain)) == 3
|
assert len(hass.states.async_all(domain)) == 3
|
||||||
|
|
||||||
|
@ -1900,9 +1877,9 @@ async def help_test_unload_config_entry_with_platform(
|
||||||
config_setup: dict[str, dict[str, Any]] = copy.deepcopy(config)
|
config_setup: dict[str, dict[str, Any]] = copy.deepcopy(config)
|
||||||
config_setup[mqtt.DOMAIN][domain]["name"] = "config_setup"
|
config_setup[mqtt.DOMAIN][domain]["name"] = "config_setup"
|
||||||
config_name = config_setup
|
config_name = config_setup
|
||||||
await help_setup_component(
|
|
||||||
hass, mqtt_mock_entry_no_yaml_config, domain, config_setup
|
with patch("homeassistant.config.load_yaml_config_file", return_value=config_name):
|
||||||
)
|
await mqtt_mock_entry_no_yaml_config()
|
||||||
|
|
||||||
# prepare setup through discovery
|
# prepare setup through discovery
|
||||||
discovery_setup = copy.deepcopy(config[mqtt.DOMAIN][domain])
|
discovery_setup = copy.deepcopy(config[mqtt.DOMAIN][domain])
|
||||||
|
|
|
@ -9,13 +9,11 @@ from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import yaml
|
|
||||||
|
|
||||||
from homeassistant import config as hass_config, config_entries, data_entry_flow
|
from homeassistant import config_entries, data_entry_flow
|
||||||
from homeassistant.components import mqtt
|
from homeassistant.components import mqtt
|
||||||
from homeassistant.components.hassio import HassioServiceInfo
|
from homeassistant.components.hassio import HassioServiceInfo
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.setup import async_setup_component
|
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient
|
from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient
|
||||||
|
@ -267,39 +265,13 @@ async def test_user_connection_fails(
|
||||||
assert len(mock_finish_setup.mock_calls) == 0
|
assert len(mock_finish_setup.mock_calls) == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_manual_config_starts_discovery_flow(
|
@pytest.mark.parametrize("hass_config", [{"mqtt": {"sensor": {"state_topic": "test"}}}])
|
||||||
hass: HomeAssistant,
|
|
||||||
mock_try_connection: MqttMockPahoClient,
|
|
||||||
mock_finish_setup: MagicMock,
|
|
||||||
) -> None:
|
|
||||||
"""Test manual config initiates a discovery flow."""
|
|
||||||
# No flows in progress
|
|
||||||
assert hass.config_entries.flow.async_progress() == []
|
|
||||||
|
|
||||||
# MQTT config present in yaml config
|
|
||||||
assert await async_setup_component(hass, "mqtt", {"mqtt": {}})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert len(mock_finish_setup.mock_calls) == 0
|
|
||||||
|
|
||||||
# There should now be a discovery flow
|
|
||||||
flows = hass.config_entries.flow.async_progress()
|
|
||||||
assert len(flows) == 1
|
|
||||||
assert flows[0]["context"]["source"] == "integration_discovery"
|
|
||||||
assert flows[0]["handler"] == "mqtt"
|
|
||||||
assert flows[0]["step_id"] == "broker"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_manual_config_set(
|
async def test_manual_config_set(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_try_connection: MqttMockPahoClient,
|
mock_try_connection: MqttMockPahoClient,
|
||||||
mock_finish_setup: MagicMock,
|
mock_finish_setup: MagicMock,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test manual config does not create an entry, and entry can be setup late."""
|
"""Test manual config does not create an entry, and entry can be setup late."""
|
||||||
# MQTT config present in yaml config
|
|
||||||
assert await async_setup_component(hass, "mqtt", {"mqtt": {"broker": "bla"}})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
# do not try to reload
|
|
||||||
hass.data["mqtt"].reload_needed = False
|
|
||||||
assert len(mock_finish_setup.mock_calls) == 0
|
assert len(mock_finish_setup.mock_calls) == 0
|
||||||
|
|
||||||
mock_try_connection.return_value = True
|
mock_try_connection.return_value = True
|
||||||
|
@ -1162,6 +1134,9 @@ async def test_options_bad_will_message_fails(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"hass_config", [{"mqtt": {"sensor": [{"state_topic": "some-topic"}]}}]
|
||||||
|
)
|
||||||
async def test_try_connection_with_advanced_parameters(
|
async def test_try_connection_with_advanced_parameters(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_try_connection_success: MqttMockPahoClient,
|
mock_try_connection_success: MqttMockPahoClient,
|
||||||
|
@ -1170,23 +1145,6 @@ async def test_try_connection_with_advanced_parameters(
|
||||||
mock_process_uploaded_file: MagicMock,
|
mock_process_uploaded_file: MagicMock,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test config flow with advanced parameters from config."""
|
"""Test config flow with advanced parameters from config."""
|
||||||
|
|
||||||
with open(tmp_path / "client.crt", "wb") as certfile:
|
|
||||||
certfile.write(MOCK_CLIENT_CERT)
|
|
||||||
with open(tmp_path / "client.key", "wb") as keyfile:
|
|
||||||
keyfile.write(MOCK_CLIENT_KEY)
|
|
||||||
|
|
||||||
config = {
|
|
||||||
"certificate": "auto",
|
|
||||||
"tls_insecure": True,
|
|
||||||
"client_cert": str(tmp_path / "client.crt"),
|
|
||||||
"client_key": str(tmp_path / "client.key"),
|
|
||||||
}
|
|
||||||
new_yaml_config_file = tmp_path / "configuration.yaml"
|
|
||||||
new_yaml_config = yaml.dump({mqtt.DOMAIN: config})
|
|
||||||
new_yaml_config_file.write_text(new_yaml_config)
|
|
||||||
assert new_yaml_config_file.read_text() == new_yaml_config
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
config_entry.data = {
|
config_entry.data = {
|
||||||
|
@ -1195,6 +1153,10 @@ async def test_try_connection_with_advanced_parameters(
|
||||||
mqtt.CONF_USERNAME: "user",
|
mqtt.CONF_USERNAME: "user",
|
||||||
mqtt.CONF_PASSWORD: "pass",
|
mqtt.CONF_PASSWORD: "pass",
|
||||||
mqtt.CONF_TRANSPORT: "websockets",
|
mqtt.CONF_TRANSPORT: "websockets",
|
||||||
|
mqtt.CONF_CERTIFICATE: "auto",
|
||||||
|
mqtt.CONF_TLS_INSECURE: True,
|
||||||
|
mqtt.CONF_CLIENT_CERT: MOCK_CLIENT_CERT.decode(encoding="utf-8)"),
|
||||||
|
mqtt.CONF_CLIENT_KEY: MOCK_CLIENT_KEY.decode(encoding="utf-8"),
|
||||||
mqtt.CONF_WS_PATH: "/path/",
|
mqtt.CONF_WS_PATH: "/path/",
|
||||||
mqtt.CONF_WS_HEADERS: {"h1": "v1", "h2": "v2"},
|
mqtt.CONF_WS_HEADERS: {"h1": "v1", "h2": "v2"},
|
||||||
mqtt.CONF_KEEPALIVE: 30,
|
mqtt.CONF_KEEPALIVE: 30,
|
||||||
|
@ -1212,95 +1174,81 @@ async def test_try_connection_with_advanced_parameters(
|
||||||
mqtt.ATTR_RETAIN: False,
|
mqtt.ATTR_RETAIN: False,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
# Test default/suggested values from config
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "broker"
|
||||||
|
defaults = {
|
||||||
|
mqtt.CONF_BROKER: "test-broker",
|
||||||
|
mqtt.CONF_PORT: 1234,
|
||||||
|
"set_client_cert": True,
|
||||||
|
"set_ca_cert": "auto",
|
||||||
|
}
|
||||||
|
suggested = {
|
||||||
|
mqtt.CONF_USERNAME: "user",
|
||||||
|
mqtt.CONF_PASSWORD: "pass",
|
||||||
|
mqtt.CONF_TLS_INSECURE: True,
|
||||||
|
mqtt.CONF_PROTOCOL: "3.1.1",
|
||||||
|
mqtt.CONF_TRANSPORT: "websockets",
|
||||||
|
mqtt.CONF_WS_PATH: "/path/",
|
||||||
|
mqtt.CONF_WS_HEADERS: '{"h1":"v1","h2":"v2"}',
|
||||||
|
}
|
||||||
|
for k, v in defaults.items():
|
||||||
|
assert get_default(result["data_schema"].schema, k) == v
|
||||||
|
for k, v in suggested.items():
|
||||||
|
assert get_suggested(result["data_schema"].schema, k) == v
|
||||||
|
|
||||||
with patch.object(hass_config, "YAML_CONFIG_FILE", new_yaml_config_file):
|
# test we can change username and password
|
||||||
await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
|
# as it was configured as auto in configuration.yaml is is migrated now
|
||||||
await hass.async_block_till_done()
|
mock_try_connection_success.reset_mock()
|
||||||
# Test default/suggested values from config
|
result = await hass.config_entries.options.async_configure(
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
result["flow_id"],
|
||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
user_input={
|
||||||
assert result["step_id"] == "broker"
|
mqtt.CONF_BROKER: "another-broker",
|
||||||
defaults = {
|
mqtt.CONF_PORT: 2345,
|
||||||
mqtt.CONF_BROKER: "test-broker",
|
mqtt.CONF_USERNAME: "us3r",
|
||||||
mqtt.CONF_PORT: 1234,
|
mqtt.CONF_PASSWORD: "p4ss",
|
||||||
"set_client_cert": True,
|
|
||||||
"set_ca_cert": "auto",
|
"set_ca_cert": "auto",
|
||||||
}
|
"set_client_cert": True,
|
||||||
suggested = {
|
|
||||||
mqtt.CONF_USERNAME: "user",
|
|
||||||
mqtt.CONF_PASSWORD: "pass",
|
|
||||||
mqtt.CONF_TLS_INSECURE: True,
|
mqtt.CONF_TLS_INSECURE: True,
|
||||||
mqtt.CONF_PROTOCOL: "3.1.1",
|
|
||||||
mqtt.CONF_TRANSPORT: "websockets",
|
mqtt.CONF_TRANSPORT: "websockets",
|
||||||
mqtt.CONF_WS_PATH: "/path/",
|
mqtt.CONF_WS_PATH: "/new/path",
|
||||||
mqtt.CONF_WS_HEADERS: '{"h1":"v1","h2":"v2"}',
|
mqtt.CONF_WS_HEADERS: '{"h3": "v3"}',
|
||||||
}
|
},
|
||||||
for k, v in defaults.items():
|
)
|
||||||
assert get_default(result["data_schema"].schema, k) == v
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
for k, v in suggested.items():
|
assert result["errors"] == {}
|
||||||
assert get_suggested(result["data_schema"].schema, k) == v
|
assert result["step_id"] == "options"
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# test the client cert and key were migrated to the entry
|
# check if the username and password was set from config flow and not from configuration.yaml
|
||||||
assert config_entry.data[mqtt.CONF_CLIENT_CERT] == MOCK_CLIENT_CERT.decode(
|
assert mock_try_connection_success.username_pw_set.mock_calls[0][1] == (
|
||||||
"utf-8"
|
"us3r",
|
||||||
)
|
"p4ss",
|
||||||
assert config_entry.data[mqtt.CONF_CLIENT_KEY] == MOCK_CLIENT_KEY.decode(
|
)
|
||||||
"utf-8"
|
# check if tls_insecure_set is called
|
||||||
)
|
assert mock_try_connection_success.tls_insecure_set.mock_calls[0][1] == (True,)
|
||||||
assert config_entry.data[mqtt.CONF_CERTIFICATE] == "auto"
|
|
||||||
|
|
||||||
# test we can change username and password
|
# check if the ca certificate settings were not set during connection test
|
||||||
# as it was configured as auto in configuration.yaml is is migrated now
|
assert mock_try_connection_success.tls_set.mock_calls[0].kwargs[
|
||||||
mock_try_connection_success.reset_mock()
|
"certfile"
|
||||||
result = await hass.config_entries.options.async_configure(
|
] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_CERT)
|
||||||
result["flow_id"],
|
assert mock_try_connection_success.tls_set.mock_calls[0].kwargs[
|
||||||
user_input={
|
"keyfile"
|
||||||
mqtt.CONF_BROKER: "another-broker",
|
] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_KEY)
|
||||||
mqtt.CONF_PORT: 2345,
|
|
||||||
mqtt.CONF_USERNAME: "us3r",
|
|
||||||
mqtt.CONF_PASSWORD: "p4ss",
|
|
||||||
"set_ca_cert": "auto",
|
|
||||||
"set_client_cert": True,
|
|
||||||
mqtt.CONF_TLS_INSECURE: True,
|
|
||||||
mqtt.CONF_TRANSPORT: "websockets",
|
|
||||||
mqtt.CONF_WS_PATH: "/new/path",
|
|
||||||
mqtt.CONF_WS_HEADERS: '{"h3": "v3"}',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
||||||
assert result["errors"] == {}
|
|
||||||
assert result["step_id"] == "options"
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# check if the username and password was set from config flow and not from configuration.yaml
|
# check if websockets options are set
|
||||||
assert mock_try_connection_success.username_pw_set.mock_calls[0][1] == (
|
assert mock_try_connection_success.ws_set_options.mock_calls[0][1] == (
|
||||||
"us3r",
|
"/new/path",
|
||||||
"p4ss",
|
{"h3": "v3"},
|
||||||
)
|
)
|
||||||
# check if tls_insecure_set is called
|
# Accept default option
|
||||||
assert mock_try_connection_success.tls_insecure_set.mock_calls[0][1] == (True,)
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
# check if the ca certificate settings were not set during connection test
|
user_input={},
|
||||||
assert mock_try_connection_success.tls_set.mock_calls[0].kwargs[
|
)
|
||||||
"certfile"
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||||
] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_CERT)
|
await hass.async_block_till_done()
|
||||||
assert mock_try_connection_success.tls_set.mock_calls[0].kwargs[
|
|
||||||
"keyfile"
|
|
||||||
] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_KEY)
|
|
||||||
|
|
||||||
# check if websockets options are set
|
|
||||||
assert mock_try_connection_success.ws_set_options.mock_calls[0][1] == (
|
|
||||||
"/new/path",
|
|
||||||
{"h3": "v3"},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Accept default option
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input={},
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_advanced_settings(
|
async def test_setup_with_advanced_settings(
|
||||||
|
|
|
@ -25,8 +25,6 @@ default_config = {
|
||||||
"port": 1883,
|
"port": 1883,
|
||||||
"protocol": "3.1.1",
|
"protocol": "3.1.1",
|
||||||
"transport": "tcp",
|
"transport": "tcp",
|
||||||
"ws_headers": {},
|
|
||||||
"ws_path": "/",
|
|
||||||
"will_message": {
|
"will_message": {
|
||||||
"payload": "offline",
|
"payload": "offline",
|
||||||
"qos": 0,
|
"qos": 0,
|
||||||
|
|
|
@ -5,18 +5,15 @@ import copy
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
|
||||||
import ssl
|
import ssl
|
||||||
from typing import Any, TypedDict
|
from typing import Any, TypedDict
|
||||||
from unittest.mock import ANY, MagicMock, call, mock_open, patch
|
from unittest.mock import ANY, MagicMock, call, mock_open, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import yaml
|
|
||||||
|
|
||||||
from homeassistant import config as module_hass_config
|
|
||||||
from homeassistant.components import mqtt
|
from homeassistant.components import mqtt
|
||||||
from homeassistant.components.mqtt import CONFIG_SCHEMA, debug_info
|
from homeassistant.components.mqtt import debug_info
|
||||||
from homeassistant.components.mqtt.client import EnsureJobAfterCooldown
|
from homeassistant.components.mqtt.client import EnsureJobAfterCooldown
|
||||||
from homeassistant.components.mqtt.mixins import MQTT_ENTITY_DEVICE_INFO_SCHEMA
|
from homeassistant.components.mqtt.mixins import MQTT_ENTITY_DEVICE_INFO_SCHEMA
|
||||||
from homeassistant.components.mqtt.models import MessageCallbackType, ReceiveMessage
|
from homeassistant.components.mqtt.models import MessageCallbackType, ReceiveMessage
|
||||||
|
@ -40,10 +37,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
from .test_common import (
|
from .test_common import help_test_validate_platform_config
|
||||||
help_test_entry_reload_with_new_config,
|
|
||||||
help_test_validate_platform_config,
|
|
||||||
)
|
|
||||||
|
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
MockConfigEntry,
|
MockConfigEntry,
|
||||||
|
@ -1529,18 +1523,17 @@ async def test_subscribed_at_highest_qos(
|
||||||
|
|
||||||
async def test_reload_entry_with_restored_subscriptions(
|
async def test_reload_entry_with_restored_subscriptions(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
tmp_path: Path,
|
|
||||||
mqtt_client_mock: MqttMockPahoClient,
|
mqtt_client_mock: MqttMockPahoClient,
|
||||||
record_calls: MessageCallbackType,
|
record_calls: MessageCallbackType,
|
||||||
calls: list[ReceiveMessage],
|
calls: list[ReceiveMessage],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test reloading the config entry with with subscriptions restored."""
|
"""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"})
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
mqtt_client_mock.connect.return_value = 0
|
mqtt_client_mock.connect.return_value = 0
|
||||||
assert await mqtt.async_setup_entry(hass, entry)
|
with patch("homeassistant.config.load_yaml_config_file", return_value={}):
|
||||||
await hass.async_block_till_done()
|
await entry.async_setup(hass)
|
||||||
|
|
||||||
await mqtt.async_subscribe(hass, "test-topic", record_calls)
|
await mqtt.async_subscribe(hass, "test-topic", record_calls)
|
||||||
await mqtt.async_subscribe(hass, "wild/+/card", record_calls)
|
await mqtt.async_subscribe(hass, "wild/+/card", record_calls)
|
||||||
|
@ -1557,10 +1550,10 @@ async def test_reload_entry_with_restored_subscriptions(
|
||||||
calls.clear()
|
calls.clear()
|
||||||
|
|
||||||
# Reload the entry
|
# Reload the entry
|
||||||
config_yaml_new = {}
|
with patch("homeassistant.config.load_yaml_config_file", return_value={}):
|
||||||
await help_test_entry_reload_with_new_config(hass, tmp_path, config_yaml_new)
|
assert await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "test-topic", "test-payload2")
|
async_fire_mqtt_message(hass, "test-topic", "test-payload2")
|
||||||
async_fire_mqtt_message(hass, "wild/any/card", "wild-card-payload2")
|
async_fire_mqtt_message(hass, "wild/any/card", "wild-card-payload2")
|
||||||
|
@ -1574,10 +1567,10 @@ async def test_reload_entry_with_restored_subscriptions(
|
||||||
calls.clear()
|
calls.clear()
|
||||||
|
|
||||||
# Reload the entry again
|
# Reload the entry again
|
||||||
config_yaml_new = {}
|
with patch("homeassistant.config.load_yaml_config_file", return_value={}):
|
||||||
await help_test_entry_reload_with_new_config(hass, tmp_path, config_yaml_new)
|
assert await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "test-topic", "test-payload3")
|
async_fire_mqtt_message(hass, "test-topic", "test-payload3")
|
||||||
async_fire_mqtt_message(hass, "wild/any/card", "wild-card-payload3")
|
async_fire_mqtt_message(hass, "wild/any/card", "wild-card-payload3")
|
||||||
|
@ -1804,55 +1797,6 @@ async def test_handle_message_callback(
|
||||||
assert callbacks[0].payload == "test-payload"
|
assert callbacks[0].payload == "test-payload"
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_override_configuration(
|
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, tmp_path: Path
|
|
||||||
) -> None:
|
|
||||||
"""Test override setup from configuration entry."""
|
|
||||||
calls_username_password_set = []
|
|
||||||
|
|
||||||
def mock_usename_password_set(username: str, password: str) -> None:
|
|
||||||
calls_username_password_set.append((username, password))
|
|
||||||
|
|
||||||
# Mock password setup from config
|
|
||||||
config = {
|
|
||||||
"username": "someuser",
|
|
||||||
"password": "someyamlconfiguredpassword",
|
|
||||||
"protocol": "3.1",
|
|
||||||
}
|
|
||||||
new_yaml_config_file = tmp_path / "configuration.yaml"
|
|
||||||
new_yaml_config = yaml.dump({mqtt.DOMAIN: config})
|
|
||||||
new_yaml_config_file.write_text(new_yaml_config)
|
|
||||||
assert new_yaml_config_file.read_text() == new_yaml_config
|
|
||||||
|
|
||||||
with patch.object(module_hass_config, "YAML_CONFIG_FILE", new_yaml_config_file):
|
|
||||||
# Mock config entry
|
|
||||||
entry = MockConfigEntry(
|
|
||||||
domain=mqtt.DOMAIN,
|
|
||||||
data={mqtt.CONF_BROKER: "test-broker", "password": "somepassword"},
|
|
||||||
)
|
|
||||||
entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
with patch("paho.mqtt.client.Client") as mock_client:
|
|
||||||
mock_client().username_pw_set = mock_usename_password_set
|
|
||||||
mock_client.on_connect(return_value=0)
|
|
||||||
await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
|
|
||||||
await entry.async_setup(hass)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert (
|
|
||||||
"Deprecated configuration settings found in configuration.yaml. "
|
|
||||||
"These settings from your configuration entry will override:"
|
|
||||||
in caplog.text
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check if the protocol was set to 3.1 from configuration.yaml
|
|
||||||
assert mock_client.call_args[1]["protocol"] == 3
|
|
||||||
|
|
||||||
# Check if the password override worked
|
|
||||||
assert calls_username_password_set[0][0] == "someuser"
|
|
||||||
assert calls_username_password_set[0][1] == "somepassword"
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
||||||
async def test_setup_manual_mqtt_with_platform_key(
|
async def test_setup_manual_mqtt_with_platform_key(
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||||
|
@ -2312,39 +2256,10 @@ async def test_mqtt_subscribes_topics_on_connect(
|
||||||
mqtt_client_mock.subscribe.assert_any_call("still/pending", 1)
|
mqtt_client_mock.subscribe.assert_any_call("still/pending", 1)
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_entry_with_config_override(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
device_registry: dr.DeviceRegistry,
|
|
||||||
mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator,
|
|
||||||
) -> None:
|
|
||||||
"""Test if the MQTT component loads with no config and config entry can be setup."""
|
|
||||||
data = (
|
|
||||||
'{ "device":{"identifiers":["0AFFD2"]},'
|
|
||||||
' "state_topic": "foobar/sensor",'
|
|
||||||
' "unique_id": "unique" }'
|
|
||||||
)
|
|
||||||
|
|
||||||
# mqtt present in yaml config
|
|
||||||
assert await async_setup_component(hass, mqtt.DOMAIN, {})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# User sets up a config entry
|
|
||||||
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
|
|
||||||
entry.add_to_hass(hass)
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# Discover a device to verify the entry was setup correctly
|
|
||||||
async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
device_entry = device_registry.async_get_device({("mqtt", "0AFFD2")})
|
|
||||||
assert device_entry is not None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_incomplete_entry(
|
async def test_update_incomplete_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device_registry: dr.DeviceRegistry,
|
device_registry: dr.DeviceRegistry,
|
||||||
|
mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator,
|
||||||
mqtt_client_mock: MqttMockPahoClient,
|
mqtt_client_mock: MqttMockPahoClient,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -2356,24 +2271,17 @@ async def test_update_incomplete_entry(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Config entry data is incomplete
|
# Config entry data is incomplete
|
||||||
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={"port": 1234})
|
entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
||||||
entry.add_to_hass(hass)
|
entry.data = {"broker": "test-broker", "port": 1234}
|
||||||
# Mqtt present in yaml config
|
await mqtt_mock_entry_no_yaml_config()
|
||||||
config = {"broker": "yaml_broker"}
|
|
||||||
await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# Config entry data should now be updated
|
# Config entry data should now be updated
|
||||||
assert dict(entry.data) == {
|
assert dict(entry.data) == {
|
||||||
|
"broker": "test-broker",
|
||||||
"port": 1234,
|
"port": 1234,
|
||||||
"discovery_prefix": "homeassistant",
|
"discovery_prefix": "homeassistant",
|
||||||
"broker": "yaml_broker",
|
|
||||||
}
|
}
|
||||||
# Warnings about broker deprecated, but not about other keys with default values
|
|
||||||
assert (
|
|
||||||
"The 'broker' option is deprecated, please remove it from your configuration"
|
|
||||||
in caplog.text
|
|
||||||
)
|
|
||||||
|
|
||||||
# Discover a device to verify the entry was setup correctly
|
# Discover a device to verify the entry was setup correctly
|
||||||
async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data)
|
async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data)
|
||||||
|
@ -3219,7 +3127,7 @@ async def test_subscribe_connection_status(
|
||||||
# This warning and test is to be removed from HA core 2023.6
|
# This warning and test is to be removed from HA core 2023.6
|
||||||
async def test_one_deprecation_warning_per_platform(
|
async def test_one_deprecation_warning_per_platform(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator,
|
mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test a deprecation warning is is logged once per platform."""
|
"""Test a deprecation warning is is logged once per platform."""
|
||||||
|
@ -3230,8 +3138,6 @@ async def test_one_deprecation_warning_per_platform(
|
||||||
config2 = copy.deepcopy(config)
|
config2 = copy.deepcopy(config)
|
||||||
config2["name"] = "test2"
|
config2["name"] = "test2"
|
||||||
await async_setup_component(hass, platform, {platform: [config1, config2]})
|
await async_setup_component(hass, platform, {platform: [config1, config2]})
|
||||||
await hass.async_block_till_done()
|
|
||||||
await mqtt_mock_entry_with_yaml_config()
|
|
||||||
count = 0
|
count = 0
|
||||||
for record in caplog.records:
|
for record in caplog.records:
|
||||||
if record.levelname == "ERROR" and (
|
if record.levelname == "ERROR" and (
|
||||||
|
@ -3242,13 +3148,6 @@ async def test_one_deprecation_warning_per_platform(
|
||||||
assert count == 1
|
assert count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_config_schema_validation(hass: HomeAssistant) -> None:
|
|
||||||
"""Test invalid platform options in the config schema do not pass the config validation."""
|
|
||||||
config = {"mqtt": {"sensor": [{"some_illegal_topic": "mystate/topic/path"}]}}
|
|
||||||
with pytest.raises(vol.MultipleInvalid):
|
|
||||||
CONFIG_SCHEMA(config)
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
|
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
|
||||||
async def test_unload_config_entry(
|
async def test_unload_config_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -3277,24 +3176,6 @@ async def test_unload_config_entry(
|
||||||
assert "No ACK from MQTT server" not in caplog.text
|
assert "No ACK from MQTT server" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
|
||||||
async def test_setup_with_disabled_entry(
|
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
||||||
) -> None:
|
|
||||||
"""Test setting up the platform with a disabled config entry."""
|
|
||||||
# Try to setup the platform with a disabled config entry
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
domain=mqtt.DOMAIN, data={}, disabled_by=ConfigEntryDisabler.USER
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
config: ConfigType = {mqtt.DOMAIN: {}}
|
|
||||||
await async_setup_component(hass, mqtt.DOMAIN, config)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert "MQTT will be not available until the config entry is enabled" in caplog.text
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
||||||
async def test_publish_or_subscribe_without_valid_config_entry(
|
async def test_publish_or_subscribe_without_valid_config_entry(
|
||||||
hass: HomeAssistant, record_calls: MessageCallbackType
|
hass: HomeAssistant, record_calls: MessageCallbackType
|
||||||
|
|
Loading…
Add table
Reference in a new issue