"""Config flow for Ambiclimate.""" import logging import ambiclimate from homeassistant import config_entries from homeassistant.components.http import HomeAssistantView from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import (AUTH_CALLBACK_NAME, AUTH_CALLBACK_PATH, CONF_CLIENT_ID, CONF_CLIENT_SECRET, DOMAIN, STORAGE_VERSION, STORAGE_KEY) DATA_AMBICLIMATE_IMPL = 'ambiclimate_flow_implementation' _LOGGER = logging.getLogger(__name__) @callback def register_flow_implementation(hass, client_id, client_secret): """Register a ambiclimate implementation. client_id: Client id. client_secret: Client secret. """ hass.data.setdefault(DATA_AMBICLIMATE_IMPL, {}) hass.data[DATA_AMBICLIMATE_IMPL] = { CONF_CLIENT_ID: client_id, CONF_CLIENT_SECRET: client_secret, } @config_entries.HANDLERS.register('ambiclimate') class AmbiclimateFlowHandler(config_entries.ConfigFlow): """Handle a config flow.""" VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL def __init__(self): """Initialize flow.""" self._registered_view = False self._oauth = None async def async_step_user(self, user_input=None): """Handle external yaml configuration.""" if self.hass.config_entries.async_entries(DOMAIN): return self.async_abort(reason='already_setup') config = self.hass.data.get(DATA_AMBICLIMATE_IMPL, {}) if not config: _LOGGER.debug("No config") return self.async_abort(reason='no_config') return await self.async_step_auth() async def async_step_auth(self, user_input=None): """Handle a flow start.""" if self.hass.config_entries.async_entries(DOMAIN): return self.async_abort(reason='already_setup') errors = {} if user_input is not None: errors['base'] = 'follow_link' if not self._registered_view: self._generate_view() return self.async_show_form( step_id='auth', description_placeholders={'authorization_url': await self._get_authorize_url(), 'cb_url': self._cb_url()}, errors=errors, ) 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') token_info = await self._get_token_info(code) if token_info is None: return self.async_abort(reason='access_token') config = self.hass.data[DATA_AMBICLIMATE_IMPL].copy() config['callback_url'] = self._cb_url() return self.async_create_entry( title="Ambiclimate", data=config, ) async def _get_token_info(self, code): oauth = self._generate_oauth() try: token_info = await oauth.get_access_token(code) except ambiclimate.AmbiclimateOauthError: _LOGGER.error("Failed to get access token", exc_info=True) return None store = self.hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) await store.async_save(token_info) return token_info def _generate_view(self): self.hass.http.register_view(AmbiclimateAuthCallbackView()) self._registered_view = True def _generate_oauth(self): config = self.hass.data[DATA_AMBICLIMATE_IMPL] clientsession = async_get_clientsession(self.hass) callback_url = self._cb_url() oauth = ambiclimate.AmbiclimateOAuth(config.get(CONF_CLIENT_ID), config.get(CONF_CLIENT_SECRET), callback_url, clientsession) return oauth def _cb_url(self): return '{}{}'.format(self.hass.config.api.base_url, AUTH_CALLBACK_PATH) async def _get_authorize_url(self): oauth = self._generate_oauth() return oauth.get_authorize_url() class AmbiclimateAuthCallbackView(HomeAssistantView): """Ambiclimate Authorization Callback View.""" requires_auth = False url = AUTH_CALLBACK_PATH name = AUTH_CALLBACK_NAME async def get(self, request): """Receive authorization token.""" code = request.query.get('code') if code is None: return "No code" hass = request.app['hass'] hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={'source': 'code'}, data=code, )) return "OK!"