mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Raise HomeAssistantError in Renault (#86071)
This commit is contained in:
parent
6c5f9c6fcb
commit
1918f21fb1
@ -1,8 +1,9 @@
|
||||
"""Support for Renault button entities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -18,7 +19,7 @@ from .renault_hub import RenaultHub
|
||||
class RenaultButtonRequiredKeysMixin:
|
||||
"""Mixin for required keys."""
|
||||
|
||||
async_press: Callable[[RenaultButtonEntity], Awaitable]
|
||||
async_press: Callable[[RenaultButtonEntity], Coroutine[Any, Any, Any]]
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -56,25 +57,15 @@ class RenaultButtonEntity(RenaultEntity, ButtonEntity):
|
||||
await self.entity_description.async_press(self)
|
||||
|
||||
|
||||
async def _start_charge(entity: RenaultButtonEntity) -> None:
|
||||
"""Start charge on the vehicle."""
|
||||
await entity.vehicle.vehicle.set_charge_start()
|
||||
|
||||
|
||||
async def _start_air_conditioner(entity: RenaultButtonEntity) -> None:
|
||||
"""Start air conditioner on the vehicle."""
|
||||
await entity.vehicle.vehicle.set_ac_start(21, None)
|
||||
|
||||
|
||||
BUTTON_TYPES: tuple[RenaultButtonEntityDescription, ...] = (
|
||||
RenaultButtonEntityDescription(
|
||||
async_press=_start_air_conditioner,
|
||||
async_press=lambda x: x.vehicle.set_ac_start(21, None),
|
||||
key="start_air_conditioner",
|
||||
icon="mdi:air-conditioner",
|
||||
name="Start air conditioner",
|
||||
),
|
||||
RenaultButtonEntityDescription(
|
||||
async_press=_start_charge,
|
||||
async_press=lambda x: x.vehicle.set_charge_start(),
|
||||
key="start_charge",
|
||||
icon="mdi:ev-station",
|
||||
name="Start charge",
|
||||
|
@ -2,22 +2,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
from collections.abc import Awaitable, Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from functools import wraps
|
||||
import logging
|
||||
from typing import cast
|
||||
from typing import Any, TypeVar, cast
|
||||
|
||||
from renault_api.exceptions import RenaultException
|
||||
from renault_api.kamereon import models
|
||||
from renault_api.renault_vehicle import RenaultVehicle
|
||||
from typing_extensions import Concatenate, ParamSpec
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
||||
from .const import DOMAIN
|
||||
from .renault_coordinator import RenaultDataUpdateCoordinator
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
_T = TypeVar("_T")
|
||||
_P = ParamSpec("_P")
|
||||
|
||||
|
||||
def with_error_wrapping(
|
||||
func: Callable[Concatenate[RenaultVehicleProxy, _P], Awaitable[_T]]
|
||||
) -> Callable[Concatenate[RenaultVehicleProxy, _P], Coroutine[Any, Any, _T]]:
|
||||
"""Catch Renault errors."""
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(
|
||||
self: RenaultVehicleProxy,
|
||||
*args: _P.args,
|
||||
**kwargs: _P.kwargs,
|
||||
) -> _T:
|
||||
"""Catch RenaultException errors and raise HomeAssistantError."""
|
||||
try:
|
||||
return await func(self, *args, **kwargs)
|
||||
except RenaultException as err:
|
||||
raise HomeAssistantError(err) from err
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -69,11 +95,6 @@ class RenaultVehicleProxy:
|
||||
"""Return a device description for device registry."""
|
||||
return self._device_info
|
||||
|
||||
@property
|
||||
def vehicle(self) -> RenaultVehicle:
|
||||
"""Return the underlying vehicle."""
|
||||
return self._vehicle
|
||||
|
||||
async def async_initialise(self) -> None:
|
||||
"""Load available coordinators."""
|
||||
self.coordinators = {
|
||||
@ -119,6 +140,42 @@ class RenaultVehicleProxy:
|
||||
)
|
||||
del self.coordinators[key]
|
||||
|
||||
@with_error_wrapping
|
||||
async def set_charge_mode(
|
||||
self, charge_mode: str
|
||||
) -> models.KamereonVehicleChargeModeActionData:
|
||||
"""Set vehicle charge mode."""
|
||||
return await self._vehicle.set_charge_mode(charge_mode)
|
||||
|
||||
@with_error_wrapping
|
||||
async def set_charge_start(self) -> models.KamereonVehicleChargingStartActionData:
|
||||
"""Start vehicle charge."""
|
||||
return await self._vehicle.set_charge_start()
|
||||
|
||||
@with_error_wrapping
|
||||
async def set_ac_stop(self) -> models.KamereonVehicleHvacStartActionData:
|
||||
"""Stop vehicle ac."""
|
||||
return await self._vehicle.set_ac_stop()
|
||||
|
||||
@with_error_wrapping
|
||||
async def set_ac_start(
|
||||
self, temperature: float, when: datetime | None = None
|
||||
) -> models.KamereonVehicleHvacStartActionData:
|
||||
"""Start vehicle ac."""
|
||||
return await self._vehicle.set_ac_start(temperature, when)
|
||||
|
||||
@with_error_wrapping
|
||||
async def get_charging_settings(self) -> models.KamereonVehicleChargingSettingsData:
|
||||
"""Get vehicle charging settings."""
|
||||
return await self._vehicle.get_charging_settings()
|
||||
|
||||
@with_error_wrapping
|
||||
async def set_charge_schedules(
|
||||
self, schedules: list[models.ChargeSchedule]
|
||||
) -> models.KamereonVehicleChargeScheduleActionData:
|
||||
"""Set vehicle charge schedules."""
|
||||
return await self._vehicle.set_charge_schedules(schedules)
|
||||
|
||||
|
||||
COORDINATORS: tuple[RenaultCoordinatorDescription, ...] = (
|
||||
RenaultCoordinatorDescription(
|
||||
|
@ -75,7 +75,7 @@ class RenaultSelectEntity(
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
await self.vehicle.vehicle.set_charge_mode(option)
|
||||
await self.vehicle.set_charge_mode(option)
|
||||
|
||||
|
||||
def _get_charge_mode_icon(entity: RenaultSelectEntity) -> str:
|
||||
|
@ -74,7 +74,7 @@ def setup_services(hass: HomeAssistant) -> None:
|
||||
proxy = get_vehicle_proxy(service_call.data)
|
||||
|
||||
LOGGER.debug("A/C cancel attempt")
|
||||
result = await proxy.vehicle.set_ac_stop()
|
||||
result = await proxy.set_ac_stop()
|
||||
LOGGER.debug("A/C cancel result: %s", result)
|
||||
|
||||
async def ac_start(service_call: ServiceCall) -> None:
|
||||
@ -84,21 +84,22 @@ def setup_services(hass: HomeAssistant) -> None:
|
||||
proxy = get_vehicle_proxy(service_call.data)
|
||||
|
||||
LOGGER.debug("A/C start attempt: %s / %s", temperature, when)
|
||||
result = await proxy.vehicle.set_ac_start(temperature, when)
|
||||
result = await proxy.set_ac_start(temperature, when)
|
||||
LOGGER.debug("A/C start result: %s", result.raw_data)
|
||||
|
||||
async def charge_set_schedules(service_call: ServiceCall) -> None:
|
||||
"""Set charge schedules."""
|
||||
schedules: list[dict[str, Any]] = service_call.data[ATTR_SCHEDULES]
|
||||
proxy = get_vehicle_proxy(service_call.data)
|
||||
charge_schedules = await proxy.vehicle.get_charging_settings()
|
||||
charge_schedules = await proxy.get_charging_settings()
|
||||
for schedule in schedules:
|
||||
charge_schedules.update(schedule)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert charge_schedules.schedules is not None
|
||||
LOGGER.debug("Charge set schedules attempt: %s", schedules)
|
||||
result = await proxy.vehicle.set_charge_schedules(charge_schedules.schedules)
|
||||
result = await proxy.set_charge_schedules(charge_schedules.schedules)
|
||||
|
||||
LOGGER.debug("Charge set schedules result: %s", result)
|
||||
LOGGER.debug(
|
||||
"It may take some time before these changes are reflected in your vehicle"
|
||||
|
@ -4,6 +4,7 @@ from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from renault_api.exceptions import RenaultException
|
||||
from renault_api.kamereon import schemas
|
||||
from renault_api.kamereon.models import ChargeSchedule
|
||||
|
||||
@ -27,6 +28,7 @@ from homeassistant.const import (
|
||||
ATTR_SW_VERSION,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from .const import MOCK_VEHICLES
|
||||
@ -89,15 +91,12 @@ async def test_service_set_ac_cancel(
|
||||
|
||||
with patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.set_ac_stop",
|
||||
return_value=(
|
||||
schemas.KamereonVehicleHvacStartActionDataSchema.loads(
|
||||
load_fixture("renault/action.set_ac_stop.json")
|
||||
)
|
||||
),
|
||||
side_effect=RenaultException("Didn't work"),
|
||||
) as mock_action:
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_AC_CANCEL, service_data=data, blocking=True
|
||||
)
|
||||
with pytest.raises(HomeAssistantError, match="Didn't work"):
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_AC_CANCEL, service_data=data, blocking=True
|
||||
)
|
||||
assert len(mock_action.mock_calls) == 1
|
||||
assert mock_action.mock_calls[0][1] == ()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user