1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 00:20:30 +01:00

Add command compatibility scaffold for Tessie migration (#166458)

This commit is contained in:
Brett Adams
2026-04-01 05:52:09 +10:00
committed by GitHub
parent 962d5386c7
commit 7d145cd3b8
4 changed files with 52 additions and 27 deletions

View File

@@ -2,21 +2,20 @@
from abc import abstractmethod from abc import abstractmethod
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from inspect import isawaitable
from typing import Any from typing import Any
from aiohttp import ClientError
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, TRANSLATED_ERRORS from .const import DOMAIN
from .coordinator import ( from .coordinator import (
TessieEnergyHistoryCoordinator, TessieEnergyHistoryCoordinator,
TessieEnergySiteInfoCoordinator, TessieEnergySiteInfoCoordinator,
TessieEnergySiteLiveCoordinator, TessieEnergySiteLiveCoordinator,
TessieStateUpdateCoordinator, TessieStateUpdateCoordinator,
) )
from .helpers import handle_command, handle_legacy_command
from .models import TessieEnergyData, TessieVehicleData from .models import TessieEnergyData, TessieVehicleData
@@ -93,30 +92,24 @@ class TessieEntity(TessieBaseEntity):
self.async_write_ha_state() self.async_write_ha_state()
async def run( async def run(
self, func: Callable[..., Awaitable[dict[str, Any]]], **kargs: Any self,
command: Callable[..., Awaitable[dict[str, Any]]] | Awaitable[dict[str, Any]],
**kargs: Any,
) -> None: ) -> None:
"""Run a tessie_api function and handle exceptions.""" """Run a legacy tessie_api command function or awaitable Vehicle command."""
try: if isawaitable(command):
response = await func( await handle_command(command)
return
await handle_legacy_command(
command(
session=self._session, session=self._session,
vin=self.vin, vin=self.vin,
api_key=self._api_key, api_key=self._api_key,
**kargs, **kargs,
) ),
except ClientError as e: name=getattr(self, "name", self.entity_id),
raise HomeAssistantError( )
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from e
if response["result"] is False:
name: str = getattr(self, "name", self.entity_id)
reason: str = response.get("reason", "unknown")
translation_key = TRANSLATED_ERRORS.get(reason, "command_failed")
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key=translation_key,
translation_placeholders={"name": name, "message": reason},
)
def _async_update_attrs(self) -> None: def _async_update_attrs(self) -> None:
"""Update the attributes of the entity.""" """Update the attributes of the entity."""

View File

@@ -1,17 +1,19 @@
"""Tessie helper functions.""" """Tessie helper functions."""
from collections.abc import Awaitable
from typing import Any from typing import Any
from aiohttp import ClientError
from tesla_fleet_api.exceptions import TeslaFleetError from tesla_fleet_api.exceptions import TeslaFleetError
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from . import _LOGGER from . import _LOGGER
from .const import DOMAIN from .const import DOMAIN, TRANSLATED_ERRORS
async def handle_command(command) -> dict[str, Any]: async def handle_command(command: Awaitable[dict[str, Any]]) -> dict[str, Any]:
"""Handle a command.""" """Handle an awaitable Vehicle/EnergySite command."""
try: try:
result = await command result = await command
except TeslaFleetError as e: except TeslaFleetError as e:
@@ -22,3 +24,22 @@ async def handle_command(command) -> dict[str, Any]:
) from e ) from e
_LOGGER.debug("Command result: %s", result) _LOGGER.debug("Command result: %s", result)
return result return result
async def handle_legacy_command(command: Awaitable[dict[str, Any]], name: str) -> None:
"""Handle a legacy tessie_api command result."""
try:
response = await command
except ClientError as e:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from e
if response["result"] is False:
reason: str = response.get("reason", "unknown")
translation_key = TRANSLATED_ERRORS.get(reason, "command_failed")
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key=translation_key,
translation_placeholders={"name": name, "message": reason},
)

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from tesla_fleet_api.tessie import EnergySite from tesla_fleet_api.tessie import EnergySite, Vehicle
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@@ -43,3 +43,4 @@ class TessieVehicleData:
data_coordinator: TessieStateUpdateCoordinator data_coordinator: TessieStateUpdateCoordinator
device: DeviceInfo device: DeviceInfo
vin: str vin: str
api: Vehicle | None = None

View File

@@ -68,3 +68,13 @@ async def test_scopes_error(hass: HomeAssistant) -> None:
): ):
entry = await setup_platform(hass) entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_RETRY assert entry.state is ConfigEntryState.SETUP_RETRY
async def test_vehicle_api_handle_is_optional(hass: HomeAssistant) -> None:
"""Test runtime vehicle API handle defaults to None during scaffold stage."""
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.LOADED
vehicles = entry.runtime_data.vehicles
assert vehicles
assert all(vehicle.api is None for vehicle in vehicles)