142 lines
4.7 KiB
Python
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>",
|
|
)
|