Validating input
Validate what users give your integration with probatio schemas and the helpers in config_validation, in config flows, actions, and YAML.
Whatever a user gives your integration, check it before you use it. That goes for a config flow form, the data of an action, and YAML configuration. A clear error up front beats a confusing failure halfway through the setup.
Core validates input with schemas from probatio. A schema says which keys are required or optional, and what type or values each key accepts.
import probatio
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
from homeassistant.helpers import config_validation as cv
DEFAULT_PORT = 993
DATA_SCHEMA = probatio.Schema(
{
probatio.Required(CONF_HOST): cv.string,
probatio.Required(probatio.Secret(CONF_PASSWORD)): cv.string,
probatio.Optional(CONF_PORT, default=DEFAULT_PORT): probatio.Port(),
}
)In a config flow, you pass a schema like this as the data_schema of a step. For YAML, the schema is the CONFIG_SCHEMA of the integration. Building integrations covers both.
- Import probatio as
import probatio. Ruff blocks the oldvolalias, and it blocks voluptuous itself. - Use the
CONF_*constants fromhomeassistant.constwhen one exists, and your own fromconst.pyotherwise. - Put the required keys first, then the optional ones.
- A default must be a valid value. Never use
default=Nonefor a string: usedefault=""if you need a default at all.
Next to the validators of probatio, homeassistant.helpers.config_validation, usually imported as cv, has validators for things that are specific to Home Assistant:
- Types:
cv.string,cv.byte,cv.boolean - Entity IDs:
cv.entity_id,cv.entity_ids - Numbers:
cv.small_float,cv.positive_int - Time:
cv.time,cv.time_zone - Other:
cv.template,cv.slug,cv.temperature_unit,cv.latitude,cv.longitude,cv.isfile,cv.sun_event,cv.url,cv.icon
For MQTT topics, the MQTT integration has valid_subscribe_topic and valid_publish_topic in homeassistant/components/mqtt/util.py.
Some of the older cv helpers are now thin wrappers around a probatio validator. In core, a lint rule makes you use the probatio version directly:
| Instead of | Use |
|---|---|
cv.ensure_list |
probatio.EnsureList() |
cv.port |
probatio.Port() |
cv.has_at_least_one_key |
probatio.AtLeastOne |
cv.has_at_most_one_key |
probatio.AtMostOne |
The cv names keep working for custom integrations.
DATA_SCHEMA = probatio.Schema(
{
probatio.Optional(CONF_METHOD, default="GET"): probatio.In(
["GET", "POST"]
),
}
)SENSOR_TYPES = {
"article_cache": ("Article cache", "MB"),
"average_download_rate": ("Average speed", "MB/s"),
}
DATA_SCHEMA = probatio.Schema(
{
probatio.Optional(CONF_MONITORED_VARIABLES, default=[]): probatio.All(
probatio.EnsureList(), [probatio.In(SENSOR_TYPES)]
),
}
)Wrap a key in probatio.Secret when its value grants access, like a password, a token, an API key, or a PIN. When validation fails, the error then names the key and the problem, but not the value, so the secret does not end up in the log:
Invalid config for 'demo': value should be a string for dictionary value
'password', got **REDACTED**Only mark real secrets. A host name, a serial number, or a GPIO pin is not a secret, and hiding it makes the error useless to the person trying to fix their configuration.