1 """Helper to check the configuration file."""
3 from __future__
import annotations
5 from collections
import OrderedDict
8 from pathlib
import Path
9 from typing
import NamedTuple, Self
11 import voluptuous
as vol
13 from homeassistant
import loader
18 extract_domain_configs,
19 format_homeassistant_error,
21 load_yaml_config_file,
22 merge_packages_config,
29 async_clear_install_history,
30 async_get_integration_with_requirements,
34 from .
import config_validation
as cv
35 from .typing
import ConfigType
39 """Configuration check error."""
43 config: ConfigType |
None
47 """Configuration result with errors attribute."""
50 """Initialize HA config."""
52 self.errors: list[CheckConfigError] = []
53 self.warnings: list[CheckConfigError] = []
58 domain: str |
None =
None,
59 config: ConfigType |
None =
None,
67 """Concatenate all errors to a string."""
68 return "\n".join([err.message
for err
in self.errors])
73 domain: str |
None =
None,
74 config: ConfigType |
None =
None,
82 """Concatenate all warnings to a string."""
83 return "\n".join([err.message
for err
in self.warnings])
88 ) -> HomeAssistantConfig:
89 """Load and check if Home Assistant configuration file is valid.
91 This method is a coroutine.
99 component: str |
None,
103 """Handle errors from packages."""
104 message = f
"Setup of package '{package}' failed: {message}"
105 domain = f
"homeassistant.packages.{package}{'.' + component if component is not None else ''}"
106 pack_config = core_config[CONF_PACKAGES].
get(package, config)
107 result.add_warning(message, domain, pack_config)
110 ex: vol.Invalid | HomeAssistantError,
112 component_config: ConfigType,
113 config_to_attach: ConfigType,
115 """Handle errors from components."""
116 if isinstance(ex, vol.Invalid):
120 if domain
in frontend_dependencies:
121 result.add_error(message, domain, config_to_attach)
123 result.add_warning(message, domain, config_to_attach)
125 async
def _get_integration(
126 hass: HomeAssistant, domain: str
128 """Get an integration."""
137 if not hass.config.recovery_mode
and not hass.config.safe_mode:
138 result.add_warning(f
"Integration error: {domain} - {ex}")
139 except RequirementsNotFound
as ex:
140 result.add_warning(f
"Integration error: {domain} - {ex}")
144 config_path = hass.config.path(YAML_CONFIG_FILE)
146 if not await hass.async_add_executor_job(os.path.isfile, config_path):
147 return result.add_error(
"File configuration.yaml not found.")
149 config = await hass.async_add_executor_job(
150 load_yaml_config_file,
152 yaml_loader.Secrets(Path(hass.config.config_dir)),
154 except FileNotFoundError:
155 return result.add_error(f
"File not found: {config_path}")
156 except HomeAssistantError
as err:
157 return result.add_error(f
"Error loading {config_path}: {err}")
160 core_config = config.pop(HOMEASSISTANT_DOMAIN, {})
163 result[HOMEASSISTANT_DOMAIN] = core_config
167 hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error
169 except vol.Invalid
as err:
172 HOMEASSISTANT_DOMAIN,
176 core_config.pop(CONF_PACKAGES,
None)
179 components = {cv.domain_key(key)
for key
in config}
181 frontend_dependencies: set[str] = set()
182 if "frontend" in components
or "default_config" in components:
183 frontend = await _get_integration(hass,
"frontend")
185 await frontend.resolve_dependencies()
186 frontend_dependencies = frontend.all_dependencies | {
"frontend"}
189 for domain
in components:
190 if not (integration := await _get_integration(hass, domain)):
194 component = await integration.async_get_component()
195 except ImportError
as ex:
196 result.add_warning(f
"Component error: {domain} - {ex}")
200 config_validator =
None
201 if integration.platforms_exists((
"config",)):
203 config_validator = await integration.async_get_platform(
"config")
204 except ImportError
as err:
208 if err.name != f
"{integration.pkg_path}.config":
209 result.add_error(f
"Error importing config platform {domain}: {err}")
212 if config_validator
is not None and hasattr(
213 config_validator,
"async_validate_config"
217 await config_validator.async_validate_config(hass, config)
220 except (vol.Invalid, HomeAssistantError)
as ex:
221 _comp_error(ex, domain, config, config[domain])
223 except Exception
as err:
224 logging.getLogger(__name__).exception(
225 "Unexpected error validating config"
228 f
"Unexpected error calling config validator: {err}",
234 config_schema = getattr(component,
"CONFIG_SCHEMA",
None)
235 if config_schema
is not None:
237 validated_config = await cv.async_validate(hass, config_schema, config)
239 if domain
in validated_config:
240 result[domain] = validated_config[domain]
241 except vol.Invalid
as ex:
242 _comp_error(ex, domain, config, config[domain])
245 component_platform_schema = getattr(
247 "PLATFORM_SCHEMA_BASE",
248 getattr(component,
"PLATFORM_SCHEMA",
None),
251 if component_platform_schema
is None:
258 p_validated = await cv.async_validate(
259 hass, component_platform_schema, p_config
261 except vol.Invalid
as ex:
262 _comp_error(ex, domain, p_config, p_config)
269 platforms.append(p_validated)
276 platform = await p_integration.async_get_platform(domain)
282 if not hass.config.recovery_mode
and not hass.config.safe_mode:
284 f
"Platform error '{domain}' from integration '{p_name}' - {ex}"
288 RequirementsNotFound,
292 f
"Platform error '{domain}' from integration '{p_name}' - {ex}"
297 platform_schema = getattr(platform,
"PLATFORM_SCHEMA",
None)
298 if platform_schema
is not None:
300 p_validated = platform_schema(p_validated)
301 except vol.Invalid
as ex:
302 _comp_error(ex, f
"{domain}.{p_name}", p_config, p_config)
305 platforms.append(p_validated)
309 del config[filter_comp]
310 result[domain] = platforms
Self add_error(self, str message, str|None domain=None, ConfigType|None config=None)
Self add_warning(self, str message, str|None domain=None, ConfigType|None config=None)
web.Response get(self, web.Request request, str config_key)
Sequence[str] extract_domain_configs(ConfigType config, str domain)
Iterable[tuple[str|None, ConfigType]] config_per_platform(ConfigType config, str domain)
str format_homeassistant_error(HomeAssistant hass, HomeAssistantError exc, str domain, dict config, str|None link=None)
dict merge_packages_config(HomeAssistant hass, dict config, dict[str, Any] packages, Callable[[HomeAssistant, str, str|None, dict, str], None] _log_pkg_error=_log_pkg_error)
str format_schema_error(HomeAssistant hass, vol.Invalid exc, str domain, dict config, str|None link=None)
HomeAssistantConfig async_check_ha_config_file(HomeAssistant hass)
None async_clear_install_history(HomeAssistant hass)
Integration async_get_integration_with_requirements(HomeAssistant hass, str domain)