Update typing 01 (#48013)
This commit is contained in:
parent
9011a54e7f
commit
e55702d635
11 changed files with 303 additions and 313 deletions
|
@ -1,11 +1,12 @@
|
||||||
"""Start Home Assistant."""
|
"""Start Home Assistant."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from homeassistant.const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
|
from homeassistant.const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
|
||||||
|
|
||||||
|
@ -206,7 +207,7 @@ def closefds_osx(min_fd: int, max_fd: int) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def cmdline() -> List[str]:
|
def cmdline() -> list[str]:
|
||||||
"""Collect path and arguments to re-execute the current hass instance."""
|
"""Collect path and arguments to re-execute the current hass instance."""
|
||||||
if os.path.basename(sys.argv[0]) == "__main__.py":
|
if os.path.basename(sys.argv[0]) == "__main__.py":
|
||||||
modulepath = os.path.dirname(sys.argv[0])
|
modulepath = os.path.dirname(sys.argv[0])
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Provide methods to bootstrap a Home Assistant instance."""
|
"""Provide methods to bootstrap a Home Assistant instance."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -8,7 +10,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Set
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import yarl
|
import yarl
|
||||||
|
@ -75,7 +77,7 @@ STAGE_1_INTEGRATIONS = {
|
||||||
|
|
||||||
async def async_setup_hass(
|
async def async_setup_hass(
|
||||||
runtime_config: "RuntimeConfig",
|
runtime_config: "RuntimeConfig",
|
||||||
) -> Optional[core.HomeAssistant]:
|
) -> core.HomeAssistant | None:
|
||||||
"""Set up Home Assistant."""
|
"""Set up Home Assistant."""
|
||||||
hass = core.HomeAssistant()
|
hass = core.HomeAssistant()
|
||||||
hass.config.config_dir = runtime_config.config_dir
|
hass.config.config_dir = runtime_config.config_dir
|
||||||
|
@ -188,7 +190,7 @@ def open_hass_ui(hass: core.HomeAssistant) -> None:
|
||||||
|
|
||||||
async def async_from_config_dict(
|
async def async_from_config_dict(
|
||||||
config: ConfigType, hass: core.HomeAssistant
|
config: ConfigType, hass: core.HomeAssistant
|
||||||
) -> Optional[core.HomeAssistant]:
|
) -> core.HomeAssistant | None:
|
||||||
"""Try to configure Home Assistant from a configuration dictionary.
|
"""Try to configure Home Assistant from a configuration dictionary.
|
||||||
|
|
||||||
Dynamically loads required components and its dependencies.
|
Dynamically loads required components and its dependencies.
|
||||||
|
@ -255,8 +257,8 @@ async def async_from_config_dict(
|
||||||
def async_enable_logging(
|
def async_enable_logging(
|
||||||
hass: core.HomeAssistant,
|
hass: core.HomeAssistant,
|
||||||
verbose: bool = False,
|
verbose: bool = False,
|
||||||
log_rotate_days: Optional[int] = None,
|
log_rotate_days: int | None = None,
|
||||||
log_file: Optional[str] = None,
|
log_file: str | None = None,
|
||||||
log_no_color: bool = False,
|
log_no_color: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the logging.
|
"""Set up the logging.
|
||||||
|
@ -362,7 +364,7 @@ async def async_mount_local_lib_path(config_dir: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
@core.callback
|
@core.callback
|
||||||
def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]:
|
def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
|
||||||
"""Get domains of components to set up."""
|
"""Get domains of components to set up."""
|
||||||
# Filter out the repeating and common config section [homeassistant]
|
# Filter out the repeating and common config section [homeassistant]
|
||||||
domains = {key.split(" ")[0] for key in config if key != core.DOMAIN}
|
domains = {key.split(" ")[0] for key in config if key != core.DOMAIN}
|
||||||
|
@ -379,7 +381,7 @@ def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]:
|
||||||
|
|
||||||
|
|
||||||
async def _async_log_pending_setups(
|
async def _async_log_pending_setups(
|
||||||
hass: core.HomeAssistant, domains: Set[str], setup_started: Dict[str, datetime]
|
hass: core.HomeAssistant, domains: set[str], setup_started: dict[str, datetime]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL."""
|
"""Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL."""
|
||||||
while True:
|
while True:
|
||||||
|
@ -396,9 +398,9 @@ async def _async_log_pending_setups(
|
||||||
|
|
||||||
async def async_setup_multi_components(
|
async def async_setup_multi_components(
|
||||||
hass: core.HomeAssistant,
|
hass: core.HomeAssistant,
|
||||||
domains: Set[str],
|
domains: set[str],
|
||||||
config: Dict[str, Any],
|
config: dict[str, Any],
|
||||||
setup_started: Dict[str, datetime],
|
setup_started: dict[str, datetime],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up multiple domains. Log on failure."""
|
"""Set up multiple domains. Log on failure."""
|
||||||
futures = {
|
futures = {
|
||||||
|
@ -422,7 +424,7 @@ async def async_setup_multi_components(
|
||||||
|
|
||||||
|
|
||||||
async def _async_set_up_integrations(
|
async def _async_set_up_integrations(
|
||||||
hass: core.HomeAssistant, config: Dict[str, Any]
|
hass: core.HomeAssistant, config: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up all the integrations."""
|
"""Set up all the integrations."""
|
||||||
setup_started = hass.data[DATA_SETUP_STARTED] = {}
|
setup_started = hass.data[DATA_SETUP_STARTED] = {}
|
||||||
|
@ -430,7 +432,7 @@ async def _async_set_up_integrations(
|
||||||
|
|
||||||
# Resolve all dependencies so we know all integrations
|
# Resolve all dependencies so we know all integrations
|
||||||
# that will have to be loaded and start rightaway
|
# that will have to be loaded and start rightaway
|
||||||
integration_cache: Dict[str, loader.Integration] = {}
|
integration_cache: dict[str, loader.Integration] = {}
|
||||||
to_resolve = domains_to_setup
|
to_resolve = domains_to_setup
|
||||||
while to_resolve:
|
while to_resolve:
|
||||||
old_to_resolve = to_resolve
|
old_to_resolve = to_resolve
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Module to help with parsing and generating configuration files."""
|
"""Module to help with parsing and generating configuration files."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -6,7 +8,7 @@ from pathlib import Path
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any, Callable, Dict, Optional, Sequence, Set, Tuple, Union
|
from typing import Any, Callable, Sequence
|
||||||
|
|
||||||
from awesomeversion import AwesomeVersion
|
from awesomeversion import AwesomeVersion
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
@ -114,14 +116,14 @@ tts:
|
||||||
|
|
||||||
|
|
||||||
def _no_duplicate_auth_provider(
|
def _no_duplicate_auth_provider(
|
||||||
configs: Sequence[Dict[str, Any]]
|
configs: Sequence[dict[str, Any]]
|
||||||
) -> Sequence[Dict[str, Any]]:
|
) -> Sequence[dict[str, Any]]:
|
||||||
"""No duplicate auth provider config allowed in a list.
|
"""No duplicate auth provider config allowed in a list.
|
||||||
|
|
||||||
Each type of auth provider can only have one config without optional id.
|
Each type of auth provider can only have one config without optional id.
|
||||||
Unique id is required if same type of auth provider used multiple times.
|
Unique id is required if same type of auth provider used multiple times.
|
||||||
"""
|
"""
|
||||||
config_keys: Set[Tuple[str, Optional[str]]] = set()
|
config_keys: set[tuple[str, str | None]] = set()
|
||||||
for config in configs:
|
for config in configs:
|
||||||
key = (config[CONF_TYPE], config.get(CONF_ID))
|
key = (config[CONF_TYPE], config.get(CONF_ID))
|
||||||
if key in config_keys:
|
if key in config_keys:
|
||||||
|
@ -135,8 +137,8 @@ def _no_duplicate_auth_provider(
|
||||||
|
|
||||||
|
|
||||||
def _no_duplicate_auth_mfa_module(
|
def _no_duplicate_auth_mfa_module(
|
||||||
configs: Sequence[Dict[str, Any]]
|
configs: Sequence[dict[str, Any]]
|
||||||
) -> Sequence[Dict[str, Any]]:
|
) -> Sequence[dict[str, Any]]:
|
||||||
"""No duplicate auth mfa module item allowed in a list.
|
"""No duplicate auth mfa module item allowed in a list.
|
||||||
|
|
||||||
Each type of mfa module can only have one config without optional id.
|
Each type of mfa module can only have one config without optional id.
|
||||||
|
@ -144,7 +146,7 @@ def _no_duplicate_auth_mfa_module(
|
||||||
times.
|
times.
|
||||||
Note: this is different than auth provider
|
Note: this is different than auth provider
|
||||||
"""
|
"""
|
||||||
config_keys: Set[str] = set()
|
config_keys: set[str] = set()
|
||||||
for config in configs:
|
for config in configs:
|
||||||
key = config.get(CONF_ID, config[CONF_TYPE])
|
key = config.get(CONF_ID, config[CONF_TYPE])
|
||||||
if key in config_keys:
|
if key in config_keys:
|
||||||
|
@ -313,7 +315,7 @@ def _write_default_config(config_dir: str) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def async_hass_config_yaml(hass: HomeAssistant) -> Dict:
|
async def async_hass_config_yaml(hass: HomeAssistant) -> dict:
|
||||||
"""Load YAML from a Home Assistant configuration file.
|
"""Load YAML from a Home Assistant configuration file.
|
||||||
|
|
||||||
This function allow a component inside the asyncio loop to reload its
|
This function allow a component inside the asyncio loop to reload its
|
||||||
|
@ -337,8 +339,8 @@ async def async_hass_config_yaml(hass: HomeAssistant) -> Dict:
|
||||||
|
|
||||||
|
|
||||||
def load_yaml_config_file(
|
def load_yaml_config_file(
|
||||||
config_path: str, secrets: Optional[Secrets] = None
|
config_path: str, secrets: Secrets | None = None
|
||||||
) -> Dict[Any, Any]:
|
) -> dict[Any, Any]:
|
||||||
"""Parse a YAML configuration file.
|
"""Parse a YAML configuration file.
|
||||||
|
|
||||||
Raises FileNotFoundError or HomeAssistantError.
|
Raises FileNotFoundError or HomeAssistantError.
|
||||||
|
@ -421,9 +423,9 @@ def process_ha_config_upgrade(hass: HomeAssistant) -> None:
|
||||||
def async_log_exception(
|
def async_log_exception(
|
||||||
ex: Exception,
|
ex: Exception,
|
||||||
domain: str,
|
domain: str,
|
||||||
config: Dict,
|
config: dict,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
link: Optional[str] = None,
|
link: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Log an error for configuration validation.
|
"""Log an error for configuration validation.
|
||||||
|
|
||||||
|
@ -437,8 +439,8 @@ def async_log_exception(
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _format_config_error(
|
def _format_config_error(
|
||||||
ex: Exception, domain: str, config: Dict, link: Optional[str] = None
|
ex: Exception, domain: str, config: dict, link: str | None = None
|
||||||
) -> Tuple[str, bool]:
|
) -> tuple[str, bool]:
|
||||||
"""Generate log exception for configuration validation.
|
"""Generate log exception for configuration validation.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -474,7 +476,7 @@ def _format_config_error(
|
||||||
return message, is_friendly
|
return message, is_friendly
|
||||||
|
|
||||||
|
|
||||||
async def async_process_ha_core_config(hass: HomeAssistant, config: Dict) -> None:
|
async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> None:
|
||||||
"""Process the [homeassistant] section from the configuration.
|
"""Process the [homeassistant] section from the configuration.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
|
@ -603,7 +605,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: Dict) -> Non
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _log_pkg_error(package: str, component: str, config: Dict, message: str) -> None:
|
def _log_pkg_error(package: str, component: str, config: dict, message: str) -> None:
|
||||||
"""Log an error while merging packages."""
|
"""Log an error while merging packages."""
|
||||||
message = f"Package {package} setup failed. Integration {component} {message}"
|
message = f"Package {package} setup failed. Integration {component} {message}"
|
||||||
|
|
||||||
|
@ -616,7 +618,7 @@ def _log_pkg_error(package: str, component: str, config: Dict, message: str) ->
|
||||||
_LOGGER.error(message)
|
_LOGGER.error(message)
|
||||||
|
|
||||||
|
|
||||||
def _identify_config_schema(module: ModuleType) -> Optional[str]:
|
def _identify_config_schema(module: ModuleType) -> str | None:
|
||||||
"""Extract the schema and identify list or dict based."""
|
"""Extract the schema and identify list or dict based."""
|
||||||
if not isinstance(module.CONFIG_SCHEMA, vol.Schema): # type: ignore
|
if not isinstance(module.CONFIG_SCHEMA, vol.Schema): # type: ignore
|
||||||
return None
|
return None
|
||||||
|
@ -664,9 +666,9 @@ def _identify_config_schema(module: ModuleType) -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _recursive_merge(conf: Dict[str, Any], package: Dict[str, Any]) -> Union[bool, str]:
|
def _recursive_merge(conf: dict[str, Any], package: dict[str, Any]) -> bool | str:
|
||||||
"""Merge package into conf, recursively."""
|
"""Merge package into conf, recursively."""
|
||||||
error: Union[bool, str] = False
|
error: bool | str = False
|
||||||
for key, pack_conf in package.items():
|
for key, pack_conf in package.items():
|
||||||
if isinstance(pack_conf, dict):
|
if isinstance(pack_conf, dict):
|
||||||
if not pack_conf:
|
if not pack_conf:
|
||||||
|
@ -688,10 +690,10 @@ def _recursive_merge(conf: Dict[str, Any], package: Dict[str, Any]) -> Union[boo
|
||||||
|
|
||||||
async def merge_packages_config(
|
async def merge_packages_config(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: Dict,
|
config: dict,
|
||||||
packages: Dict[str, Any],
|
packages: dict[str, Any],
|
||||||
_log_pkg_error: Callable = _log_pkg_error,
|
_log_pkg_error: Callable = _log_pkg_error,
|
||||||
) -> Dict:
|
) -> dict:
|
||||||
"""Merge packages into the top-level configuration. Mutate config."""
|
"""Merge packages into the top-level configuration. Mutate config."""
|
||||||
PACKAGES_CONFIG_SCHEMA(packages)
|
PACKAGES_CONFIG_SCHEMA(packages)
|
||||||
for pack_name, pack_conf in packages.items():
|
for pack_name, pack_conf in packages.items():
|
||||||
|
@ -754,7 +756,7 @@ async def merge_packages_config(
|
||||||
|
|
||||||
async def async_process_component_config(
|
async def async_process_component_config(
|
||||||
hass: HomeAssistant, config: ConfigType, integration: Integration
|
hass: HomeAssistant, config: ConfigType, integration: Integration
|
||||||
) -> Optional[ConfigType]:
|
) -> ConfigType | None:
|
||||||
"""Check component configuration and return processed configuration.
|
"""Check component configuration and return processed configuration.
|
||||||
|
|
||||||
Returns None on error.
|
Returns None on error.
|
||||||
|
@ -879,13 +881,13 @@ async def async_process_component_config(
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def config_without_domain(config: Dict, domain: str) -> Dict:
|
def config_without_domain(config: dict, domain: str) -> dict:
|
||||||
"""Return a config with all configuration for a domain removed."""
|
"""Return a config with all configuration for a domain removed."""
|
||||||
filter_keys = extract_domain_configs(config, domain)
|
filter_keys = extract_domain_configs(config, domain)
|
||||||
return {key: value for key, value in config.items() if key not in filter_keys}
|
return {key: value for key, value in config.items() if key not in filter_keys}
|
||||||
|
|
||||||
|
|
||||||
async def async_check_ha_config_file(hass: HomeAssistant) -> Optional[str]:
|
async def async_check_ha_config_file(hass: HomeAssistant) -> str | None:
|
||||||
"""Check if Home Assistant configuration file is valid.
|
"""Check if Home Assistant configuration file is valid.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
|
@ -902,7 +904,7 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> Optional[str]:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_notify_setup_error(
|
def async_notify_setup_error(
|
||||||
hass: HomeAssistant, component: str, display_link: Optional[str] = None
|
hass: HomeAssistant, component: str, display_link: str | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Print a persistent notification.
|
"""Print a persistent notification.
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import asyncio
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
from types import MappingProxyType, MethodType
|
from types import MappingProxyType, MethodType
|
||||||
from typing import Any, Callable, Dict, List, Optional, Set, Union, cast
|
from typing import Any, Callable, Optional, cast
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
@ -143,11 +143,11 @@ class ConfigEntry:
|
||||||
source: str,
|
source: str,
|
||||||
connection_class: str,
|
connection_class: str,
|
||||||
system_options: dict,
|
system_options: dict,
|
||||||
options: Optional[dict] = None,
|
options: dict | None = None,
|
||||||
unique_id: Optional[str] = None,
|
unique_id: str | None = None,
|
||||||
entry_id: Optional[str] = None,
|
entry_id: str | None = None,
|
||||||
state: str = ENTRY_STATE_NOT_LOADED,
|
state: str = ENTRY_STATE_NOT_LOADED,
|
||||||
disabled_by: Optional[str] = None,
|
disabled_by: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a config entry."""
|
"""Initialize a config entry."""
|
||||||
# Unique id of the config entry
|
# Unique id of the config entry
|
||||||
|
@ -190,18 +190,18 @@ class ConfigEntry:
|
||||||
self.supports_unload = False
|
self.supports_unload = False
|
||||||
|
|
||||||
# Listeners to call on update
|
# Listeners to call on update
|
||||||
self.update_listeners: List[
|
self.update_listeners: list[
|
||||||
Union[weakref.ReferenceType[UpdateListenerType], weakref.WeakMethod]
|
weakref.ReferenceType[UpdateListenerType] | weakref.WeakMethod
|
||||||
] = []
|
] = []
|
||||||
|
|
||||||
# Function to cancel a scheduled retry
|
# Function to cancel a scheduled retry
|
||||||
self._async_cancel_retry_setup: Optional[Callable[[], Any]] = None
|
self._async_cancel_retry_setup: Callable[[], Any] | None = None
|
||||||
|
|
||||||
async def async_setup(
|
async def async_setup(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
*,
|
*,
|
||||||
integration: Optional[loader.Integration] = None,
|
integration: loader.Integration | None = None,
|
||||||
tries: int = 0,
|
tries: int = 0,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up an entry."""
|
"""Set up an entry."""
|
||||||
|
@ -295,7 +295,7 @@ class ConfigEntry:
|
||||||
self.state = ENTRY_STATE_SETUP_ERROR
|
self.state = ENTRY_STATE_SETUP_ERROR
|
||||||
|
|
||||||
async def async_unload(
|
async def async_unload(
|
||||||
self, hass: HomeAssistant, *, integration: Optional[loader.Integration] = None
|
self, hass: HomeAssistant, *, integration: loader.Integration | None = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Unload an entry.
|
"""Unload an entry.
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ class ConfigEntry:
|
||||||
|
|
||||||
return lambda: self.update_listeners.remove(weak_listener)
|
return lambda: self.update_listeners.remove(weak_listener)
|
||||||
|
|
||||||
def as_dict(self) -> Dict[str, Any]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
"""Return dictionary version of this entry."""
|
"""Return dictionary version of this entry."""
|
||||||
return {
|
return {
|
||||||
"entry_id": self.entry_id,
|
"entry_id": self.entry_id,
|
||||||
|
@ -471,8 +471,8 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager):
|
||||||
self._hass_config = hass_config
|
self._hass_config = hass_config
|
||||||
|
|
||||||
async def async_finish_flow(
|
async def async_finish_flow(
|
||||||
self, flow: data_entry_flow.FlowHandler, result: Dict[str, Any]
|
self, flow: data_entry_flow.FlowHandler, result: dict[str, Any]
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Finish a config flow and add an entry."""
|
"""Finish a config flow and add an entry."""
|
||||||
flow = cast(ConfigFlow, flow)
|
flow = cast(ConfigFlow, flow)
|
||||||
|
|
||||||
|
@ -542,7 +542,7 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def async_create_flow(
|
async def async_create_flow(
|
||||||
self, handler_key: Any, *, context: Optional[Dict] = None, data: Any = None
|
self, handler_key: Any, *, context: dict | None = None, data: Any = None
|
||||||
) -> ConfigFlow:
|
) -> ConfigFlow:
|
||||||
"""Create a flow for specified handler.
|
"""Create a flow for specified handler.
|
||||||
|
|
||||||
|
@ -619,14 +619,14 @@ class ConfigEntries:
|
||||||
self.flow = ConfigEntriesFlowManager(hass, self, hass_config)
|
self.flow = ConfigEntriesFlowManager(hass, self, hass_config)
|
||||||
self.options = OptionsFlowManager(hass)
|
self.options = OptionsFlowManager(hass)
|
||||||
self._hass_config = hass_config
|
self._hass_config = hass_config
|
||||||
self._entries: List[ConfigEntry] = []
|
self._entries: list[ConfigEntry] = []
|
||||||
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
|
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
|
||||||
EntityRegistryDisabledHandler(hass).async_setup()
|
EntityRegistryDisabledHandler(hass).async_setup()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_domains(self) -> List[str]:
|
def async_domains(self) -> list[str]:
|
||||||
"""Return domains for which we have entries."""
|
"""Return domains for which we have entries."""
|
||||||
seen: Set[str] = set()
|
seen: set[str] = set()
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for entry in self._entries:
|
for entry in self._entries:
|
||||||
|
@ -637,7 +637,7 @@ class ConfigEntries:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_get_entry(self, entry_id: str) -> Optional[ConfigEntry]:
|
def async_get_entry(self, entry_id: str) -> ConfigEntry | None:
|
||||||
"""Return entry with matching entry_id."""
|
"""Return entry with matching entry_id."""
|
||||||
for entry in self._entries:
|
for entry in self._entries:
|
||||||
if entry_id == entry.entry_id:
|
if entry_id == entry.entry_id:
|
||||||
|
@ -645,7 +645,7 @@ class ConfigEntries:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_entries(self, domain: Optional[str] = None) -> List[ConfigEntry]:
|
def async_entries(self, domain: str | None = None) -> list[ConfigEntry]:
|
||||||
"""Return all entries or entries for a specific domain."""
|
"""Return all entries or entries for a specific domain."""
|
||||||
if domain is None:
|
if domain is None:
|
||||||
return list(self._entries)
|
return list(self._entries)
|
||||||
|
@ -657,7 +657,7 @@ class ConfigEntries:
|
||||||
await self.async_setup(entry.entry_id)
|
await self.async_setup(entry.entry_id)
|
||||||
self._async_schedule_save()
|
self._async_schedule_save()
|
||||||
|
|
||||||
async def async_remove(self, entry_id: str) -> Dict[str, Any]:
|
async def async_remove(self, entry_id: str) -> dict[str, Any]:
|
||||||
"""Remove an entry."""
|
"""Remove an entry."""
|
||||||
entry = self.async_get_entry(entry_id)
|
entry = self.async_get_entry(entry_id)
|
||||||
|
|
||||||
|
@ -789,7 +789,7 @@ class ConfigEntries:
|
||||||
return await self.async_setup(entry_id)
|
return await self.async_setup(entry_id)
|
||||||
|
|
||||||
async def async_set_disabled_by(
|
async def async_set_disabled_by(
|
||||||
self, entry_id: str, disabled_by: Optional[str]
|
self, entry_id: str, disabled_by: str | None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Disable an entry.
|
"""Disable an entry.
|
||||||
|
|
||||||
|
@ -829,11 +829,11 @@ class ConfigEntries:
|
||||||
self,
|
self,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
*,
|
*,
|
||||||
unique_id: Union[str, dict, None, UndefinedType] = UNDEFINED,
|
unique_id: str | dict | None | UndefinedType = UNDEFINED,
|
||||||
title: Union[str, dict, UndefinedType] = UNDEFINED,
|
title: str | dict | UndefinedType = UNDEFINED,
|
||||||
data: Union[dict, UndefinedType] = UNDEFINED,
|
data: dict | UndefinedType = UNDEFINED,
|
||||||
options: Union[dict, UndefinedType] = UNDEFINED,
|
options: dict | UndefinedType = UNDEFINED,
|
||||||
system_options: Union[dict, UndefinedType] = UNDEFINED,
|
system_options: dict | UndefinedType = UNDEFINED,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Update a config entry.
|
"""Update a config entry.
|
||||||
|
|
||||||
|
@ -918,12 +918,12 @@ class ConfigEntries:
|
||||||
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _data_to_save(self) -> Dict[str, List[Dict[str, Any]]]:
|
def _data_to_save(self) -> dict[str, list[dict[str, Any]]]:
|
||||||
"""Return data to save."""
|
"""Return data to save."""
|
||||||
return {"entries": [entry.as_dict() for entry in self._entries]}
|
return {"entries": [entry.as_dict() for entry in self._entries]}
|
||||||
|
|
||||||
|
|
||||||
async def _old_conf_migrator(old_config: Dict[str, Any]) -> Dict[str, Any]:
|
async def _old_conf_migrator(old_config: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""Migrate the pre-0.73 config format to the latest version."""
|
"""Migrate the pre-0.73 config format to the latest version."""
|
||||||
return {"entries": old_config}
|
return {"entries": old_config}
|
||||||
|
|
||||||
|
@ -931,7 +931,7 @@ async def _old_conf_migrator(old_config: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
class ConfigFlow(data_entry_flow.FlowHandler):
|
class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
"""Base class for config flows with some helpers."""
|
"""Base class for config flows with some helpers."""
|
||||||
|
|
||||||
def __init_subclass__(cls, domain: Optional[str] = None, **kwargs: Any) -> None:
|
def __init_subclass__(cls, domain: str | None = None, **kwargs: Any) -> None:
|
||||||
"""Initialize a subclass, register if possible."""
|
"""Initialize a subclass, register if possible."""
|
||||||
super().__init_subclass__(**kwargs) # type: ignore
|
super().__init_subclass__(**kwargs) # type: ignore
|
||||||
if domain is not None:
|
if domain is not None:
|
||||||
|
@ -940,7 +940,7 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
CONNECTION_CLASS = CONN_CLASS_UNKNOWN
|
CONNECTION_CLASS = CONN_CLASS_UNKNOWN
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self) -> Optional[str]:
|
def unique_id(self) -> str | None:
|
||||||
"""Return unique ID if available."""
|
"""Return unique ID if available."""
|
||||||
if not self.context:
|
if not self.context:
|
||||||
return None
|
return None
|
||||||
|
@ -956,7 +956,7 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
@callback
|
@callback
|
||||||
def _abort_if_unique_id_configured(
|
def _abort_if_unique_id_configured(
|
||||||
self,
|
self,
|
||||||
updates: Optional[Dict[Any, Any]] = None,
|
updates: dict[Any, Any] | None = None,
|
||||||
reload_on_update: bool = True,
|
reload_on_update: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Abort if the unique ID is already configured."""
|
"""Abort if the unique ID is already configured."""
|
||||||
|
@ -983,8 +983,8 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
raise data_entry_flow.AbortFlow("already_configured")
|
raise data_entry_flow.AbortFlow("already_configured")
|
||||||
|
|
||||||
async def async_set_unique_id(
|
async def async_set_unique_id(
|
||||||
self, unique_id: Optional[str] = None, *, raise_on_progress: bool = True
|
self, unique_id: str | None = None, *, raise_on_progress: bool = True
|
||||||
) -> Optional[ConfigEntry]:
|
) -> ConfigEntry | None:
|
||||||
"""Set a unique ID for the config flow.
|
"""Set a unique ID for the config flow.
|
||||||
|
|
||||||
Returns optionally existing config entry with same ID.
|
Returns optionally existing config entry with same ID.
|
||||||
|
@ -1020,7 +1020,7 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
self.context["confirm_only"] = True
|
self.context["confirm_only"] = True
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_current_entries(self, include_ignore: bool = False) -> List[ConfigEntry]:
|
def _async_current_entries(self, include_ignore: bool = False) -> list[ConfigEntry]:
|
||||||
"""Return current entries.
|
"""Return current entries.
|
||||||
|
|
||||||
If the flow is user initiated, filter out ignored entries unless include_ignore is True.
|
If the flow is user initiated, filter out ignored entries unless include_ignore is True.
|
||||||
|
@ -1033,7 +1033,7 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
return [entry for entry in config_entries if entry.source != SOURCE_IGNORE]
|
return [entry for entry in config_entries if entry.source != SOURCE_IGNORE]
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_current_ids(self, include_ignore: bool = True) -> Set[Optional[str]]:
|
def _async_current_ids(self, include_ignore: bool = True) -> set[str | None]:
|
||||||
"""Return current unique IDs."""
|
"""Return current unique IDs."""
|
||||||
return {
|
return {
|
||||||
entry.unique_id
|
entry.unique_id
|
||||||
|
@ -1042,7 +1042,7 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
}
|
}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_in_progress(self) -> List[Dict]:
|
def _async_in_progress(self) -> list[dict]:
|
||||||
"""Return other in progress flows for current domain."""
|
"""Return other in progress flows for current domain."""
|
||||||
return [
|
return [
|
||||||
flw
|
flw
|
||||||
|
@ -1050,18 +1050,18 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
if flw["handler"] == self.handler and flw["flow_id"] != self.flow_id
|
if flw["handler"] == self.handler and flw["flow_id"] != self.flow_id
|
||||||
]
|
]
|
||||||
|
|
||||||
async def async_step_ignore(self, user_input: Dict[str, Any]) -> Dict[str, Any]:
|
async def async_step_ignore(self, user_input: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""Ignore this config flow."""
|
"""Ignore this config flow."""
|
||||||
await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False)
|
await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False)
|
||||||
return self.async_create_entry(title=user_input["title"], data={})
|
return self.async_create_entry(title=user_input["title"], data={})
|
||||||
|
|
||||||
async def async_step_unignore(self, user_input: Dict[str, Any]) -> Dict[str, Any]:
|
async def async_step_unignore(self, user_input: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""Rediscover a config entry by it's unique_id."""
|
"""Rediscover a config entry by it's unique_id."""
|
||||||
return self.async_abort(reason="not_implemented")
|
return self.async_abort(reason="not_implemented")
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: Optional[Dict[str, Any]] = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Handle a flow initiated by the user."""
|
"""Handle a flow initiated by the user."""
|
||||||
return self.async_abort(reason="not_implemented")
|
return self.async_abort(reason="not_implemented")
|
||||||
|
|
||||||
|
@ -1090,16 +1090,16 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
||||||
raise data_entry_flow.AbortFlow("already_in_progress")
|
raise data_entry_flow.AbortFlow("already_in_progress")
|
||||||
|
|
||||||
async def async_step_discovery(
|
async def async_step_discovery(
|
||||||
self, discovery_info: Dict[str, Any]
|
self, discovery_info: dict[str, Any]
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Handle a flow initialized by discovery."""
|
"""Handle a flow initialized by discovery."""
|
||||||
await self._async_handle_discovery_without_unique_id()
|
await self._async_handle_discovery_without_unique_id()
|
||||||
return await self.async_step_user()
|
return await self.async_step_user()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_abort(
|
def async_abort(
|
||||||
self, *, reason: str, description_placeholders: Optional[Dict] = None
|
self, *, reason: str, description_placeholders: dict | None = None
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Abort the config flow."""
|
"""Abort the config flow."""
|
||||||
# Remove reauth notification if no reauth flows are in progress
|
# Remove reauth notification if no reauth flows are in progress
|
||||||
if self.source == SOURCE_REAUTH and not any(
|
if self.source == SOURCE_REAUTH and not any(
|
||||||
|
@ -1130,8 +1130,8 @@ class OptionsFlowManager(data_entry_flow.FlowManager):
|
||||||
self,
|
self,
|
||||||
handler_key: Any,
|
handler_key: Any,
|
||||||
*,
|
*,
|
||||||
context: Optional[Dict[str, Any]] = None,
|
context: dict[str, Any] | None = None,
|
||||||
data: Optional[Dict[str, Any]] = None,
|
data: dict[str, Any] | None = None,
|
||||||
) -> OptionsFlow:
|
) -> OptionsFlow:
|
||||||
"""Create an options flow for a config entry.
|
"""Create an options flow for a config entry.
|
||||||
|
|
||||||
|
@ -1147,8 +1147,8 @@ class OptionsFlowManager(data_entry_flow.FlowManager):
|
||||||
return cast(OptionsFlow, HANDLERS[entry.domain].async_get_options_flow(entry))
|
return cast(OptionsFlow, HANDLERS[entry.domain].async_get_options_flow(entry))
|
||||||
|
|
||||||
async def async_finish_flow(
|
async def async_finish_flow(
|
||||||
self, flow: data_entry_flow.FlowHandler, result: Dict[str, Any]
|
self, flow: data_entry_flow.FlowHandler, result: dict[str, Any]
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Finish an options flow and update options for configuration entry.
|
"""Finish an options flow and update options for configuration entry.
|
||||||
|
|
||||||
Flow.handler and entry_id is the same thing to map flow with entry.
|
Flow.handler and entry_id is the same thing to map flow with entry.
|
||||||
|
@ -1184,7 +1184,7 @@ class SystemOptions:
|
||||||
"""Update properties."""
|
"""Update properties."""
|
||||||
self.disable_new_entities = disable_new_entities
|
self.disable_new_entities = disable_new_entities
|
||||||
|
|
||||||
def as_dict(self) -> Dict[str, Any]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
"""Return dictionary version of this config entries system options."""
|
"""Return dictionary version of this config entries system options."""
|
||||||
return {"disable_new_entities": self.disable_new_entities}
|
return {"disable_new_entities": self.disable_new_entities}
|
||||||
|
|
||||||
|
@ -1195,9 +1195,9 @@ class EntityRegistryDisabledHandler:
|
||||||
def __init__(self, hass: HomeAssistant) -> None:
|
def __init__(self, hass: HomeAssistant) -> None:
|
||||||
"""Initialize the handler."""
|
"""Initialize the handler."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.registry: Optional[entity_registry.EntityRegistry] = None
|
self.registry: entity_registry.EntityRegistry | None = None
|
||||||
self.changed: Set[str] = set()
|
self.changed: set[str] = set()
|
||||||
self._remove_call_later: Optional[Callable[[], None]] = None
|
self._remove_call_later: Callable[[], None] | None = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_setup(self) -> None:
|
def async_setup(self) -> None:
|
||||||
|
|
|
@ -4,6 +4,8 @@ Core components of Home Assistant.
|
||||||
Home Assistant is a Home Automation framework for observing the state
|
Home Assistant is a Home Automation framework for observing the state
|
||||||
of entities and react to changes.
|
of entities and react to changes.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
|
@ -22,15 +24,10 @@ from typing import (
|
||||||
Callable,
|
Callable,
|
||||||
Collection,
|
Collection,
|
||||||
Coroutine,
|
Coroutine,
|
||||||
Dict,
|
|
||||||
Iterable,
|
Iterable,
|
||||||
List,
|
|
||||||
Mapping,
|
Mapping,
|
||||||
Optional,
|
Optional,
|
||||||
Set,
|
|
||||||
Tuple,
|
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Union,
|
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -119,7 +116,7 @@ TIMEOUT_EVENT_START = 15
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def split_entity_id(entity_id: str) -> List[str]:
|
def split_entity_id(entity_id: str) -> list[str]:
|
||||||
"""Split a state entity ID into domain and object ID."""
|
"""Split a state entity ID into domain and object ID."""
|
||||||
return entity_id.split(".", 1)
|
return entity_id.split(".", 1)
|
||||||
|
|
||||||
|
@ -237,7 +234,7 @@ class HomeAssistant:
|
||||||
self.state: CoreState = CoreState.not_running
|
self.state: CoreState = CoreState.not_running
|
||||||
self.exit_code: int = 0
|
self.exit_code: int = 0
|
||||||
# If not None, use to signal end-of-loop
|
# If not None, use to signal end-of-loop
|
||||||
self._stopped: Optional[asyncio.Event] = None
|
self._stopped: asyncio.Event | None = None
|
||||||
# Timeout handler for Core/Helper namespace
|
# Timeout handler for Core/Helper namespace
|
||||||
self.timeout: TimeoutManager = TimeoutManager()
|
self.timeout: TimeoutManager = TimeoutManager()
|
||||||
|
|
||||||
|
@ -342,7 +339,7 @@ class HomeAssistant:
|
||||||
@callback
|
@callback
|
||||||
def async_add_job(
|
def async_add_job(
|
||||||
self, target: Callable[..., Any], *args: Any
|
self, target: Callable[..., Any], *args: Any
|
||||||
) -> Optional[asyncio.Future]:
|
) -> asyncio.Future | None:
|
||||||
"""Add a job from within the event loop.
|
"""Add a job from within the event loop.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -359,9 +356,7 @@ class HomeAssistant:
|
||||||
return self.async_add_hass_job(HassJob(target), *args)
|
return self.async_add_hass_job(HassJob(target), *args)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_hass_job(
|
def async_add_hass_job(self, hassjob: HassJob, *args: Any) -> asyncio.Future | None:
|
||||||
self, hassjob: HassJob, *args: Any
|
|
||||||
) -> Optional[asyncio.Future]:
|
|
||||||
"""Add a HassJob from within the event loop.
|
"""Add a HassJob from within the event loop.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -423,9 +418,7 @@ class HomeAssistant:
|
||||||
self._track_task = False
|
self._track_task = False
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_run_hass_job(
|
def async_run_hass_job(self, hassjob: HassJob, *args: Any) -> asyncio.Future | None:
|
||||||
self, hassjob: HassJob, *args: Any
|
|
||||||
) -> Optional[asyncio.Future]:
|
|
||||||
"""Run a HassJob from within the event loop.
|
"""Run a HassJob from within the event loop.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -441,8 +434,8 @@ class HomeAssistant:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_run_job(
|
def async_run_job(
|
||||||
self, target: Callable[..., Union[None, Awaitable]], *args: Any
|
self, target: Callable[..., None | Awaitable], *args: Any
|
||||||
) -> Optional[asyncio.Future]:
|
) -> asyncio.Future | None:
|
||||||
"""Run a job from within the event loop.
|
"""Run a job from within the event loop.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -465,7 +458,7 @@ class HomeAssistant:
|
||||||
"""Block until all pending work is done."""
|
"""Block until all pending work is done."""
|
||||||
# To flush out any call_soon_threadsafe
|
# To flush out any call_soon_threadsafe
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
start_time: Optional[float] = None
|
start_time: float | None = None
|
||||||
|
|
||||||
while self._pending_tasks:
|
while self._pending_tasks:
|
||||||
pending = [task for task in self._pending_tasks if not task.done()]
|
pending = [task for task in self._pending_tasks if not task.done()]
|
||||||
|
@ -582,10 +575,10 @@ class Context:
|
||||||
"""The context that triggered something."""
|
"""The context that triggered something."""
|
||||||
|
|
||||||
user_id: str = attr.ib(default=None)
|
user_id: str = attr.ib(default=None)
|
||||||
parent_id: Optional[str] = attr.ib(default=None)
|
parent_id: str | None = attr.ib(default=None)
|
||||||
id: str = attr.ib(factory=uuid_util.random_uuid_hex)
|
id: str = attr.ib(factory=uuid_util.random_uuid_hex)
|
||||||
|
|
||||||
def as_dict(self) -> Dict[str, Optional[str]]:
|
def as_dict(self) -> dict[str, str | None]:
|
||||||
"""Return a dictionary representation of the context."""
|
"""Return a dictionary representation of the context."""
|
||||||
return {"id": self.id, "parent_id": self.parent_id, "user_id": self.user_id}
|
return {"id": self.id, "parent_id": self.parent_id, "user_id": self.user_id}
|
||||||
|
|
||||||
|
@ -610,10 +603,10 @@ class Event:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
event_type: str,
|
event_type: str,
|
||||||
data: Optional[Dict[str, Any]] = None,
|
data: dict[str, Any] | None = None,
|
||||||
origin: EventOrigin = EventOrigin.local,
|
origin: EventOrigin = EventOrigin.local,
|
||||||
time_fired: Optional[datetime.datetime] = None,
|
time_fired: datetime.datetime | None = None,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a new event."""
|
"""Initialize a new event."""
|
||||||
self.event_type = event_type
|
self.event_type = event_type
|
||||||
|
@ -627,7 +620,7 @@ class Event:
|
||||||
# The only event type that shares context are the TIME_CHANGED
|
# The only event type that shares context are the TIME_CHANGED
|
||||||
return hash((self.event_type, self.context.id, self.time_fired))
|
return hash((self.event_type, self.context.id, self.time_fired))
|
||||||
|
|
||||||
def as_dict(self) -> Dict[str, Any]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
"""Create a dict representation of this Event.
|
"""Create a dict representation of this Event.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -664,11 +657,11 @@ class EventBus:
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant) -> None:
|
def __init__(self, hass: HomeAssistant) -> None:
|
||||||
"""Initialize a new event bus."""
|
"""Initialize a new event bus."""
|
||||||
self._listeners: Dict[str, List[Tuple[HassJob, Optional[Callable]]]] = {}
|
self._listeners: dict[str, list[tuple[HassJob, Callable | None]]] = {}
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_listeners(self) -> Dict[str, int]:
|
def async_listeners(self) -> dict[str, int]:
|
||||||
"""Return dictionary with events and the number of listeners.
|
"""Return dictionary with events and the number of listeners.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -676,16 +669,16 @@ class EventBus:
|
||||||
return {key: len(self._listeners[key]) for key in self._listeners}
|
return {key: len(self._listeners[key]) for key in self._listeners}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def listeners(self) -> Dict[str, int]:
|
def listeners(self) -> dict[str, int]:
|
||||||
"""Return dictionary with events and the number of listeners."""
|
"""Return dictionary with events and the number of listeners."""
|
||||||
return run_callback_threadsafe(self._hass.loop, self.async_listeners).result()
|
return run_callback_threadsafe(self._hass.loop, self.async_listeners).result()
|
||||||
|
|
||||||
def fire(
|
def fire(
|
||||||
self,
|
self,
|
||||||
event_type: str,
|
event_type: str,
|
||||||
event_data: Optional[Dict] = None,
|
event_data: dict | None = None,
|
||||||
origin: EventOrigin = EventOrigin.local,
|
origin: EventOrigin = EventOrigin.local,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Fire an event."""
|
"""Fire an event."""
|
||||||
self._hass.loop.call_soon_threadsafe(
|
self._hass.loop.call_soon_threadsafe(
|
||||||
|
@ -696,10 +689,10 @@ class EventBus:
|
||||||
def async_fire(
|
def async_fire(
|
||||||
self,
|
self,
|
||||||
event_type: str,
|
event_type: str,
|
||||||
event_data: Optional[Dict[str, Any]] = None,
|
event_data: dict[str, Any] | None = None,
|
||||||
origin: EventOrigin = EventOrigin.local,
|
origin: EventOrigin = EventOrigin.local,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
time_fired: Optional[datetime.datetime] = None,
|
time_fired: datetime.datetime | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Fire an event.
|
"""Fire an event.
|
||||||
|
|
||||||
|
@ -751,7 +744,7 @@ class EventBus:
|
||||||
self,
|
self,
|
||||||
event_type: str,
|
event_type: str,
|
||||||
listener: Callable,
|
listener: Callable,
|
||||||
event_filter: Optional[Callable] = None,
|
event_filter: Callable | None = None,
|
||||||
) -> CALLBACK_TYPE:
|
) -> CALLBACK_TYPE:
|
||||||
"""Listen for all events or events of a specific type.
|
"""Listen for all events or events of a specific type.
|
||||||
|
|
||||||
|
@ -772,7 +765,7 @@ class EventBus:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_listen_filterable_job(
|
def _async_listen_filterable_job(
|
||||||
self, event_type: str, filterable_job: Tuple[HassJob, Optional[Callable]]
|
self, event_type: str, filterable_job: tuple[HassJob, Callable | None]
|
||||||
) -> CALLBACK_TYPE:
|
) -> CALLBACK_TYPE:
|
||||||
self._listeners.setdefault(event_type, []).append(filterable_job)
|
self._listeners.setdefault(event_type, []).append(filterable_job)
|
||||||
|
|
||||||
|
@ -811,7 +804,7 @@ class EventBus:
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
"""
|
"""
|
||||||
filterable_job: Optional[Tuple[HassJob, Optional[Callable]]] = None
|
filterable_job: tuple[HassJob, Callable | None] | None = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _onetime_listener(event: Event) -> None:
|
def _onetime_listener(event: Event) -> None:
|
||||||
|
@ -835,7 +828,7 @@ class EventBus:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_remove_listener(
|
def _async_remove_listener(
|
||||||
self, event_type: str, filterable_job: Tuple[HassJob, Optional[Callable]]
|
self, event_type: str, filterable_job: tuple[HassJob, Callable | None]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Remove a listener of a specific event_type.
|
"""Remove a listener of a specific event_type.
|
||||||
|
|
||||||
|
@ -884,11 +877,11 @@ class State:
|
||||||
self,
|
self,
|
||||||
entity_id: str,
|
entity_id: str,
|
||||||
state: str,
|
state: str,
|
||||||
attributes: Optional[Mapping[str, Any]] = None,
|
attributes: Mapping[str, Any] | None = None,
|
||||||
last_changed: Optional[datetime.datetime] = None,
|
last_changed: datetime.datetime | None = None,
|
||||||
last_updated: Optional[datetime.datetime] = None,
|
last_updated: datetime.datetime | None = None,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
validate_entity_id: Optional[bool] = True,
|
validate_entity_id: bool | None = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a new state."""
|
"""Initialize a new state."""
|
||||||
state = str(state)
|
state = str(state)
|
||||||
|
@ -912,7 +905,7 @@ class State:
|
||||||
self.last_changed = last_changed or self.last_updated
|
self.last_changed = last_changed or self.last_updated
|
||||||
self.context = context or Context()
|
self.context = context or Context()
|
||||||
self.domain, self.object_id = split_entity_id(self.entity_id)
|
self.domain, self.object_id = split_entity_id(self.entity_id)
|
||||||
self._as_dict: Optional[Dict[str, Collection[Any]]] = None
|
self._as_dict: dict[str, Collection[Any]] | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -921,7 +914,7 @@ class State:
|
||||||
"_", " "
|
"_", " "
|
||||||
)
|
)
|
||||||
|
|
||||||
def as_dict(self) -> Dict:
|
def as_dict(self) -> dict:
|
||||||
"""Return a dict representation of the State.
|
"""Return a dict representation of the State.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -946,7 +939,7 @@ class State:
|
||||||
return self._as_dict
|
return self._as_dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, json_dict: Dict) -> Any:
|
def from_dict(cls, json_dict: dict) -> Any:
|
||||||
"""Initialize a state from a dict.
|
"""Initialize a state from a dict.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -1004,12 +997,12 @@ class StateMachine:
|
||||||
|
|
||||||
def __init__(self, bus: EventBus, loop: asyncio.events.AbstractEventLoop) -> None:
|
def __init__(self, bus: EventBus, loop: asyncio.events.AbstractEventLoop) -> None:
|
||||||
"""Initialize state machine."""
|
"""Initialize state machine."""
|
||||||
self._states: Dict[str, State] = {}
|
self._states: dict[str, State] = {}
|
||||||
self._reservations: Set[str] = set()
|
self._reservations: set[str] = set()
|
||||||
self._bus = bus
|
self._bus = bus
|
||||||
self._loop = loop
|
self._loop = loop
|
||||||
|
|
||||||
def entity_ids(self, domain_filter: Optional[str] = None) -> List[str]:
|
def entity_ids(self, domain_filter: str | None = None) -> list[str]:
|
||||||
"""List of entity ids that are being tracked."""
|
"""List of entity ids that are being tracked."""
|
||||||
future = run_callback_threadsafe(
|
future = run_callback_threadsafe(
|
||||||
self._loop, self.async_entity_ids, domain_filter
|
self._loop, self.async_entity_ids, domain_filter
|
||||||
|
@ -1018,8 +1011,8 @@ class StateMachine:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_entity_ids(
|
def async_entity_ids(
|
||||||
self, domain_filter: Optional[Union[str, Iterable]] = None
|
self, domain_filter: str | Iterable | None = None
|
||||||
) -> List[str]:
|
) -> list[str]:
|
||||||
"""List of entity ids that are being tracked.
|
"""List of entity ids that are being tracked.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -1038,7 +1031,7 @@ class StateMachine:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_entity_ids_count(
|
def async_entity_ids_count(
|
||||||
self, domain_filter: Optional[Union[str, Iterable]] = None
|
self, domain_filter: str | Iterable | None = None
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Count the entity ids that are being tracked.
|
"""Count the entity ids that are being tracked.
|
||||||
|
|
||||||
|
@ -1054,16 +1047,14 @@ class StateMachine:
|
||||||
[None for state in self._states.values() if state.domain in domain_filter]
|
[None for state in self._states.values() if state.domain in domain_filter]
|
||||||
)
|
)
|
||||||
|
|
||||||
def all(self, domain_filter: Optional[Union[str, Iterable]] = None) -> List[State]:
|
def all(self, domain_filter: str | Iterable | None = None) -> list[State]:
|
||||||
"""Create a list of all states."""
|
"""Create a list of all states."""
|
||||||
return run_callback_threadsafe(
|
return run_callback_threadsafe(
|
||||||
self._loop, self.async_all, domain_filter
|
self._loop, self.async_all, domain_filter
|
||||||
).result()
|
).result()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_all(
|
def async_all(self, domain_filter: str | Iterable | None = None) -> list[State]:
|
||||||
self, domain_filter: Optional[Union[str, Iterable]] = None
|
|
||||||
) -> List[State]:
|
|
||||||
"""Create a list of all states matching the filter.
|
"""Create a list of all states matching the filter.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -1078,7 +1069,7 @@ class StateMachine:
|
||||||
state for state in self._states.values() if state.domain in domain_filter
|
state for state in self._states.values() if state.domain in domain_filter
|
||||||
]
|
]
|
||||||
|
|
||||||
def get(self, entity_id: str) -> Optional[State]:
|
def get(self, entity_id: str) -> State | None:
|
||||||
"""Retrieve state of entity_id or None if not found.
|
"""Retrieve state of entity_id or None if not found.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -1103,7 +1094,7 @@ class StateMachine:
|
||||||
).result()
|
).result()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_remove(self, entity_id: str, context: Optional[Context] = None) -> bool:
|
def async_remove(self, entity_id: str, context: Context | None = None) -> bool:
|
||||||
"""Remove the state of an entity.
|
"""Remove the state of an entity.
|
||||||
|
|
||||||
Returns boolean to indicate if an entity was removed.
|
Returns boolean to indicate if an entity was removed.
|
||||||
|
@ -1131,9 +1122,9 @@ class StateMachine:
|
||||||
self,
|
self,
|
||||||
entity_id: str,
|
entity_id: str,
|
||||||
new_state: str,
|
new_state: str,
|
||||||
attributes: Optional[Mapping[str, Any]] = None,
|
attributes: Mapping[str, Any] | None = None,
|
||||||
force_update: bool = False,
|
force_update: bool = False,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set the state of an entity, add entity if it does not exist.
|
"""Set the state of an entity, add entity if it does not exist.
|
||||||
|
|
||||||
|
@ -1180,9 +1171,9 @@ class StateMachine:
|
||||||
self,
|
self,
|
||||||
entity_id: str,
|
entity_id: str,
|
||||||
new_state: str,
|
new_state: str,
|
||||||
attributes: Optional[Mapping[str, Any]] = None,
|
attributes: Mapping[str, Any] | None = None,
|
||||||
force_update: bool = False,
|
force_update: bool = False,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set the state of an entity, add entity if it does not exist.
|
"""Set the state of an entity, add entity if it does not exist.
|
||||||
|
|
||||||
|
@ -1241,8 +1232,8 @@ class Service:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
func: Callable,
|
func: Callable,
|
||||||
schema: Optional[vol.Schema],
|
schema: vol.Schema | None,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a service."""
|
"""Initialize a service."""
|
||||||
self.job = HassJob(func)
|
self.job = HassJob(func)
|
||||||
|
@ -1258,8 +1249,8 @@ class ServiceCall:
|
||||||
self,
|
self,
|
||||||
domain: str,
|
domain: str,
|
||||||
service: str,
|
service: str,
|
||||||
data: Optional[Dict] = None,
|
data: dict | None = None,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a service call."""
|
"""Initialize a service call."""
|
||||||
self.domain = domain.lower()
|
self.domain = domain.lower()
|
||||||
|
@ -1283,16 +1274,16 @@ class ServiceRegistry:
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant) -> None:
|
def __init__(self, hass: HomeAssistant) -> None:
|
||||||
"""Initialize a service registry."""
|
"""Initialize a service registry."""
|
||||||
self._services: Dict[str, Dict[str, Service]] = {}
|
self._services: dict[str, dict[str, Service]] = {}
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def services(self) -> Dict[str, Dict[str, Service]]:
|
def services(self) -> dict[str, dict[str, Service]]:
|
||||||
"""Return dictionary with per domain a list of available services."""
|
"""Return dictionary with per domain a list of available services."""
|
||||||
return run_callback_threadsafe(self._hass.loop, self.async_services).result()
|
return run_callback_threadsafe(self._hass.loop, self.async_services).result()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_services(self) -> Dict[str, Dict[str, Service]]:
|
def async_services(self) -> dict[str, dict[str, Service]]:
|
||||||
"""Return dictionary with per domain a list of available services.
|
"""Return dictionary with per domain a list of available services.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
|
@ -1311,7 +1302,7 @@ class ServiceRegistry:
|
||||||
domain: str,
|
domain: str,
|
||||||
service: str,
|
service: str,
|
||||||
service_func: Callable,
|
service_func: Callable,
|
||||||
schema: Optional[vol.Schema] = None,
|
schema: vol.Schema | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Register a service.
|
Register a service.
|
||||||
|
@ -1328,7 +1319,7 @@ class ServiceRegistry:
|
||||||
domain: str,
|
domain: str,
|
||||||
service: str,
|
service: str,
|
||||||
service_func: Callable,
|
service_func: Callable,
|
||||||
schema: Optional[vol.Schema] = None,
|
schema: vol.Schema | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Register a service.
|
Register a service.
|
||||||
|
@ -1382,12 +1373,12 @@ class ServiceRegistry:
|
||||||
self,
|
self,
|
||||||
domain: str,
|
domain: str,
|
||||||
service: str,
|
service: str,
|
||||||
service_data: Optional[Dict] = None,
|
service_data: dict | None = None,
|
||||||
blocking: bool = False,
|
blocking: bool = False,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
limit: Optional[float] = SERVICE_CALL_LIMIT,
|
limit: float | None = SERVICE_CALL_LIMIT,
|
||||||
target: Optional[Dict] = None,
|
target: dict | None = None,
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""
|
"""
|
||||||
Call a service.
|
Call a service.
|
||||||
|
|
||||||
|
@ -1404,12 +1395,12 @@ class ServiceRegistry:
|
||||||
self,
|
self,
|
||||||
domain: str,
|
domain: str,
|
||||||
service: str,
|
service: str,
|
||||||
service_data: Optional[Dict] = None,
|
service_data: dict | None = None,
|
||||||
blocking: bool = False,
|
blocking: bool = False,
|
||||||
context: Optional[Context] = None,
|
context: Context | None = None,
|
||||||
limit: Optional[float] = SERVICE_CALL_LIMIT,
|
limit: float | None = SERVICE_CALL_LIMIT,
|
||||||
target: Optional[Dict] = None,
|
target: dict | None = None,
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""
|
"""
|
||||||
Call a service.
|
Call a service.
|
||||||
|
|
||||||
|
@ -1497,7 +1488,7 @@ class ServiceRegistry:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _run_service_in_background(
|
def _run_service_in_background(
|
||||||
self, coro_or_task: Union[Coroutine, asyncio.Task], service_call: ServiceCall
|
self, coro_or_task: Coroutine | asyncio.Task, service_call: ServiceCall
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run service call in background, catching and logging any exceptions."""
|
"""Run service call in background, catching and logging any exceptions."""
|
||||||
|
|
||||||
|
@ -1542,8 +1533,8 @@ class Config:
|
||||||
self.location_name: str = "Home"
|
self.location_name: str = "Home"
|
||||||
self.time_zone: datetime.tzinfo = dt_util.UTC
|
self.time_zone: datetime.tzinfo = dt_util.UTC
|
||||||
self.units: UnitSystem = METRIC_SYSTEM
|
self.units: UnitSystem = METRIC_SYSTEM
|
||||||
self.internal_url: Optional[str] = None
|
self.internal_url: str | None = None
|
||||||
self.external_url: Optional[str] = None
|
self.external_url: str | None = None
|
||||||
|
|
||||||
self.config_source: str = "default"
|
self.config_source: str = "default"
|
||||||
|
|
||||||
|
@ -1551,22 +1542,22 @@ class Config:
|
||||||
self.skip_pip: bool = False
|
self.skip_pip: bool = False
|
||||||
|
|
||||||
# List of loaded components
|
# List of loaded components
|
||||||
self.components: Set[str] = set()
|
self.components: set[str] = set()
|
||||||
|
|
||||||
# API (HTTP) server configuration, see components.http.ApiConfig
|
# API (HTTP) server configuration, see components.http.ApiConfig
|
||||||
self.api: Optional[Any] = None
|
self.api: Any | None = None
|
||||||
|
|
||||||
# Directory that holds the configuration
|
# Directory that holds the configuration
|
||||||
self.config_dir: Optional[str] = None
|
self.config_dir: str | None = None
|
||||||
|
|
||||||
# List of allowed external dirs to access
|
# List of allowed external dirs to access
|
||||||
self.allowlist_external_dirs: Set[str] = set()
|
self.allowlist_external_dirs: set[str] = set()
|
||||||
|
|
||||||
# List of allowed external URLs that integrations may use
|
# List of allowed external URLs that integrations may use
|
||||||
self.allowlist_external_urls: Set[str] = set()
|
self.allowlist_external_urls: set[str] = set()
|
||||||
|
|
||||||
# Dictionary of Media folders that integrations may use
|
# Dictionary of Media folders that integrations may use
|
||||||
self.media_dirs: Dict[str, str] = {}
|
self.media_dirs: dict[str, str] = {}
|
||||||
|
|
||||||
# If Home Assistant is running in safe mode
|
# If Home Assistant is running in safe mode
|
||||||
self.safe_mode: bool = False
|
self.safe_mode: bool = False
|
||||||
|
@ -1574,7 +1565,7 @@ class Config:
|
||||||
# Use legacy template behavior
|
# Use legacy template behavior
|
||||||
self.legacy_templates: bool = False
|
self.legacy_templates: bool = False
|
||||||
|
|
||||||
def distance(self, lat: float, lon: float) -> Optional[float]:
|
def distance(self, lat: float, lon: float) -> float | None:
|
||||||
"""Calculate distance from Home Assistant.
|
"""Calculate distance from Home Assistant.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -1625,7 +1616,7 @@ class Config:
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def as_dict(self) -> Dict:
|
def as_dict(self) -> dict:
|
||||||
"""Create a dictionary representation of the configuration.
|
"""Create a dictionary representation of the configuration.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -1670,15 +1661,15 @@ class Config:
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
source: str,
|
source: str,
|
||||||
latitude: Optional[float] = None,
|
latitude: float | None = None,
|
||||||
longitude: Optional[float] = None,
|
longitude: float | None = None,
|
||||||
elevation: Optional[int] = None,
|
elevation: int | None = None,
|
||||||
unit_system: Optional[str] = None,
|
unit_system: str | None = None,
|
||||||
location_name: Optional[str] = None,
|
location_name: str | None = None,
|
||||||
time_zone: Optional[str] = None,
|
time_zone: str | None = None,
|
||||||
# pylint: disable=dangerous-default-value # _UNDEFs not modified
|
# pylint: disable=dangerous-default-value # _UNDEFs not modified
|
||||||
external_url: Optional[Union[str, dict]] = _UNDEF,
|
external_url: str | dict | None = _UNDEF,
|
||||||
internal_url: Optional[Union[str, dict]] = _UNDEF,
|
internal_url: str | dict | None = _UNDEF,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Update the configuration from a dictionary."""
|
"""Update the configuration from a dictionary."""
|
||||||
self.config_source = source
|
self.config_source = source
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
import abc
|
import abc
|
||||||
import asyncio
|
import asyncio
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
@ -43,7 +43,7 @@ class UnknownStep(FlowError):
|
||||||
class AbortFlow(FlowError):
|
class AbortFlow(FlowError):
|
||||||
"""Exception to indicate a flow needs to be aborted."""
|
"""Exception to indicate a flow needs to be aborted."""
|
||||||
|
|
||||||
def __init__(self, reason: str, description_placeholders: Optional[Dict] = None):
|
def __init__(self, reason: str, description_placeholders: dict | None = None):
|
||||||
"""Initialize an abort flow exception."""
|
"""Initialize an abort flow exception."""
|
||||||
super().__init__(f"Flow aborted: {reason}")
|
super().__init__(f"Flow aborted: {reason}")
|
||||||
self.reason = reason
|
self.reason = reason
|
||||||
|
@ -59,8 +59,8 @@ class FlowManager(abc.ABC):
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the flow manager."""
|
"""Initialize the flow manager."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._initializing: Dict[str, List[asyncio.Future]] = {}
|
self._initializing: dict[str, list[asyncio.Future]] = {}
|
||||||
self._progress: Dict[str, Any] = {}
|
self._progress: dict[str, Any] = {}
|
||||||
|
|
||||||
async def async_wait_init_flow_finish(self, handler: str) -> None:
|
async def async_wait_init_flow_finish(self, handler: str) -> None:
|
||||||
"""Wait till all flows in progress are initialized."""
|
"""Wait till all flows in progress are initialized."""
|
||||||
|
@ -76,8 +76,8 @@ class FlowManager(abc.ABC):
|
||||||
self,
|
self,
|
||||||
handler_key: Any,
|
handler_key: Any,
|
||||||
*,
|
*,
|
||||||
context: Optional[Dict[str, Any]] = None,
|
context: dict[str, Any] | None = None,
|
||||||
data: Optional[Dict[str, Any]] = None,
|
data: dict[str, Any] | None = None,
|
||||||
) -> FlowHandler:
|
) -> FlowHandler:
|
||||||
"""Create a flow for specified handler.
|
"""Create a flow for specified handler.
|
||||||
|
|
||||||
|
@ -86,17 +86,17 @@ class FlowManager(abc.ABC):
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_finish_flow(
|
async def async_finish_flow(
|
||||||
self, flow: "FlowHandler", result: Dict[str, Any]
|
self, flow: "FlowHandler", result: dict[str, Any]
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Finish a config flow and add an entry."""
|
"""Finish a config flow and add an entry."""
|
||||||
|
|
||||||
async def async_post_init(
|
async def async_post_init(
|
||||||
self, flow: "FlowHandler", result: Dict[str, Any]
|
self, flow: "FlowHandler", result: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Entry has finished executing its first step asynchronously."""
|
"""Entry has finished executing its first step asynchronously."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_progress(self) -> List[Dict]:
|
def async_progress(self) -> list[dict]:
|
||||||
"""Return the flows in progress."""
|
"""Return the flows in progress."""
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -110,7 +110,7 @@ class FlowManager(abc.ABC):
|
||||||
]
|
]
|
||||||
|
|
||||||
async def async_init(
|
async def async_init(
|
||||||
self, handler: str, *, context: Optional[Dict] = None, data: Any = None
|
self, handler: str, *, context: dict | None = None, data: Any = None
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""Start a configuration flow."""
|
"""Start a configuration flow."""
|
||||||
if context is None:
|
if context is None:
|
||||||
|
@ -142,7 +142,7 @@ class FlowManager(abc.ABC):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def async_configure(
|
async def async_configure(
|
||||||
self, flow_id: str, user_input: Optional[Dict] = None
|
self, flow_id: str, user_input: dict | None = None
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""Continue a configuration flow."""
|
"""Continue a configuration flow."""
|
||||||
flow = self._progress.get(flow_id)
|
flow = self._progress.get(flow_id)
|
||||||
|
@ -198,9 +198,9 @@ class FlowManager(abc.ABC):
|
||||||
self,
|
self,
|
||||||
flow: Any,
|
flow: Any,
|
||||||
step_id: str,
|
step_id: str,
|
||||||
user_input: Optional[Dict],
|
user_input: dict | None,
|
||||||
step_done: Optional[asyncio.Future] = None,
|
step_done: asyncio.Future | None = None,
|
||||||
) -> Dict:
|
) -> dict:
|
||||||
"""Handle a step of a flow."""
|
"""Handle a step of a flow."""
|
||||||
method = f"async_step_{step_id}"
|
method = f"async_step_{step_id}"
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ class FlowManager(abc.ABC):
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result: Dict = await getattr(flow, method)(user_input)
|
result: dict = await getattr(flow, method)(user_input)
|
||||||
except AbortFlow as err:
|
except AbortFlow as err:
|
||||||
result = _create_abort_data(
|
result = _create_abort_data(
|
||||||
flow.flow_id, flow.handler, err.reason, err.description_placeholders
|
flow.flow_id, flow.handler, err.reason, err.description_placeholders
|
||||||
|
@ -265,13 +265,13 @@ class FlowHandler:
|
||||||
"""Handle the configuration flow of a component."""
|
"""Handle the configuration flow of a component."""
|
||||||
|
|
||||||
# Set by flow manager
|
# Set by flow manager
|
||||||
cur_step: Optional[Dict[str, str]] = None
|
cur_step: dict[str, str] | None = None
|
||||||
# Ignore types: https://github.com/PyCQA/pylint/issues/3167
|
# Ignore types: https://github.com/PyCQA/pylint/issues/3167
|
||||||
flow_id: str = None # type: ignore
|
flow_id: str = None # type: ignore
|
||||||
hass: HomeAssistant = None # type: ignore
|
hass: HomeAssistant = None # type: ignore
|
||||||
handler: str = None # type: ignore
|
handler: str = None # type: ignore
|
||||||
# Ensure the attribute has a subscriptable, but immutable, default value.
|
# Ensure the attribute has a subscriptable, but immutable, default value.
|
||||||
context: Dict = MappingProxyType({}) # type: ignore
|
context: dict = MappingProxyType({}) # type: ignore
|
||||||
|
|
||||||
# Set by _async_create_flow callback
|
# Set by _async_create_flow callback
|
||||||
init_step = "init"
|
init_step = "init"
|
||||||
|
@ -280,7 +280,7 @@ class FlowHandler:
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self) -> Optional[str]:
|
def source(self) -> str | None:
|
||||||
"""Source that initialized the flow."""
|
"""Source that initialized the flow."""
|
||||||
if not hasattr(self, "context"):
|
if not hasattr(self, "context"):
|
||||||
return None
|
return None
|
||||||
|
@ -301,9 +301,9 @@ class FlowHandler:
|
||||||
*,
|
*,
|
||||||
step_id: str,
|
step_id: str,
|
||||||
data_schema: vol.Schema = None,
|
data_schema: vol.Schema = None,
|
||||||
errors: Optional[Dict] = None,
|
errors: dict | None = None,
|
||||||
description_placeholders: Optional[Dict] = None,
|
description_placeholders: dict | None = None,
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return the definition of a form to gather user input."""
|
"""Return the definition of a form to gather user input."""
|
||||||
return {
|
return {
|
||||||
"type": RESULT_TYPE_FORM,
|
"type": RESULT_TYPE_FORM,
|
||||||
|
@ -320,10 +320,10 @@ class FlowHandler:
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
title: str,
|
title: str,
|
||||||
data: Dict,
|
data: dict,
|
||||||
description: Optional[str] = None,
|
description: str | None = None,
|
||||||
description_placeholders: Optional[Dict] = None,
|
description_placeholders: dict | None = None,
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Finish config flow and create a config entry."""
|
"""Finish config flow and create a config entry."""
|
||||||
return {
|
return {
|
||||||
"version": self.VERSION,
|
"version": self.VERSION,
|
||||||
|
@ -338,8 +338,8 @@ class FlowHandler:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_abort(
|
def async_abort(
|
||||||
self, *, reason: str, description_placeholders: Optional[Dict] = None
|
self, *, reason: str, description_placeholders: dict | None = None
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Abort the config flow."""
|
"""Abort the config flow."""
|
||||||
return _create_abort_data(
|
return _create_abort_data(
|
||||||
self.flow_id, self.handler, reason, description_placeholders
|
self.flow_id, self.handler, reason, description_placeholders
|
||||||
|
@ -347,8 +347,8 @@ class FlowHandler:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_external_step(
|
def async_external_step(
|
||||||
self, *, step_id: str, url: str, description_placeholders: Optional[Dict] = None
|
self, *, step_id: str, url: str, description_placeholders: dict | None = None
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return the definition of an external step for the user to take."""
|
"""Return the definition of an external step for the user to take."""
|
||||||
return {
|
return {
|
||||||
"type": RESULT_TYPE_EXTERNAL_STEP,
|
"type": RESULT_TYPE_EXTERNAL_STEP,
|
||||||
|
@ -360,7 +360,7 @@ class FlowHandler:
|
||||||
}
|
}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_external_step_done(self, *, next_step_id: str) -> Dict[str, Any]:
|
def async_external_step_done(self, *, next_step_id: str) -> dict[str, Any]:
|
||||||
"""Return the definition of an external step for the user to take."""
|
"""Return the definition of an external step for the user to take."""
|
||||||
return {
|
return {
|
||||||
"type": RESULT_TYPE_EXTERNAL_STEP_DONE,
|
"type": RESULT_TYPE_EXTERNAL_STEP_DONE,
|
||||||
|
@ -375,8 +375,8 @@ class FlowHandler:
|
||||||
*,
|
*,
|
||||||
step_id: str,
|
step_id: str,
|
||||||
progress_action: str,
|
progress_action: str,
|
||||||
description_placeholders: Optional[Dict] = None,
|
description_placeholders: dict | None = None,
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Show a progress message to the user, without user input allowed."""
|
"""Show a progress message to the user, without user input allowed."""
|
||||||
return {
|
return {
|
||||||
"type": RESULT_TYPE_SHOW_PROGRESS,
|
"type": RESULT_TYPE_SHOW_PROGRESS,
|
||||||
|
@ -388,7 +388,7 @@ class FlowHandler:
|
||||||
}
|
}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_show_progress_done(self, *, next_step_id: str) -> Dict[str, Any]:
|
def async_show_progress_done(self, *, next_step_id: str) -> dict[str, Any]:
|
||||||
"""Mark the progress done."""
|
"""Mark the progress done."""
|
||||||
return {
|
return {
|
||||||
"type": RESULT_TYPE_SHOW_PROGRESS_DONE,
|
"type": RESULT_TYPE_SHOW_PROGRESS_DONE,
|
||||||
|
@ -403,8 +403,8 @@ def _create_abort_data(
|
||||||
flow_id: str,
|
flow_id: str,
|
||||||
handler: str,
|
handler: str,
|
||||||
reason: str,
|
reason: str,
|
||||||
description_placeholders: Optional[Dict] = None,
|
description_placeholders: dict | None = None,
|
||||||
) -> Dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return the definition of an external step for the user to take."""
|
"""Return the definition of an external step for the user to take."""
|
||||||
return {
|
return {
|
||||||
"type": RESULT_TYPE_ABORT,
|
"type": RESULT_TYPE_ABORT,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
"""The exceptions used by Home Assistant."""
|
"""The exceptions used by Home Assistant."""
|
||||||
from typing import TYPE_CHECKING, Generator, Optional, Sequence
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Generator, Sequence
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
|
@ -113,12 +115,12 @@ class Unauthorized(HomeAssistantError):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
context: Optional["Context"] = None,
|
context: "Context" | None = None,
|
||||||
user_id: Optional[str] = None,
|
user_id: str | None = None,
|
||||||
entity_id: Optional[str] = None,
|
entity_id: str | None = None,
|
||||||
config_entry_id: Optional[str] = None,
|
config_entry_id: str | None = None,
|
||||||
perm_category: Optional[str] = None,
|
perm_category: str | None = None,
|
||||||
permission: Optional[str] = None,
|
permission: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Unauthorized error."""
|
"""Unauthorized error."""
|
||||||
super().__init__(self.__class__.__name__)
|
super().__init__(self.__class__.__name__)
|
||||||
|
|
|
@ -14,19 +14,7 @@ import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import (
|
from typing import TYPE_CHECKING, Any, Callable, Dict, TypedDict, TypeVar, cast
|
||||||
TYPE_CHECKING,
|
|
||||||
Any,
|
|
||||||
Callable,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Optional,
|
|
||||||
Set,
|
|
||||||
TypedDict,
|
|
||||||
TypeVar,
|
|
||||||
Union,
|
|
||||||
cast,
|
|
||||||
)
|
|
||||||
|
|
||||||
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
|
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
|
||||||
|
|
||||||
|
@ -86,21 +74,21 @@ class Manifest(TypedDict, total=False):
|
||||||
name: str
|
name: str
|
||||||
disabled: str
|
disabled: str
|
||||||
domain: str
|
domain: str
|
||||||
dependencies: List[str]
|
dependencies: list[str]
|
||||||
after_dependencies: List[str]
|
after_dependencies: list[str]
|
||||||
requirements: List[str]
|
requirements: list[str]
|
||||||
config_flow: bool
|
config_flow: bool
|
||||||
documentation: str
|
documentation: str
|
||||||
issue_tracker: str
|
issue_tracker: str
|
||||||
quality_scale: str
|
quality_scale: str
|
||||||
mqtt: List[str]
|
mqtt: list[str]
|
||||||
ssdp: List[Dict[str, str]]
|
ssdp: list[dict[str, str]]
|
||||||
zeroconf: List[Union[str, Dict[str, str]]]
|
zeroconf: list[str | dict[str, str]]
|
||||||
dhcp: List[Dict[str, str]]
|
dhcp: list[dict[str, str]]
|
||||||
homekit: Dict[str, List[str]]
|
homekit: dict[str, list[str]]
|
||||||
is_built_in: bool
|
is_built_in: bool
|
||||||
version: str
|
version: str
|
||||||
codeowners: List[str]
|
codeowners: list[str]
|
||||||
|
|
||||||
|
|
||||||
def manifest_from_legacy_module(domain: str, module: ModuleType) -> Manifest:
|
def manifest_from_legacy_module(domain: str, module: ModuleType) -> Manifest:
|
||||||
|
@ -116,7 +104,7 @@ def manifest_from_legacy_module(domain: str, module: ModuleType) -> Manifest:
|
||||||
|
|
||||||
async def _async_get_custom_components(
|
async def _async_get_custom_components(
|
||||||
hass: "HomeAssistant",
|
hass: "HomeAssistant",
|
||||||
) -> Dict[str, Integration]:
|
) -> dict[str, Integration]:
|
||||||
"""Return list of custom integrations."""
|
"""Return list of custom integrations."""
|
||||||
if hass.config.safe_mode:
|
if hass.config.safe_mode:
|
||||||
return {}
|
return {}
|
||||||
|
@ -126,7 +114,7 @@ async def _async_get_custom_components(
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_sub_directories(paths: List[str]) -> List[pathlib.Path]:
|
def get_sub_directories(paths: list[str]) -> list[pathlib.Path]:
|
||||||
"""Return all sub directories in a set of paths."""
|
"""Return all sub directories in a set of paths."""
|
||||||
return [
|
return [
|
||||||
entry
|
entry
|
||||||
|
@ -157,7 +145,7 @@ async def _async_get_custom_components(
|
||||||
|
|
||||||
async def async_get_custom_components(
|
async def async_get_custom_components(
|
||||||
hass: "HomeAssistant",
|
hass: "HomeAssistant",
|
||||||
) -> Dict[str, Integration]:
|
) -> dict[str, Integration]:
|
||||||
"""Return cached list of custom integrations."""
|
"""Return cached list of custom integrations."""
|
||||||
reg_or_evt = hass.data.get(DATA_CUSTOM_COMPONENTS)
|
reg_or_evt = hass.data.get(DATA_CUSTOM_COMPONENTS)
|
||||||
|
|
||||||
|
@ -177,12 +165,12 @@ async def async_get_custom_components(
|
||||||
return cast(Dict[str, "Integration"], reg_or_evt)
|
return cast(Dict[str, "Integration"], reg_or_evt)
|
||||||
|
|
||||||
|
|
||||||
async def async_get_config_flows(hass: HomeAssistant) -> Set[str]:
|
async def async_get_config_flows(hass: HomeAssistant) -> set[str]:
|
||||||
"""Return cached list of config flows."""
|
"""Return cached list of config flows."""
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
from homeassistant.generated.config_flows import FLOWS
|
from homeassistant.generated.config_flows import FLOWS
|
||||||
|
|
||||||
flows: Set[str] = set()
|
flows: set[str] = set()
|
||||||
flows.update(FLOWS)
|
flows.update(FLOWS)
|
||||||
|
|
||||||
integrations = await async_get_custom_components(hass)
|
integrations = await async_get_custom_components(hass)
|
||||||
|
@ -197,9 +185,9 @@ async def async_get_config_flows(hass: HomeAssistant) -> Set[str]:
|
||||||
return flows
|
return flows
|
||||||
|
|
||||||
|
|
||||||
async def async_get_zeroconf(hass: HomeAssistant) -> Dict[str, List[Dict[str, str]]]:
|
async def async_get_zeroconf(hass: HomeAssistant) -> dict[str, list[dict[str, str]]]:
|
||||||
"""Return cached list of zeroconf types."""
|
"""Return cached list of zeroconf types."""
|
||||||
zeroconf: Dict[str, List[Dict[str, str]]] = ZEROCONF.copy()
|
zeroconf: dict[str, list[dict[str, str]]] = ZEROCONF.copy()
|
||||||
|
|
||||||
integrations = await async_get_custom_components(hass)
|
integrations = await async_get_custom_components(hass)
|
||||||
for integration in integrations.values():
|
for integration in integrations.values():
|
||||||
|
@ -220,9 +208,9 @@ async def async_get_zeroconf(hass: HomeAssistant) -> Dict[str, List[Dict[str, st
|
||||||
return zeroconf
|
return zeroconf
|
||||||
|
|
||||||
|
|
||||||
async def async_get_dhcp(hass: HomeAssistant) -> List[Dict[str, str]]:
|
async def async_get_dhcp(hass: HomeAssistant) -> list[dict[str, str]]:
|
||||||
"""Return cached list of dhcp types."""
|
"""Return cached list of dhcp types."""
|
||||||
dhcp: List[Dict[str, str]] = DHCP.copy()
|
dhcp: list[dict[str, str]] = DHCP.copy()
|
||||||
|
|
||||||
integrations = await async_get_custom_components(hass)
|
integrations = await async_get_custom_components(hass)
|
||||||
for integration in integrations.values():
|
for integration in integrations.values():
|
||||||
|
@ -234,10 +222,10 @@ async def async_get_dhcp(hass: HomeAssistant) -> List[Dict[str, str]]:
|
||||||
return dhcp
|
return dhcp
|
||||||
|
|
||||||
|
|
||||||
async def async_get_homekit(hass: HomeAssistant) -> Dict[str, str]:
|
async def async_get_homekit(hass: HomeAssistant) -> dict[str, str]:
|
||||||
"""Return cached list of homekit models."""
|
"""Return cached list of homekit models."""
|
||||||
|
|
||||||
homekit: Dict[str, str] = HOMEKIT.copy()
|
homekit: dict[str, str] = HOMEKIT.copy()
|
||||||
|
|
||||||
integrations = await async_get_custom_components(hass)
|
integrations = await async_get_custom_components(hass)
|
||||||
for integration in integrations.values():
|
for integration in integrations.values():
|
||||||
|
@ -253,10 +241,10 @@ async def async_get_homekit(hass: HomeAssistant) -> Dict[str, str]:
|
||||||
return homekit
|
return homekit
|
||||||
|
|
||||||
|
|
||||||
async def async_get_ssdp(hass: HomeAssistant) -> Dict[str, List[Dict[str, str]]]:
|
async def async_get_ssdp(hass: HomeAssistant) -> dict[str, list[dict[str, str]]]:
|
||||||
"""Return cached list of ssdp mappings."""
|
"""Return cached list of ssdp mappings."""
|
||||||
|
|
||||||
ssdp: Dict[str, List[Dict[str, str]]] = SSDP.copy()
|
ssdp: dict[str, list[dict[str, str]]] = SSDP.copy()
|
||||||
|
|
||||||
integrations = await async_get_custom_components(hass)
|
integrations = await async_get_custom_components(hass)
|
||||||
for integration in integrations.values():
|
for integration in integrations.values():
|
||||||
|
@ -268,10 +256,10 @@ async def async_get_ssdp(hass: HomeAssistant) -> Dict[str, List[Dict[str, str]]]
|
||||||
return ssdp
|
return ssdp
|
||||||
|
|
||||||
|
|
||||||
async def async_get_mqtt(hass: HomeAssistant) -> Dict[str, List[str]]:
|
async def async_get_mqtt(hass: HomeAssistant) -> dict[str, list[str]]:
|
||||||
"""Return cached list of MQTT mappings."""
|
"""Return cached list of MQTT mappings."""
|
||||||
|
|
||||||
mqtt: Dict[str, List[str]] = MQTT.copy()
|
mqtt: dict[str, list[str]] = MQTT.copy()
|
||||||
|
|
||||||
integrations = await async_get_custom_components(hass)
|
integrations = await async_get_custom_components(hass)
|
||||||
for integration in integrations.values():
|
for integration in integrations.values():
|
||||||
|
@ -289,7 +277,7 @@ class Integration:
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_from_root(
|
def resolve_from_root(
|
||||||
cls, hass: "HomeAssistant", root_module: ModuleType, domain: str
|
cls, hass: "HomeAssistant", root_module: ModuleType, domain: str
|
||||||
) -> Optional[Integration]:
|
) -> Integration | None:
|
||||||
"""Resolve an integration from a root module."""
|
"""Resolve an integration from a root module."""
|
||||||
for base in root_module.__path__: # type: ignore
|
for base in root_module.__path__: # type: ignore
|
||||||
manifest_path = pathlib.Path(base) / domain / "manifest.json"
|
manifest_path = pathlib.Path(base) / domain / "manifest.json"
|
||||||
|
@ -312,9 +300,7 @@ class Integration:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_legacy(
|
def resolve_legacy(cls, hass: "HomeAssistant", domain: str) -> Integration | None:
|
||||||
cls, hass: "HomeAssistant", domain: str
|
|
||||||
) -> Optional[Integration]:
|
|
||||||
"""Resolve legacy component.
|
"""Resolve legacy component.
|
||||||
|
|
||||||
Will create a stub manifest.
|
Will create a stub manifest.
|
||||||
|
@ -346,8 +332,8 @@ class Integration:
|
||||||
manifest["is_built_in"] = self.is_built_in
|
manifest["is_built_in"] = self.is_built_in
|
||||||
|
|
||||||
if self.dependencies:
|
if self.dependencies:
|
||||||
self._all_dependencies_resolved: Optional[bool] = None
|
self._all_dependencies_resolved: bool | None = None
|
||||||
self._all_dependencies: Optional[Set[str]] = None
|
self._all_dependencies: set[str] | None = None
|
||||||
else:
|
else:
|
||||||
self._all_dependencies_resolved = True
|
self._all_dependencies_resolved = True
|
||||||
self._all_dependencies = set()
|
self._all_dependencies = set()
|
||||||
|
@ -360,7 +346,7 @@ class Integration:
|
||||||
return self.manifest["name"]
|
return self.manifest["name"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def disabled(self) -> Optional[str]:
|
def disabled(self) -> str | None:
|
||||||
"""Return reason integration is disabled."""
|
"""Return reason integration is disabled."""
|
||||||
return self.manifest.get("disabled")
|
return self.manifest.get("disabled")
|
||||||
|
|
||||||
|
@ -370,17 +356,17 @@ class Integration:
|
||||||
return self.manifest["domain"]
|
return self.manifest["domain"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dependencies(self) -> List[str]:
|
def dependencies(self) -> list[str]:
|
||||||
"""Return dependencies."""
|
"""Return dependencies."""
|
||||||
return self.manifest.get("dependencies", [])
|
return self.manifest.get("dependencies", [])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def after_dependencies(self) -> List[str]:
|
def after_dependencies(self) -> list[str]:
|
||||||
"""Return after_dependencies."""
|
"""Return after_dependencies."""
|
||||||
return self.manifest.get("after_dependencies", [])
|
return self.manifest.get("after_dependencies", [])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def requirements(self) -> List[str]:
|
def requirements(self) -> list[str]:
|
||||||
"""Return requirements."""
|
"""Return requirements."""
|
||||||
return self.manifest.get("requirements", [])
|
return self.manifest.get("requirements", [])
|
||||||
|
|
||||||
|
@ -390,42 +376,42 @@ class Integration:
|
||||||
return self.manifest.get("config_flow") or False
|
return self.manifest.get("config_flow") or False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def documentation(self) -> Optional[str]:
|
def documentation(self) -> str | None:
|
||||||
"""Return documentation."""
|
"""Return documentation."""
|
||||||
return self.manifest.get("documentation")
|
return self.manifest.get("documentation")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def issue_tracker(self) -> Optional[str]:
|
def issue_tracker(self) -> str | None:
|
||||||
"""Return issue tracker link."""
|
"""Return issue tracker link."""
|
||||||
return self.manifest.get("issue_tracker")
|
return self.manifest.get("issue_tracker")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def quality_scale(self) -> Optional[str]:
|
def quality_scale(self) -> str | None:
|
||||||
"""Return Integration Quality Scale."""
|
"""Return Integration Quality Scale."""
|
||||||
return self.manifest.get("quality_scale")
|
return self.manifest.get("quality_scale")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mqtt(self) -> Optional[List[str]]:
|
def mqtt(self) -> list[str] | None:
|
||||||
"""Return Integration MQTT entries."""
|
"""Return Integration MQTT entries."""
|
||||||
return self.manifest.get("mqtt")
|
return self.manifest.get("mqtt")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ssdp(self) -> Optional[List[Dict[str, str]]]:
|
def ssdp(self) -> list[dict[str, str]] | None:
|
||||||
"""Return Integration SSDP entries."""
|
"""Return Integration SSDP entries."""
|
||||||
return self.manifest.get("ssdp")
|
return self.manifest.get("ssdp")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def zeroconf(self) -> Optional[List[Union[str, Dict[str, str]]]]:
|
def zeroconf(self) -> list[str | dict[str, str]] | None:
|
||||||
"""Return Integration zeroconf entries."""
|
"""Return Integration zeroconf entries."""
|
||||||
return self.manifest.get("zeroconf")
|
return self.manifest.get("zeroconf")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dhcp(self) -> Optional[List[Dict[str, str]]]:
|
def dhcp(self) -> list[dict[str, str]] | None:
|
||||||
"""Return Integration dhcp entries."""
|
"""Return Integration dhcp entries."""
|
||||||
return self.manifest.get("dhcp")
|
return self.manifest.get("dhcp")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def homekit(self) -> Optional[Dict[str, List[str]]]:
|
def homekit(self) -> dict[str, list[str]] | None:
|
||||||
"""Return Integration homekit entries."""
|
"""Return Integration homekit entries."""
|
||||||
return self.manifest.get("homekit")
|
return self.manifest.get("homekit")
|
||||||
|
|
||||||
|
@ -435,14 +421,14 @@ class Integration:
|
||||||
return self.pkg_path.startswith(PACKAGE_BUILTIN)
|
return self.pkg_path.startswith(PACKAGE_BUILTIN)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self) -> Optional[AwesomeVersion]:
|
def version(self) -> AwesomeVersion | None:
|
||||||
"""Return the version of the integration."""
|
"""Return the version of the integration."""
|
||||||
if "version" not in self.manifest:
|
if "version" not in self.manifest:
|
||||||
return None
|
return None
|
||||||
return AwesomeVersion(self.manifest["version"])
|
return AwesomeVersion(self.manifest["version"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all_dependencies(self) -> Set[str]:
|
def all_dependencies(self) -> set[str]:
|
||||||
"""Return all dependencies including sub-dependencies."""
|
"""Return all dependencies including sub-dependencies."""
|
||||||
if self._all_dependencies is None:
|
if self._all_dependencies is None:
|
||||||
raise RuntimeError("Dependencies not resolved!")
|
raise RuntimeError("Dependencies not resolved!")
|
||||||
|
@ -516,7 +502,7 @@ async def async_get_integration(hass: "HomeAssistant", domain: str) -> Integrati
|
||||||
raise IntegrationNotFound(domain)
|
raise IntegrationNotFound(domain)
|
||||||
cache = hass.data[DATA_INTEGRATIONS] = {}
|
cache = hass.data[DATA_INTEGRATIONS] = {}
|
||||||
|
|
||||||
int_or_evt: Union[Integration, asyncio.Event, None] = cache.get(domain, _UNDEF)
|
int_or_evt: Integration | asyncio.Event | None = cache.get(domain, _UNDEF)
|
||||||
|
|
||||||
if isinstance(int_or_evt, asyncio.Event):
|
if isinstance(int_or_evt, asyncio.Event):
|
||||||
await int_or_evt.wait()
|
await int_or_evt.wait()
|
||||||
|
@ -593,8 +579,8 @@ class CircularDependency(LoaderError):
|
||||||
|
|
||||||
|
|
||||||
def _load_file(
|
def _load_file(
|
||||||
hass: "HomeAssistant", comp_or_platform: str, base_paths: List[str]
|
hass: "HomeAssistant", comp_or_platform: str, base_paths: list[str]
|
||||||
) -> Optional[ModuleType]:
|
) -> ModuleType | None:
|
||||||
"""Try to load specified file.
|
"""Try to load specified file.
|
||||||
|
|
||||||
Looks in config dir first, then built-in components.
|
Looks in config dir first, then built-in components.
|
||||||
|
@ -683,7 +669,7 @@ class Components:
|
||||||
integration = self._hass.data.get(DATA_INTEGRATIONS, {}).get(comp_name)
|
integration = self._hass.data.get(DATA_INTEGRATIONS, {}).get(comp_name)
|
||||||
|
|
||||||
if isinstance(integration, Integration):
|
if isinstance(integration, Integration):
|
||||||
component: Optional[ModuleType] = integration.get_component()
|
component: ModuleType | None = integration.get_component()
|
||||||
else:
|
else:
|
||||||
# Fallback to importing old-school
|
# Fallback to importing old-school
|
||||||
component = _load_file(self._hass, comp_name, _lookup_path(self._hass))
|
component = _load_file(self._hass, comp_name, _lookup_path(self._hass))
|
||||||
|
@ -721,9 +707,9 @@ async def _async_component_dependencies(
|
||||||
hass: "HomeAssistant",
|
hass: "HomeAssistant",
|
||||||
start_domain: str,
|
start_domain: str,
|
||||||
integration: Integration,
|
integration: Integration,
|
||||||
loaded: Set[str],
|
loaded: set[str],
|
||||||
loading: Set[str],
|
loading: set[str],
|
||||||
) -> Set[str]:
|
) -> set[str]:
|
||||||
"""Recursive function to get component dependencies.
|
"""Recursive function to get component dependencies.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -773,7 +759,7 @@ def _async_mount_config_dir(hass: HomeAssistant) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _lookup_path(hass: HomeAssistant) -> List[str]:
|
def _lookup_path(hass: HomeAssistant) -> list[str]:
|
||||||
"""Return the lookup paths for legacy lookups."""
|
"""Return the lookup paths for legacy lookups."""
|
||||||
if hass.config.safe_mode:
|
if hass.config.safe_mode:
|
||||||
return [PACKAGE_BUILTIN]
|
return [PACKAGE_BUILTIN]
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
"""Module to handle installing requirements."""
|
"""Module to handle installing requirements."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from typing import Any, Dict, Iterable, List, Optional, Set, Union, cast
|
from typing import Any, Iterable, cast
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
@ -15,7 +17,7 @@ DATA_PIP_LOCK = "pip_lock"
|
||||||
DATA_PKG_CACHE = "pkg_cache"
|
DATA_PKG_CACHE = "pkg_cache"
|
||||||
DATA_INTEGRATIONS_WITH_REQS = "integrations_with_reqs"
|
DATA_INTEGRATIONS_WITH_REQS = "integrations_with_reqs"
|
||||||
CONSTRAINT_FILE = "package_constraints.txt"
|
CONSTRAINT_FILE = "package_constraints.txt"
|
||||||
DISCOVERY_INTEGRATIONS: Dict[str, Iterable[str]] = {
|
DISCOVERY_INTEGRATIONS: dict[str, Iterable[str]] = {
|
||||||
"dhcp": ("dhcp",),
|
"dhcp": ("dhcp",),
|
||||||
"mqtt": ("mqtt",),
|
"mqtt": ("mqtt",),
|
||||||
"ssdp": ("ssdp",),
|
"ssdp": ("ssdp",),
|
||||||
|
@ -26,7 +28,7 @@ DISCOVERY_INTEGRATIONS: Dict[str, Iterable[str]] = {
|
||||||
class RequirementsNotFound(HomeAssistantError):
|
class RequirementsNotFound(HomeAssistantError):
|
||||||
"""Raised when a component is not found."""
|
"""Raised when a component is not found."""
|
||||||
|
|
||||||
def __init__(self, domain: str, requirements: List[str]) -> None:
|
def __init__(self, domain: str, requirements: list[str]) -> None:
|
||||||
"""Initialize a component not found error."""
|
"""Initialize a component not found error."""
|
||||||
super().__init__(f"Requirements for {domain} not found: {requirements}.")
|
super().__init__(f"Requirements for {domain} not found: {requirements}.")
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
@ -34,7 +36,7 @@ class RequirementsNotFound(HomeAssistantError):
|
||||||
|
|
||||||
|
|
||||||
async def async_get_integration_with_requirements(
|
async def async_get_integration_with_requirements(
|
||||||
hass: HomeAssistant, domain: str, done: Optional[Set[str]] = None
|
hass: HomeAssistant, domain: str, done: set[str] | None = None
|
||||||
) -> Integration:
|
) -> Integration:
|
||||||
"""Get an integration with all requirements installed, including the dependencies.
|
"""Get an integration with all requirements installed, including the dependencies.
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ async def async_get_integration_with_requirements(
|
||||||
if cache is None:
|
if cache is None:
|
||||||
cache = hass.data[DATA_INTEGRATIONS_WITH_REQS] = {}
|
cache = hass.data[DATA_INTEGRATIONS_WITH_REQS] = {}
|
||||||
|
|
||||||
int_or_evt: Union[Integration, asyncio.Event, None, UndefinedType] = cache.get(
|
int_or_evt: Integration | asyncio.Event | None | UndefinedType = cache.get(
|
||||||
domain, UNDEFINED
|
domain, UNDEFINED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -108,7 +110,7 @@ async def async_get_integration_with_requirements(
|
||||||
|
|
||||||
|
|
||||||
async def async_process_requirements(
|
async def async_process_requirements(
|
||||||
hass: HomeAssistant, name: str, requirements: List[str]
|
hass: HomeAssistant, name: str, requirements: list[str]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Install the requirements for a component or platform.
|
"""Install the requirements for a component or platform.
|
||||||
|
|
||||||
|
@ -126,7 +128,7 @@ async def async_process_requirements(
|
||||||
if pkg_util.is_installed(req):
|
if pkg_util.is_installed(req):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def _install(req: str, kwargs: Dict[str, Any]) -> bool:
|
def _install(req: str, kwargs: dict[str, Any]) -> bool:
|
||||||
"""Install requirement."""
|
"""Install requirement."""
|
||||||
return pkg_util.install_package(req, **kwargs)
|
return pkg_util.install_package(req, **kwargs)
|
||||||
|
|
||||||
|
@ -136,7 +138,7 @@ async def async_process_requirements(
|
||||||
raise RequirementsNotFound(name, [req])
|
raise RequirementsNotFound(name, [req])
|
||||||
|
|
||||||
|
|
||||||
def pip_kwargs(config_dir: Optional[str]) -> Dict[str, Any]:
|
def pip_kwargs(config_dir: str | None) -> dict[str, Any]:
|
||||||
"""Return keyword arguments for PIP install."""
|
"""Return keyword arguments for PIP install."""
|
||||||
is_docker = pkg_util.is_docker_env()
|
is_docker = pkg_util.is_docker_env()
|
||||||
kwargs = {
|
kwargs = {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
"""Run Home Assistant."""
|
"""Run Home Assistant."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
@ -34,8 +36,8 @@ class RuntimeConfig:
|
||||||
|
|
||||||
verbose: bool = False
|
verbose: bool = False
|
||||||
|
|
||||||
log_rotate_days: Optional[int] = None
|
log_rotate_days: int | None = None
|
||||||
log_file: Optional[str] = None
|
log_file: str | None = None
|
||||||
log_no_color: bool = False
|
log_no_color: bool = False
|
||||||
|
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
|
@ -83,7 +85,7 @@ class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore[valid
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_loop_exception_handler(_: Any, context: Dict[str, Any]) -> None:
|
def _async_loop_exception_handler(_: Any, context: dict[str, Any]) -> None:
|
||||||
"""Handle all exception inside the core loop."""
|
"""Handle all exception inside the core loop."""
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
exception = context.get("exception")
|
exception = context.get("exception")
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
"""All methods needed to bootstrap a Home Assistant instance."""
|
"""All methods needed to bootstrap a Home Assistant instance."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
from timeit import default_timer as timer
|
from timeit import default_timer as timer
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Awaitable, Callable, Optional, Set
|
from typing import Awaitable, Callable
|
||||||
|
|
||||||
from homeassistant import config as conf_util, core, loader, requirements
|
from homeassistant import config as conf_util, core, loader, requirements
|
||||||
from homeassistant.config import async_notify_setup_error
|
from homeassistant.config import async_notify_setup_error
|
||||||
|
@ -26,7 +28,7 @@ SLOW_SETUP_MAX_WAIT = 300
|
||||||
|
|
||||||
|
|
||||||
@core.callback
|
@core.callback
|
||||||
def async_set_domains_to_be_loaded(hass: core.HomeAssistant, domains: Set[str]) -> None:
|
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.
|
"""Set domains that are going to be loaded from the config.
|
||||||
|
|
||||||
This will allow us to properly handle after_dependencies.
|
This will allow us to properly handle after_dependencies.
|
||||||
|
@ -133,7 +135,7 @@ async def _async_setup_component(
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def log_error(msg: str, link: Optional[str] = None) -> None:
|
def log_error(msg: str, link: str | None = None) -> None:
|
||||||
"""Log helper."""
|
"""Log helper."""
|
||||||
_LOGGER.error("Setup failed for %s: %s", domain, msg)
|
_LOGGER.error("Setup failed for %s: %s", domain, msg)
|
||||||
async_notify_setup_error(hass, domain, link)
|
async_notify_setup_error(hass, domain, link)
|
||||||
|
@ -268,7 +270,7 @@ async def _async_setup_component(
|
||||||
|
|
||||||
async def async_prepare_setup_platform(
|
async def async_prepare_setup_platform(
|
||||||
hass: core.HomeAssistant, hass_config: ConfigType, domain: str, platform_name: str
|
hass: core.HomeAssistant, hass_config: ConfigType, domain: str, platform_name: str
|
||||||
) -> Optional[ModuleType]:
|
) -> ModuleType | None:
|
||||||
"""Load a platform and makes sure dependencies are setup.
|
"""Load a platform and makes sure dependencies are setup.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue