1 """Config flow for Subaru integration."""
3 from __future__
import annotations
5 from datetime
import datetime
7 from typing
import TYPE_CHECKING, Any
9 from subarulink
import (
10 Controller
as SubaruAPI,
15 from subarulink.const
import COUNTRY_CAN, COUNTRY_USA
16 import voluptuous
as vol
34 from .const
import CONF_UPDATE_ENABLED, DOMAIN
36 _LOGGER = logging.getLogger(__name__)
37 CONF_CONTACT_METHOD =
"contact_method"
38 CONF_VALIDATION_CODE =
"validation_code"
39 PIN_SCHEMA = vol.Schema({vol.Required(CONF_PIN): str})
43 """Handle a config flow for Subaru."""
48 """Initialize config flow."""
49 self.config_data: dict[str, Any] = {CONF_PIN:
None}
50 self.
controllercontroller: SubaruAPI |
None =
None
53 self, user_input: dict[str, Any] |
None =
None
54 ) -> ConfigFlowResult:
55 """Handle the start of the config flow."""
63 except InvalidCredentials:
64 error = {
"base":
"invalid_auth"}
65 except SubaruException
as ex:
66 _LOGGER.error(
"Unable to communicate with Subaru API: %s", ex.message)
71 if not self.
controllercontroller.device_registered:
72 _LOGGER.debug(
"2FA validation is required")
74 if self.
controllercontroller.is_pin_required():
77 title=user_input[CONF_USERNAME], data=self.config_data
82 data_schema=vol.Schema(
86 default=user_input.get(CONF_USERNAME)
if user_input
else "",
90 default=user_input.get(CONF_PASSWORD)
if user_input
else "",
94 default=user_input.get(CONF_COUNTRY)
97 ): vol.In([COUNTRY_CAN, COUNTRY_USA]),
106 config_entry: ConfigEntry,
107 ) -> OptionsFlowHandler:
108 """Get the options flow for this handler."""
112 """Validate the user input allows us to connect.
114 data: contains values provided by the user.
116 websession = aiohttp_client.async_get_clientsession(self.hass)
118 if not data.get(CONF_DEVICE_ID):
119 data[CONF_DEVICE_ID] =
int(now.timestamp())
120 date = now.strftime(
"%Y-%m-%d")
121 device_name =
"Home Assistant: Added " + date
125 username=data[CONF_USERNAME],
126 password=data[CONF_PASSWORD],
127 device_id=data[CONF_DEVICE_ID],
129 device_name=device_name,
130 country=data[CONF_COUNTRY],
132 _LOGGER.debug(
"Setting up first time connection to Subaru API")
134 _LOGGER.debug(
"Successfully authenticated with Subaru API")
135 self.config_data.
update(data)
138 self, user_input: dict[str, Any] |
None =
None
139 ) -> ConfigFlowResult:
140 """Select contact method and request 2FA code from Subaru."""
147 selected_method = next(
149 for k, v
in self.
controllercontroller.contact_methods.items()
150 if v == user_input[CONF_CONTACT_METHOD]
152 if await self.
controllercontroller.request_auth_code(selected_method):
156 data_schema = vol.Schema(
158 vol.Required(CONF_CONTACT_METHOD): vol.In(
164 step_id=
"two_factor", data_schema=data_schema, errors=error
168 self, user_input: dict[str, Any] |
None =
None
169 ) -> ConfigFlowResult:
170 """Validate received 2FA code with Subaru."""
176 vol.Match(
r"^[0-9]{6}$")(user_input[CONF_VALIDATION_CODE])
177 if await self.
controllercontroller.submit_auth_code(
178 user_input[CONF_VALIDATION_CODE]
180 if self.
controllercontroller.is_pin_required():
183 title=self.config_data[CONF_USERNAME], data=self.config_data
185 error = {
"base":
"incorrect_validation_code"}
187 error = {
"base":
"bad_validation_code_format"}
189 data_schema = vol.Schema({vol.Required(CONF_VALIDATION_CODE): str})
191 step_id=
"two_factor_validate", data_schema=data_schema, errors=error
195 self, user_input: dict[str, Any] |
None =
None
196 ) -> ConfigFlowResult:
197 """Handle second part of config flow, if required."""
201 if user_input
and self.
controllercontroller.update_saved_pin(user_input[CONF_PIN]):
203 vol.Match(
r"[0-9]{4}")(user_input[CONF_PIN])
206 error = {
"base":
"bad_pin_format"}
208 error = {
"base":
"incorrect_pin"}
210 _LOGGER.debug(
"PIN successfully tested")
211 self.config_data.
update(user_input)
213 title=self.config_data[CONF_USERNAME], data=self.config_data
219 """Handle a option flow for Subaru."""
222 self, user_input: dict[str, Any] |
None =
None
223 ) -> ConfigFlowResult:
224 """Handle options flow."""
225 if user_input
is not None:
228 data_schema = vol.Schema(
236 return self.
async_show_formasync_show_form(step_id=
"init", data_schema=data_schema)
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_two_factor(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_pin(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_two_factor_validate(self, dict[str, Any]|None user_input=None)
OptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
def validate_login_creds(self, data)
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_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=None)
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)
ConfigEntry config_entry(self)
None config_entry(self, ConfigEntry value)
_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)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
IssData update(pyiss.ISS iss)