mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Convert august to async (#32586)
* Convert august to async Async io was added to py-august 0.24 * Fix lint
This commit is contained in:
parent
09512e9f8b
commit
3318e65948
@ -3,9 +3,9 @@ import asyncio
|
|||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from august.api import AugustApiHTTPError
|
from aiohttp import ClientError
|
||||||
from august.authenticator import ValidationResult
|
from august.authenticator import ValidationResult
|
||||||
from requests import RequestException
|
from august.exceptions import AugustApiAIOHTTPError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
@ -66,8 +66,8 @@ async def async_request_validation(hass, config_entry, august_gateway):
|
|||||||
|
|
||||||
async def async_august_configuration_validation_callback(data):
|
async def async_august_configuration_validation_callback(data):
|
||||||
code = data.get(VERIFICATION_CODE_KEY)
|
code = data.get(VERIFICATION_CODE_KEY)
|
||||||
result = await hass.async_add_executor_job(
|
result = await august_gateway.authenticator.async_validate_verification_code(
|
||||||
august_gateway.authenticator.validate_verification_code, code
|
code
|
||||||
)
|
)
|
||||||
|
|
||||||
if result == ValidationResult.INVALID_VERIFICATION_CODE:
|
if result == ValidationResult.INVALID_VERIFICATION_CODE:
|
||||||
@ -81,9 +81,7 @@ async def async_request_validation(hass, config_entry, august_gateway):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if TWO_FA_REVALIDATE not in hass.data[DOMAIN][entry_id]:
|
if TWO_FA_REVALIDATE not in hass.data[DOMAIN][entry_id]:
|
||||||
await hass.async_add_executor_job(
|
await august_gateway.authenticator.async_send_verification_code()
|
||||||
august_gateway.authenticator.send_verification_code
|
|
||||||
)
|
|
||||||
|
|
||||||
entry_data = config_entry.data
|
entry_data = config_entry.data
|
||||||
login_method = entry_data.get(CONF_LOGIN_METHOD)
|
login_method = entry_data.get(CONF_LOGIN_METHOD)
|
||||||
@ -109,7 +107,7 @@ async def async_setup_august(hass, config_entry, august_gateway):
|
|||||||
hass.data[DOMAIN].setdefault(entry_id, {})
|
hass.data[DOMAIN].setdefault(entry_id, {})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
august_gateway.authenticate()
|
await august_gateway.async_authenticate()
|
||||||
except RequireValidation:
|
except RequireValidation:
|
||||||
await async_request_validation(hass, config_entry, august_gateway)
|
await async_request_validation(hass, config_entry, august_gateway)
|
||||||
return False
|
return False
|
||||||
@ -125,10 +123,9 @@ async def async_setup_august(hass, config_entry, august_gateway):
|
|||||||
hass.data[DOMAIN][entry_id].pop(TWO_FA_REVALIDATE)
|
hass.data[DOMAIN][entry_id].pop(TWO_FA_REVALIDATE)
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.data[DOMAIN][entry_id][DATA_AUGUST] = await hass.async_add_executor_job(
|
hass.data[DOMAIN][entry_id][DATA_AUGUST] = AugustData(hass, august_gateway)
|
||||||
AugustData, hass, august_gateway
|
|
||||||
)
|
await hass.data[DOMAIN][entry_id][DATA_AUGUST].async_setup()
|
||||||
await hass.data[DOMAIN][entry_id][DATA_AUGUST].activity_stream.async_setup()
|
|
||||||
|
|
||||||
for component in AUGUST_COMPONENTS:
|
for component in AUGUST_COMPONENTS:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
@ -167,7 +164,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
"""Set up August from a config entry."""
|
"""Set up August from a config entry."""
|
||||||
|
|
||||||
august_gateway = AugustGateway(hass)
|
august_gateway = AugustGateway(hass)
|
||||||
august_gateway.async_setup(entry.data)
|
await august_gateway.async_setup(entry.data)
|
||||||
|
|
||||||
return await async_setup_august(hass, entry, august_gateway)
|
return await async_setup_august(hass, entry, august_gateway)
|
||||||
|
|
||||||
@ -197,11 +194,22 @@ class AugustData(AugustSubscriberMixin):
|
|||||||
super().__init__(hass, MIN_TIME_BETWEEN_DETAIL_UPDATES)
|
super().__init__(hass, MIN_TIME_BETWEEN_DETAIL_UPDATES)
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._august_gateway = august_gateway
|
self._august_gateway = august_gateway
|
||||||
|
self.activity_stream = None
|
||||||
self._api = august_gateway.api
|
self._api = august_gateway.api
|
||||||
self._device_detail_by_id = {}
|
self._device_detail_by_id = {}
|
||||||
|
self._doorbells_by_id = {}
|
||||||
|
self._locks_by_id = {}
|
||||||
|
self._house_ids = set()
|
||||||
|
|
||||||
locks = self._api.get_operable_locks(self._august_gateway.access_token) or []
|
async def async_setup(self):
|
||||||
doorbells = self._api.get_doorbells(self._august_gateway.access_token) or []
|
"""Async setup of august device data and activities."""
|
||||||
|
locks = (
|
||||||
|
await self._api.async_get_operable_locks(self._august_gateway.access_token)
|
||||||
|
or []
|
||||||
|
)
|
||||||
|
doorbells = (
|
||||||
|
await self._api.async_get_doorbells(self._august_gateway.access_token) or []
|
||||||
|
)
|
||||||
|
|
||||||
self._doorbells_by_id = dict((device.device_id, device) for device in doorbells)
|
self._doorbells_by_id = dict((device.device_id, device) for device in doorbells)
|
||||||
self._locks_by_id = dict((device.device_id, device) for device in locks)
|
self._locks_by_id = dict((device.device_id, device) for device in locks)
|
||||||
@ -209,7 +217,7 @@ class AugustData(AugustSubscriberMixin):
|
|||||||
device.house_id for device in itertools.chain(locks, doorbells)
|
device.house_id for device in itertools.chain(locks, doorbells)
|
||||||
)
|
)
|
||||||
|
|
||||||
self._refresh_device_detail_by_ids(
|
await self._async_refresh_device_detail_by_ids(
|
||||||
[device.device_id for device in itertools.chain(locks, doorbells)]
|
[device.device_id for device in itertools.chain(locks, doorbells)]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -221,8 +229,9 @@ class AugustData(AugustSubscriberMixin):
|
|||||||
self._remove_inoperative_doorbells()
|
self._remove_inoperative_doorbells()
|
||||||
|
|
||||||
self.activity_stream = ActivityStream(
|
self.activity_stream = ActivityStream(
|
||||||
hass, self._api, self._august_gateway, self._house_ids
|
self._hass, self._api, self._august_gateway, self._house_ids
|
||||||
)
|
)
|
||||||
|
await self.activity_stream.async_setup()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def doorbells(self):
|
def doorbells(self):
|
||||||
@ -238,25 +247,26 @@ class AugustData(AugustSubscriberMixin):
|
|||||||
"""Return the py-august LockDetail or DoorbellDetail object for a device."""
|
"""Return the py-august LockDetail or DoorbellDetail object for a device."""
|
||||||
return self._device_detail_by_id[device_id]
|
return self._device_detail_by_id[device_id]
|
||||||
|
|
||||||
def _refresh(self, time):
|
async def _async_refresh(self, time):
|
||||||
self._refresh_device_detail_by_ids(self._subscriptions.keys())
|
await self._async_refresh_device_detail_by_ids(self._subscriptions.keys())
|
||||||
|
|
||||||
def _refresh_device_detail_by_ids(self, device_ids_list):
|
async def _async_refresh_device_detail_by_ids(self, device_ids_list):
|
||||||
for device_id in device_ids_list:
|
for device_id in device_ids_list:
|
||||||
if device_id in self._locks_by_id:
|
if device_id in self._locks_by_id:
|
||||||
self._update_device_detail(
|
await self._async_update_device_detail(
|
||||||
self._locks_by_id[device_id], self._api.get_lock_detail
|
self._locks_by_id[device_id], self._api.async_get_lock_detail
|
||||||
)
|
)
|
||||||
elif device_id in self._doorbells_by_id:
|
elif device_id in self._doorbells_by_id:
|
||||||
self._update_device_detail(
|
await self._async_update_device_detail(
|
||||||
self._doorbells_by_id[device_id], self._api.get_doorbell_detail
|
self._doorbells_by_id[device_id],
|
||||||
|
self._api.async_get_doorbell_detail,
|
||||||
)
|
)
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"signal_device_id_update (from detail updates): %s", device_id,
|
"async_signal_device_id_update (from detail updates): %s", device_id,
|
||||||
)
|
)
|
||||||
self.signal_device_id_update(device_id)
|
self.async_signal_device_id_update(device_id)
|
||||||
|
|
||||||
def _update_device_detail(self, device, api_call):
|
async def _async_update_device_detail(self, device, api_call):
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Started retrieving detail for %s (%s)",
|
"Started retrieving detail for %s (%s)",
|
||||||
device.device_name,
|
device.device_name,
|
||||||
@ -264,10 +274,10 @@ class AugustData(AugustSubscriberMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._device_detail_by_id[device.device_id] = api_call(
|
self._device_detail_by_id[device.device_id] = await api_call(
|
||||||
self._august_gateway.access_token, device.device_id
|
self._august_gateway.access_token, device.device_id
|
||||||
)
|
)
|
||||||
except RequestException as ex:
|
except ClientError as ex:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Request error trying to retrieve %s details for %s. %s",
|
"Request error trying to retrieve %s details for %s. %s",
|
||||||
device.device_id,
|
device.device_id,
|
||||||
@ -287,30 +297,32 @@ class AugustData(AugustSubscriberMixin):
|
|||||||
if self._doorbells_by_id.get(device_id):
|
if self._doorbells_by_id.get(device_id):
|
||||||
return self._doorbells_by_id[device_id].device_name
|
return self._doorbells_by_id[device_id].device_name
|
||||||
|
|
||||||
def lock(self, device_id):
|
async def async_lock(self, device_id):
|
||||||
"""Lock the device."""
|
"""Lock the device."""
|
||||||
return self._call_api_op_requires_bridge(
|
return await self._async_call_api_op_requires_bridge(
|
||||||
device_id,
|
device_id,
|
||||||
self._api.lock_return_activities,
|
self._api.async_lock_return_activities,
|
||||||
self._august_gateway.access_token,
|
self._august_gateway.access_token,
|
||||||
device_id,
|
device_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
def unlock(self, device_id):
|
async def async_unlock(self, device_id):
|
||||||
"""Unlock the device."""
|
"""Unlock the device."""
|
||||||
return self._call_api_op_requires_bridge(
|
return await self._async_call_api_op_requires_bridge(
|
||||||
device_id,
|
device_id,
|
||||||
self._api.unlock_return_activities,
|
self._api.async_unlock_return_activities,
|
||||||
self._august_gateway.access_token,
|
self._august_gateway.access_token,
|
||||||
device_id,
|
device_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _call_api_op_requires_bridge(self, device_id, func, *args, **kwargs):
|
async def _async_call_api_op_requires_bridge(
|
||||||
|
self, device_id, func, *args, **kwargs
|
||||||
|
):
|
||||||
"""Call an API that requires the bridge to be online and will change the device state."""
|
"""Call an API that requires the bridge to be online and will change the device state."""
|
||||||
ret = None
|
ret = None
|
||||||
try:
|
try:
|
||||||
ret = func(*args, **kwargs)
|
ret = await func(*args, **kwargs)
|
||||||
except AugustApiHTTPError as err:
|
except AugustApiAIOHTTPError as err:
|
||||||
device_name = self._get_device_name(device_id)
|
device_name = self._get_device_name(device_id)
|
||||||
if device_name is None:
|
if device_name is None:
|
||||||
device_name = f"DeviceID: {device_id}"
|
device_name = f"DeviceID: {device_id}"
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
"""Consume the august activity stream."""
|
"""Consume the august activity stream."""
|
||||||
from functools import partial
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from requests import RequestException
|
from aiohttp import ClientError
|
||||||
|
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ class ActivityStream(AugustSubscriberMixin):
|
|||||||
|
|
||||||
async def async_setup(self):
|
async def async_setup(self):
|
||||||
"""Token refresh check and catch up the activity stream."""
|
"""Token refresh check and catch up the activity stream."""
|
||||||
await self._refresh(utcnow)
|
await self._async_refresh(utcnow)
|
||||||
|
|
||||||
def get_latest_device_activity(self, device_id, activity_types):
|
def get_latest_device_activity(self, device_id, activity_types):
|
||||||
"""Return latest activity that is one of the acitivty_types."""
|
"""Return latest activity that is one of the acitivty_types."""
|
||||||
@ -53,7 +52,7 @@ class ActivityStream(AugustSubscriberMixin):
|
|||||||
|
|
||||||
return latest_activity
|
return latest_activity
|
||||||
|
|
||||||
async def _refresh(self, time):
|
async def _async_refresh(self, time):
|
||||||
"""Update the activity stream from August."""
|
"""Update the activity stream from August."""
|
||||||
|
|
||||||
# This is the only place we refresh the api token
|
# This is the only place we refresh the api token
|
||||||
@ -72,15 +71,10 @@ class ActivityStream(AugustSubscriberMixin):
|
|||||||
for house_id in self._house_ids:
|
for house_id in self._house_ids:
|
||||||
_LOGGER.debug("Updating device activity for house id %s", house_id)
|
_LOGGER.debug("Updating device activity for house id %s", house_id)
|
||||||
try:
|
try:
|
||||||
activities = await self._hass.async_add_executor_job(
|
activities = await self._api.async_get_house_activities(
|
||||||
partial(
|
self._august_gateway.access_token, house_id, limit=limit
|
||||||
self._api.get_house_activities,
|
|
||||||
self._august_gateway.access_token,
|
|
||||||
house_id,
|
|
||||||
limit=limit,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
except RequestException as ex:
|
except ClientError as ex:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Request error trying to retrieve activity for house id %s: %s",
|
"Request error trying to retrieve activity for house id %s: %s",
|
||||||
house_id,
|
house_id,
|
||||||
|
@ -5,6 +5,7 @@ from august.util import update_doorbell_image_from_activity
|
|||||||
|
|
||||||
from homeassistant.components.camera import Camera
|
from homeassistant.components.camera import Camera
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
|
||||||
from .const import DATA_AUGUST, DEFAULT_NAME, DEFAULT_TIMEOUT, DOMAIN
|
from .const import DATA_AUGUST, DEFAULT_NAME, DEFAULT_TIMEOUT, DOMAIN
|
||||||
from .entity import AugustEntityMixin
|
from .entity import AugustEntityMixin
|
||||||
@ -74,15 +75,11 @@ class AugustCamera(AugustEntityMixin, Camera):
|
|||||||
|
|
||||||
if self._image_url is not self._detail.image_url:
|
if self._image_url is not self._detail.image_url:
|
||||||
self._image_url = self._detail.image_url
|
self._image_url = self._detail.image_url
|
||||||
self._image_content = await self.hass.async_add_executor_job(
|
self._image_content = await self._detail.async_get_doorbell_image(
|
||||||
self._camera_image
|
aiohttp_client.async_get_clientsession(self.hass), timeout=self._timeout
|
||||||
)
|
)
|
||||||
return self._image_content
|
return self._image_content
|
||||||
|
|
||||||
def _camera_image(self):
|
|
||||||
"""Return bytes of camera image."""
|
|
||||||
return self._detail.get_doorbell_image(timeout=self._timeout)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Get the unique id of the camera."""
|
"""Get the unique id of the camera."""
|
||||||
|
@ -42,15 +42,15 @@ async def async_validate_input(
|
|||||||
code = data.get(VERIFICATION_CODE_KEY)
|
code = data.get(VERIFICATION_CODE_KEY)
|
||||||
|
|
||||||
if code is not None:
|
if code is not None:
|
||||||
result = await hass.async_add_executor_job(
|
result = await august_gateway.authenticator.async_validate_verification_code(
|
||||||
august_gateway.authenticator.validate_verification_code, code
|
code
|
||||||
)
|
)
|
||||||
_LOGGER.debug("Verification code validation: %s", result)
|
_LOGGER.debug("Verification code validation: %s", result)
|
||||||
if result != ValidationResult.VALIDATED:
|
if result != ValidationResult.VALIDATED:
|
||||||
raise RequireValidation
|
raise RequireValidation
|
||||||
|
|
||||||
try:
|
try:
|
||||||
august_gateway.authenticate()
|
await august_gateway.async_authenticate()
|
||||||
except RequireValidation:
|
except RequireValidation:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Requesting new verification code for %s via %s",
|
"Requesting new verification code for %s via %s",
|
||||||
@ -58,9 +58,7 @@ async def async_validate_input(
|
|||||||
data.get(CONF_LOGIN_METHOD),
|
data.get(CONF_LOGIN_METHOD),
|
||||||
)
|
)
|
||||||
if code is None:
|
if code is None:
|
||||||
await hass.async_add_executor_job(
|
await august_gateway.authenticator.async_send_verification_code()
|
||||||
august_gateway.authenticator.send_verification_code
|
|
||||||
)
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -87,7 +85,7 @@ class AugustConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self._august_gateway = AugustGateway(self.hass)
|
self._august_gateway = AugustGateway(self.hass)
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._august_gateway.async_setup(user_input)
|
await self._august_gateway.async_setup(user_input)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info = await async_validate_input(
|
info = await async_validate_input(
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from august.api import Api
|
from aiohttp import ClientError
|
||||||
from august.authenticator import AuthenticationState, Authenticator
|
from august.api_async import ApiAsync
|
||||||
from requests import RequestException, Session
|
from august.authenticator_async import AuthenticationState, AuthenticatorAsync
|
||||||
|
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
|
||||||
from homeassistant.core import callback
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_ACCESS_TOKEN_CACHE_FILE,
|
CONF_ACCESS_TOKEN_CACHE_FILE,
|
||||||
@ -27,7 +27,7 @@ class AugustGateway:
|
|||||||
|
|
||||||
def __init__(self, hass):
|
def __init__(self, hass):
|
||||||
"""Init the connection."""
|
"""Init the connection."""
|
||||||
self._api_http_session = Session()
|
self._aiohttp_session = aiohttp_client.async_get_clientsession(hass)
|
||||||
self._token_refresh_lock = asyncio.Lock()
|
self._token_refresh_lock = asyncio.Lock()
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._config = None
|
self._config = None
|
||||||
@ -66,8 +66,7 @@ class AugustGateway:
|
|||||||
CONF_ACCESS_TOKEN_CACHE_FILE: self._config[CONF_ACCESS_TOKEN_CACHE_FILE],
|
CONF_ACCESS_TOKEN_CACHE_FILE: self._config[CONF_ACCESS_TOKEN_CACHE_FILE],
|
||||||
}
|
}
|
||||||
|
|
||||||
@callback
|
async def async_setup(self, conf):
|
||||||
def async_setup(self, conf):
|
|
||||||
"""Create the api and authenticator objects."""
|
"""Create the api and authenticator objects."""
|
||||||
if conf.get(VERIFICATION_CODE_KEY):
|
if conf.get(VERIFICATION_CODE_KEY):
|
||||||
return
|
return
|
||||||
@ -77,11 +76,11 @@ class AugustGateway:
|
|||||||
] = f".{conf[CONF_USERNAME]}{DEFAULT_AUGUST_CONFIG_FILE}"
|
] = f".{conf[CONF_USERNAME]}{DEFAULT_AUGUST_CONFIG_FILE}"
|
||||||
self._config = conf
|
self._config = conf
|
||||||
|
|
||||||
self._api = Api(
|
self._api = ApiAsync(
|
||||||
timeout=self._config.get(CONF_TIMEOUT), http_session=self._api_http_session,
|
self._aiohttp_session, timeout=self._config.get(CONF_TIMEOUT)
|
||||||
)
|
)
|
||||||
|
|
||||||
self._authenticator = Authenticator(
|
self._authenticator = AuthenticatorAsync(
|
||||||
self._api,
|
self._api,
|
||||||
self._config[CONF_LOGIN_METHOD],
|
self._config[CONF_LOGIN_METHOD],
|
||||||
self._config[CONF_USERNAME],
|
self._config[CONF_USERNAME],
|
||||||
@ -92,12 +91,14 @@ class AugustGateway:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def authenticate(self):
|
await self._authenticator.async_setup_authentication()
|
||||||
|
|
||||||
|
async def async_authenticate(self):
|
||||||
"""Authenticate with the details provided to setup."""
|
"""Authenticate with the details provided to setup."""
|
||||||
self._authentication = None
|
self._authentication = None
|
||||||
try:
|
try:
|
||||||
self._authentication = self.authenticator.authenticate()
|
self._authentication = await self.authenticator.async_authenticate()
|
||||||
except RequestException as ex:
|
except ClientError as ex:
|
||||||
_LOGGER.error("Unable to connect to August service: %s", str(ex))
|
_LOGGER.error("Unable to connect to August service: %s", str(ex))
|
||||||
raise CannotConnect
|
raise CannotConnect
|
||||||
|
|
||||||
@ -119,25 +120,12 @@ class AugustGateway:
|
|||||||
"""Refresh the august access token if needed."""
|
"""Refresh the august access token if needed."""
|
||||||
if self.authenticator.should_refresh():
|
if self.authenticator.should_refresh():
|
||||||
async with self._token_refresh_lock:
|
async with self._token_refresh_lock:
|
||||||
await self._hass.async_add_executor_job(self._refresh_access_token)
|
refreshed_authentication = await self.authenticator.async_refresh_access_token(
|
||||||
|
force=False
|
||||||
def _refresh_access_token(self):
|
)
|
||||||
refreshed_authentication = self.authenticator.refresh_access_token(force=False)
|
_LOGGER.info(
|
||||||
_LOGGER.info(
|
"Refreshed august access token. The old token expired at %s, and the new token expires at %s",
|
||||||
"Refreshed august access token. The old token expired at %s, and the new token expires at %s",
|
self.authentication.access_token_expires,
|
||||||
self.authentication.access_token_expires,
|
refreshed_authentication.access_token_expires,
|
||||||
refreshed_authentication.access_token_expires,
|
)
|
||||||
)
|
self._authentication = refreshed_authentication
|
||||||
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()
|
|
||||||
|
@ -43,16 +43,14 @@ class AugustLock(AugustEntityMixin, RestoreEntity, LockDevice):
|
|||||||
|
|
||||||
async def async_lock(self, **kwargs):
|
async def async_lock(self, **kwargs):
|
||||||
"""Lock the device."""
|
"""Lock the device."""
|
||||||
await self._call_lock_operation(self._data.lock)
|
await self._call_lock_operation(self._data.async_lock)
|
||||||
|
|
||||||
async def async_unlock(self, **kwargs):
|
async def async_unlock(self, **kwargs):
|
||||||
"""Unlock the device."""
|
"""Unlock the device."""
|
||||||
await self._call_lock_operation(self._data.unlock)
|
await self._call_lock_operation(self._data.async_unlock)
|
||||||
|
|
||||||
async def _call_lock_operation(self, lock_operation):
|
async def _call_lock_operation(self, lock_operation):
|
||||||
activities = await self.hass.async_add_executor_job(
|
activities = await lock_operation(self._device_id)
|
||||||
lock_operation, self._device_id
|
|
||||||
)
|
|
||||||
for lock_activity in activities:
|
for lock_activity in activities:
|
||||||
update_lock_detail_from_activity(self._detail, lock_activity)
|
update_lock_detail_from_activity(self._detail, lock_activity)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "August",
|
"name": "August",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/august",
|
"documentation": "https://www.home-assistant.io/integrations/august",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"py-august==0.22.0"
|
"py-august==0.24.0"
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"configurator"
|
"configurator"
|
||||||
|
@ -21,7 +21,7 @@ class AugustSubscriberMixin:
|
|||||||
"""Add an callback subscriber."""
|
"""Add an callback subscriber."""
|
||||||
if not self._subscriptions:
|
if not self._subscriptions:
|
||||||
self._unsub_interval = async_track_time_interval(
|
self._unsub_interval = async_track_time_interval(
|
||||||
self._hass, self._refresh, self._update_interval
|
self._hass, self._async_refresh, self._update_interval
|
||||||
)
|
)
|
||||||
self._subscriptions.setdefault(device_id, []).append(update_callback)
|
self._subscriptions.setdefault(device_id, []).append(update_callback)
|
||||||
|
|
||||||
@ -43,11 +43,3 @@ class AugustSubscriberMixin:
|
|||||||
|
|
||||||
for update_callback in self._subscriptions[device_id]:
|
for update_callback in self._subscriptions[device_id]:
|
||||||
update_callback()
|
update_callback()
|
||||||
|
|
||||||
def signal_device_id_update(self, device_id):
|
|
||||||
"""Call the callbacks for a device_id."""
|
|
||||||
if not self._subscriptions.get(device_id):
|
|
||||||
return
|
|
||||||
|
|
||||||
for update_callback in self._subscriptions[device_id]:
|
|
||||||
self._hass.loop.call_soon_threadsafe(update_callback)
|
|
||||||
|
@ -1085,7 +1085,7 @@ pushover_complete==1.1.1
|
|||||||
pwmled==1.5.0
|
pwmled==1.5.0
|
||||||
|
|
||||||
# homeassistant.components.august
|
# homeassistant.components.august
|
||||||
py-august==0.22.0
|
py-august==0.24.0
|
||||||
|
|
||||||
# homeassistant.components.canary
|
# homeassistant.components.canary
|
||||||
py-canary==0.5.0
|
py-canary==0.5.0
|
||||||
|
@ -394,7 +394,7 @@ pure-python-adb==0.2.2.dev0
|
|||||||
pushbullet.py==0.11.0
|
pushbullet.py==0.11.0
|
||||||
|
|
||||||
# homeassistant.components.august
|
# homeassistant.components.august
|
||||||
py-august==0.22.0
|
py-august==0.24.0
|
||||||
|
|
||||||
# homeassistant.components.canary
|
# homeassistant.components.canary
|
||||||
py-canary==0.5.0
|
py-canary==0.5.0
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from unittest.mock import MagicMock, PropertyMock
|
|
||||||
|
|
||||||
from asynctest import mock
|
from asynctest import mock
|
||||||
|
from asynctest.mock import CoroutineMock, MagicMock, PropertyMock
|
||||||
from august.activity import (
|
from august.activity import (
|
||||||
ACTIVITY_ACTIONS_DOOR_OPERATION,
|
ACTIVITY_ACTIONS_DOOR_OPERATION,
|
||||||
ACTIVITY_ACTIONS_DOORBELL_DING,
|
ACTIVITY_ACTIONS_DOORBELL_DING,
|
||||||
@ -43,8 +43,10 @@ def _mock_get_config():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("homeassistant.components.august.gateway.Api")
|
@mock.patch("homeassistant.components.august.gateway.ApiAsync")
|
||||||
@mock.patch("homeassistant.components.august.gateway.Authenticator.authenticate")
|
@mock.patch(
|
||||||
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_authenticate"
|
||||||
|
)
|
||||||
async def _mock_setup_august(hass, api_instance, authenticate_mock, api_mock):
|
async def _mock_setup_august(hass, api_instance, authenticate_mock, api_mock):
|
||||||
"""Set up august integration."""
|
"""Set up august integration."""
|
||||||
authenticate_mock.side_effect = MagicMock(
|
authenticate_mock.side_effect = MagicMock(
|
||||||
@ -147,37 +149,40 @@ async def _mock_setup_august_with_api_side_effects(hass, api_call_side_effects):
|
|||||||
api_instance = MagicMock(name="Api")
|
api_instance = MagicMock(name="Api")
|
||||||
|
|
||||||
if api_call_side_effects["get_lock_detail"]:
|
if api_call_side_effects["get_lock_detail"]:
|
||||||
api_instance.get_lock_detail.side_effect = api_call_side_effects[
|
type(api_instance).async_get_lock_detail = CoroutineMock(
|
||||||
"get_lock_detail"
|
side_effect=api_call_side_effects["get_lock_detail"]
|
||||||
]
|
)
|
||||||
|
|
||||||
if api_call_side_effects["get_operable_locks"]:
|
if api_call_side_effects["get_operable_locks"]:
|
||||||
api_instance.get_operable_locks.side_effect = api_call_side_effects[
|
type(api_instance).async_get_operable_locks = CoroutineMock(
|
||||||
"get_operable_locks"
|
side_effect=api_call_side_effects["get_operable_locks"]
|
||||||
]
|
)
|
||||||
|
|
||||||
if api_call_side_effects["get_doorbells"]:
|
if api_call_side_effects["get_doorbells"]:
|
||||||
api_instance.get_doorbells.side_effect = api_call_side_effects["get_doorbells"]
|
type(api_instance).async_get_doorbells = CoroutineMock(
|
||||||
|
side_effect=api_call_side_effects["get_doorbells"]
|
||||||
|
)
|
||||||
|
|
||||||
if api_call_side_effects["get_doorbell_detail"]:
|
if api_call_side_effects["get_doorbell_detail"]:
|
||||||
api_instance.get_doorbell_detail.side_effect = api_call_side_effects[
|
type(api_instance).async_get_doorbell_detail = CoroutineMock(
|
||||||
"get_doorbell_detail"
|
side_effect=api_call_side_effects["get_doorbell_detail"]
|
||||||
]
|
)
|
||||||
|
|
||||||
if api_call_side_effects["get_house_activities"]:
|
if api_call_side_effects["get_house_activities"]:
|
||||||
api_instance.get_house_activities.side_effect = api_call_side_effects[
|
type(api_instance).async_get_house_activities = CoroutineMock(
|
||||||
"get_house_activities"
|
side_effect=api_call_side_effects["get_house_activities"]
|
||||||
]
|
)
|
||||||
|
|
||||||
if api_call_side_effects["lock_return_activities"]:
|
if api_call_side_effects["lock_return_activities"]:
|
||||||
api_instance.lock_return_activities.side_effect = api_call_side_effects[
|
type(api_instance).async_lock_return_activities = CoroutineMock(
|
||||||
"lock_return_activities"
|
side_effect=api_call_side_effects["lock_return_activities"]
|
||||||
]
|
)
|
||||||
|
|
||||||
if api_call_side_effects["unlock_return_activities"]:
|
if api_call_side_effects["unlock_return_activities"]:
|
||||||
api_instance.unlock_return_activities.side_effect = api_call_side_effects[
|
type(api_instance).async_unlock_return_activities = CoroutineMock(
|
||||||
"unlock_return_activities"
|
side_effect=api_call_side_effects["unlock_return_activities"]
|
||||||
]
|
)
|
||||||
|
|
||||||
return await _mock_setup_august(hass, api_instance)
|
return await _mock_setup_august(hass, api_instance)
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ async def test_create_doorbell(hass, aiohttp_client):
|
|||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
||||||
|
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
doorbell_one, "get_doorbell_image", create=False, return_value="image"
|
doorbell_one, "async_get_doorbell_image", create=False, return_value="image"
|
||||||
):
|
):
|
||||||
await _create_august_with_devices(hass, [doorbell_one])
|
await _create_august_with_devices(hass, [doorbell_one])
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ async def test_form(hass):
|
|||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.august.async_setup", return_value=True
|
"homeassistant.components.august.async_setup", return_value=True
|
||||||
@ -66,7 +66,7 @@ async def test_form_invalid_auth(hass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
side_effect=InvalidAuth,
|
side_effect=InvalidAuth,
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
@ -89,7 +89,7 @@ async def test_form_cannot_connect(hass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
side_effect=CannotConnect,
|
side_effect=CannotConnect,
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
@ -112,10 +112,10 @@ async def test_form_needs_validate(hass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
side_effect=RequireValidation,
|
side_effect=RequireValidation,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.august.gateway.Authenticator.send_verification_code",
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_send_verification_code",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_send_verification_code:
|
) as mock_send_verification_code:
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
@ -134,13 +134,13 @@ async def test_form_needs_validate(hass):
|
|||||||
|
|
||||||
# Try with the WRONG verification code give us the form back again
|
# Try with the WRONG verification code give us the form back again
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
side_effect=RequireValidation,
|
side_effect=RequireValidation,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.august.gateway.Authenticator.validate_verification_code",
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_validate_verification_code",
|
||||||
return_value=ValidationResult.INVALID_VERIFICATION_CODE,
|
return_value=ValidationResult.INVALID_VERIFICATION_CODE,
|
||||||
) as mock_validate_verification_code, patch(
|
) as mock_validate_verification_code, patch(
|
||||||
"homeassistant.components.august.gateway.Authenticator.send_verification_code",
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_send_verification_code",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_send_verification_code, patch(
|
) as mock_send_verification_code, patch(
|
||||||
"homeassistant.components.august.async_setup", return_value=True
|
"homeassistant.components.august.async_setup", return_value=True
|
||||||
@ -161,13 +161,13 @@ async def test_form_needs_validate(hass):
|
|||||||
|
|
||||||
# Try with the CORRECT verification code and we setup
|
# Try with the CORRECT verification code and we setup
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.august.gateway.Authenticator.validate_verification_code",
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_validate_verification_code",
|
||||||
return_value=ValidationResult.VALIDATED,
|
return_value=ValidationResult.VALIDATED,
|
||||||
) as mock_validate_verification_code, patch(
|
) as mock_validate_verification_code, patch(
|
||||||
"homeassistant.components.august.gateway.Authenticator.send_verification_code",
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_send_verification_code",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_send_verification_code, patch(
|
) as mock_send_verification_code, patch(
|
||||||
"homeassistant.components.august.async_setup", return_value=True
|
"homeassistant.components.august.async_setup", return_value=True
|
||||||
|
@ -14,10 +14,12 @@ async def test_refresh_access_token(hass):
|
|||||||
await _patched_refresh_access_token(hass, "new_token", 5678)
|
await _patched_refresh_access_token(hass, "new_token", 5678)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("homeassistant.components.august.gateway.Authenticator.authenticate")
|
|
||||||
@mock.patch("homeassistant.components.august.gateway.Authenticator.should_refresh")
|
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
"homeassistant.components.august.gateway.Authenticator.refresh_access_token"
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_authenticate"
|
||||||
|
)
|
||||||
|
@mock.patch("homeassistant.components.august.gateway.AuthenticatorAsync.should_refresh")
|
||||||
|
@mock.patch(
|
||||||
|
"homeassistant.components.august.gateway.AuthenticatorAsync.async_refresh_access_token"
|
||||||
)
|
)
|
||||||
async def _patched_refresh_access_token(
|
async def _patched_refresh_access_token(
|
||||||
hass,
|
hass,
|
||||||
@ -32,8 +34,8 @@ async def _patched_refresh_access_token(
|
|||||||
)
|
)
|
||||||
august_gateway = AugustGateway(hass)
|
august_gateway = AugustGateway(hass)
|
||||||
mocked_config = _mock_get_config()
|
mocked_config = _mock_get_config()
|
||||||
august_gateway.async_setup(mocked_config[DOMAIN])
|
await august_gateway.async_setup(mocked_config[DOMAIN])
|
||||||
august_gateway.authenticate()
|
await august_gateway.async_authenticate()
|
||||||
|
|
||||||
should_refresh_mock.return_value = False
|
should_refresh_mock.return_value = False
|
||||||
await august_gateway.async_refresh_access_token_if_needed()
|
await august_gateway.async_refresh_access_token_if_needed()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""The tests for the august platform."""
|
"""The tests for the august platform."""
|
||||||
from asynctest import patch
|
from asynctest import patch
|
||||||
from august.exceptions import AugustApiHTTPError
|
from august.exceptions import AugustApiAIOHTTPError
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
from homeassistant.components.august.const import (
|
from homeassistant.components.august.const import (
|
||||||
@ -38,7 +38,7 @@ async def test_unlock_throws_august_api_http_error(hass):
|
|||||||
mocked_lock_detail = await _mock_operative_august_lock_detail(hass)
|
mocked_lock_detail = await _mock_operative_august_lock_detail(hass)
|
||||||
|
|
||||||
def _unlock_return_activities_side_effect(access_token, device_id):
|
def _unlock_return_activities_side_effect(access_token, device_id):
|
||||||
raise AugustApiHTTPError("This should bubble up as its user consumable")
|
raise AugustApiAIOHTTPError("This should bubble up as its user consumable")
|
||||||
|
|
||||||
await _create_august_with_devices(
|
await _create_august_with_devices(
|
||||||
hass,
|
hass,
|
||||||
@ -64,7 +64,7 @@ async def test_lock_throws_august_api_http_error(hass):
|
|||||||
mocked_lock_detail = await _mock_operative_august_lock_detail(hass)
|
mocked_lock_detail = await _mock_operative_august_lock_detail(hass)
|
||||||
|
|
||||||
def _lock_return_activities_side_effect(access_token, device_id):
|
def _lock_return_activities_side_effect(access_token, device_id):
|
||||||
raise AugustApiHTTPError("This should bubble up as its user consumable")
|
raise AugustApiAIOHTTPError("This should bubble up as its user consumable")
|
||||||
|
|
||||||
await _create_august_with_devices(
|
await _create_august_with_devices(
|
||||||
hass,
|
hass,
|
||||||
@ -124,7 +124,7 @@ async def test_set_up_from_yaml(hass):
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.august.async_setup_august", return_value=True,
|
"homeassistant.components.august.async_setup_august", return_value=True,
|
||||||
) as mock_setup_august, patch(
|
) as mock_setup_august, patch(
|
||||||
"homeassistant.components.august.config_flow.AugustGateway.authenticate",
|
"homeassistant.components.august.config_flow.AugustGateway.async_authenticate",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
):
|
):
|
||||||
mocked_config = _mock_get_config()
|
mocked_config = _mock_get_config()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user