192 lines
6.7 KiB
Python
192 lines
6.7 KiB
Python
"""
|
|
1. User can enter login/pass from GUI
|
|
2. User can set login/pass in YAML
|
|
3. If the password requires updating, user need to configure another component
|
|
with the same login.
|
|
4. Captcha will be requested if necessary
|
|
5. If authorization through YAML does not work, user can continue it through
|
|
the GUI.
|
|
"""
|
|
import logging
|
|
from functools import lru_cache
|
|
|
|
import voluptuous as vol
|
|
from homeassistant.config_entries import ConfigFlow
|
|
from homeassistant.data_entry_flow import AbortFlow
|
|
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
|
|
|
from . import DOMAIN
|
|
from .core.yandex_session import YandexSession, LoginResponse
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
# noinspection PyUnusedLocal
|
|
class YandexStationFlowHandler(ConfigFlow, domain=DOMAIN):
|
|
@property
|
|
@lru_cache()
|
|
def yandex(self):
|
|
session = async_create_clientsession(self.hass)
|
|
return YandexSession(session)
|
|
|
|
async def async_step_import(self, data: dict):
|
|
"""Init by component setup. Forward YAML login/pass to auth."""
|
|
await self.async_set_unique_id(data['username'])
|
|
self._abort_if_unique_id_configured()
|
|
|
|
if 'x_token' in data:
|
|
return self.async_create_entry(
|
|
title=data['username'],
|
|
data={'x_token': data['x_token']})
|
|
|
|
else:
|
|
return await self.async_step_auth(data)
|
|
|
|
async def async_step_user(self, user_input=None):
|
|
"""Init by user via GUI"""
|
|
if user_input is None:
|
|
return self.async_show_form(
|
|
step_id='user',
|
|
data_schema=vol.Schema({
|
|
vol.Required('method', default='qr'): vol.In({
|
|
'qr': "QR-код",
|
|
'auth': "Пароль или одноразовый ключ",
|
|
'email': "Ссылка на E-mail",
|
|
'cookies': "Cookies",
|
|
'token': "Токен"
|
|
})
|
|
})
|
|
)
|
|
|
|
method = user_input['method']
|
|
if method == "qr":
|
|
return self.async_show_form(
|
|
step_id="qr", description_placeholders={
|
|
"qr_url": await self.yandex.get_qr()
|
|
}
|
|
)
|
|
|
|
if method == "auth":
|
|
return self.async_show_form(
|
|
step_id=method, data_schema=vol.Schema({
|
|
vol.Required("username"): str,
|
|
vol.Required("password"): str,
|
|
})
|
|
)
|
|
|
|
if method == "email":
|
|
return self.async_show_form(
|
|
step_id=method, data_schema=vol.Schema({
|
|
vol.Required('username'): str,
|
|
})
|
|
)
|
|
|
|
# cookies, token
|
|
return self.async_show_form(
|
|
step_id=method, data_schema=vol.Schema({
|
|
vol.Required(method): str,
|
|
})
|
|
)
|
|
|
|
async def async_step_qr(self, user_input):
|
|
resp = await self.yandex.login_qr()
|
|
if not resp:
|
|
self.cur_step["errors"] = {"base": "unauthorised"}
|
|
return self.cur_step
|
|
return await self._check_yandex_response(resp)
|
|
|
|
async def async_step_auth(self, user_input):
|
|
"""User submited username and password. Or YAML error."""
|
|
resp = await self.yandex.login_username(user_input['username'])
|
|
if resp.ok:
|
|
resp = await self.yandex.login_password(user_input['password'])
|
|
return await self._check_yandex_response(resp)
|
|
|
|
async def async_step_email(self, user_input):
|
|
resp = await self.yandex.login_username(user_input['username'])
|
|
if not resp.magic_link_email:
|
|
self.cur_step["errors"] = {"base": "email.unsupported"}
|
|
return self.cur_step
|
|
|
|
await self.yandex.get_letter()
|
|
return self.async_show_form(
|
|
step_id="email2", description_placeholders={
|
|
"email": resp.magic_link_email
|
|
}
|
|
)
|
|
|
|
async def async_step_email2(self, user_input):
|
|
resp = await self.yandex.login_letter()
|
|
if not resp:
|
|
self.cur_step["errors"] = {"base": "unauthorised"}
|
|
return self.cur_step
|
|
|
|
return await self._check_yandex_response(resp)
|
|
|
|
async def async_step_cookies(self, user_input):
|
|
resp = await self.yandex.login_cookies(user_input['cookies'])
|
|
return await self._check_yandex_response(resp)
|
|
|
|
async def async_step_token(self, user_input):
|
|
resp = await self.yandex.validate_token(user_input['token'])
|
|
return await self._check_yandex_response(resp)
|
|
|
|
async def async_step_captcha(self, user_input):
|
|
"""User submited captcha. Or YAML error."""
|
|
if user_input is None:
|
|
return self.cur_step
|
|
|
|
ok = await self.yandex.login_captcha(user_input['captcha_answer'])
|
|
if not ok:
|
|
return self.cur_step
|
|
|
|
return self.async_show_form(
|
|
step_id="captcha2", data_schema=vol.Schema({
|
|
vol.Required('password'): str,
|
|
})
|
|
)
|
|
|
|
async def async_step_captcha2(self, user_input):
|
|
resp = await self.yandex.login_password(user_input['password'])
|
|
return await self._check_yandex_response(resp)
|
|
|
|
async def _check_yandex_response(self, resp: LoginResponse):
|
|
"""Check Yandex response. Do not create entry for the same login. Show
|
|
captcha form if captcha required. Show auth form with error if error.
|
|
"""
|
|
if resp.ok:
|
|
# set unique_id or return existing entry
|
|
entry = await self.async_set_unique_id(resp.display_login)
|
|
if entry:
|
|
# update existing entry with same login
|
|
self.hass.config_entries.async_update_entry(
|
|
entry, data={'x_token': resp.x_token}
|
|
)
|
|
return self.async_abort(reason='account_updated')
|
|
|
|
else:
|
|
# create new entry for new login
|
|
return self.async_create_entry(
|
|
title=resp.display_login, data={'x_token': resp.x_token}
|
|
)
|
|
|
|
elif resp.error_captcha_required:
|
|
_LOGGER.debug(f"Captcha required")
|
|
return self.async_show_form(
|
|
step_id='captcha',
|
|
data_schema=vol.Schema({
|
|
vol.Required('captcha_answer'): str,
|
|
}),
|
|
description_placeholders={
|
|
'captcha_url': await self.yandex.get_captcha()
|
|
}
|
|
)
|
|
|
|
elif resp.errors:
|
|
_LOGGER.debug(f"Config error: {resp.error}")
|
|
if self.cur_step:
|
|
self.cur_step["errors"] = {'base': resp.error}
|
|
return self.cur_step
|
|
|
|
raise AbortFlow("not_implemented")
|