core/homeassistant/components/somfy/config_flow.py

142 lines
4.7 KiB
Python

"""Config flow for Somfy."""
import asyncio
import logging
import async_timeout
from homeassistant import config_entries
from homeassistant.components.http import HomeAssistantView
from homeassistant.core import callback
from .const import CLIENT_ID, CLIENT_SECRET, DOMAIN
AUTH_CALLBACK_PATH = "/auth/somfy/callback"
AUTH_CALLBACK_NAME = "auth:somfy:callback"
_LOGGER = logging.getLogger(__name__)
@callback
def register_flow_implementation(hass, client_id, client_secret):
"""Register a flow implementation.
client_id: Client id.
client_secret: Client secret.
"""
hass.data[DOMAIN][CLIENT_ID] = client_id
hass.data[DOMAIN][CLIENT_SECRET] = client_secret
@config_entries.HANDLERS.register("somfy")
class SomfyFlowHandler(config_entries.ConfigFlow):
"""Handle a config flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
def __init__(self):
"""Instantiate config flow."""
self.code = 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")
return await self.async_step_auth()
async def async_step_user(self, user_input=None):
"""Handle a flow start."""
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="already_setup")
if DOMAIN not in self.hass.data:
return self.async_abort(reason="missing_configuration")
return await self.async_step_auth()
async def async_step_auth(self, user_input=None):
"""Create an entry for auth."""
# Flow has been triggered from Somfy website
if user_input:
return await self.async_step_code(user_input)
try:
with async_timeout.timeout(10):
url, _ = await self._get_authorization_url()
except asyncio.TimeoutError:
return self.async_abort(reason="authorize_url_timeout")
return self.async_external_step(step_id="auth", url=url)
async def _get_authorization_url(self):
"""Get Somfy authorization url."""
from pymfy.api.somfy_api import SomfyApi
client_id = self.hass.data[DOMAIN][CLIENT_ID]
client_secret = self.hass.data[DOMAIN][CLIENT_SECRET]
redirect_uri = f"{self.hass.config.api.base_url}{AUTH_CALLBACK_PATH}"
api = SomfyApi(client_id, client_secret, redirect_uri)
self.hass.http.register_view(SomfyAuthCallbackView())
# Thanks to the state, we can forward the flow id to Somfy that will
# add it in the callback.
return await self.hass.async_add_executor_job(
api.get_authorization_url, self.flow_id
)
async def async_step_code(self, code):
"""Received code for authentication."""
self.code = code
return self.async_external_step_done(next_step_id="creation")
async def async_step_creation(self, user_input=None):
"""Create Somfy api and entries."""
client_id = self.hass.data[DOMAIN][CLIENT_ID]
client_secret = self.hass.data[DOMAIN][CLIENT_SECRET]
code = self.code
from pymfy.api.somfy_api import SomfyApi
redirect_uri = f"{self.hass.config.api.base_url}{AUTH_CALLBACK_PATH}"
api = SomfyApi(client_id, client_secret, redirect_uri)
token = await self.hass.async_add_executor_job(api.request_token, None, code)
_LOGGER.info("Successfully authenticated Somfy")
return self.async_create_entry(
title="Somfy",
data={
"token": token,
"refresh_args": {
"client_id": client_id,
"client_secret": client_secret,
},
},
)
class SomfyAuthCallbackView(HomeAssistantView):
"""Somfy Authorization Callback View."""
requires_auth = False
url = AUTH_CALLBACK_PATH
name = AUTH_CALLBACK_NAME
@staticmethod
async def get(request):
"""Receive authorization code."""
from aiohttp import web_response
if "code" not in request.query or "state" not in request.query:
return web_response.Response(
text="Missing code or state parameter in " + request.url
)
hass = request.app["hass"]
hass.async_create_task(
hass.config_entries.flow.async_configure(
flow_id=request.query["state"], user_input=request.query["code"]
)
)
return web_response.Response(
headers={"content-type": "text/html"},
text="<script>window.close()</script>",
)