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:
parent
900714a3ee
commit
2925e0617c
25 changed files with 1686 additions and 444 deletions
143
homeassistant/components/august/gateway.py
Normal file
143
homeassistant/components/august/gateway.py
Normal 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()
|
Loading…
Add table
Add a link
Reference in a new issue