1 """Config validation helper for the script integration."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
6 from contextlib
import suppress
7 from enum
import StrEnum
10 import voluptuous
as vol
11 from voluptuous.humanize
import humanize_error
15 is_blueprint_instance_config,
38 async_validate_actions_config,
54 from .helpers
import async_get_blueprints
56 PACKAGE_MERGE_HINT =
"dict"
58 _MINIMAL_SCRIPT_ENTITY_SCHEMA = vol.Schema(
60 CONF_ALIAS: cv.string,
61 vol.Optional(CONF_DESCRIPTION): cv.string,
63 extra=vol.ALLOW_EXTRA,
66 _INVALID_OBJECT_IDS = {
73 _SCRIPT_OBJECT_ID_SCHEMA = vol.All(
78 "A script's object_id must not be one of "
79 f
"{', '.join(sorted(_INVALID_OBJECT_IDS))}"
86 vol.Optional(CONF_ALIAS): cv.string,
87 vol.Optional(CONF_TRACE, default={}): TRACE_CONFIG_SCHEMA,
88 vol.Optional(CONF_ICON): cv.icon,
89 vol.Required(CONF_SEQUENCE): cv.SCRIPT_SCHEMA,
90 vol.Optional(CONF_DESCRIPTION, default=
""): cv.string,
91 vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA,
92 vol.Optional(CONF_FIELDS, default={}): {
94 vol.Optional(CONF_ADVANCED, default=
False): cv.boolean,
95 vol.Optional(CONF_DEFAULT): cv.match_all,
96 vol.Optional(CONF_DESCRIPTION): cv.string,
97 vol.Optional(CONF_EXAMPLE): cv.string,
98 vol.Optional(CONF_NAME): cv.string,
99 vol.Optional(CONF_REQUIRED, default=
False): cv.boolean,
100 vol.Optional(CONF_SELECTOR): validate_selector,
112 raise_on_errors: bool,
113 warn_on_errors: bool,
115 """Validate config item."""
117 raw_blueprint_inputs =
None
118 uses_blueprint =
False
119 with suppress(ValueError):
120 raw_config =
dict(config)
122 def _humanize(err: Exception, data: Any) -> str:
123 """Humanize vol.Invalid, stringify other exceptions."""
124 if isinstance(err, vol.Invalid):
128 def _log_invalid_script(
134 """Log an error about invalid script."""
135 if not warn_on_errors:
140 "Blueprint '%s' generated invalid script with inputs %s: %s",
141 blueprint_inputs.blueprint.name,
142 blueprint_inputs.inputs,
143 _humanize(err, data),
148 "%s %s and has been disabled: %s",
151 _humanize(err, data),
155 def _set_validation_status(
156 script_config: ScriptConfig,
157 validation_status: ValidationStatus,
158 validation_error: Exception,
161 """Set validation status."""
163 validation_status = ValidationStatus.FAILED_BLUEPRINT
164 script_config.validation_status = validation_status
165 script_config.validation_error = _humanize(validation_error, config)
168 validation_status: ValidationStatus,
169 validation_error: Exception,
172 """Try validating id, alias and description."""
175 script_config.raw_blueprint_inputs = raw_blueprint_inputs
176 script_config.raw_config = raw_config
177 _set_validation_status(
178 script_config, validation_status, validation_error, config
183 uses_blueprint =
True
186 blueprint_inputs = await blueprints.async_inputs_from_config(config)
187 except BlueprintException
as err:
190 "Failed to generate script from blueprint: %s",
195 return _minimal_config(ValidationStatus.FAILED_BLUEPRINT, err, config)
197 raw_blueprint_inputs = blueprint_inputs.config_with_inputs
200 config = blueprint_inputs.async_substitute()
201 raw_config =
dict(config)
202 except UndefinedSubstitution
as err:
205 "Blueprint '%s' failed to generate script with inputs %s: %s",
206 blueprint_inputs.blueprint.name,
207 blueprint_inputs.inputs,
212 return _minimal_config(ValidationStatus.FAILED_BLUEPRINT, err, config)
214 script_name = f
"Script with object id '{object_id}'"
215 if isinstance(config, Mapping):
216 if CONF_ALIAS
in config:
217 script_name = f
"Script with alias '{config[CONF_ALIAS]}'"
221 except vol.Invalid
as err:
222 _log_invalid_script(err, script_name,
"has invalid object id", object_id)
226 except vol.Invalid
as err:
227 _log_invalid_script(err, script_name,
"could not be validated", config)
230 return _minimal_config(ValidationStatus.FAILED_SCHEMA, err, config)
233 script_config.raw_blueprint_inputs = raw_blueprint_inputs
234 script_config.raw_config = raw_config
238 hass, validated_config[CONF_SEQUENCE]
245 err, script_name,
"failed to setup sequence", validated_config
249 _set_validation_status(
250 script_config, ValidationStatus.FAILED_SEQUENCE, err, validated_config
258 """What was changed in a config entry."""
260 FAILED_BLUEPRINT =
"failed_blueprint"
261 FAILED_SCHEMA =
"failed_schema"
262 FAILED_SEQUENCE =
"failed_sequence"
267 """Dummy class to allow adding attributes."""
269 raw_config: ConfigType |
None =
None
270 raw_blueprint_inputs: ConfigType |
None =
None
271 validation_status: ValidationStatus = ValidationStatus.OK
272 validation_error: str |
None =
None
279 ) -> ScriptConfig |
None:
280 """Validate config item."""
283 except (vol.Invalid, HomeAssistantError):
290 config: dict[str, Any],
291 ) -> ScriptConfig |
None:
292 """Validate config item, called by EditScriptConfigView."""
297 """Validate config."""
300 for object_id, cfg
in p_config.items():
301 if object_id
in scripts:
302 LOGGER.warning(
"Duplicate script detected with name: '%s'", object_id)
306 scripts[object_id] = cfg
311 config[DOMAIN] = scripts
blueprint.DomainBlueprints async_get_blueprints(HomeAssistant hass)
bool is_blueprint_instance_config(Any config)
ScriptConfig|None async_validate_config_item(HomeAssistant hass, str object_id, dict[str, Any] config)
_MINIMAL_SCRIPT_ENTITY_SCHEMA
ScriptConfig|None _try_async_validate_config_item(HomeAssistant hass, str object_id, ConfigType config)
ScriptConfig _async_validate_config_item(HomeAssistant hass, str object_id, ConfigType config, bool raise_on_errors, bool warn_on_errors)
ConfigType async_validate_config(HomeAssistant hass, ConfigType config)
Iterable[tuple[str|None, ConfigType]] config_per_platform(ConfigType config, str domain)
ConfigType config_without_domain(ConfigType config, str domain)
str humanize_error(HomeAssistant hass, vol.Invalid validation_error, str domain, dict config, str|None link, int max_sub_error_length=MAX_VALIDATION_ERROR_ITEM_LENGTH)
list[ConfigType] async_validate_actions_config(HomeAssistant hass, list[ConfigType] actions)
vol.Schema make_script_schema(Mapping[Any, Any] schema, str default_script_mode, int extra=vol.PREVENT_EXTRA)