Add Config flow to august (#32133)

* Add battery sensors for August devices

* Additional tests and cleanup in prep for config flow
  and device registry

* pylint

* update name for new style guidelines - https://developers.home-assistant.io/docs/development_guidelines/#use-new-style-string-formatting

* Config Flow for august push

* Update homeassistant/components/august/__init__.py

Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>

* Address review items

* Update tests

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
J. Nick Koston 2020-02-25 08:18:15 -10:00 committed by GitHub
parent 900714a3ee
commit 2925e0617c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1686 additions and 444 deletions

View file

@ -0,0 +1,143 @@
"""Handle August connection setup and authentication."""
import asyncio
import logging
from august.api import Api
from august.authenticator import AuthenticationState, Authenticator
from requests import RequestException, Session
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
from homeassistant.core import callback
from .const import (
CONF_ACCESS_TOKEN_CACHE_FILE,
CONF_INSTALL_ID,
CONF_LOGIN_METHOD,
DEFAULT_AUGUST_CONFIG_FILE,
VERIFICATION_CODE_KEY,
)
from .exceptions import CannotConnect, InvalidAuth, RequireValidation
_LOGGER = logging.getLogger(__name__)
class AugustGateway:
"""Handle the connection to August."""
def __init__(self, hass):
"""Init the connection."""
self._api_http_session = Session()
self._token_refresh_lock = asyncio.Lock()
self._hass = hass
self._config = None
self._api = None
self._authenticator = None
self._authentication = None
@property
def authenticator(self):
"""August authentication object from py-august."""
return self._authenticator
@property
def authentication(self):
"""August authentication object from py-august."""
return self._authentication
@property
def access_token(self):
"""Access token for the api."""
return self._authentication.access_token
@property
def api(self):
"""August api object from py-august."""
return self._api
def config_entry(self):
"""Config entry."""
return {
CONF_LOGIN_METHOD: self._config[CONF_LOGIN_METHOD],
CONF_USERNAME: self._config[CONF_USERNAME],
CONF_PASSWORD: self._config[CONF_PASSWORD],
CONF_INSTALL_ID: self._config.get(CONF_INSTALL_ID),
CONF_TIMEOUT: self._config.get(CONF_TIMEOUT),
CONF_ACCESS_TOKEN_CACHE_FILE: self._config[CONF_ACCESS_TOKEN_CACHE_FILE],
}
@callback
def async_setup(self, conf):
"""Create the api and authenticator objects."""
if conf.get(VERIFICATION_CODE_KEY):
return
if conf.get(CONF_ACCESS_TOKEN_CACHE_FILE) is None:
conf[
CONF_ACCESS_TOKEN_CACHE_FILE
] = f".{conf[CONF_USERNAME]}{DEFAULT_AUGUST_CONFIG_FILE}"
self._config = conf
self._api = Api(
timeout=self._config.get(CONF_TIMEOUT), http_session=self._api_http_session,
)
self._authenticator = Authenticator(
self._api,
self._config[CONF_LOGIN_METHOD],
self._config[CONF_USERNAME],
self._config[CONF_PASSWORD],
install_id=self._config.get(CONF_INSTALL_ID),
access_token_cache_file=self._hass.config.path(
self._config[CONF_ACCESS_TOKEN_CACHE_FILE]
),
)
def authenticate(self):
"""Authenticate with the details provided to setup."""
self._authentication = None
try:
self._authentication = self.authenticator.authenticate()
except RequestException as ex:
_LOGGER.error("Unable to connect to August service: %s", str(ex))
raise CannotConnect
if self._authentication.state == AuthenticationState.BAD_PASSWORD:
raise InvalidAuth
if self._authentication.state == AuthenticationState.REQUIRES_VALIDATION:
raise RequireValidation
if self._authentication.state != AuthenticationState.AUTHENTICATED:
_LOGGER.error(
"Unknown authentication state: %s", self._authentication.state
)
raise InvalidAuth
return self._authentication
async def async_refresh_access_token_if_needed(self):
"""Refresh the august access token if needed."""
if self.authenticator.should_refresh():
async with self._token_refresh_lock:
await self._hass.async_add_executor_job(self._refresh_access_token)
def _refresh_access_token(self):
refreshed_authentication = self.authenticator.refresh_access_token(force=False)
_LOGGER.info(
"Refreshed august access token. The old token expired at %s, and the new token expires at %s",
self.authentication.access_token_expires,
refreshed_authentication.access_token_expires,
)
self._authentication = refreshed_authentication
def _close_http_session(self):
"""Close API sessions used to connect to August."""
if self._api_http_session:
try:
self._api_http_session.close()
except RequestException:
pass
def __del__(self):
"""Close out the http session on destroy."""
self._close_http_session()