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
parent
9d377aabdb
commit
ed1c2ea2b8
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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] = {}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"],
|
||||
|
|
Loading…
Reference in New Issue