1
mirror of https://github.com/home-assistant/core synced 2024-08-31 05:57:13 +02:00
ha-core/homeassistant/components/point/config_flow.py
2021-03-21 00:34:46 +01:00

192 lines
6.0 KiB
Python

"""Config flow for Minut Point."""
import asyncio
from collections import OrderedDict
import logging
import async_timeout
from pypoint import PointSession
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import callback
from .const import DOMAIN
AUTH_CALLBACK_PATH = "/api/minut"
AUTH_CALLBACK_NAME = "api:minut"
DATA_FLOW_IMPL = "point_flow_implementation"
_LOGGER = logging.getLogger(__name__)
@callback
def register_flow_implementation(hass, domain, client_id, client_secret):
"""Register a flow implementation.
domain: Domain of the component responsible for the implementation.
name: Name of the component.
client_id: Client id.
client_secret: Client secret.
"""
if DATA_FLOW_IMPL not in hass.data:
hass.data[DATA_FLOW_IMPL] = OrderedDict()
hass.data[DATA_FLOW_IMPL][domain] = {
CONF_CLIENT_ID: client_id,
CONF_CLIENT_SECRET: client_secret,
}
@config_entries.HANDLERS.register(DOMAIN)
class PointFlowHandler(config_entries.ConfigFlow):
"""Handle a config flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
def __init__(self):
"""Initialize flow."""
self.flow_impl = None
async def async_step_import(self, user_input=None):
"""Handle external yaml configuration."""
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="already_setup")
self.flow_impl = DOMAIN
return await self.async_step_auth()
async def async_step_user(self, user_input=None):
"""Handle a flow start."""
flows = self.hass.data.get(DATA_FLOW_IMPL, {})
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="already_setup")
if not flows:
_LOGGER.debug("no flows")
return self.async_abort(reason="no_flows")
if len(flows) == 1:
self.flow_impl = list(flows)[0]
return await self.async_step_auth()
if user_input is not None:
self.flow_impl = user_input["flow_impl"]
return await self.async_step_auth()
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required("flow_impl"): vol.In(list(flows))}),
)
async def async_step_auth(self, user_input=None):
"""Create an entry for auth."""
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="external_setup")
errors = {}
if user_input is not None:
errors["base"] = "follow_link"
try:
with async_timeout.timeout(10):
url = await self._get_authorization_url()
except asyncio.TimeoutError:
return self.async_abort(reason="authorize_url_timeout")
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected error generating auth url")
return self.async_abort(reason="unknown_authorize_url_generation")
return self.async_show_form(
step_id="auth",
description_placeholders={"authorization_url": url},
errors=errors,
)
async def _get_authorization_url(self):
"""Create Minut Point session and get authorization url."""
flow = self.hass.data[DATA_FLOW_IMPL][self.flow_impl]
client_id = flow[CONF_CLIENT_ID]
client_secret = flow[CONF_CLIENT_SECRET]
point_session = PointSession(
self.hass.helpers.aiohttp_client.async_get_clientsession(),
client_id,
client_secret,
)
self.hass.http.register_view(MinutAuthCallbackView())
return point_session.get_authorization_url
async def async_step_code(self, code=None):
"""Received code for authentication."""
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="already_setup")
if code is None:
return self.async_abort(reason="no_code")
_LOGGER.debug(
"Should close all flows below %s",
self.hass.config_entries.flow.async_progress(),
)
# Remove notification if no other discovery config entries in progress
return await self._async_create_session(code)
async def _async_create_session(self, code):
"""Create point session and entries."""
flow = self.hass.data[DATA_FLOW_IMPL][DOMAIN]
client_id = flow[CONF_CLIENT_ID]
client_secret = flow[CONF_CLIENT_SECRET]
point_session = PointSession(
self.hass.helpers.aiohttp_client.async_get_clientsession(),
client_id,
client_secret,
)
token = await point_session.get_access_token(code)
_LOGGER.debug("Got new token")
if not point_session.is_authorized:
_LOGGER.error("Authentication Error")
return self.async_abort(reason="auth_error")
_LOGGER.info("Successfully authenticated Point")
user_email = (await point_session.user()).get("email") or ""
return self.async_create_entry(
title=user_email,
data={
"token": token,
"refresh_args": {
CONF_CLIENT_ID: client_id,
CONF_CLIENT_SECRET: client_secret,
},
},
)
class MinutAuthCallbackView(HomeAssistantView):
"""Minut Authorization Callback View."""
requires_auth = False
url = AUTH_CALLBACK_PATH
name = AUTH_CALLBACK_NAME
@staticmethod
async def get(request):
"""Receive authorization code."""
hass = request.app["hass"]
if "code" in request.query:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": "code"}, data=request.query["code"]
)
)
return "OK!"