144 lines
4.7 KiB
Python
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()
|