home-assistant-core/homeassistant/setup.py

575 lines
19 KiB
Python
Raw Normal View History

"""All methods needed to bootstrap a Home Assistant instance."""
2021-03-17 16:34:55 +00:00
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable, Generator, Iterable
import contextlib
2022-02-14 13:24:58 +00:00
from datetime import timedelta
import logging.handlers
from timeit import default_timer as timer
from types import ModuleType
from typing import Any
from . import config as conf_util, core, loader, requirements
from .const import (
EVENT_COMPONENT_LOADED,
EVENT_HOMEASSISTANT_START,
PLATFORM_FORMAT,
Platform,
)
from .core import CALLBACK_TYPE, DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback
from .exceptions import DependencyError, HomeAssistantError
from .helpers.issue_registry import IssueSeverity, async_create_issue
from .helpers.typing import ConfigType
from .util import dt as dt_util, ensure_unique_string
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
ATTR_COMPONENT = "component"
BASE_PLATFORMS = {platform.value for platform in Platform}
# DATA_SETUP is a dict[str, asyncio.Task[bool]], indicating domains which are currently
# being setup or which failed to setup:
# - Tasks are added to DATA_SETUP by `async_setup_component`, the key is the domain
# being setup and the Task is the `_async_setup_component` helper.
# - Tasks are removed from DATA_SETUP if setup was successful, that is,
# the task returned True.
DATA_SETUP = "setup_tasks"
# DATA_SETUP_DONE is a dict [str, asyncio.Event], indicating components which
# will be setup:
# - Events are added to DATA_SETUP_DONE during bootstrap by
# async_set_domains_to_be_loaded, the key is the domain which will be loaded.
# - Events are set and removed from DATA_SETUP_DONE when async_setup_component
# is finished, regardless of if the setup was successful or not.
DATA_SETUP_DONE = "setup_done"
# DATA_SETUP_DONE is a dict [str, datetime], indicating when an attempt
# to setup a component started.
DATA_SETUP_STARTED = "setup_started"
# DATA_SETUP_TIME is a dict [str, timedelta], indicating how time was spent
# setting up a component.
DATA_SETUP_TIME = "setup_time"
2019-07-31 19:25:30 +00:00
DATA_DEPS_REQS = "deps_reqs_processed"
DATA_PERSISTENT_ERRORS = "bootstrap_persistent_errors"
NOTIFY_FOR_TRANSLATION_KEYS = [
"config_validation_err",
"platform_config_validation_err",
]
SLOW_SETUP_WARNING = 10
SLOW_SETUP_MAX_WAIT = 300
@callback
def async_notify_setup_error(
hass: HomeAssistant, component: str, display_link: str | None = None
) -> None:
"""Print a persistent notification.
This method must be run in the event loop.
"""
# pylint: disable-next=import-outside-toplevel
from .components import persistent_notification
if (errors := hass.data.get(DATA_PERSISTENT_ERRORS)) is None:
errors = hass.data[DATA_PERSISTENT_ERRORS] = {}
errors[component] = errors.get(component) or display_link
message = "The following integrations and platforms could not be set up:\n\n"
for name, link in errors.items():
show_logs = f"[Show logs](/config/logs?filter={name})"
part = f"[{name}]({link})" if link else name
message += f" - {part} ({show_logs})\n"
message += "\nPlease check your config and [logs](/config/logs)."
persistent_notification.async_create(
hass, message, "Invalid config", "invalid_config"
)
@core.callback
2021-03-17 16:34:55 +00:00
def async_set_domains_to_be_loaded(hass: core.HomeAssistant, domains: set[str]) -> None:
"""Set domains that are going to be loaded from the config.
This allow us to:
- Properly handle after_dependencies.
- Keep track of domains which will load but have not yet finished loading
"""
hass.data.setdefault(DATA_SETUP_DONE, {})
hass.data[DATA_SETUP_DONE].update({domain: asyncio.Event() for domain in domains})
2020-03-14 10:39:28 +00:00
def setup_component(hass: core.HomeAssistant, domain: str, config: ConfigType) -> bool:
"""Set up a component and all its dependencies."""
return asyncio.run_coroutine_threadsafe(
2019-07-31 19:25:30 +00:00
async_setup_component(hass, domain, config), hass.loop
).result()
2019-07-31 19:25:30 +00:00
async def async_setup_component(
2020-03-14 10:39:28 +00:00
hass: core.HomeAssistant, domain: str, config: ConfigType
2019-07-31 19:25:30 +00:00
) -> bool:
"""Set up a component and all its dependencies.
This method is a coroutine.
"""
if domain in hass.config.components:
return True
setup_tasks: dict[str, asyncio.Task[bool]] = hass.data.setdefault(DATA_SETUP, {})
if domain in setup_tasks:
return await setup_tasks[domain]
task = setup_tasks[domain] = hass.async_create_task(
_async_setup_component(hass, domain, config), f"setup component {domain}"
2019-07-31 19:25:30 +00:00
)
try:
return await task
finally:
if domain in hass.data.get(DATA_SETUP_DONE, {}):
hass.data[DATA_SETUP_DONE].pop(domain).set()
async def _async_process_dependencies(
hass: core.HomeAssistant, config: ConfigType, integration: loader.Integration
) -> list[str]:
"""Ensure all dependencies are set up.
Returns a list of dependencies which failed to set up.
"""
dependencies_tasks = {
dep: hass.loop.create_task(async_setup_component(hass, dep, config))
for dep in integration.dependencies
if dep not in hass.config.components
}
2020-10-06 13:02:23 +00:00
after_dependencies_tasks = {}
to_be_loaded = hass.data.get(DATA_SETUP_DONE, {})
for dep in integration.after_dependencies:
if (
dep not in dependencies_tasks
and dep in to_be_loaded
and dep not in hass.config.components
):
after_dependencies_tasks[dep] = hass.loop.create_task(
to_be_loaded[dep].wait()
)
if not dependencies_tasks and not after_dependencies_tasks:
return []
if dependencies_tasks:
_LOGGER.debug(
"Dependency %s will wait for dependencies %s",
integration.domain,
list(dependencies_tasks),
)
if after_dependencies_tasks:
_LOGGER.debug(
"Dependency %s will wait for after dependencies %s",
integration.domain,
list(after_dependencies_tasks),
)
async with hass.timeout.async_freeze(integration.domain):
results = await asyncio.gather(
*dependencies_tasks.values(), *after_dependencies_tasks.values()
)
failed = [
domain for idx, domain in enumerate(dependencies_tasks) if not results[idx]
]
if failed:
2019-07-31 19:25:30 +00:00
_LOGGER.error(
"Unable to set up dependencies of '%s'. Setup failed for dependencies: %s",
integration.domain,
2019-07-31 19:25:30 +00:00
", ".join(failed),
)
return failed
2019-07-31 19:25:30 +00:00
async def _async_setup_component(
2020-03-14 10:39:28 +00:00
hass: core.HomeAssistant, domain: str, config: ConfigType
2019-07-31 19:25:30 +00:00
) -> bool:
"""Set up a component for Home Assistant.
This method is a coroutine.
"""
integration: loader.Integration | None = None
2019-07-31 19:25:30 +00:00
Log a traceback when importing a component fails (#94778) `2023-06-17 12:44:37.961 ERROR (MainThread) [homeassistant.setup] Setup failed for switchbot: Unable to import component: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py)` is not very helpful as it does not show which module tried to import. adding a traceback makes it more obvious, and since ImportError is usually not something the user can easily solve, it makes issue reports much more helpful ``` DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) Traceback (most recent call last): File "/Users/bdraco/home-assistant/homeassistant/setup.py", line 213, in _async_setup_component component = integration.get_component() ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/bdraco/home-assistant/homeassistant/loader.py", line 813, in get_component ComponentProtocol, importlib.import_module(self.pkg_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.11/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<frozen importlib._bootstrap>", line 1206, in _gcd_import File "<frozen importlib._bootstrap>", line 1178, in _find_and_load File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 690, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 940, in exec_module File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed File "/Users/bdraco/home-assistant/homeassistant/components/switchbot/__init__.py", line 5, in <module> import switchbot File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/__init__.py", line 22, in <module> from .devices.lock import SwitchbotLock File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/devices/lock.py", line 12, in <module> import boto3 File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/__init__.py", line 16, in <module> from boto3.session import Session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/session.py", line 17, in <module> import botocore.session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/session.py", line 29, in <module> import botocore.credentials File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/credentials.py", line 34, in <module> from botocore.config import Config File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/config.py", line 16, in <module> from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/endpoint.py", line 22, in <module> from botocore.awsrequest import create_request_object File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/awsrequest.py", line 24, in <module> import botocore.utils File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/utils.py", line 32, in <module> import botocore.httpsession File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/httpsession.py", line 10, in <module> from urllib3.util.ssl_ import ( ImportError: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) ```
2023-06-19 11:10:39 +00:00
def log_error(msg: str, exc_info: Exception | None = None) -> None:
"""Log helper."""
if integration is None:
custom = ""
link = None
else:
custom = "" if integration.is_built_in else "custom integration "
link = integration.documentation
Log a traceback when importing a component fails (#94778) `2023-06-17 12:44:37.961 ERROR (MainThread) [homeassistant.setup] Setup failed for switchbot: Unable to import component: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py)` is not very helpful as it does not show which module tried to import. adding a traceback makes it more obvious, and since ImportError is usually not something the user can easily solve, it makes issue reports much more helpful ``` DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) Traceback (most recent call last): File "/Users/bdraco/home-assistant/homeassistant/setup.py", line 213, in _async_setup_component component = integration.get_component() ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/bdraco/home-assistant/homeassistant/loader.py", line 813, in get_component ComponentProtocol, importlib.import_module(self.pkg_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.11/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<frozen importlib._bootstrap>", line 1206, in _gcd_import File "<frozen importlib._bootstrap>", line 1178, in _find_and_load File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 690, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 940, in exec_module File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed File "/Users/bdraco/home-assistant/homeassistant/components/switchbot/__init__.py", line 5, in <module> import switchbot File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/__init__.py", line 22, in <module> from .devices.lock import SwitchbotLock File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/devices/lock.py", line 12, in <module> import boto3 File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/__init__.py", line 16, in <module> from boto3.session import Session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/session.py", line 17, in <module> import botocore.session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/session.py", line 29, in <module> import botocore.credentials File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/credentials.py", line 34, in <module> from botocore.config import Config File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/config.py", line 16, in <module> from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/endpoint.py", line 22, in <module> from botocore.awsrequest import create_request_object File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/awsrequest.py", line 24, in <module> import botocore.utils File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/utils.py", line 32, in <module> import botocore.httpsession File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/httpsession.py", line 10, in <module> from urllib3.util.ssl_ import ( ImportError: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) ```
2023-06-19 11:10:39 +00:00
_LOGGER.error(
"Setup failed for %s'%s': %s", custom, domain, msg, exc_info=exc_info
Log a traceback when importing a component fails (#94778) `2023-06-17 12:44:37.961 ERROR (MainThread) [homeassistant.setup] Setup failed for switchbot: Unable to import component: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py)` is not very helpful as it does not show which module tried to import. adding a traceback makes it more obvious, and since ImportError is usually not something the user can easily solve, it makes issue reports much more helpful ``` DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) Traceback (most recent call last): File "/Users/bdraco/home-assistant/homeassistant/setup.py", line 213, in _async_setup_component component = integration.get_component() ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/bdraco/home-assistant/homeassistant/loader.py", line 813, in get_component ComponentProtocol, importlib.import_module(self.pkg_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.11/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<frozen importlib._bootstrap>", line 1206, in _gcd_import File "<frozen importlib._bootstrap>", line 1178, in _find_and_load File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 690, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 940, in exec_module File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed File "/Users/bdraco/home-assistant/homeassistant/components/switchbot/__init__.py", line 5, in <module> import switchbot File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/__init__.py", line 22, in <module> from .devices.lock import SwitchbotLock File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/devices/lock.py", line 12, in <module> import boto3 File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/__init__.py", line 16, in <module> from boto3.session import Session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/session.py", line 17, in <module> import botocore.session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/session.py", line 29, in <module> import botocore.credentials File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/credentials.py", line 34, in <module> from botocore.config import Config File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/config.py", line 16, in <module> from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/endpoint.py", line 22, in <module> from botocore.awsrequest import create_request_object File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/awsrequest.py", line 24, in <module> import botocore.utils File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/utils.py", line 32, in <module> import botocore.httpsession File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/httpsession.py", line 10, in <module> from urllib3.util.ssl_ import ( ImportError: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) ```
2023-06-19 11:10:39 +00:00
)
async_notify_setup_error(hass, domain, link)
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
try:
integration = await loader.async_get_integration(hass, domain)
except loader.IntegrationNotFound:
log_error("Integration not found.")
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
return False
if integration.disabled:
log_error(f"Dependency is disabled - {integration.disabled}")
return False
# Validate all dependencies exist and there are no circular dependencies
if not await integration.resolve_dependencies():
return False
# Process requirements as soon as possible, so we can import the component
# without requiring imports to be in functions.
try:
await async_process_deps_reqs(hass, config, integration)
except HomeAssistantError as err:
log_error(str(err))
return False
# Some integrations fail on import because they call functions incorrectly.
# So we do it before validating config to catch these errors.
try:
component = integration.get_component()
except ImportError as err:
Log a traceback when importing a component fails (#94778) `2023-06-17 12:44:37.961 ERROR (MainThread) [homeassistant.setup] Setup failed for switchbot: Unable to import component: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py)` is not very helpful as it does not show which module tried to import. adding a traceback makes it more obvious, and since ImportError is usually not something the user can easily solve, it makes issue reports much more helpful ``` DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) Traceback (most recent call last): File "/Users/bdraco/home-assistant/homeassistant/setup.py", line 213, in _async_setup_component component = integration.get_component() ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/bdraco/home-assistant/homeassistant/loader.py", line 813, in get_component ComponentProtocol, importlib.import_module(self.pkg_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.11/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<frozen importlib._bootstrap>", line 1206, in _gcd_import File "<frozen importlib._bootstrap>", line 1178, in _find_and_load File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 690, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 940, in exec_module File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed File "/Users/bdraco/home-assistant/homeassistant/components/switchbot/__init__.py", line 5, in <module> import switchbot File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/__init__.py", line 22, in <module> from .devices.lock import SwitchbotLock File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/switchbot/devices/lock.py", line 12, in <module> import boto3 File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/__init__.py", line 16, in <module> from boto3.session import Session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/boto3/session.py", line 17, in <module> import botocore.session File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/session.py", line 29, in <module> import botocore.credentials File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/credentials.py", line 34, in <module> from botocore.config import Config File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/config.py", line 16, in <module> from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/endpoint.py", line 22, in <module> from botocore.awsrequest import create_request_object File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/awsrequest.py", line 24, in <module> import botocore.utils File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/utils.py", line 32, in <module> import botocore.httpsession File "/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/botocore/httpsession.py", line 10, in <module> from urllib3.util.ssl_ import ( ImportError: cannot import name DEFAULT_CIPHERS from urllib3.util.ssl_ (/Users/bdraco/home-assistant/venv/lib/python3.11/site-packages/urllib3/util/ssl_.py) ```
2023-06-19 11:10:39 +00:00
log_error(f"Unable to import component: {err}", err)
return False
integration_config_info = await conf_util.async_process_component_config(
2019-07-31 19:25:30 +00:00
hass, config, integration
)
conf_util.async_handle_component_errors(hass, integration_config_info, integration)
processed_config = conf_util.async_drop_config_annotations(
integration_config_info, integration
)
for platform_exception in integration_config_info.exception_info_list:
if platform_exception.translation_key not in NOTIFY_FOR_TRANSLATION_KEYS:
continue
async_notify_setup_error(
hass, platform_exception.platform_path, platform_exception.integration_link
)
if processed_config is None:
log_error("Invalid config.")
return False
# Detect attempt to setup integration which can be setup only from config entry
if (
domain in processed_config
and not hasattr(component, "async_setup")
and not hasattr(component, "setup")
2023-05-31 08:11:02 +00:00
and not hasattr(component, "CONFIG_SCHEMA")
):
_LOGGER.error(
(
"The '%s' integration does not support YAML setup, please remove it "
"from your configuration"
),
domain,
)
async_create_issue(
hass,
HOMEASSISTANT_DOMAIN,
f"config_entry_only_{domain}",
is_fixable=False,
severity=IssueSeverity.ERROR,
issue_domain=domain,
translation_key="config_entry_only",
translation_placeholders={
"domain": domain,
"add_integration": f"/config/integrations/dashboard/add?domain={domain}",
},
)
start = timer()
_LOGGER.info("Setting up %s", domain)
with async_start_setup(hass, [domain]):
if hasattr(component, "PLATFORM_SCHEMA"):
# Entity components have their own warning
warn_task = None
else:
warn_task = hass.loop.call_later(
SLOW_SETUP_WARNING,
_LOGGER.warning,
"Setup of %s is taking over %s seconds.",
domain,
SLOW_SETUP_WARNING,
)
task: Awaitable[bool] | None = None
result: Any | bool = True
try:
if hasattr(component, "async_setup"):
task = component.async_setup(hass, processed_config)
elif hasattr(component, "setup"):
# This should not be replaced with hass.async_add_executor_job because
# we don't want to track this task in case it blocks startup.
task = hass.loop.run_in_executor(
None, component.setup, hass, processed_config
)
elif not hasattr(component, "async_setup_entry"):
log_error("No setup or config entry setup function defined.")
return False
if task:
async with hass.timeout.async_timeout(SLOW_SETUP_MAX_WAIT, domain):
result = await task
except TimeoutError:
_LOGGER.error(
(
"Setup of '%s' is taking longer than %s seconds."
" Startup will proceed without waiting any longer"
),
domain,
SLOW_SETUP_MAX_WAIT,
)
return False
# pylint: disable-next=broad-except
except (asyncio.CancelledError, SystemExit, Exception):
_LOGGER.exception("Error during setup of component %s", domain)
async_notify_setup_error(hass, domain, integration.documentation)
return False
finally:
end = timer()
if warn_task:
warn_task.cancel()
_LOGGER.info("Setup of domain %s took %.1f seconds", domain, end - start)
if result is False:
log_error("Integration failed to initialize.")
return False
if result is not True:
log_error(
f"Integration {domain!r} did not return boolean if setup was "
"successful. Disabling component."
2019-07-31 20:08:31 +00:00
)
return False
# Flush out async_setup calling create_task. Fragile but covered by test.
await asyncio.sleep(0)
await hass.config_entries.flow.async_wait_import_flow_initialized(domain)
# Add to components before the entry.async_setup
# call to avoid a deadlock when forwarding platforms
hass.config.components.add(domain)
if entries := hass.config_entries.async_entries(
domain, include_ignore=False, include_disabled=False
):
await asyncio.gather(
*(
asyncio.create_task(
entry.async_setup(hass, integration=integration),
name=f"config entry setup {entry.title} {entry.domain} {entry.entry_id}",
)
for entry in entries
)
)
# Cleanup
if domain in hass.data[DATA_SETUP]:
hass.data[DATA_SETUP].pop(domain)
2019-07-31 19:25:30 +00:00
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: domain})
return True
2019-07-31 19:25:30 +00:00
async def async_prepare_setup_platform(
2020-03-14 10:39:28 +00:00
hass: core.HomeAssistant, hass_config: ConfigType, domain: str, platform_name: str
2021-03-17 16:34:55 +00:00
) -> ModuleType | None:
"""Load a platform and makes sure dependencies are setup.
This method is a coroutine.
"""
2019-07-31 19:25:30 +00:00
platform_path = PLATFORM_FORMAT.format(domain=domain, platform=platform_name)
def log_error(msg: str) -> None:
"""Log helper."""
_LOGGER.error(
"Unable to prepare setup for platform '%s': %s", platform_path, msg
)
async_notify_setup_error(hass, platform_path)
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
try:
integration = await loader.async_get_integration(hass, platform_name)
except loader.IntegrationNotFound:
log_error("Integration not found")
return None
# Process deps and reqs as soon as possible, so that requirements are
# available when we import the platform.
try:
await async_process_deps_reqs(hass, hass_config, integration)
except HomeAssistantError as err:
log_error(str(err))
return None
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
try:
platform = integration.get_platform(domain)
except ImportError as exc:
log_error(f"Platform not found ({exc}).")
return None
# Already loaded
if platform_path in hass.config.components:
return platform
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
# Platforms cannot exist on their own, they are part of their integration.
# If the integration is not set up yet, and can be set up, set it up.
if integration.domain not in hass.config.components:
try:
component = integration.get_component()
except ImportError as exc:
log_error(f"Unable to import the component ({exc}).")
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
return None
if (
hasattr(component, "setup") or hasattr(component, "async_setup")
) and not await async_setup_component(hass, integration.domain, hass_config):
log_error("Unable to set up component.")
return None
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
2018-01-30 11:30:47 +00:00
return platform
async def async_process_deps_reqs(
2020-03-14 10:39:28 +00:00
hass: core.HomeAssistant, config: ConfigType, integration: loader.Integration
2019-07-31 19:25:30 +00:00
) -> None:
2018-01-30 11:30:47 +00:00
"""Process all dependencies and requirements for a module.
Module is a Python module of either a component or platform.
"""
2021-09-18 23:31:35 +00:00
if (processed := hass.data.get(DATA_DEPS_REQS)) is None:
2018-01-30 11:30:47 +00:00
processed = hass.data[DATA_DEPS_REQS] = set()
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
elif integration.domain in processed:
2018-01-30 11:30:47 +00:00
return
if failed_deps := await _async_process_dependencies(hass, config, integration):
raise DependencyError(failed_deps)
async with hass.timeout.async_freeze(integration.domain):
await requirements.async_get_integration_with_requirements(
hass, integration.domain
)
Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts
2019-04-11 08:26:36 +00:00
processed.add(integration.domain)
@core.callback
def async_when_setup(
2019-07-31 19:25:30 +00:00
hass: core.HomeAssistant,
component: str,
when_setup_cb: Callable[[core.HomeAssistant, str], Awaitable[None]],
) -> None:
"""Call a method when a component is setup."""
_async_when_setup(hass, component, when_setup_cb, False)
@core.callback
def async_when_setup_or_start(
hass: core.HomeAssistant,
component: str,
when_setup_cb: Callable[[core.HomeAssistant, str], Awaitable[None]],
) -> None:
"""Call a method when a component is setup or state is fired."""
_async_when_setup(hass, component, when_setup_cb, True)
@core.callback
def _async_when_setup(
hass: core.HomeAssistant,
component: str,
when_setup_cb: Callable[[core.HomeAssistant, str], Awaitable[None]],
start_event: bool,
) -> None:
"""Call a method when a component is setup or the start event fires."""
2019-07-31 19:25:30 +00:00
async def when_setup() -> None:
"""Call the callback."""
try:
await when_setup_cb(hass, component)
except Exception: # pylint: disable=broad-except
2019-07-31 19:25:30 +00:00
_LOGGER.exception("Error handling when_setup callback for %s", component)
if component in hass.config.components:
hass.async_create_task(when_setup(), f"when setup {component}")
return
listeners: list[CALLBACK_TYPE] = []
async def _matched_event(event: core.Event) -> None:
"""Call the callback when we matched an event."""
for listener in listeners:
listener()
await when_setup()
async def _loaded_event(event: core.Event) -> None:
"""Call the callback if we loaded the expected component."""
if event.data[ATTR_COMPONENT] == component:
await _matched_event(event)
listeners.append(hass.bus.async_listen(EVENT_COMPONENT_LOADED, _loaded_event))
if start_event:
listeners.append(
hass.bus.async_listen(EVENT_HOMEASSISTANT_START, _matched_event)
)
@core.callback
def async_get_loaded_integrations(hass: core.HomeAssistant) -> set[str]:
"""Return the complete list of loaded integrations."""
integrations = set()
for component in hass.config.components:
if "." not in component:
integrations.add(component)
continue
platform, _, domain = component.partition(".")
if domain in BASE_PLATFORMS:
integrations.add(platform)
return integrations
@contextlib.contextmanager
def async_start_setup(
hass: core.HomeAssistant, components: Iterable[str]
) -> Generator[None, None, None]:
"""Keep track of when setup starts and finishes."""
setup_started = hass.data.setdefault(DATA_SETUP_STARTED, {})
started = dt_util.utcnow()
2022-02-14 13:24:58 +00:00
unique_components: dict[str, str] = {}
for domain in components:
unique = ensure_unique_string(domain, setup_started)
unique_components[unique] = domain
setup_started[unique] = started
yield
2022-02-14 13:24:58 +00:00
setup_time: dict[str, timedelta] = hass.data.setdefault(DATA_SETUP_TIME, {})
time_taken = dt_util.utcnow() - started
for unique, domain in unique_components.items():
del setup_started[unique]
integration = domain.partition(".")[0]
if integration in setup_time:
setup_time[integration] += time_taken
else:
setup_time[integration] = time_taken