diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index 3830419c53754..89a05e20eb22c 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -4,13 +4,14 @@ import asyncio from collections import OrderedDict from datetime import timedelta -from typing import Any, Dict, Optional, Tuple, cast +from typing import Any, Dict, Mapping, Optional, Tuple, cast import jwt from homeassistant import data_entry_flow from homeassistant.auth.const import ACCESS_TOKEN_EXPIRATION from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.util import dt as dt_util from . import auth_store, models @@ -97,8 +98,8 @@ async def async_create_flow( return await auth_provider.async_login_flow(context) async def async_finish_flow( - self, flow: data_entry_flow.FlowHandler, result: dict[str, Any] - ) -> dict[str, Any]: + self, flow: data_entry_flow.FlowHandler, result: FlowResultDict + ) -> FlowResultDict: """Return a user as result of login flow.""" flow = cast(LoginFlow, flow) @@ -115,7 +116,7 @@ async def async_finish_flow( raise KeyError(f"Unknown auth provider {result['handler']}") credentials = await auth_provider.async_get_or_create_credentials( - result["data"] + cast(Mapping[str, str], result["data"]), ) if flow.context.get("credential_only"): diff --git a/homeassistant/auth/mfa_modules/__init__.py b/homeassistant/auth/mfa_modules/__init__.py index d6989b6416fc9..80e0a0d834a3f 100644 --- a/homeassistant/auth/mfa_modules/__init__.py +++ b/homeassistant/auth/mfa_modules/__init__.py @@ -12,6 +12,7 @@ from homeassistant import data_entry_flow, requirements from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError from homeassistant.util.decorator import Registry @@ -105,7 +106,7 @@ def __init__( async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the first step of setup flow. Return self.async_show_form(step_id='init') if user_input is None. diff --git a/homeassistant/auth/mfa_modules/notify.py b/homeassistant/auth/mfa_modules/notify.py index 76a5676d562c8..c590b6195e46e 100644 --- a/homeassistant/auth/mfa_modules/notify.py +++ b/homeassistant/auth/mfa_modules/notify.py @@ -14,6 +14,7 @@ from homeassistant.const import CONF_EXCLUDE, CONF_INCLUDE from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import ServiceNotFound from homeassistant.helpers import config_validation as cv @@ -292,7 +293,7 @@ def __init__( async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Let user select available notify services.""" errors: dict[str, str] = {} @@ -318,7 +319,7 @@ async def async_step_init( async def async_step_setup( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Verify user can receive one-time password.""" errors: dict[str, str] = {} diff --git a/homeassistant/auth/mfa_modules/totp.py b/homeassistant/auth/mfa_modules/totp.py index d20c84655463a..cb9ff95f808a9 100644 --- a/homeassistant/auth/mfa_modules/totp.py +++ b/homeassistant/auth/mfa_modules/totp.py @@ -9,6 +9,7 @@ from homeassistant.auth.models import User from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultDict from . import ( MULTI_FACTOR_AUTH_MODULE_SCHEMA, @@ -189,7 +190,7 @@ def __init__( async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the first step of setup flow. Return self.async_show_form(step_id='init') if user_input is None. diff --git a/homeassistant/auth/providers/__init__.py b/homeassistant/auth/providers/__init__.py index 6e188be1ffc94..cdd5029f1d983 100644 --- a/homeassistant/auth/providers/__init__.py +++ b/homeassistant/auth/providers/__init__.py @@ -1,6 +1,7 @@ """Auth providers for Home Assistant.""" from __future__ import annotations +from collections.abc import Mapping import importlib import logging import types @@ -12,6 +13,7 @@ from homeassistant import data_entry_flow, requirements from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError from homeassistant.util import dt as dt_util from homeassistant.util.decorator import Registry @@ -102,7 +104,7 @@ async def async_login_flow(self, context: dict | None) -> LoginFlow: raise NotImplementedError async def async_get_or_create_credentials( - self, flow_result: dict[str, str] + self, flow_result: Mapping[str, str] ) -> Credentials: """Get credentials based on the flow result.""" raise NotImplementedError @@ -198,7 +200,7 @@ def __init__(self, auth_provider: AuthProvider) -> None: async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the first step of login flow. Return self.async_show_form(step_id='init') if user_input is None. @@ -208,7 +210,7 @@ async def async_step_init( async def async_step_select_mfa_module( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of select mfa module.""" errors = {} @@ -233,7 +235,7 @@ async def async_step_select_mfa_module( async def async_step_mfa( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of mfa validation.""" assert self.credential assert self.user @@ -285,6 +287,6 @@ async def async_step_mfa( errors=errors, ) - async def async_finish(self, flow_result: Any) -> dict: + async def async_finish(self, flow_result: Any) -> FlowResultDict: """Handle the pass of login flow.""" return self.async_create_entry(title=self._auth_provider.name, data=flow_result) diff --git a/homeassistant/auth/providers/command_line.py b/homeassistant/auth/providers/command_line.py index 47a56d87097c4..9413072fd4b63 100644 --- a/homeassistant/auth/providers/command_line.py +++ b/homeassistant/auth/providers/command_line.py @@ -3,6 +3,7 @@ import asyncio.subprocess import collections +from collections.abc import Mapping import logging import os from typing import Any, cast @@ -10,6 +11,7 @@ import voluptuous as vol from homeassistant.const import CONF_COMMAND +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow @@ -100,7 +102,7 @@ async def async_validate_login(self, username: str, password: str) -> None: self._user_meta[username] = meta async def async_get_or_create_credentials( - self, flow_result: dict[str, str] + self, flow_result: Mapping[str, str] ) -> Credentials: """Get credentials based on the flow result.""" username = flow_result["username"] @@ -127,7 +129,7 @@ class CommandLineLoginFlow(LoginFlow): async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of the form.""" errors = {} diff --git a/homeassistant/auth/providers/homeassistant.py b/homeassistant/auth/providers/homeassistant.py index 54d82013a7580..7544ae9aa1485 100644 --- a/homeassistant/auth/providers/homeassistant.py +++ b/homeassistant/auth/providers/homeassistant.py @@ -4,6 +4,7 @@ import asyncio import base64 from collections import OrderedDict +from collections.abc import Mapping import logging from typing import Any, cast @@ -12,6 +13,7 @@ from homeassistant.const import CONF_ID from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow @@ -277,7 +279,7 @@ async def async_change_password(self, username: str, new_password: str) -> None: await self.data.async_save() async def async_get_or_create_credentials( - self, flow_result: dict[str, str] + self, flow_result: Mapping[str, str] ) -> Credentials: """Get credentials based on the flow result.""" if self.data is None: @@ -319,7 +321,7 @@ class HassLoginFlow(LoginFlow): async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of the form.""" errors = {} diff --git a/homeassistant/auth/providers/insecure_example.py b/homeassistant/auth/providers/insecure_example.py index c938a6fac8153..ac6171a346c5c 100644 --- a/homeassistant/auth/providers/insecure_example.py +++ b/homeassistant/auth/providers/insecure_example.py @@ -2,12 +2,14 @@ from __future__ import annotations from collections import OrderedDict +from collections.abc import Mapping import hmac -from typing import Any, cast +from typing import cast import voluptuous as vol from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow @@ -62,7 +64,7 @@ def async_validate_login(self, username: str, password: str) -> None: raise InvalidAuthError async def async_get_or_create_credentials( - self, flow_result: dict[str, str] + self, flow_result: Mapping[str, str] ) -> Credentials: """Get credentials based on the flow result.""" username = flow_result["username"] @@ -97,7 +99,7 @@ class ExampleLoginFlow(LoginFlow): async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of the form.""" errors = {} diff --git a/homeassistant/auth/providers/legacy_api_password.py b/homeassistant/auth/providers/legacy_api_password.py index 522751c70d698..5ffb59638dbb8 100644 --- a/homeassistant/auth/providers/legacy_api_password.py +++ b/homeassistant/auth/providers/legacy_api_password.py @@ -5,12 +5,14 @@ """ from __future__ import annotations +from collections.abc import Mapping import hmac -from typing import Any, cast +from typing import cast import voluptuous as vol from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv @@ -57,7 +59,7 @@ def async_validate_login(self, password: str) -> None: raise InvalidAuthError async def async_get_or_create_credentials( - self, flow_result: dict[str, str] + self, flow_result: Mapping[str, str] ) -> Credentials: """Return credentials for this login.""" credentials = await self.async_credentials() @@ -82,7 +84,7 @@ class LegacyLoginFlow(LoginFlow): async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of the form.""" errors = {} diff --git a/homeassistant/auth/providers/trusted_networks.py b/homeassistant/auth/providers/trusted_networks.py index 85b43d89f3fb4..a6a5cfb94f08f 100644 --- a/homeassistant/auth/providers/trusted_networks.py +++ b/homeassistant/auth/providers/trusted_networks.py @@ -5,6 +5,7 @@ """ from __future__ import annotations +from collections.abc import Mapping from ipaddress import ( IPv4Address, IPv4Network, @@ -18,6 +19,7 @@ import voluptuous as vol from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv @@ -127,7 +129,7 @@ async def async_login_flow(self, context: dict | None) -> LoginFlow: ) async def async_get_or_create_credentials( - self, flow_result: dict[str, str] + self, flow_result: Mapping[str, str] ) -> Credentials: """Get credentials based on the flow result.""" user_id = flow_result["user"] @@ -199,7 +201,7 @@ def __init__( async def async_step_init( self, user_input: dict[str, str] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the step of the form.""" try: cast( diff --git a/homeassistant/components/bond/config_flow.py b/homeassistant/components/bond/config_flow.py index 2e1f106193efe..d4bf0275ad9e3 100644 --- a/homeassistant/components/bond/config_flow.py +++ b/homeassistant/components/bond/config_flow.py @@ -16,6 +16,7 @@ HTTP_UNAUTHORIZED, ) from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.typing import DiscoveryInfoType @@ -91,7 +92,9 @@ async def _async_try_automatic_configure(self) -> None: _, hub_name = await _validate_input(self.hass, self._discovered) self._discovered[CONF_NAME] = hub_name - async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType) -> dict[str, Any]: # type: ignore + async def async_step_zeroconf( # type: ignore[override] + self, discovery_info: DiscoveryInfoType + ) -> FlowResultDict: """Handle a flow initialized by zeroconf discovery.""" name: str = discovery_info[CONF_NAME] host: str = discovery_info[CONF_HOST] @@ -115,7 +118,7 @@ async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType) -> dict[s async def async_step_confirm( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle confirmation flow for discovered bond hub.""" errors = {} if user_input is not None: @@ -156,7 +159,7 @@ async def async_step_confirm( async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle a flow initialized by the user.""" errors = {} if user_input is not None: diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index a5a2a1886d744..6dd2a067c8906 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -323,7 +323,7 @@ def get_core_info(hass): @callback @bind_hass -def is_hassio(hass): +def is_hassio(hass: HomeAssistant) -> bool: """Return true if Hass.io is loaded. Async friendly. diff --git a/homeassistant/components/huawei_lte/config_flow.py b/homeassistant/components/huawei_lte/config_flow.py index fc455f865fd30..5f1cdf9325281 100644 --- a/homeassistant/components/huawei_lte/config_flow.py +++ b/homeassistant/components/huawei_lte/config_flow.py @@ -29,6 +29,8 @@ CONF_USERNAME, ) from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResultDict +from homeassistant.helpers.typing import DiscoveryInfoType from .const import ( CONNECTION_TIMEOUT, @@ -58,7 +60,7 @@ async def _async_show_user_form( self, user_input: dict[str, Any] | None = None, errors: dict[str, str] | None = None, - ) -> dict[str, Any]: + ) -> FlowResultDict: if user_input is None: user_input = {} return self.async_show_form( @@ -85,7 +87,7 @@ async def _async_show_user_form( async def async_step_import( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle import initiated config flow.""" return await self.async_step_user(user_input) @@ -99,7 +101,7 @@ def _already_configured(self, user_input: dict[str, Any]) -> bool: async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle user initiated config flow.""" if user_input is None: return await self._async_show_user_form() @@ -211,9 +213,9 @@ def get_router_title(conn: Connection) -> str: return self.async_create_entry(title=title, data=user_input) - async def async_step_ssdp( # type: ignore # mypy says signature incompatible with supertype, but it's the same? - self, discovery_info: dict[str, Any] - ) -> dict[str, Any]: + async def async_step_ssdp( # type: ignore[override] + self, discovery_info: DiscoveryInfoType + ) -> FlowResultDict: """Handle SSDP initiated config flow.""" await self.async_set_unique_id(discovery_info[ssdp.ATTR_UPNP_UDN]) self._abort_if_unique_id_configured() @@ -254,7 +256,7 @@ def __init__(self, config_entry: config_entries.ConfigEntry): async def async_step_init( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle options flow.""" # Recipients are persisted as a list, but handled as comma separated string in UI diff --git a/homeassistant/components/hyperion/config_flow.py b/homeassistant/components/hyperion/config_flow.py index 7ceedcbf00544..1a087460151fd 100644 --- a/homeassistant/components/hyperion/config_flow.py +++ b/homeassistant/components/hyperion/config_flow.py @@ -27,6 +27,7 @@ CONF_TOKEN, ) from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResultDict import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType @@ -130,7 +131,7 @@ def _create_client(self, raw_connection: bool = False) -> client.HyperionClient: async def _advance_to_auth_step_if_necessary( self, hyperion_client: client.HyperionClient - ) -> dict[str, Any]: + ) -> FlowResultDict: """Determine if auth is required.""" auth_resp = await hyperion_client.async_is_auth_required() @@ -145,7 +146,7 @@ async def _advance_to_auth_step_if_necessary( async def async_step_reauth( self, config_data: ConfigType, - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle a reauthentication flow.""" self._data = dict(config_data) async with self._create_client(raw_connection=True) as hyperion_client: @@ -153,9 +154,7 @@ async def async_step_reauth( return self.async_abort(reason="cannot_connect") return await self._advance_to_auth_step_if_necessary(hyperion_client) - async def async_step_ssdp( # type: ignore[override] - self, discovery_info: dict[str, Any] - ) -> dict[str, Any]: + async def async_step_ssdp(self, discovery_info: dict[str, Any]) -> FlowResultDict: # type: ignore[override] """Handle a flow initiated by SSDP.""" # Sample data provided by SSDP: { # 'ssdp_location': 'http://192.168.0.1:8090/description.xml', @@ -226,7 +225,7 @@ async def async_step_ssdp( # type: ignore[override] async def async_step_user( self, user_input: ConfigType | None = None, - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle a flow initiated by the user.""" errors = {} if user_input: @@ -297,7 +296,7 @@ async def _can_login(self) -> bool | None: async def async_step_auth( self, user_input: ConfigType | None = None, - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the auth step of a flow.""" errors = {} if user_input: @@ -326,7 +325,7 @@ async def async_step_auth( async def async_step_create_token( self, user_input: ConfigType | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Send a request for a new token.""" if user_input is None: self._auth_id = client.generate_random_auth_id() @@ -352,7 +351,7 @@ async def async_step_create_token( async def async_step_create_token_external( self, auth_resp: ConfigType | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle completion of the request for a new token.""" if auth_resp is not None and client.ResponseOK(auth_resp): token = auth_resp.get(const.KEY_INFO, {}).get(const.KEY_TOKEN) @@ -365,7 +364,7 @@ async def async_step_create_token_external( async def async_step_create_token_success( self, _: ConfigType | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Create an entry after successful token creation.""" # Clean-up the request task. await self._cancel_request_token_task() @@ -381,7 +380,7 @@ async def async_step_create_token_success( async def async_step_create_token_fail( self, _: ConfigType | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Show an error on the auth form.""" # Clean-up the request task. await self._cancel_request_token_task() @@ -389,7 +388,7 @@ async def async_step_create_token_fail( async def async_step_confirm( self, user_input: ConfigType | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Get final confirmation before entry creation.""" if user_input is None and self._require_confirm: return self.async_show_form( @@ -449,7 +448,7 @@ def _create_client(self) -> client.HyperionClient: async def async_step_init( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Manage the options.""" effects = {source: source for source in const.KEY_COMPONENTID_EXTERNAL_SOURCES} diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 313f4e146a51e..a2429a25c1b12 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -14,7 +14,7 @@ from homeassistant.components.hassio import is_hassio from homeassistant.const import CONF_URL from homeassistant.core import HomeAssistant, callback -from homeassistant.data_entry_flow import AbortFlow +from homeassistant.data_entry_flow import AbortFlow, FlowResultDict from homeassistant.helpers.aiohttp_client import async_get_clientsession from .addon import AddonError, AddonManager, get_addon_manager @@ -89,16 +89,16 @@ def __init__(self) -> None: async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle the initial step.""" - if is_hassio(self.hass): # type: ignore # no-untyped-call + if is_hassio(self.hass): return await self.async_step_on_supervisor() return await self.async_step_manual() async def async_step_manual( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle a manual configuration.""" if user_input is None: return self.async_show_form( @@ -134,9 +134,7 @@ async def async_step_manual( step_id="manual", data_schema=STEP_USER_DATA_SCHEMA, errors=errors ) - async def async_step_hassio( # type: ignore # override - self, discovery_info: dict[str, Any] - ) -> dict[str, Any]: + async def async_step_hassio(self, discovery_info: dict[str, Any]) -> FlowResultDict: # type: ignore[override] """Receive configuration from add-on discovery info. This flow is triggered by the Z-Wave JS add-on. @@ -154,7 +152,7 @@ async def async_step_hassio( # type: ignore # override async def async_step_hassio_confirm( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Confirm the add-on discovery.""" if user_input is not None: return await self.async_step_on_supervisor( @@ -164,7 +162,7 @@ async def async_step_hassio_confirm( return self.async_show_form(step_id="hassio_confirm") @callback - def _async_create_entry_from_vars(self) -> dict[str, Any]: + def _async_create_entry_from_vars(self) -> FlowResultDict: """Return a config entry for the flow.""" return self.async_create_entry( title=TITLE, @@ -179,7 +177,7 @@ def _async_create_entry_from_vars(self) -> dict[str, Any]: async def async_step_on_supervisor( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle logic when on Supervisor host.""" if user_input is None: return self.async_show_form( @@ -203,7 +201,7 @@ async def async_step_on_supervisor( async def async_step_install_addon( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Install Z-Wave JS add-on.""" if not self.install_task: self.install_task = self.hass.async_create_task(self._async_install_addon()) @@ -223,13 +221,13 @@ async def async_step_install_addon( async def async_step_install_failed( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Add-on installation failed.""" return self.async_abort(reason="addon_install_failed") async def async_step_configure_addon( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Ask for config for Z-Wave JS add-on.""" addon_config = await self._async_get_addon_config() @@ -265,7 +263,7 @@ async def async_step_configure_addon( async def async_step_start_addon( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Start Z-Wave JS add-on.""" if not self.start_task: self.start_task = self.hass.async_create_task(self._async_start_addon()) @@ -283,7 +281,7 @@ async def async_step_start_addon( async def async_step_start_failed( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Add-on start failed.""" return self.async_abort(reason="addon_start_failed") @@ -320,7 +318,7 @@ async def _async_start_addon(self) -> None: async def async_step_finish_addon_setup( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Prepare info needed to complete the config entry. Get add-on discovery info and server version info. diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 34afc77e52809..6a1fa9e2b9b9b 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from collections.abc import Mapping from contextvars import ContextVar import functools import logging @@ -21,7 +22,7 @@ ) from homeassistant.helpers import device_registry, entity_registry from homeassistant.helpers.event import Event -from homeassistant.helpers.typing import UNDEFINED, UndefinedType +from homeassistant.helpers.typing import UNDEFINED, DiscoveryInfoType, UndefinedType from homeassistant.setup import async_process_deps_reqs, async_setup_component from homeassistant.util.decorator import Registry import homeassistant.util.uuid as uuid_util @@ -146,7 +147,7 @@ def __init__( version: int, domain: str, title: str, - data: dict, + data: Mapping[str, Any], source: str, connection_class: str, system_options: dict, @@ -559,8 +560,8 @@ def __init__( self._hass_config = hass_config async def async_finish_flow( - self, flow: data_entry_flow.FlowHandler, result: dict[str, Any] - ) -> dict[str, Any]: + self, flow: data_entry_flow.FlowHandler, result: data_entry_flow.FlowResultDict + ) -> data_entry_flow.FlowResultDict: """Finish a config flow and add an entry.""" flow = cast(ConfigFlow, flow) @@ -668,7 +669,7 @@ async def async_create_flow( return flow async def async_post_init( - self, flow: data_entry_flow.FlowHandler, result: dict + self, flow: data_entry_flow.FlowHandler, result: data_entry_flow.FlowResultDict ) -> None: """After a flow is initialised trigger new flow notifications.""" source = flow.context["source"] @@ -930,7 +931,7 @@ def async_update_entry( unique_id: str | dict | None | UndefinedType = UNDEFINED, title: str | dict | UndefinedType = UNDEFINED, data: dict | UndefinedType = UNDEFINED, - options: dict | UndefinedType = UNDEFINED, + options: Mapping[str, Any] | UndefinedType = UNDEFINED, system_options: dict | UndefinedType = UNDEFINED, ) -> bool: """Update a config entry. @@ -955,7 +956,7 @@ def async_update_entry( changed = True entry.data = MappingProxyType(data) - if options is not UNDEFINED and entry.options != options: # type: ignore + if options is not UNDEFINED and entry.options != options: changed = True entry.options = MappingProxyType(options) @@ -1146,7 +1147,9 @@ def _async_current_ids(self, include_ignore: bool = True) -> set[str | None]: } @callback - def _async_in_progress(self, include_uninitialized: bool = False) -> list[dict]: + def _async_in_progress( + self, include_uninitialized: bool = False + ) -> list[data_entry_flow.FlowResultDict]: """Return other in progress flows for current domain.""" return [ flw @@ -1156,18 +1159,22 @@ def _async_in_progress(self, include_uninitialized: bool = False) -> list[dict]: 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] + ) -> data_entry_flow.FlowResultDict: """Ignore this config flow.""" await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False) 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] + ) -> data_entry_flow.FlowResultDict: """Rediscover a config entry by it's unique_id.""" return self.async_abort(reason="not_implemented") async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> data_entry_flow.FlowResultDict: """Handle a flow initiated by the user.""" return self.async_abort(reason="not_implemented") @@ -1196,8 +1203,8 @@ async def _async_handle_discovery_without_unique_id(self) -> None: raise data_entry_flow.AbortFlow("already_in_progress") async def async_step_discovery( - self, discovery_info: dict[str, Any] - ) -> dict[str, Any]: + self, discovery_info: DiscoveryInfoType + ) -> data_entry_flow.FlowResultDict: """Handle a flow initialized by discovery.""" await self._async_handle_discovery_without_unique_id() return await self.async_step_user() @@ -1205,7 +1212,7 @@ async def async_step_discovery( @callback def async_abort( self, *, reason: str, description_placeholders: dict | None = None - ) -> dict[str, Any]: + ) -> data_entry_flow.FlowResultDict: """Abort the config flow.""" # Remove reauth notification if no reauth flows are in progress if self.source == SOURCE_REAUTH and not any( @@ -1253,8 +1260,8 @@ async def async_create_flow( return cast(OptionsFlow, HANDLERS[entry.domain].async_get_options_flow(entry)) async def async_finish_flow( - self, flow: data_entry_flow.FlowHandler, result: dict[str, Any] - ) -> dict[str, Any]: + self, flow: data_entry_flow.FlowHandler, result: data_entry_flow.FlowResultDict + ) -> data_entry_flow.FlowResultDict: """Finish an options flow and update options for configuration entry. Flow.handler and entry_id is the same thing to map flow with entry. diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 46ec967bd94e5..c00a7a903b5d6 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -5,7 +5,7 @@ import asyncio from collections.abc import Mapping from types import MappingProxyType -from typing import Any +from typing import Any, TypedDict import uuid import voluptuous as vol @@ -51,6 +51,29 @@ def __init__(self, reason: str, description_placeholders: dict | None = None): self.description_placeholders = description_placeholders +class FlowResultDict(TypedDict, total=False): + """Typed result dict.""" + + version: int + type: str + flow_id: str + handler: str + title: str + data: Mapping[str, Any] + step_id: str + data_schema: vol.Schema + extra: str + required: bool + errors: dict[str, str] | None + description: str | None + description_placeholders: dict[str, Any] | None + progress_action: str + url: str + reason: str + context: dict[str, Any] + result: Any + + class FlowManager(abc.ABC): """Manage all the flows that are in progress.""" @@ -87,15 +110,17 @@ async def async_create_flow( @abc.abstractmethod async def async_finish_flow( - self, flow: FlowHandler, result: dict[str, Any] - ) -> dict[str, Any]: + self, flow: FlowHandler, result: FlowResultDict + ) -> FlowResultDict: """Finish a config flow and add an entry.""" - async def async_post_init(self, flow: FlowHandler, result: dict[str, Any]) -> None: + async def async_post_init(self, flow: FlowHandler, result: FlowResultDict) -> None: """Entry has finished executing its first step asynchronously.""" @callback - def async_progress(self, include_uninitialized: bool = False) -> list[dict]: + def async_progress( + self, include_uninitialized: bool = False + ) -> list[FlowResultDict]: """Return the flows in progress.""" return [ { @@ -109,8 +134,8 @@ def async_progress(self, include_uninitialized: bool = False) -> list[dict]: ] async def async_init( - self, handler: str, *, context: dict | None = None, data: Any = None - ) -> Any: + self, handler: str, *, context: dict[str, Any] | None = None, data: Any = None + ) -> FlowResultDict: """Start a configuration flow.""" if context is None: context = {} @@ -142,7 +167,7 @@ async def async_init( async def async_configure( self, flow_id: str, user_input: dict | None = None - ) -> Any: + ) -> FlowResultDict: """Continue a configuration flow.""" flow = self._progress.get(flow_id) @@ -199,7 +224,7 @@ async def _async_handle_step( step_id: str, user_input: dict | None, step_done: asyncio.Future | None = None, - ) -> dict: + ) -> FlowResultDict: """Handle a step of a flow.""" method = f"async_step_{step_id}" @@ -212,7 +237,7 @@ async def _async_handle_step( ) try: - result: dict = await getattr(flow, method)(user_input) + result: FlowResultDict = await getattr(flow, method)(user_input) except AbortFlow as err: result = _create_abort_data( flow.flow_id, flow.handler, err.reason, err.description_placeholders @@ -247,7 +272,7 @@ async def _async_handle_step( return result # We pass a copy of the result because we're mutating our version - result = await self.async_finish_flow(flow, dict(result)) + result = await self.async_finish_flow(flow, result.copy()) # _async_finish_flow may change result type, check it again if result["type"] == RESULT_TYPE_FORM: @@ -270,7 +295,7 @@ class FlowHandler: hass: HomeAssistant = None # type: ignore handler: str = None # type: ignore # Ensure the attribute has a subscriptable, but immutable, default value. - context: dict = MappingProxyType({}) # type: ignore + context: dict[str, Any] = MappingProxyType({}) # type: ignore # Set by _async_create_flow callback init_step = "init" @@ -300,9 +325,9 @@ def async_show_form( *, step_id: str, data_schema: vol.Schema = None, - errors: dict | None = None, - description_placeholders: dict | None = None, - ) -> dict[str, Any]: + errors: dict[str, str] | None = None, + description_placeholders: dict[str, Any] | None = None, + ) -> FlowResultDict: """Return the definition of a form to gather user input.""" return { "type": RESULT_TYPE_FORM, @@ -322,7 +347,7 @@ def async_create_entry( data: Mapping[str, Any], description: str | None = None, description_placeholders: dict | None = None, - ) -> dict[str, Any]: + ) -> FlowResultDict: """Finish config flow and create a config entry.""" return { "version": self.VERSION, @@ -338,7 +363,7 @@ def async_create_entry( @callback def async_abort( self, *, reason: str, description_placeholders: dict | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Abort the config flow.""" return _create_abort_data( self.flow_id, self.handler, reason, description_placeholders @@ -347,7 +372,7 @@ def async_abort( @callback def async_external_step( self, *, step_id: str, url: str, description_placeholders: dict | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Return the definition of an external step for the user to take.""" return { "type": RESULT_TYPE_EXTERNAL_STEP, @@ -359,7 +384,7 @@ def async_external_step( } @callback - def async_external_step_done(self, *, next_step_id: str) -> dict[str, Any]: + def async_external_step_done(self, *, next_step_id: str) -> FlowResultDict: """Return the definition of an external step for the user to take.""" return { "type": RESULT_TYPE_EXTERNAL_STEP_DONE, @@ -375,7 +400,7 @@ def async_show_progress( step_id: str, progress_action: str, description_placeholders: dict | None = None, - ) -> dict[str, Any]: + ) -> FlowResultDict: """Show a progress message to the user, without user input allowed.""" return { "type": RESULT_TYPE_SHOW_PROGRESS, @@ -387,7 +412,7 @@ def async_show_progress( } @callback - def async_show_progress_done(self, *, next_step_id: str) -> dict[str, Any]: + def async_show_progress_done(self, *, next_step_id: str) -> FlowResultDict: """Mark the progress done.""" return { "type": RESULT_TYPE_SHOW_PROGRESS_DONE, @@ -403,7 +428,7 @@ def _create_abort_data( handler: str, reason: str, description_placeholders: dict | None = None, -) -> dict[str, Any]: +) -> FlowResultDict: """Return the definition of an external step for the user to take.""" return { "type": RESULT_TYPE_ABORT, diff --git a/homeassistant/helpers/config_entry_flow.py b/homeassistant/helpers/config_entry_flow.py index 6abcf0ece56bf..c9ac765ecbbe1 100644 --- a/homeassistant/helpers/config_entry_flow.py +++ b/homeassistant/helpers/config_entry_flow.py @@ -5,6 +5,8 @@ from homeassistant import config_entries from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultDict +from homeassistant.helpers.typing import DiscoveryInfoType DiscoveryFunctionType = Callable[[], Union[Awaitable[bool], bool]] @@ -29,7 +31,7 @@ def __init__( async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle a flow initialized by the user.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") @@ -40,7 +42,7 @@ async def async_step_user( async def async_step_confirm( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Confirm setup.""" if user_input is None: self._set_confirm_only() @@ -69,8 +71,8 @@ async def async_step_confirm( return self.async_create_entry(title=self._title, data={}) async def async_step_discovery( - self, discovery_info: dict[str, Any] - ) -> dict[str, Any]: + self, discovery_info: DiscoveryInfoType + ) -> FlowResultDict: """Handle a flow initialized by discovery.""" if self._async_in_progress() or self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") @@ -85,7 +87,7 @@ async def async_step_discovery( async_step_homekit = async_step_discovery async_step_dhcp = async_step_discovery - async def async_step_import(self, _: dict[str, Any] | None) -> dict[str, Any]: + async def async_step_import(self, _: dict[str, Any] | None) -> FlowResultDict: """Handle a flow initialized by import.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") @@ -135,7 +137,7 @@ def __init__( async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Handle a user initiated set up flow to create a webhook.""" if not self._allow_multiple and self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index 795c08dd1c98f..891d6c7d28c4c 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -23,6 +23,7 @@ from homeassistant import config_entries from homeassistant.components import http from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResultDict from homeassistant.helpers.network import NoURLAvailableError from .aiohttp_client import async_get_clientsession @@ -234,7 +235,7 @@ def extra_authorize_data(self) -> dict: async def async_step_pick_implementation( self, user_input: dict | None = None - ) -> dict: + ) -> FlowResultDict: """Handle a flow start.""" implementations = await async_get_implementations(self.hass, self.DOMAIN) @@ -265,7 +266,7 @@ async def async_step_pick_implementation( async def async_step_auth( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Create an entry for auth.""" # Flow has been triggered by external data if user_input: @@ -291,7 +292,7 @@ async def async_step_auth( async def async_step_creation( self, user_input: dict[str, Any] | None = None - ) -> dict[str, Any]: + ) -> FlowResultDict: """Create config entry from external data.""" token = await self.flow_impl.async_resolve_external_data(self.external_data) # Force int for non-compliant oauth2 providers @@ -308,7 +309,7 @@ async def async_step_creation( {"auth_implementation": self.flow_impl.domain, "token": token} ) - async def async_oauth_create_entry(self, data: dict) -> dict: + async def async_oauth_create_entry(self, data: dict) -> FlowResultDict: """Create an entry for the flow. Ok to override if you want to fetch extra info or even add another step. diff --git a/homeassistant/helpers/data_entry_flow.py b/homeassistant/helpers/data_entry_flow.py index 00d12d3ab907d..af0ea22d50359 100644 --- a/homeassistant/helpers/data_entry_flow.py +++ b/homeassistant/helpers/data_entry_flow.py @@ -21,7 +21,9 @@ def __init__(self, flow_mgr: data_entry_flow.FlowManager) -> None: self._flow_mgr = flow_mgr # pylint: disable=no-self-use - def _prepare_result_json(self, result: dict[str, Any]) -> dict[str, Any]: + def _prepare_result_json( + self, result: data_entry_flow.FlowResultDict + ) -> data_entry_flow.FlowResultDict: """Convert result to JSON.""" if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: data = result.copy()