Move core fundamental components into bootstrap (#105560)

Co-authored-by: Erik <erik@montnemery.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/108670/head
Franck Nijhof 2024-01-22 20:09:48 +01:00 committed by GitHub
parent 31ef034c3f
commit 80207835d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 40 deletions

View File

@ -39,7 +39,6 @@ from .helpers import (
from .helpers.dispatcher import async_dispatcher_send
from .helpers.typing import ConfigType
from .setup import (
DATA_SETUP,
DATA_SETUP_STARTED,
DATA_SETUP_TIME,
async_notify_setup_error,
@ -106,6 +105,52 @@ STAGE_1_INTEGRATIONS = {
# Ensure supervisor is available
"hassio",
}
DEFAULT_INTEGRATIONS = {
# These integrations are set up unless recovery mode is activated.
#
# Integrations providing core functionality:
"application_credentials",
"frontend",
"hardware",
"logger",
"network",
"system_health",
#
# Key-feature:
"automation",
"person",
"scene",
"script",
"tag",
"zone",
#
# Built-in helpers:
"counter",
"input_boolean",
"input_button",
"input_datetime",
"input_number",
"input_select",
"input_text",
"schedule",
"timer",
}
DEFAULT_INTEGRATIONS_RECOVERY_MODE = {
# These integrations are set up if recovery mode is activated.
"frontend",
}
DEFAULT_INTEGRATIONS_SUPERVISOR = {
# These integrations are set up if using the Supervisor
"hassio",
}
DEFAULT_INTEGRATIONS_NON_SUPERVISOR = {
# These integrations are set up if not using the Supervisor
"backup",
}
CRITICAL_INTEGRATIONS = {
# Recovery mode is activated if these integrations fail to set up
"frontend",
}
async def async_setup_hass(
@ -165,11 +210,11 @@ async def async_setup_hass(
_LOGGER.warning("Unable to set up core integrations. Activating recovery mode")
recovery_mode = True
elif (
"frontend" in hass.data.get(DATA_SETUP, {})
and "frontend" not in hass.config.components
):
_LOGGER.warning("Detected that frontend did not load. Activating recovery mode")
elif any(domain not in hass.config.components for domain in CRITICAL_INTEGRATIONS):
_LOGGER.warning(
"Detected that %s did not load. Activating recovery mode",
",".join(CRITICAL_INTEGRATIONS),
)
# Ask integrations to shut down. It's messy but we can't
# do a clean stop without knowing what is broken
with contextlib.suppress(asyncio.TimeoutError):
@ -478,13 +523,18 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
domain for key in config if (domain := cv.domain_key(key)) != core.DOMAIN
}
# Add config entry domains
# Add config entry and default domains
if not hass.config.recovery_mode:
domains.update(DEFAULT_INTEGRATIONS)
domains.update(hass.config_entries.async_domains())
else:
domains.update(DEFAULT_INTEGRATIONS_RECOVERY_MODE)
# Make sure the Hass.io component is loaded
# Add domains depending on if the Supervisor is used or not
if "SUPERVISOR" in os.environ:
domains.add("hassio")
domains.update(DEFAULT_INTEGRATIONS_SUPERVISOR)
else:
domains.update(DEFAULT_INTEGRATIONS_NON_SUPERVISOR)
return domains

View File

@ -1,9 +1,7 @@
"""Component providing default configuration for new users."""
from homeassistant.components.hassio import is_hassio
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
DOMAIN = "default_config"
@ -12,7 +10,4 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Initialize default configuration."""
if not is_hassio(hass):
await async_setup_component(hass, "backup", config)
return True

View File

@ -3,46 +3,25 @@
"name": "Default Config",
"codeowners": ["@home-assistant/core"],
"dependencies": [
"application_credentials",
"assist_pipeline",
"automation",
"bluetooth",
"cloud",
"conversation",
"counter",
"dhcp",
"energy",
"frontend",
"hardware",
"history",
"homeassistant_alerts",
"input_boolean",
"input_button",
"input_datetime",
"input_number",
"input_select",
"input_text",
"logbook",
"logger",
"map",
"media_source",
"mobile_app",
"my",
"network",
"person",
"scene",
"schedule",
"script",
"ssdp",
"stream",
"sun",
"system_health",
"tag",
"timer",
"usb",
"webhook",
"zeroconf",
"zone"
"zeroconf"
],
"documentation": "https://www.home-assistant.io/integrations/default_config",
"integration_type": "system",

View File

@ -3,6 +3,7 @@ from unittest.mock import patch
import pytest
from homeassistant import bootstrap
from homeassistant.core import HomeAssistant
from homeassistant.helpers import recorder as recorder_helper
from homeassistant.setup import async_setup_component
@ -34,4 +35,9 @@ async def test_setup(
) -> None:
"""Test setup."""
recorder_helper.async_initialize_recorder(hass)
# default_config needs the homeassistant integration, assert it will be
# automatically setup by bootstrap and set it up manually for this test
assert "homeassistant" in bootstrap.CORE_INTEGRATIONS
assert await async_setup_component(hass, "homeassistant", {"foo": "bar"})
assert await async_setup_component(hass, "default_config", {"foo": "bar"})

View File

@ -87,12 +87,21 @@ async def test_async_enable_logging(
async def test_load_hassio(hass: HomeAssistant) -> None:
"""Test that we load Hass.io component."""
"""Test that we load the hassio integration when using Supervisor."""
with patch.dict(os.environ, {}, clear=True):
assert bootstrap._get_domains(hass, {}) == set()
assert "hassio" not in bootstrap._get_domains(hass, {})
with patch.dict(os.environ, {"SUPERVISOR": "1"}):
assert bootstrap._get_domains(hass, {}) == {"hassio"}
assert "hassio" in bootstrap._get_domains(hass, {})
async def test_load_backup(hass: HomeAssistant) -> None:
"""Test that we load the backup integration when not using Supervisor."""
with patch.dict(os.environ, {}, clear=True):
assert "backup" in bootstrap._get_domains(hass, {})
with patch.dict(os.environ, {"SUPERVISOR": "1"}):
assert "backup" not in bootstrap._get_domains(hass, {})
@pytest.mark.parametrize("load_registries", [False])
@ -784,6 +793,7 @@ async def test_setup_recovery_mode_if_no_frontend(
@pytest.mark.parametrize("load_registries", [False])
@patch("homeassistant.bootstrap.DEFAULT_INTEGRATIONS", set())
async def test_empty_integrations_list_is_only_sent_at_the_end_of_bootstrap(
hass: HomeAssistant,
) -> None:
@ -836,7 +846,7 @@ async def test_empty_integrations_list_is_only_sent_at_the_end_of_bootstrap(
assert integrations[0] != {}
assert "an_after_dep" in integrations[0]
assert integrations[-3] != {}
assert integrations[-2] != {}
assert integrations[-1] == {}
assert "normal_integration" in hass.config.components