Skip to content

Commit

Permalink
fix(entity): retry all calls while refreshing the access token if exp…
Browse files Browse the repository at this point in the history
…ired (#164)
  • Loading branch information
palazzem authored May 20, 2024
1 parent 2d71539 commit 1cb3dae
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
8 changes: 7 additions & 1 deletion custom_components/econnect_metronet/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import CONF_SYSTEM_NAME, DOMAIN, KEY_COORDINATOR, KEY_DEVICE
from .decorators import set_device_state
from .decorators import retry_refresh_token, set_device_state
from .helpers import generate_entity_id

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -56,6 +56,7 @@ def __init__(self, unique_id, config, device, coordinator):
"""Construct."""
super().__init__(coordinator)
self.entity_id = generate_entity_id(config)
self._config = config
self._unique_id = unique_id
self._name = f"Alarm Panel {config.data.get(CONF_SYSTEM_NAME) or config.data.get(CONF_USERNAME)}"
self._device = device
Expand Down Expand Up @@ -91,16 +92,19 @@ def supported_features(self):
return AlarmFeatures.ARM_HOME | AlarmFeatures.ARM_AWAY | AlarmFeatures.ARM_NIGHT | AlarmFeatures.ARM_VACATION

@set_device_state(STATE_ALARM_DISARMED, STATE_ALARM_DISARMING)
@retry_refresh_token
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
await self.hass.async_add_executor_job(self._device.disarm, code)

@set_device_state(STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING)
@retry_refresh_token
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_away)

@set_device_state(STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMING)
@retry_refresh_token
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._device._sectors_home:
Expand All @@ -110,6 +114,7 @@ async def async_alarm_arm_home(self, code=None):
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_home)

@set_device_state(STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMING)
@retry_refresh_token
async def async_alarm_arm_night(self, code=None):
"""Send arm night command."""
if not self._device._sectors_night:
Expand All @@ -119,6 +124,7 @@ async def async_alarm_arm_night(self, code=None):
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_night)

@set_device_state(STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMING)
@retry_refresh_token
async def async_alarm_arm_vacation(self, code=None):
"""Send arm vacation command."""
if not self._device._sectors_vacation:
Expand Down
49 changes: 48 additions & 1 deletion custom_components/econnect_metronet/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import functools
import logging

from elmo.api.exceptions import CodeError, LockError
from elmo.api.exceptions import CodeError, InvalidToken, LockError
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME

from .const import DOMAIN, KEY_DEVICE

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -34,10 +37,54 @@ async def func_wrapper(*args, **kwargs):
)
except CodeError:
_LOGGER.warning("Inserted code is not correct. Retry.")
except Exception as err:
# All other exceptions are unexpected errors that must revert the state
_LOGGER.error(f"Device | Error during operation '{func.__name__}': {err}")
# Reverting the state in case of any error
self._device.state = previous_state
self.async_write_ha_state()

return func_wrapper

return decorator


def retry_refresh_token(func):
@functools.wraps(func)
async def wrapper(self, *args, **kwargs):
attempts = 0
while attempts < 2:
try:
return await func(self, *args, **kwargs)
except InvalidToken as err:
_LOGGER.debug(f"Device | Invalid access token: {err}")
if attempts < 1:
username = self._config.data[CONF_USERNAME]
password = self._config.data[CONF_PASSWORD]
await self.hass.async_add_executor_job(self._device.connect, username, password)
_LOGGER.debug("Device | Access token has been refreshed")
attempts += 1

return wrapper


def retry_refresh_token_service(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
attempts = 0
while attempts < 2:
try:
return await func(*args, **kwargs)
except InvalidToken as err:
_LOGGER.debug(f"Device | Invalid access token: {err}")
if attempts < 1:
hass, config_id, _ = args
config = hass.config_entries.async_entries(DOMAIN)[0]
device = hass.data[DOMAIN][config_id][KEY_DEVICE]
username = config.data[CONF_USERNAME]
password = config.data[CONF_PASSWORD]
await hass.async_add_executor_job(device.connect, username, password)
_LOGGER.debug("Device | Access token has been refreshed")
attempts += 1

return wrapper
3 changes: 3 additions & 0 deletions custom_components/econnect_metronet/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from homeassistant.core import HomeAssistant, ServiceCall

from .const import DOMAIN, KEY_DEVICE
from .decorators import retry_refresh_token_service

_LOGGER = logging.getLogger(__name__)


@retry_refresh_token_service
async def arm_sectors(hass: HomeAssistant, config_id: str, call: ServiceCall):
_LOGGER.debug(f"Service | Triggered action {call.service}")
device = hass.data[DOMAIN][config_id][KEY_DEVICE]
Expand All @@ -16,6 +18,7 @@ async def arm_sectors(hass: HomeAssistant, config_id: str, call: ServiceCall):
await hass.async_add_executor_job(device.arm, code, sectors)


@retry_refresh_token_service
async def disarm_sectors(hass: HomeAssistant, config_id: str, call: ServiceCall):
_LOGGER.debug(f"Service | Triggered action {call.service}")
device = hass.data[DOMAIN][config_id][KEY_DEVICE]
Expand Down

0 comments on commit 1cb3dae

Please sign in to comment.