core/homeassistant/components/august/gateway.py

144 lines
4.7 KiB
Python

"""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()