mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Wallbox Add Authentication Decorator (#102520)
This commit is contained in:
parent
24a65808ac
commit
5bb3c7ca55
@ -1,17 +1,18 @@
|
|||||||
"""DataUpdateCoordinator for the wallbox integration."""
|
"""DataUpdateCoordinator for the wallbox integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Concatenate, ParamSpec, TypeVar
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from wallbox import Wallbox
|
from wallbox import Wallbox
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
|
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CHARGER_CURRENCY_KEY,
|
CHARGER_CURRENCY_KEY,
|
||||||
@ -62,6 +63,29 @@ CHARGER_STATUS: dict[int, ChargerStatus] = {
|
|||||||
210: ChargerStatus.LOCKED_CAR_CONNECTED,
|
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
|
||||||
|
raise ConnectionError from wallbox_connection_error
|
||||||
|
|
||||||
|
return require_authentication
|
||||||
|
|
||||||
|
|
||||||
class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
"""Wallbox Coordinator class."""
|
"""Wallbox Coordinator class."""
|
||||||
@ -78,16 +102,10 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
update_interval=timedelta(seconds=UPDATE_INTERVAL),
|
update_interval=timedelta(seconds=UPDATE_INTERVAL),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _authenticate(self) -> None:
|
def authenticate(self) -> None:
|
||||||
"""Authenticate using Wallbox API."""
|
"""Authenticate using Wallbox API."""
|
||||||
try:
|
|
||||||
self._wallbox.authenticate()
|
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
|
|
||||||
|
|
||||||
def _validate(self) -> None:
|
def _validate(self) -> None:
|
||||||
"""Authenticate using Wallbox API."""
|
"""Authenticate using Wallbox API."""
|
||||||
try:
|
try:
|
||||||
@ -101,10 +119,9 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
"""Get new sensor data for Wallbox component."""
|
"""Get new sensor data for Wallbox component."""
|
||||||
await self.hass.async_add_executor_job(self._validate)
|
await self.hass.async_add_executor_job(self._validate)
|
||||||
|
|
||||||
|
@_require_authentication
|
||||||
def _get_data(self) -> dict[str, Any]:
|
def _get_data(self) -> dict[str, Any]:
|
||||||
"""Get new sensor data for Wallbox component."""
|
"""Get new sensor data for Wallbox component."""
|
||||||
try:
|
|
||||||
self._authenticate()
|
|
||||||
data: dict[str, Any] = self._wallbox.getChargerStatus(self._station)
|
data: dict[str, Any] = self._wallbox.getChargerStatus(self._station)
|
||||||
data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][
|
data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][
|
||||||
CHARGER_MAX_CHARGING_CURRENT_KEY
|
CHARGER_MAX_CHARGING_CURRENT_KEY
|
||||||
@ -123,25 +140,20 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
|
data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
|
||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
except (
|
|
||||||
ConnectionError,
|
|
||||||
requests.exceptions.HTTPError,
|
|
||||||
) as wallbox_connection_error:
|
|
||||||
raise UpdateFailed from wallbox_connection_error
|
|
||||||
|
|
||||||
async def _async_update_data(self) -> dict[str, Any]:
|
async def _async_update_data(self) -> dict[str, Any]:
|
||||||
"""Get new sensor data for Wallbox component."""
|
"""Get new sensor data for Wallbox component."""
|
||||||
return await self.hass.async_add_executor_job(self._get_data)
|
return await self.hass.async_add_executor_job(self._get_data)
|
||||||
|
|
||||||
|
@_require_authentication
|
||||||
def _set_charging_current(self, charging_current: float) -> None:
|
def _set_charging_current(self, charging_current: float) -> None:
|
||||||
"""Set maximum charging current for Wallbox."""
|
"""Set maximum charging current for Wallbox."""
|
||||||
try:
|
try:
|
||||||
self._authenticate()
|
|
||||||
self._wallbox.setMaxChargingCurrent(self._station, charging_current)
|
self._wallbox.setMaxChargingCurrent(self._station, charging_current)
|
||||||
except requests.exceptions.HTTPError as wallbox_connection_error:
|
except requests.exceptions.HTTPError as wallbox_connection_error:
|
||||||
if wallbox_connection_error.response.status_code == 403:
|
if wallbox_connection_error.response.status_code == 403:
|
||||||
raise InvalidAuth from wallbox_connection_error
|
raise InvalidAuth from wallbox_connection_error
|
||||||
raise ConnectionError from wallbox_connection_error
|
raise wallbox_connection_error
|
||||||
|
|
||||||
async def async_set_charging_current(self, charging_current: float) -> None:
|
async def async_set_charging_current(self, charging_current: float) -> None:
|
||||||
"""Set maximum charging current for Wallbox."""
|
"""Set maximum charging current for Wallbox."""
|
||||||
@ -150,25 +162,21 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
)
|
)
|
||||||
await self.async_request_refresh()
|
await self.async_request_refresh()
|
||||||
|
|
||||||
|
@_require_authentication
|
||||||
def _set_energy_cost(self, energy_cost: float) -> None:
|
def _set_energy_cost(self, energy_cost: float) -> None:
|
||||||
"""Set energy cost for Wallbox."""
|
"""Set energy cost for Wallbox."""
|
||||||
try:
|
|
||||||
self._authenticate()
|
|
||||||
self._wallbox.setEnergyCost(self._station, energy_cost)
|
self._wallbox.setEnergyCost(self._station, energy_cost)
|
||||||
except requests.exceptions.HTTPError as wallbox_connection_error:
|
|
||||||
if wallbox_connection_error.response.status_code == 403:
|
|
||||||
raise InvalidAuth from wallbox_connection_error
|
|
||||||
raise ConnectionError from wallbox_connection_error
|
|
||||||
|
|
||||||
async def async_set_energy_cost(self, energy_cost: float) -> None:
|
async def async_set_energy_cost(self, energy_cost: float) -> None:
|
||||||
"""Set energy cost for Wallbox."""
|
"""Set energy cost for Wallbox."""
|
||||||
await self.hass.async_add_executor_job(self._set_energy_cost, energy_cost)
|
await self.hass.async_add_executor_job(self._set_energy_cost, energy_cost)
|
||||||
await self.async_request_refresh()
|
await self.async_request_refresh()
|
||||||
|
|
||||||
|
@_require_authentication
|
||||||
def _set_lock_unlock(self, lock: bool) -> None:
|
def _set_lock_unlock(self, lock: bool) -> None:
|
||||||
"""Set wallbox to locked or unlocked."""
|
"""Set wallbox to locked or unlocked."""
|
||||||
try:
|
try:
|
||||||
self._authenticate()
|
|
||||||
if lock:
|
if lock:
|
||||||
self._wallbox.lockCharger(self._station)
|
self._wallbox.lockCharger(self._station)
|
||||||
else:
|
else:
|
||||||
@ -176,25 +184,21 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
except requests.exceptions.HTTPError as wallbox_connection_error:
|
except requests.exceptions.HTTPError as wallbox_connection_error:
|
||||||
if wallbox_connection_error.response.status_code == 403:
|
if wallbox_connection_error.response.status_code == 403:
|
||||||
raise InvalidAuth from wallbox_connection_error
|
raise InvalidAuth from wallbox_connection_error
|
||||||
raise ConnectionError from wallbox_connection_error
|
raise wallbox_connection_error
|
||||||
|
|
||||||
async def async_set_lock_unlock(self, lock: bool) -> None:
|
async def async_set_lock_unlock(self, lock: bool) -> None:
|
||||||
"""Set wallbox to locked or unlocked."""
|
"""Set wallbox to locked or unlocked."""
|
||||||
await self.hass.async_add_executor_job(self._set_lock_unlock, lock)
|
await self.hass.async_add_executor_job(self._set_lock_unlock, lock)
|
||||||
await self.async_request_refresh()
|
await self.async_request_refresh()
|
||||||
|
|
||||||
|
@_require_authentication
|
||||||
def _pause_charger(self, pause: bool) -> None:
|
def _pause_charger(self, pause: bool) -> None:
|
||||||
"""Set wallbox to pause or resume."""
|
"""Set wallbox to pause or resume."""
|
||||||
try:
|
|
||||||
self._authenticate()
|
|
||||||
if pause:
|
if pause:
|
||||||
self._wallbox.pauseChargingSession(self._station)
|
self._wallbox.pauseChargingSession(self._station)
|
||||||
else:
|
else:
|
||||||
self._wallbox.resumeChargingSession(self._station)
|
self._wallbox.resumeChargingSession(self._station)
|
||||||
except requests.exceptions.HTTPError as wallbox_connection_error:
|
|
||||||
if wallbox_connection_error.response.status_code == 403:
|
|
||||||
raise InvalidAuth from wallbox_connection_error
|
|
||||||
raise ConnectionError from wallbox_connection_error
|
|
||||||
|
|
||||||
async def async_pause_charger(self, pause: bool) -> None:
|
async def async_pause_charger(self, pause: bool) -> None:
|
||||||
"""Set wallbox to pause or resume."""
|
"""Set wallbox to pause or resume."""
|
||||||
|
@ -9,9 +9,9 @@ from homeassistant.components.wallbox.const import (
|
|||||||
CHARGER_ENERGY_PRICE_KEY,
|
CHARGER_ENERGY_PRICE_KEY,
|
||||||
CHARGER_MAX_CHARGING_CURRENT_KEY,
|
CHARGER_MAX_CHARGING_CURRENT_KEY,
|
||||||
)
|
)
|
||||||
from homeassistant.components.wallbox.coordinator import InvalidAuth
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
authorisation_response,
|
authorisation_response,
|
||||||
@ -186,7 +186,7 @@ async def test_wallbox_number_class_energy_price_auth_error(
|
|||||||
status_code=403,
|
status_code=403,
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(InvalidAuth):
|
with pytest.raises(ConfigEntryAuthFailed):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"number",
|
"number",
|
||||||
SERVICE_SET_VALUE,
|
SERVICE_SET_VALUE,
|
||||||
|
@ -6,9 +6,9 @@ import requests_mock
|
|||||||
|
|
||||||
from homeassistant.components.switch import SERVICE_TURN_OFF, SERVICE_TURN_ON
|
from homeassistant.components.switch import SERVICE_TURN_OFF, SERVICE_TURN_ON
|
||||||
from homeassistant.components.wallbox.const import CHARGER_STATUS_ID_KEY
|
from homeassistant.components.wallbox.const import CHARGER_STATUS_ID_KEY
|
||||||
from homeassistant.components.wallbox.coordinator import InvalidAuth
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
|
|
||||||
from . import authorisation_response, setup_integration
|
from . import authorisation_response, setup_integration
|
||||||
from .const import MOCK_SWITCH_ENTITY_ID
|
from .const import MOCK_SWITCH_ENTITY_ID
|
||||||
@ -120,7 +120,7 @@ async def test_wallbox_switch_class_authentication_error(
|
|||||||
status_code=403,
|
status_code=403,
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(InvalidAuth):
|
with pytest.raises(ConfigEntryAuthFailed):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"switch",
|
"switch",
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
@ -129,7 +129,7 @@ async def test_wallbox_switch_class_authentication_error(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
with pytest.raises(InvalidAuth):
|
with pytest.raises(ConfigEntryAuthFailed):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"switch",
|
"switch",
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user