core/homeassistant/components/abode/config_flow.py

174 lines
5.7 KiB
Python
Raw Normal View History

"""Config flow for the Abode Security System component."""
2022-01-10 14:54:09 +00:00
from __future__ import annotations
from collections.abc import Mapping
from http import HTTPStatus
2022-01-10 14:54:09 +00:00
from typing import Any, cast
from jaraco.abode.client import Client as Abode
from jaraco.abode.exceptions import (
AuthenticationException as AbodeAuthenticationException,
Exception as AbodeException,
)
from jaraco.abode.helpers.errors import MFA_CODE_REQUIRED
from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
2022-01-10 14:54:09 +00:00
from homeassistant.data_entry_flow import FlowResult
from .const import CONF_POLLING, DOMAIN, LOGGER
2020-11-27 12:39:26 +00:00
CONF_MFA = "mfa_code"
class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for Abode."""
VERSION = 1
2022-01-10 14:54:09 +00:00
def __init__(self) -> None:
"""Initialize."""
self.data_schema = {
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
}
2020-11-27 12:39:26 +00:00
self.mfa_data_schema = {
vol.Required(CONF_MFA): str,
}
2022-01-10 14:54:09 +00:00
self._mfa_code: str | None = None
self._password: str | None = None
self._polling: bool = False
self._username: str | None = None
2020-11-27 12:39:26 +00:00
2022-01-10 14:54:09 +00:00
async def _async_abode_login(self, step_id: str) -> FlowResult:
2020-11-27 12:39:26 +00:00
"""Handle login with Abode."""
errors = {}
try:
await self.hass.async_add_executor_job(
Abode, self._username, self._password, True, False, False
2020-11-27 12:39:26 +00:00
)
2022-01-10 14:54:09 +00:00
except AbodeException as ex:
2020-11-27 12:39:26 +00:00
if ex.errcode == MFA_CODE_REQUIRED[0]:
return await self.async_step_mfa()
LOGGER.error("Unable to connect to Abode: %s", ex)
if ex.errcode == HTTPStatus.BAD_REQUEST:
2020-11-27 12:39:26 +00:00
errors = {"base": "invalid_auth"}
else:
errors = {"base": "cannot_connect"}
2022-01-10 14:54:09 +00:00
except (ConnectTimeout, HTTPError):
errors = {"base": "cannot_connect"}
2020-11-27 12:39:26 +00:00
if errors:
return self.async_show_form(
step_id=step_id, data_schema=vol.Schema(self.data_schema), errors=errors
)
return await self._async_create_entry()
2022-01-10 14:54:09 +00:00
async def _async_abode_mfa_login(self) -> FlowResult:
2020-11-27 12:39:26 +00:00
"""Handle multi-factor authentication (MFA) login with Abode."""
try:
# Create instance to access login method for passing MFA code
abode = Abode(auto_login=False, get_devices=False, get_automations=False)
2020-11-27 12:39:26 +00:00
await self.hass.async_add_executor_job(
abode.login, self._username, self._password, self._mfa_code
)
except AbodeAuthenticationException:
return self.async_show_form(
step_id="mfa",
data_schema=vol.Schema(self.mfa_data_schema),
errors={"base": "invalid_mfa_code"},
)
return await self._async_create_entry()
2022-01-10 14:54:09 +00:00
async def _async_create_entry(self) -> FlowResult:
2020-11-27 12:39:26 +00:00
"""Create the config entry."""
config_data = {
CONF_USERNAME: self._username,
CONF_PASSWORD: self._password,
CONF_POLLING: self._polling,
}
existing_entry = await self.async_set_unique_id(self._username)
if existing_entry:
self.hass.config_entries.async_update_entry(
existing_entry, data=config_data
)
# Reload the Abode config entry otherwise devices will remain unavailable
self.hass.async_create_task(
self.hass.config_entries.async_reload(existing_entry.entry_id)
)
return self.async_abort(reason="reauth_successful")
2022-01-10 14:54:09 +00:00
return self.async_create_entry(
title=cast(str, self._username), data=config_data
)
2022-01-10 14:54:09 +00:00
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
2020-11-27 12:39:26 +00:00
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=vol.Schema(self.data_schema)
)
2020-11-27 12:39:26 +00:00
self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD]
2020-11-27 12:39:26 +00:00
return await self._async_abode_login(step_id="user")
2022-01-10 14:54:09 +00:00
async def async_step_mfa(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
2020-11-27 12:39:26 +00:00
"""Handle a multi-factor authentication (MFA) flow."""
if user_input is None:
return self.async_show_form(
step_id="mfa", data_schema=vol.Schema(self.mfa_data_schema)
)
2020-11-27 12:39:26 +00:00
self._mfa_code = user_input[CONF_MFA]
return await self._async_abode_mfa_login()
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
2020-11-27 12:39:26 +00:00
"""Handle reauthorization request from Abode."""
self._username = entry_data[CONF_USERNAME]
2020-11-27 12:39:26 +00:00
return await self.async_step_reauth_confirm()
2022-01-10 14:54:09 +00:00
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
2020-11-27 12:39:26 +00:00
"""Handle reauthorization flow."""
if user_input is None:
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_USERNAME, default=self._username): str,
vol.Required(CONF_PASSWORD): str,
}
),
)
self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD]
return await self._async_abode_login(step_id="reauth_confirm")