Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallbox Add Authentication Decorator #102520

Merged
merged 8 commits into from
Nov 8, 2023
46 changes: 32 additions & 14 deletions homeassistant/components/wallbox/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""DataUpdateCoordinator for the wallbox integration."""
from __future__ import annotations

from collections.abc import Callable
from datetime import timedelta
from http import HTTPStatus
import logging
from typing import Any
from typing import Any, Concatenate, ParamSpec, TypeVar

import requests
from wallbox import Wallbox
Expand Down Expand Up @@ -62,6 +63,29 @@
210: ChargerStatus.LOCKED_CAR_CONNECTED,
}

_WallboxCoordinatorT = TypeVar("_WallboxCoordinatorT", bound="WallboxCoordinator")
_P = ParamSpec("_P")


def _require_authentication(
func: Callable[Concatenate[_WallboxCoordinatorT, _P], Any]
) -> Callable[Concatenate[_WallboxCoordinatorT, _P], Any]:
"""Authenticate with decorator using Wallbox API."""

def require_authentication(
self: _WallboxCoordinatorT, *args: _P.args, **kwargs: _P.kwargs
) -> Any:
"""Authenticate using Wallbox API."""
try:
self.authenticate()
return func(self, *args, **kwargs)
except requests.exceptions.HTTPError as wallbox_connection_error:
if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN:
raise ConfigEntryAuthFailed from wallbox_connection_error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we start a reauth flow whenever we fail authentication? If this is raised as part of a service call currently no reauth flow is started. It's only _async_update_data that will automatically handle ConfigEntryAuthFailed and start a reauth flow.

raise ConnectionError from wallbox_connection_error

return require_authentication


class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Wallbox Coordinator class."""
Expand All @@ -78,15 +102,9 @@ def __init__(self, station: str, wallbox: Wallbox, hass: HomeAssistant) -> None:
update_interval=timedelta(seconds=UPDATE_INTERVAL),
)

def _authenticate(self) -> None:
def authenticate(self) -> None:
"""Authenticate using Wallbox API."""
try:
self._wallbox.authenticate()

except requests.exceptions.HTTPError as wallbox_connection_error:
if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN:
raise ConfigEntryAuthFailed from wallbox_connection_error
raise ConnectionError from wallbox_connection_error
self._wallbox.authenticate()

def _validate(self) -> None:
"""Authenticate using Wallbox API."""
Expand All @@ -101,10 +119,10 @@ async def async_validate_input(self) -> None:
"""Get new sensor data for Wallbox component."""
await self.hass.async_add_executor_job(self._validate)

@_require_authentication
def _get_data(self) -> dict[str, Any]:
"""Get new sensor data for Wallbox component."""
try:
self._authenticate()
edenhaus marked this conversation as resolved.
Show resolved Hide resolved
data: dict[str, Any] = self._wallbox.getChargerStatus(self._station)
data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][
CHARGER_MAX_CHARGING_CURRENT_KEY
Expand Down Expand Up @@ -133,10 +151,10 @@ async def _async_update_data(self) -> dict[str, Any]:
"""Get new sensor data for Wallbox component."""
return await self.hass.async_add_executor_job(self._get_data)

@_require_authentication
edenhaus marked this conversation as resolved.
Show resolved Hide resolved
def _set_charging_current(self, charging_current: float) -> None:
"""Set maximum charging current for Wallbox."""
try:
self._authenticate()
self._wallbox.setMaxChargingCurrent(self._station, charging_current)
except requests.exceptions.HTTPError as wallbox_connection_error:
edenhaus marked this conversation as resolved.
Show resolved Hide resolved
if wallbox_connection_error.response.status_code == 403:
Expand All @@ -150,10 +168,10 @@ async def async_set_charging_current(self, charging_current: float) -> None:
)
await self.async_request_refresh()

@_require_authentication
def _set_energy_cost(self, energy_cost: float) -> None:
"""Set energy cost for Wallbox."""
try:
self._authenticate()
self._wallbox.setEnergyCost(self._station, energy_cost)
except requests.exceptions.HTTPError as wallbox_connection_error:
if wallbox_connection_error.response.status_code == 403:
Expand All @@ -165,10 +183,10 @@ async def async_set_energy_cost(self, energy_cost: float) -> None:
await self.hass.async_add_executor_job(self._set_energy_cost, energy_cost)
await self.async_request_refresh()

@_require_authentication
def _set_lock_unlock(self, lock: bool) -> None:
"""Set wallbox to locked or unlocked."""
try:
self._authenticate()
if lock:
self._wallbox.lockCharger(self._station)
else:
Expand All @@ -183,10 +201,10 @@ async def async_set_lock_unlock(self, lock: bool) -> None:
await self.hass.async_add_executor_job(self._set_lock_unlock, lock)
await self.async_request_refresh()

@_require_authentication
def _pause_charger(self, pause: bool) -> None:
"""Set wallbox to pause or resume."""
try:
self._authenticate()
if pause:
self._wallbox.pauseChargingSession(self._station)
else:
Expand Down