Fix MQTT config schema to ensure correct validation (#73619)
* Ensure config schema validation * Use correct schema for device_tracker * Remove schema validation from the platform setup * Remove loop to build schemapull/73642/head^2
parent
fcd8859542
commit
57daeaa174
|
@ -47,7 +47,11 @@ from .client import ( # noqa: F401
|
|||
publish,
|
||||
subscribe,
|
||||
)
|
||||
from .config import CONFIG_SCHEMA_BASE, DEFAULT_VALUES, DEPRECATED_CONFIG_KEYS
|
||||
from .config_integration import (
|
||||
CONFIG_SCHEMA_BASE,
|
||||
DEFAULT_VALUES,
|
||||
DEPRECATED_CONFIG_KEYS,
|
||||
)
|
||||
from .const import ( # noqa: F401
|
||||
ATTR_PAYLOAD,
|
||||
ATTR_QOS,
|
||||
|
|
|
@ -148,7 +148,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT alarm control panel through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, alarm.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
await async_setup_platform_discovery(hass, alarm.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -103,9 +103,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT binary sensor through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, binary_sensor.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, binary_sensor.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -83,9 +83,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT button through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, button.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, button.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -81,9 +81,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT camera through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, camera.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, camera.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -392,9 +392,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT climate device through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, climate.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, climate.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -3,126 +3,21 @@ from __future__ import annotations
|
|||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_CLIENT_ID,
|
||||
CONF_DISCOVERY,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_PROTOCOL,
|
||||
CONF_USERNAME,
|
||||
CONF_VALUE_TEMPLATE,
|
||||
)
|
||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from .const import (
|
||||
ATTR_PAYLOAD,
|
||||
ATTR_QOS,
|
||||
ATTR_RETAIN,
|
||||
ATTR_TOPIC,
|
||||
CONF_BIRTH_MESSAGE,
|
||||
CONF_BROKER,
|
||||
CONF_CERTIFICATE,
|
||||
CONF_CLIENT_CERT,
|
||||
CONF_CLIENT_KEY,
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_DISCOVERY_PREFIX,
|
||||
CONF_ENCODING,
|
||||
CONF_KEEPALIVE,
|
||||
CONF_QOS,
|
||||
CONF_RETAIN,
|
||||
CONF_STATE_TOPIC,
|
||||
CONF_TLS_INSECURE,
|
||||
CONF_TLS_VERSION,
|
||||
CONF_WILL_MESSAGE,
|
||||
DEFAULT_BIRTH,
|
||||
DEFAULT_DISCOVERY,
|
||||
DEFAULT_ENCODING,
|
||||
DEFAULT_PREFIX,
|
||||
DEFAULT_QOS,
|
||||
DEFAULT_RETAIN,
|
||||
DEFAULT_WILL,
|
||||
PLATFORMS,
|
||||
PROTOCOL_31,
|
||||
PROTOCOL_311,
|
||||
)
|
||||
from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic
|
||||
|
||||
DEFAULT_PORT = 1883
|
||||
DEFAULT_KEEPALIVE = 60
|
||||
DEFAULT_PROTOCOL = PROTOCOL_311
|
||||
DEFAULT_TLS_PROTOCOL = "auto"
|
||||
|
||||
DEFAULT_VALUES = {
|
||||
CONF_BIRTH_MESSAGE: DEFAULT_BIRTH,
|
||||
CONF_DISCOVERY: DEFAULT_DISCOVERY,
|
||||
CONF_PORT: DEFAULT_PORT,
|
||||
CONF_TLS_VERSION: DEFAULT_TLS_PROTOCOL,
|
||||
CONF_WILL_MESSAGE: DEFAULT_WILL,
|
||||
}
|
||||
|
||||
CLIENT_KEY_AUTH_MSG = (
|
||||
"client_key and client_cert must both be present in "
|
||||
"the MQTT broker configuration"
|
||||
)
|
||||
|
||||
MQTT_WILL_BIRTH_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Inclusive(ATTR_TOPIC, "topic_payload"): valid_publish_topic,
|
||||
vol.Inclusive(ATTR_PAYLOAD, "topic_payload"): cv.string,
|
||||
vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
|
||||
vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
},
|
||||
required=True,
|
||||
)
|
||||
|
||||
PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema(
|
||||
{vol.Optional(platform.value): cv.ensure_list for platform in PLATFORMS}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend(
|
||||
{
|
||||
vol.Optional(CONF_CLIENT_ID): cv.string,
|
||||
vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=15)
|
||||
),
|
||||
vol.Optional(CONF_BROKER): cv.string,
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
vol.Optional(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_CERTIFICATE): vol.Any("auto", cv.isfile),
|
||||
vol.Inclusive(
|
||||
CONF_CLIENT_KEY, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG
|
||||
): cv.isfile,
|
||||
vol.Inclusive(
|
||||
CONF_CLIENT_CERT, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG
|
||||
): cv.isfile,
|
||||
vol.Optional(CONF_TLS_INSECURE): cv.boolean,
|
||||
vol.Optional(CONF_TLS_VERSION): vol.Any("auto", "1.0", "1.1", "1.2"),
|
||||
vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All(
|
||||
cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])
|
||||
),
|
||||
vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
|
||||
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
|
||||
vol.Optional(CONF_DISCOVERY): cv.boolean,
|
||||
# discovery_prefix must be a valid publish topic because if no
|
||||
# state topic is specified, it will be created with the given prefix.
|
||||
vol.Optional(
|
||||
CONF_DISCOVERY_PREFIX, default=DEFAULT_PREFIX
|
||||
): valid_publish_topic,
|
||||
}
|
||||
)
|
||||
|
||||
DEPRECATED_CONFIG_KEYS = [
|
||||
CONF_BIRTH_MESSAGE,
|
||||
CONF_BROKER,
|
||||
CONF_DISCOVERY,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_TLS_VERSION,
|
||||
CONF_USERNAME,
|
||||
CONF_WILL_MESSAGE,
|
||||
]
|
||||
|
||||
SCHEMA_BASE = {
|
||||
vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
|
||||
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
"""Support for MQTT platform config setup."""
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_CLIENT_ID,
|
||||
CONF_DISCOVERY,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_PROTOCOL,
|
||||
CONF_USERNAME,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from . import (
|
||||
alarm_control_panel as alarm_control_panel_platform,
|
||||
binary_sensor as binary_sensor_platform,
|
||||
button as button_platform,
|
||||
camera as camera_platform,
|
||||
climate as climate_platform,
|
||||
cover as cover_platform,
|
||||
device_tracker as device_tracker_platform,
|
||||
fan as fan_platform,
|
||||
humidifier as humidifier_platform,
|
||||
light as light_platform,
|
||||
lock as lock_platform,
|
||||
number as number_platform,
|
||||
scene as scene_platform,
|
||||
select as select_platform,
|
||||
sensor as sensor_platform,
|
||||
siren as siren_platform,
|
||||
switch as switch_platform,
|
||||
vacuum as vacuum_platform,
|
||||
)
|
||||
from .const import (
|
||||
ATTR_PAYLOAD,
|
||||
ATTR_QOS,
|
||||
ATTR_RETAIN,
|
||||
ATTR_TOPIC,
|
||||
CONF_BIRTH_MESSAGE,
|
||||
CONF_BROKER,
|
||||
CONF_CERTIFICATE,
|
||||
CONF_CLIENT_CERT,
|
||||
CONF_CLIENT_KEY,
|
||||
CONF_DISCOVERY_PREFIX,
|
||||
CONF_KEEPALIVE,
|
||||
CONF_TLS_INSECURE,
|
||||
CONF_TLS_VERSION,
|
||||
CONF_WILL_MESSAGE,
|
||||
DEFAULT_BIRTH,
|
||||
DEFAULT_DISCOVERY,
|
||||
DEFAULT_PREFIX,
|
||||
DEFAULT_QOS,
|
||||
DEFAULT_RETAIN,
|
||||
DEFAULT_WILL,
|
||||
PROTOCOL_31,
|
||||
PROTOCOL_311,
|
||||
)
|
||||
from .util import _VALID_QOS_SCHEMA, valid_publish_topic
|
||||
|
||||
DEFAULT_PORT = 1883
|
||||
DEFAULT_KEEPALIVE = 60
|
||||
DEFAULT_PROTOCOL = PROTOCOL_311
|
||||
DEFAULT_TLS_PROTOCOL = "auto"
|
||||
|
||||
DEFAULT_VALUES = {
|
||||
CONF_BIRTH_MESSAGE: DEFAULT_BIRTH,
|
||||
CONF_DISCOVERY: DEFAULT_DISCOVERY,
|
||||
CONF_PORT: DEFAULT_PORT,
|
||||
CONF_TLS_VERSION: DEFAULT_TLS_PROTOCOL,
|
||||
CONF_WILL_MESSAGE: DEFAULT_WILL,
|
||||
}
|
||||
|
||||
PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema(
|
||||
{
|
||||
Platform.ALARM_CONTROL_PANEL.value: vol.All(
|
||||
cv.ensure_list, [alarm_control_panel_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.BINARY_SENSOR.value: vol.All(
|
||||
cv.ensure_list, [binary_sensor_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.BUTTON.value: vol.All(
|
||||
cv.ensure_list, [button_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.CAMERA.value: vol.All(
|
||||
cv.ensure_list, [camera_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.CLIMATE.value: vol.All(
|
||||
cv.ensure_list, [climate_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.COVER.value: vol.All(
|
||||
cv.ensure_list, [cover_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.DEVICE_TRACKER.value: vol.All(
|
||||
cv.ensure_list, [device_tracker_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.FAN.value: vol.All(
|
||||
cv.ensure_list, [fan_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.HUMIDIFIER.value: vol.All(
|
||||
cv.ensure_list, [humidifier_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.LOCK.value: vol.All(
|
||||
cv.ensure_list, [lock_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.LIGHT.value: vol.All(
|
||||
cv.ensure_list, [light_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.NUMBER.value: vol.All(
|
||||
cv.ensure_list, [number_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.SCENE.value: vol.All(
|
||||
cv.ensure_list, [scene_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.SELECT.value: vol.All(
|
||||
cv.ensure_list, [select_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.SENSOR.value: vol.All(
|
||||
cv.ensure_list, [sensor_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.SIREN.value: vol.All(
|
||||
cv.ensure_list, [siren_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.SWITCH.value: vol.All(
|
||||
cv.ensure_list, [switch_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
Platform.VACUUM.value: vol.All(
|
||||
cv.ensure_list, [vacuum_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type]
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
CLIENT_KEY_AUTH_MSG = (
|
||||
"client_key and client_cert must both be present in "
|
||||
"the MQTT broker configuration"
|
||||
)
|
||||
|
||||
MQTT_WILL_BIRTH_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Inclusive(ATTR_TOPIC, "topic_payload"): valid_publish_topic,
|
||||
vol.Inclusive(ATTR_PAYLOAD, "topic_payload"): cv.string,
|
||||
vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
|
||||
vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
},
|
||||
required=True,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend(
|
||||
{
|
||||
vol.Optional(CONF_CLIENT_ID): cv.string,
|
||||
vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=15)
|
||||
),
|
||||
vol.Optional(CONF_BROKER): cv.string,
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
vol.Optional(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_CERTIFICATE): vol.Any("auto", cv.isfile),
|
||||
vol.Inclusive(
|
||||
CONF_CLIENT_KEY, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG
|
||||
): cv.isfile,
|
||||
vol.Inclusive(
|
||||
CONF_CLIENT_CERT, "client_key_auth", msg=CLIENT_KEY_AUTH_MSG
|
||||
): cv.isfile,
|
||||
vol.Optional(CONF_TLS_INSECURE): cv.boolean,
|
||||
vol.Optional(CONF_TLS_VERSION): vol.Any("auto", "1.0", "1.1", "1.2"),
|
||||
vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All(
|
||||
cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])
|
||||
),
|
||||
vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
|
||||
vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA,
|
||||
vol.Optional(CONF_DISCOVERY): cv.boolean,
|
||||
# discovery_prefix must be a valid publish topic because if no
|
||||
# state topic is specified, it will be created with the given prefix.
|
||||
vol.Optional(
|
||||
CONF_DISCOVERY_PREFIX, default=DEFAULT_PREFIX
|
||||
): valid_publish_topic,
|
||||
}
|
||||
)
|
||||
|
||||
DEPRECATED_CONFIG_KEYS = [
|
||||
CONF_BIRTH_MESSAGE,
|
||||
CONF_BROKER,
|
||||
CONF_DISCOVERY,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_TLS_VERSION,
|
||||
CONF_USERNAME,
|
||||
CONF_WILL_MESSAGE,
|
||||
]
|
|
@ -241,7 +241,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT cover through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, cover.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
await async_setup_platform_discovery(hass, cover.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -4,6 +4,7 @@ import voluptuous as vol
|
|||
from homeassistant.components import device_tracker
|
||||
|
||||
from ..mixins import warn_for_legacy_schema
|
||||
from .schema_discovery import PLATFORM_SCHEMA_MODERN # noqa: F401
|
||||
from .schema_discovery import async_setup_entry_from_discovery
|
||||
from .schema_yaml import PLATFORM_SCHEMA_YAML, async_setup_scanner_from_yaml
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ async def async_setup_entry_from_discovery(hass, config_entry, async_add_entitie
|
|||
*(
|
||||
_async_setup_entity(hass, async_add_entities, config, config_entry)
|
||||
for config in await async_get_platform_config_from_yaml(
|
||||
hass, device_tracker.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
hass, device_tracker.DOMAIN
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -231,9 +231,7 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
)
|
||||
config_entry.async_on_unload(await async_setup_platform_discovery(hass, fan.DOMAIN))
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
|
||||
|
|
|
@ -188,9 +188,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT humidifier through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, humidifier.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, humidifier.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -112,7 +112,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT lights configured under the light platform key (deprecated)."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
await async_setup_platform_discovery(hass, light.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -104,7 +104,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT lock through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, lock.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
await async_setup_platform_discovery(hass, lock.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -10,7 +10,6 @@ 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,
|
||||
|
@ -263,7 +262,7 @@ class SetupEntity(Protocol):
|
|||
|
||||
|
||||
async def async_setup_platform_discovery(
|
||||
hass: HomeAssistant, platform_domain: str, schema: vol.Schema
|
||||
hass: HomeAssistant, platform_domain: str
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Set up platform discovery for manual config."""
|
||||
|
||||
|
@ -282,7 +281,7 @@ async def async_setup_platform_discovery(
|
|||
*(
|
||||
discovery.async_load_platform(hass, platform_domain, DOMAIN, config, {})
|
||||
for config in await async_get_platform_config_from_yaml(
|
||||
hass, platform_domain, schema, config_yaml
|
||||
hass, platform_domain, config_yaml
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -295,32 +294,17 @@ async def async_setup_platform_discovery(
|
|||
async def async_get_platform_config_from_yaml(
|
||||
hass: HomeAssistant,
|
||||
platform_domain: str,
|
||||
schema: vol.Schema,
|
||||
config_yaml: ConfigType = None,
|
||||
) -> 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, platform_domain, config_item, hass)
|
||||
|
||||
return validated_config
|
||||
|
||||
if config_yaml is None:
|
||||
config_yaml = hass.data.get(DATA_MQTT_CONFIG)
|
||||
if not config_yaml:
|
||||
return []
|
||||
if not (platform_configs := config_yaml.get(platform_domain)):
|
||||
return []
|
||||
return async_validate_config(hass, platform_configs)
|
||||
return platform_configs
|
||||
|
||||
|
||||
async def async_setup_entry_helper(hass, domain, async_setup, schema):
|
||||
|
|
|
@ -136,9 +136,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT number through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, number.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, number.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -80,7 +80,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT scene through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, scene.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
await async_setup_platform_discovery(hass, scene.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -95,9 +95,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT select through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, select.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, select.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -148,9 +148,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT sensor through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, sensor.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, sensor.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -144,7 +144,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT siren through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(hass, siren.DOMAIN, PLATFORM_SCHEMA_MODERN)
|
||||
await async_setup_platform_discovery(hass, siren.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -98,9 +98,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT switch through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, switch.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, switch.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -92,9 +92,7 @@ async def async_setup_entry(
|
|||
"""Set up MQTT vacuum through configuration.yaml and dynamically through MQTT discovery."""
|
||||
# load and initialize platform config from configuration.yaml
|
||||
config_entry.async_on_unload(
|
||||
await async_setup_platform_discovery(
|
||||
hass, vacuum.DOMAIN, PLATFORM_SCHEMA_MODERN
|
||||
)
|
||||
await async_setup_platform_discovery(hass, vacuum.DOMAIN)
|
||||
)
|
||||
# setup for discovery
|
||||
setup = functools.partial(
|
||||
|
|
|
@ -13,6 +13,7 @@ from homeassistant.components.humidifier import (
|
|||
SERVICE_SET_HUMIDITY,
|
||||
SERVICE_SET_MODE,
|
||||
)
|
||||
from homeassistant.components.mqtt import CONFIG_SCHEMA
|
||||
from homeassistant.components.mqtt.humidifier import (
|
||||
CONF_MODE_COMMAND_TOPIC,
|
||||
CONF_MODE_STATE_TOPIC,
|
||||
|
@ -1283,3 +1284,15 @@ async def test_setup_manual_entity_from_yaml(hass):
|
|||
del config["platform"]
|
||||
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
|
||||
assert hass.states.get(f"{platform}.test") is not None
|
||||
|
||||
|
||||
async def test_config_schema_validation(hass):
|
||||
"""Test invalid platform options in the config schema do pass the config validation."""
|
||||
platform = humidifier.DOMAIN
|
||||
config = copy.deepcopy(DEFAULT_CONFIG[platform])
|
||||
config["name"] = "test"
|
||||
del config["platform"]
|
||||
CONFIG_SCHEMA({DOMAIN: {platform: config}})
|
||||
CONFIG_SCHEMA({DOMAIN: {platform: [config]}})
|
||||
with pytest.raises(MultipleInvalid):
|
||||
CONFIG_SCHEMA({"mqtt": {"humidifier": [{"bla": "bla"}]}})
|
||||
|
|
|
@ -14,7 +14,7 @@ import yaml
|
|||
|
||||
from homeassistant import config as hass_config
|
||||
from homeassistant.components import mqtt
|
||||
from homeassistant.components.mqtt import debug_info
|
||||
from homeassistant.components.mqtt import CONFIG_SCHEMA, debug_info
|
||||
from homeassistant.components.mqtt.mixins import MQTT_ENTITY_DEVICE_INFO_SCHEMA
|
||||
from homeassistant.components.mqtt.models import ReceiveMessage
|
||||
from homeassistant.const import (
|
||||
|
@ -1373,40 +1373,47 @@ async def test_setup_override_configuration(hass, caplog, tmp_path):
|
|||
assert calls_username_password_set[0][1] == "somepassword"
|
||||
|
||||
|
||||
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
|
||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
||||
async def test_setup_manual_mqtt_with_platform_key(hass, caplog):
|
||||
"""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, "light", config)
|
||||
with pytest.raises(AssertionError):
|
||||
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
|
||||
assert (
|
||||
"Invalid config for [light]: [platform] is an invalid option for [light]. "
|
||||
"Check: light->platform. (See ?, line ?)" in caplog.text
|
||||
"Invalid config for [mqtt]: [platform] is an invalid option for [mqtt]"
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
|
||||
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
|
||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
||||
async def test_setup_manual_mqtt_with_invalid_config(hass, caplog):
|
||||
"""Test set up a manual MQTT item with an invalid config."""
|
||||
config = {"name": "test"}
|
||||
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
|
||||
with pytest.raises(AssertionError):
|
||||
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
|
||||
assert (
|
||||
"Invalid config for [light]: required key not provided @ data['command_topic']."
|
||||
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']."
|
||||
" Got None. (See ?, line ?)" in caplog.text
|
||||
)
|
||||
|
||||
|
||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
||||
async def test_setup_manual_mqtt_empty_platform(hass, caplog):
|
||||
"""Test set up a manual MQTT platform without items."""
|
||||
config = None
|
||||
config = []
|
||||
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
|
||||
assert "voluptuous.error.MultipleInvalid" not in caplog.text
|
||||
|
||||
|
||||
@patch("homeassistant.components.mqtt.PLATFORMS", [])
|
||||
async def test_setup_mqtt_client_protocol(hass):
|
||||
"""Test MQTT client protocol setup."""
|
||||
entry = MockConfigEntry(
|
||||
domain=mqtt.DOMAIN,
|
||||
data={mqtt.CONF_BROKER: "test-broker", mqtt.config.CONF_PROTOCOL: "3.1"},
|
||||
data={
|
||||
mqtt.CONF_BROKER: "test-broker",
|
||||
mqtt.config_integration.CONF_PROTOCOL: "3.1",
|
||||
},
|
||||
)
|
||||
with patch("paho.mqtt.client.Client") as mock_client:
|
||||
mock_client.on_connect(return_value=0)
|
||||
|
@ -2612,3 +2619,10 @@ async def test_one_deprecation_warning_per_platform(
|
|||
):
|
||||
count += 1
|
||||
assert count == 1
|
||||
|
||||
|
||||
async def test_config_schema_validation(hass):
|
||||
"""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)
|
||||
|
|
Loading…
Reference in New Issue