1 """Config flow for August integration."""
3 from collections.abc
import Mapping
4 from dataclasses
import dataclass
6 from pathlib
import Path
10 import voluptuous
as vol
11 from yalexs.authenticator_common
import ValidationResult
12 from yalexs.const
import BRANDS_WITHOUT_OAUTH, DEFAULT_BRAND, Brand
13 from yalexs.manager.exceptions
import CannotConnect, InvalidAuth, RequireValidation
20 CONF_ACCESS_TOKEN_CACHE_FILE,
26 VERIFICATION_CODE_KEY,
28 from .gateway
import AugustGateway
29 from .util
import async_create_august_clientsession
33 AVAILABLE_BRANDS = BRANDS_WITHOUT_OAUTH.copy()
34 del AVAILABLE_BRANDS[Brand.YALE_HOME]
37 _LOGGER = logging.getLogger(__name__)
41 data: dict[str, Any], august_gateway: AugustGateway
43 """Validate the user input allows us to connect.
45 Data has the keys from DATA_SCHEMA with values provided by the user.
47 Request configuration steps from the user.
49 assert august_gateway.authenticator
is not None
50 authenticator = august_gateway.authenticator
51 if (code := data.get(VERIFICATION_CODE_KEY))
is not None:
52 result = await authenticator.async_validate_verification_code(code)
53 _LOGGER.debug(
"Verification code validation: %s", result)
54 if result != ValidationResult.VALIDATED:
55 raise RequireValidation
58 await august_gateway.async_authenticate()
59 except RequireValidation:
61 "Requesting new verification code for %s via %s",
62 data.get(CONF_USERNAME),
63 data.get(CONF_LOGIN_METHOD),
66 await august_gateway.authenticator.async_send_verification_code()
70 "title": data.get(CONF_USERNAME),
71 "data": august_gateway.config_entry(),
75 @dataclass(slots=True)
77 """Result from validation."""
79 validation_required: bool
81 errors: dict[str, str]
82 description_placeholders: dict[str, str]
86 """Handle a config flow for August."""
91 """Store an AugustGateway()."""
99 self, user_input: dict[str, Any] |
None =
None
100 ) -> ConfigFlowResult:
101 """Handle the initial step."""
105 self, user_input: dict[str, Any] |
None =
None
106 ) -> ConfigFlowResult:
107 """Handle authentication."""
108 errors: dict[str, str] = {}
109 description_placeholders: dict[str, str] = {}
110 if user_input
is not None:
113 description_placeholders = validate_result.description_placeholders
114 if validate_result.validation_required:
116 if not (errors := validate_result.errors):
120 step_id=
"user_validate",
121 data_schema=vol.Schema(
126 ): vol.In(AVAILABLE_BRANDS),
130 CONF_LOGIN_METHOD, DEFAULT_LOGIN_METHOD
132 ): vol.In(LOGIN_METHODS),
137 vol.Required(CONF_PASSWORD): str,
141 description_placeholders=description_placeholders,
145 self, user_input: dict[str, Any] |
None =
None
146 ) -> ConfigFlowResult:
147 """Handle validation (2fa) step."""
153 previously_failed = VERIFICATION_CODE_KEY
in self.
_user_auth_details_user_auth_details
155 step_id=
"validation",
156 data_schema=vol.Schema(
157 {vol.Required(VERIFICATION_CODE_KEY): vol.All(str, vol.Strip)}
159 errors={
"base":
"invalid_verification_code"}
if previously_failed
else None,
160 description_placeholders={
169 """Set up the gateway."""
180 """Shutdown the gateway."""
186 self, entry_data: Mapping[str, Any]
187 ) -> ConfigFlowResult:
188 """Handle configuration by re-auth."""
193 self, user_input: dict[str, Any] |
None =
None
194 ) -> ConfigFlowResult:
195 """Handle reauth and validation."""
196 errors: dict[str, str] = {}
197 description_placeholders: dict[str, str] = {}
198 if user_input
is not None:
201 description_placeholders = validate_result.description_placeholders
202 if validate_result.validation_required:
204 if not (errors := validate_result.errors):
208 step_id=
"reauth_validate",
209 data_schema=vol.Schema(
214 ): vol.In(BRANDS_WITHOUT_OAUTH),
215 vol.Required(CONF_PASSWORD): str,
219 description_placeholders=description_placeholders
226 self, gateway: AugustGateway, username: str, access_token_cache_file: str |
None
228 """Reset the access token cache if needed."""
231 gateway.async_configure_access_token_cache_file(
232 username, access_token_cache_file
236 await gateway.async_reset_authentication()
239 """Authenticate or validate."""
242 assert gateway
is not None
245 user_auth_details[CONF_USERNAME],
246 user_auth_details.get(CONF_ACCESS_TOKEN_CACHE_FILE),
248 await gateway.async_setup(user_auth_details)
250 errors: dict[str, str] = {}
251 info: dict[str, Any] = {}
252 description_placeholders: dict[str, str] = {}
253 validation_required =
False
257 except CannotConnect:
258 errors[
"base"] =
"cannot_connect"
260 errors[
"base"] =
"invalid_auth"
261 except RequireValidation:
262 validation_required =
True
263 except Exception
as ex:
264 _LOGGER.exception(
"Unexpected exception")
265 errors[
"base"] =
"unhandled"
266 description_placeholders = {
"error":
str(ex)}
269 validation_required, info, errors, description_placeholders
273 self, info: dict[str, Any]
274 ) -> ConfigFlowResult:
275 """Update existing entry or create a new one."""
281 if not existing_entry:
ConfigFlowResult async_step_reauth_validate(self, dict[str, Any]|None user_input=None)
None _async_shutdown_gateway(self)
AugustGateway _async_get_gateway(self)
ConfigFlowResult _async_update_or_create_entry(self, dict[str, Any] info)
ValidateResult _async_auth_or_validate(self)
ConfigFlowResult async_step_validation(self, dict[str, Any]|None user_input=None)
None _async_reset_access_token_cache_if_needed(self, AugustGateway gateway, str username, str|None access_token_cache_file)
ConfigFlowResult async_step_user_validate(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigEntry|None async_set_unique_id(self, str|None unique_id=None, *bool raise_on_progress=True)
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
ConfigFlowResult async_update_reload_and_abort(self, ConfigEntry entry, *str|None|UndefinedType unique_id=UNDEFINED, str|UndefinedType title=UNDEFINED, Mapping[str, Any]|UndefinedType data=UNDEFINED, Mapping[str, Any]|UndefinedType data_updates=UNDEFINED, Mapping[str, Any]|UndefinedType options=UNDEFINED, str|UndefinedType reason=UNDEFINED, bool reload_even_if_entry_is_unchanged=True)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
dict[str, Any] async_validate_input(dict[str, Any] data, AugustGateway august_gateway)
aiohttp.ClientSession async_create_august_clientsession(HomeAssistant hass)
web.Response get(self, web.Request request, str config_key)
IssData update(pyiss.ISS iss)