Move manual configuration of MQTT fan and light to the integration key (#71676)

* Processing yaml config through entry setup

* Setup all platforms

* Update homeassistant/components/mqtt/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* adjust mock_mqtt - reference config from cache

* Fix test config entry override

* Add tests yaml setup

* additional tests

* Introduce PLATFORM_SCHEMA_MODERN

* recover temporary MQTT_BASE_PLATFORM_SCHEMA

* Allow extra key in light base schema, restore test

* Fix test for exception on platform key

* One deprecation message per platform

* Remove deprecation checks from modern schema

* Update homeassistant/components/mqtt/fan.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/mqtt/fan.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/mqtt/light/__init__.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/mqtt/light/__init__.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/mqtt/light/schema_json.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/mqtt/light/schema_template.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/mqtt/mixins.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* rename validate_modern_schema

* Do not fail platform if a single config is broken

* Update homeassistant/components/mqtt/__init__.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Fix tests on asserting log

* Update log. Make helper transparant, remove patch

* Perform parallel processing

* Update tests/components/mqtt/test_init.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Apply suggestions from code review

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/mqtt/mixins.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* black

* Fix tests and add #new_format anchor

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
pull/72160/head^2
Jan Bouwhuis 2022-05-19 15:04:53 +02:00 committed by GitHub
parent 9d377aabdb
commit ed1c2ea2b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 339 additions and 28 deletions

View File

@ -81,6 +81,8 @@ from .const import (
CONF_TLS_VERSION,
CONF_TOPIC,
CONF_WILL_MESSAGE,
CONFIG_ENTRY_IS_SETUP,
DATA_CONFIG_ENTRY_LOCK,
DATA_MQTT_CONFIG,
DATA_MQTT_RELOAD_NEEDED,
DEFAULT_BIRTH,
@ -171,7 +173,6 @@ PLATFORMS = [
Platform.VACUUM,
]
CLIENT_KEY_AUTH_MSG = (
"client_key and client_cert must both be present in "
"the MQTT broker configuration"
@ -187,7 +188,14 @@ MQTT_WILL_BIRTH_SCHEMA = vol.Schema(
required=True,
)
CONFIG_SCHEMA_BASE = vol.Schema(
PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema(
{
vol.Optional(Platform.FAN.value): cv.ensure_list,
vol.Optional(Platform.LIGHT.value): cv.ensure_list,
}
)
CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend(
{
vol.Optional(CONF_CLIENT_ID): cv.string,
vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All(
@ -253,10 +261,28 @@ SCHEMA_BASE = {
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
}
MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE)
# Will be removed when all platforms support a modern platform schema
MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE)
# Will be removed when all platforms support a modern platform schema
MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
}
)
# Will be removed when all platforms support a modern platform schema
MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic,
}
)
# Sensor type platforms subscribe to MQTT events
MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend(
MQTT_RO_SCHEMA = MQTT_BASE_SCHEMA.extend(
{
vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
@ -264,7 +290,7 @@ MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend(
)
# Switch type platforms publish to MQTT and may subscribe
MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend(
MQTT_RW_SCHEMA = MQTT_BASE_SCHEMA.extend(
{
vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
@ -774,6 +800,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
),
)
# setup platforms and discovery
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
for component in PLATFORMS:
config_entries_key = f"{component}.mqtt"
if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]:
hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)
if conf.get(CONF_DISCOVERY):
await _async_setup_discovery(hass, conf, entry)

View File

@ -28,6 +28,8 @@ CONF_CLIENT_CERT = "client_cert"
CONF_TLS_INSECURE = "tls_insecure"
CONF_TLS_VERSION = "tls_version"
CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup"
DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock"
DATA_MQTT_CONFIG = "mqtt_config"
DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed"

View File

@ -27,6 +27,8 @@ from .const import (
ATTR_DISCOVERY_TOPIC,
CONF_AVAILABILITY,
CONF_TOPIC,
CONFIG_ENTRY_IS_SETUP,
DATA_CONFIG_ENTRY_LOCK,
DOMAIN,
)
@ -62,8 +64,6 @@ SUPPORTED_COMPONENTS = [
ALREADY_DISCOVERED = "mqtt_discovered_components"
PENDING_DISCOVERED = "mqtt_pending_components"
CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup"
DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock"
DATA_CONFIG_FLOW_LOCK = "mqtt_discovery_config_flow_lock"
DISCOVERY_UNSUBSCRIBE = "mqtt_discovery_unsubscribe"
INTEGRATION_UNSUBSCRIBE = "mqtt_integration_discovery_unsubscribe"
@ -258,9 +258,7 @@ async def async_start( # noqa: C901
hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None
)
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
hass.data[DATA_CONFIG_FLOW_LOCK] = asyncio.Lock()
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
hass.data[ALREADY_DISCOVERED] = {}
hass.data[PENDING_DISCOVERED] = {}

View File

@ -1,6 +1,7 @@
"""Support for MQTT fans."""
from __future__ import annotations
import asyncio
import functools
import logging
import math
@ -49,8 +50,10 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_get_platform_config_from_yaml,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
)
CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic"
@ -122,7 +125,7 @@ def valid_preset_mode_configuration(config):
return config
_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
@ -172,7 +175,15 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
}
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
# Configuring MQTT Fans under the fan platform key is deprecated in HA Core 2022.6
PLATFORM_SCHEMA = vol.All(
cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema),
valid_speed_range_configuration,
valid_preset_mode_configuration,
warn_for_legacy_schema(fan.DOMAIN),
)
PLATFORM_SCHEMA_MODERN = vol.All(
_PLATFORM_SCHEMA_BASE,
valid_speed_range_configuration,
valid_preset_mode_configuration,
@ -201,7 +212,8 @@ async def async_setup_platform(
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up MQTT fan through configuration.yaml."""
"""Set up MQTT fans configured under the fan platform key (deprecated)."""
# Deprecated in HA Core 2022.6
await async_setup_platform_helper(
hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity
)
@ -212,7 +224,17 @@ async def async_setup_entry(
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT fan dynamically through MQTT discovery."""
"""Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await asyncio.gather(
*(
_async_setup_entity(hass, async_add_entities, config, config_entry)
for config in await async_get_platform_config_from_yaml(
hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN
)
)
)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -1,36 +1,47 @@
"""Support for MQTT lights."""
from __future__ import annotations
import asyncio
import functools
import voluptuous as vol
from homeassistant.components import light
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from ..mixins import async_setup_entry_helper, async_setup_platform_helper
from ..mixins import (
async_get_platform_config_from_yaml,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
)
from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA
from .schema_basic import (
DISCOVERY_SCHEMA_BASIC,
PLATFORM_SCHEMA_BASIC,
PLATFORM_SCHEMA_MODERN_BASIC,
async_setup_entity_basic,
)
from .schema_json import (
DISCOVERY_SCHEMA_JSON,
PLATFORM_SCHEMA_JSON,
PLATFORM_SCHEMA_MODERN_JSON,
async_setup_entity_json,
)
from .schema_template import (
DISCOVERY_SCHEMA_TEMPLATE,
PLATFORM_SCHEMA_MODERN_TEMPLATE,
PLATFORM_SCHEMA_TEMPLATE,
async_setup_entity_template,
)
def validate_mqtt_light_discovery(value):
"""Validate MQTT light schema."""
"""Validate MQTT light schema for."""
schemas = {
"basic": DISCOVERY_SCHEMA_BASIC,
"json": DISCOVERY_SCHEMA_JSON,
@ -49,14 +60,31 @@ def validate_mqtt_light(value):
return schemas[value[CONF_SCHEMA]](value)
def validate_mqtt_light_modern(value):
"""Validate MQTT light schema."""
schemas = {
"basic": PLATFORM_SCHEMA_MODERN_BASIC,
"json": PLATFORM_SCHEMA_MODERN_JSON,
"template": PLATFORM_SCHEMA_MODERN_TEMPLATE,
}
return schemas[value[CONF_SCHEMA]](value)
DISCOVERY_SCHEMA = vol.All(
MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA),
validate_mqtt_light_discovery,
)
# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6
PLATFORM_SCHEMA = vol.All(
MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_light
cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA),
validate_mqtt_light,
warn_for_legacy_schema(light.DOMAIN),
)
PLATFORM_SCHEMA_MODERN = vol.All(
MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA),
validate_mqtt_light_modern,
)
@ -66,14 +94,29 @@ async def async_setup_platform(
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up MQTT light through configuration.yaml."""
"""Set up MQTT light through configuration.yaml (deprecated)."""
# Deprecated in HA Core 2022.6
await async_setup_platform_helper(
hass, light.DOMAIN, config, async_add_entities, _async_setup_entity
)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up MQTT light dynamically through MQTT discovery."""
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT lights configured under the light platform key (deprecated)."""
# load and initialize platform config from configuration.yaml
await asyncio.gather(
*(
_async_setup_entity(hass, async_add_entities, config, config_entry)
for config in await async_get_platform_config_from_yaml(
hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN
)
)
)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -156,7 +156,7 @@ VALUE_TEMPLATE_KEYS = [
]
_PLATFORM_SCHEMA_BASE = (
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
mqtt.MQTT_RW_SCHEMA.extend(
{
vol.Optional(CONF_BRIGHTNESS_COMMAND_TEMPLATE): cv.template,
vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic,
@ -220,13 +220,14 @@ _PLATFORM_SCHEMA_BASE = (
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
)
# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6
PLATFORM_SCHEMA_BASIC = vol.All(
# CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9
cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC),
cv.deprecated(CONF_WHITE_VALUE_SCALE),
cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC),
cv.deprecated(CONF_WHITE_VALUE_TEMPLATE),
_PLATFORM_SCHEMA_BASE,
cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema),
)
DISCOVERY_SCHEMA_BASIC = vol.All(
@ -240,6 +241,8 @@ DISCOVERY_SCHEMA_BASIC = vol.All(
_PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA),
)
PLATFORM_SCHEMA_MODERN_BASIC = _PLATFORM_SCHEMA_BASE
async def async_setup_entity_basic(
hass, config, async_add_entities, config_entry, discovery_data=None

View File

@ -103,7 +103,7 @@ def valid_color_configuration(config):
_PLATFORM_SCHEMA_BASE = (
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
mqtt.MQTT_RW_SCHEMA.extend(
{
vol.Optional(CONF_BRIGHTNESS, default=DEFAULT_BRIGHTNESS): cv.boolean,
vol.Optional(
@ -146,10 +146,11 @@ _PLATFORM_SCHEMA_BASE = (
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
)
# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6
PLATFORM_SCHEMA_JSON = vol.All(
# CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9
cv.deprecated(CONF_WHITE_VALUE),
_PLATFORM_SCHEMA_BASE,
cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema),
valid_color_configuration,
)
@ -160,6 +161,11 @@ DISCOVERY_SCHEMA_JSON = vol.All(
valid_color_configuration,
)
PLATFORM_SCHEMA_MODERN_JSON = vol.All(
_PLATFORM_SCHEMA_BASE,
valid_color_configuration,
)
async def async_setup_entity_json(
hass, config: ConfigType, async_add_entities, config_entry, discovery_data

View File

@ -67,7 +67,7 @@ CONF_RED_TEMPLATE = "red_template"
CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
_PLATFORM_SCHEMA_BASE = (
mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
mqtt.MQTT_RW_SCHEMA.extend(
{
vol.Optional(CONF_BLUE_TEMPLATE): cv.template,
vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template,
@ -90,10 +90,11 @@ _PLATFORM_SCHEMA_BASE = (
.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema)
)
# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6
PLATFORM_SCHEMA_TEMPLATE = vol.All(
# CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9
cv.deprecated(CONF_WHITE_VALUE_TEMPLATE),
_PLATFORM_SCHEMA_BASE,
cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema),
)
DISCOVERY_SCHEMA_TEMPLATE = vol.All(
@ -102,6 +103,8 @@ DISCOVERY_SCHEMA_TEMPLATE = vol.All(
_PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA),
)
PLATFORM_SCHEMA_MODERN_TEMPLATE = _PLATFORM_SCHEMA_BASE
async def async_setup_entity_template(
hass, config, async_add_entities, config_entry, discovery_data

View File

@ -9,6 +9,7 @@ from typing import Any, Protocol, cast, final
import voluptuous as vol
from homeassistant.config import async_log_exception
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_CONFIGURATION_URL,
@ -64,6 +65,7 @@ from .const import (
CONF_ENCODING,
CONF_QOS,
CONF_TOPIC,
DATA_MQTT_CONFIG,
DATA_MQTT_RELOAD_NEEDED,
DEFAULT_ENCODING,
DEFAULT_PAYLOAD_AVAILABLE,
@ -223,6 +225,31 @@ MQTT_ENTITY_COMMON_SCHEMA = MQTT_AVAILABILITY_SCHEMA.extend(
)
def warn_for_legacy_schema(domain: str) -> Callable:
"""Warn once when a legacy platform schema is used."""
warned = set()
def validator(config: ConfigType) -> ConfigType:
"""Return a validator."""
nonlocal warned
if domain in warned:
return config
_LOGGER.warning(
"Manually configured MQTT %s(s) found under platform key '%s', "
"please move to the mqtt integration key, see "
"https://www.home-assistant.io/integrations/%s.mqtt/#new_format",
domain,
domain,
domain,
)
warned.add(domain)
return config
return validator
class SetupEntity(Protocol):
"""Protocol type for async_setup_entities."""
@ -237,6 +264,31 @@ class SetupEntity(Protocol):
"""Define setup_entities type."""
async def async_get_platform_config_from_yaml(
hass: HomeAssistant, domain: str, schema: vol.Schema
) -> list[ConfigType]:
"""Return a list of validated configurations for the domain."""
def async_validate_config(
hass: HomeAssistant,
config: list[ConfigType],
) -> list[ConfigType]:
"""Validate config."""
validated_config = []
for config_item in config:
try:
validated_config.append(schema(config_item))
except vol.MultipleInvalid as err:
async_log_exception(err, domain, config_item, hass)
return validated_config
config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {})
if not (platform_configs := config_yaml.get(domain)):
return []
return async_validate_config(hass, platform_configs)
async def async_setup_entry_helper(hass, domain, async_setup, schema):
"""Set up entity, automation or tag creation dynamically through MQTT discovery."""

View File

@ -1690,3 +1690,24 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config):
assert hass.states.get(f"{domain}.test_new_1")
assert hass.states.get(f"{domain}.test_new_2")
assert hass.states.get(f"{domain}.test_new_3")
async def help_test_setup_manual_entity_from_yaml(
hass,
caplog,
tmp_path,
platform,
config,
):
"""Help to test setup from yaml through configuration entry."""
config_structure = {mqtt.DOMAIN: {platform: config}}
await async_setup_component(hass, mqtt.DOMAIN, config_structure)
# Mock config entry
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry.add_to_hass(hass)
with patch("paho.mqtt.client.Client") as mock_client:
mock_client().connect = lambda *args: 0
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

View File

@ -55,6 +55,7 @@ from .test_common import (
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_not_dict,
@ -1805,3 +1806,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path):
"""Test setup manual configured MQTT entity."""
platform = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(
hass, caplog, tmp_path, platform, config
)
assert hass.states.get(f"{platform}.test") is not None

View File

@ -1,5 +1,6 @@
"""The tests for the MQTT component."""
import asyncio
import copy
from datetime import datetime, timedelta
from functools import partial
import json
@ -30,6 +31,8 @@ from homeassistant.helpers.entity import Entity
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from .test_common import help_test_setup_manual_entity_from_yaml
from tests.common import (
MockConfigEntry,
async_fire_mqtt_message,
@ -1279,6 +1282,51 @@ async def test_setup_override_configuration(hass, caplog, tmp_path):
assert calls_username_password_set[0][1] == "somepassword"
async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path):
"""Test set up a manual MQTT item with a platform key."""
config = {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
await help_test_setup_manual_entity_from_yaml(
hass,
caplog,
tmp_path,
"light",
config,
)
assert (
"Invalid config for [light]: [platform] is an invalid option for [light]. "
"Check: light->platform. (See ?, line ?)" in caplog.text
)
async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path):
"""Test set up a manual MQTT item with an invalid config."""
config = {"name": "test"}
await help_test_setup_manual_entity_from_yaml(
hass,
caplog,
tmp_path,
"light",
config,
)
assert (
"Invalid config for [light]: required key not provided @ data['command_topic']."
" Got None. (See ?, line ?)" in caplog.text
)
async def test_setup_manual_mqtt_empty_platform(hass, caplog, tmp_path):
"""Test set up a manual MQTT platform without items."""
config = None
await help_test_setup_manual_entity_from_yaml(
hass,
caplog,
tmp_path,
"light",
config,
)
assert "voluptuous.error.MultipleInvalid" not in caplog.text
async def test_setup_mqtt_client_protocol(hass):
"""Test MQTT client protocol setup."""
entry = MockConfigEntry(
@ -1628,7 +1676,8 @@ async def test_setup_entry_with_config_override(hass, device_reg, mqtt_client_mo
# 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)
with patch("homeassistant.components.mqtt.PLATFORMS", []):
assert await hass.config_entries.async_setup(entry.entry_id)
# Discover a device to verify the entry was setup correctly
async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data)
@ -2413,3 +2462,23 @@ async def test_subscribe_connection_status(hass, mqtt_mock, mqtt_client_mock):
assert len(mqtt_connected_calls) == 2
assert mqtt_connected_calls[0] is True
assert mqtt_connected_calls[1] is False
async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog):
"""Test a deprecation warning is is logged once per platform."""
platform = "light"
config = {"platform": "mqtt", "command_topic": "test-topic"}
config1 = copy.deepcopy(config)
config1["name"] = "test1"
config2 = copy.deepcopy(config)
config2["name"] = "test2"
await async_setup_component(hass, platform, {platform: [config1, config2]})
await hass.async_block_till_done()
count = 0
for record in caplog.records:
if record.levelname == "WARNING" and (
f"Manually configured MQTT {platform}(s) found under platform key '{platform}'"
in record.message
):
count += 1
assert count == 1

View File

@ -237,6 +237,7 @@ from .test_common import (
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_not_dict,
@ -3674,3 +3675,15 @@ async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("effect") == "colorloop"
async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path):
"""Test setup manual configured MQTT entity."""
platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(
hass, caplog, tmp_path, platform, config
)
assert hass.states.get(f"{platform}.test") is not None

View File

@ -131,6 +131,7 @@ from .test_common import (
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_not_dict,
@ -2038,3 +2039,15 @@ async def test_encoding_subscribable_topics(
init_payload,
skip_raw_test=True,
)
async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path):
"""Test setup manual configured MQTT entity."""
platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(
hass, caplog, tmp_path, platform, config
)
assert hass.states.get(f"{platform}.test") is not None

View File

@ -69,6 +69,7 @@ from .test_common import (
help_test_setting_attribute_via_mqtt_json_message,
help_test_setting_attribute_with_template,
help_test_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_not_dict,
@ -1213,3 +1214,15 @@ async def test_encoding_subscribable_topics(
attribute_value,
init_payload,
)
async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path):
"""Test setup manual configured MQTT entity."""
platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(
hass, caplog, tmp_path, platform, config
)
assert hass.states.get(f"{platform}.test") is not None

View File

@ -561,9 +561,10 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config):
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# Do not forward the entry setup to the components here
with patch("homeassistant.components.mqtt.PLATFORMS", []):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
mqtt_component_mock = MagicMock(
return_value=hass.data["mqtt"],