Check Energy Live API works before creating the coordinator in Tessie (#142510)

* Check live API works before creating the coordinator

* Fix diag

* Fix mypy on entity

* is not None
This commit is contained in:
Brett Adams 2025-04-13 21:55:16 +10:00 committed by GitHub
parent 5eb25b2d4a
commit 31c2d22912
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 46 additions and 9 deletions

View File

@ -6,7 +6,12 @@ import logging
from aiohttp import ClientError, ClientResponseError from aiohttp import ClientError, ClientResponseError
from tesla_fleet_api.const import Scope from tesla_fleet_api.const import Scope
from tesla_fleet_api.exceptions import TeslaFleetError from tesla_fleet_api.exceptions import (
Forbidden,
InvalidToken,
SubscriptionRequired,
TeslaFleetError,
)
from tesla_fleet_api.tessie import Tessie from tesla_fleet_api.tessie import Tessie
from tessie_api import get_state_of_all_vehicles from tessie_api import get_state_of_all_vehicles
@ -124,12 +129,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo
continue continue
api = tessie.energySites.create(site_id) api = tessie.energySites.create(site_id)
try:
live_status = (await api.live_status())["response"]
except (InvalidToken, Forbidden, SubscriptionRequired) as e:
raise ConfigEntryAuthFailed from e
except TeslaFleetError as e:
raise ConfigEntryNotReady(e.message) from e
energysites.append( energysites.append(
TessieEnergyData( TessieEnergyData(
api=api, api=api,
id=site_id, id=site_id,
live_coordinator=TessieEnergySiteLiveCoordinator( live_coordinator=(
hass, entry, api TessieEnergySiteLiveCoordinator(
hass, entry, api, live_status
)
if isinstance(live_status, dict)
else None
), ),
info_coordinator=TessieEnergySiteInfoCoordinator( info_coordinator=TessieEnergySiteInfoCoordinator(
hass, entry, api hass, entry, api
@ -147,6 +164,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo
*( *(
energysite.live_coordinator.async_config_entry_first_refresh() energysite.live_coordinator.async_config_entry_first_refresh()
for energysite in energysites for energysite in energysites
if energysite.live_coordinator is not None
), ),
*( *(
energysite.info_coordinator.async_config_entry_first_refresh() energysite.info_coordinator.async_config_entry_first_refresh()

View File

@ -191,6 +191,7 @@ async def async_setup_entry(
TessieEnergyLiveBinarySensorEntity(energy, description) TessieEnergyLiveBinarySensorEntity(energy, description)
for energy in entry.runtime_data.energysites for energy in entry.runtime_data.energysites
for description in ENERGY_LIVE_DESCRIPTIONS for description in ENERGY_LIVE_DESCRIPTIONS
if energy.live_coordinator is not None
), ),
( (
TessieEnergyInfoBinarySensorEntity(vehicle, description) TessieEnergyInfoBinarySensorEntity(vehicle, description)
@ -233,6 +234,7 @@ class TessieEnergyLiveBinarySensorEntity(TessieEnergyEntity, BinarySensorEntity)
) -> None: ) -> None:
"""Initialize the binary sensor.""" """Initialize the binary sensor."""
self.entity_description = description self.entity_description = description
assert data.live_coordinator is not None
super().__init__(data, data.live_coordinator, description.key) super().__init__(data, data.live_coordinator, description.key)
def _async_update_attrs(self) -> None: def _async_update_attrs(self) -> None:

View File

@ -102,7 +102,11 @@ class TessieEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
config_entry: TessieConfigEntry config_entry: TessieConfigEntry
def __init__( def __init__(
self, hass: HomeAssistant, config_entry: TessieConfigEntry, api: EnergySite self,
hass: HomeAssistant,
config_entry: TessieConfigEntry,
api: EnergySite,
data: dict[str, Any],
) -> None: ) -> None:
"""Initialize Tessie Energy Site Live coordinator.""" """Initialize Tessie Energy Site Live coordinator."""
super().__init__( super().__init__(
@ -114,6 +118,12 @@ class TessieEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
) )
self.api = api self.api = api
# Convert Wall Connectors from array to dict
data["wall_connectors"] = {
wc["din"]: wc for wc in (data.get("wall_connectors") or [])
}
self.data = data
async def _async_update_data(self) -> dict[str, Any]: async def _async_update_data(self) -> dict[str, Any]:
"""Update energy site data using Tessie API.""" """Update energy site data using Tessie API."""

View File

@ -41,7 +41,9 @@ async def async_get_config_entry_diagnostics(
] ]
energysites = [ energysites = [
{ {
"live": async_redact_data(x.live_coordinator.data, ENERGY_LIVE_REDACT), "live": async_redact_data(x.live_coordinator.data, ENERGY_LIVE_REDACT)
if x.live_coordinator
else None,
"info": async_redact_data(x.info_coordinator.data, ENERGY_INFO_REDACT), "info": async_redact_data(x.info_coordinator.data, ENERGY_INFO_REDACT),
} }
for x in entry.runtime_data.energysites for x in entry.runtime_data.energysites

View File

@ -155,7 +155,7 @@ class TessieWallConnectorEntity(TessieBaseEntity):
via_device=(DOMAIN, str(data.id)), via_device=(DOMAIN, str(data.id)),
serial_number=din.split("-")[-1], serial_number=din.split("-")[-1],
) )
assert data.live_coordinator
super().__init__(data.live_coordinator, key) super().__init__(data.live_coordinator, key)
@property @property

View File

@ -28,7 +28,7 @@ class TessieEnergyData:
"""Data for a Energy Site in the Tessie integration.""" """Data for a Energy Site in the Tessie integration."""
api: EnergySite api: EnergySite
live_coordinator: TessieEnergySiteLiveCoordinator live_coordinator: TessieEnergySiteLiveCoordinator | None
info_coordinator: TessieEnergySiteInfoCoordinator info_coordinator: TessieEnergySiteInfoCoordinator
id: int id: int
device: DeviceInfo device: DeviceInfo

View File

@ -396,12 +396,16 @@ async def async_setup_entry(
TessieEnergyLiveSensorEntity(energysite, description) TessieEnergyLiveSensorEntity(energysite, description)
for energysite in entry.runtime_data.energysites for energysite in entry.runtime_data.energysites
for description in ENERGY_LIVE_DESCRIPTIONS for description in ENERGY_LIVE_DESCRIPTIONS
if description.key in energysite.live_coordinator.data if energysite.live_coordinator is not None
and (
description.key in energysite.live_coordinator.data
or description.key == "percentage_charged" or description.key == "percentage_charged"
)
), ),
( # Add wall connectors ( # Add wall connectors
TessieWallConnectorSensorEntity(energysite, din, description) TessieWallConnectorSensorEntity(energysite, din, description)
for energysite in entry.runtime_data.energysites for energysite in entry.runtime_data.energysites
if energysite.live_coordinator is not None
for din in energysite.live_coordinator.data.get("wall_connectors", {}) for din in energysite.live_coordinator.data.get("wall_connectors", {})
for description in WALL_CONNECTOR_DESCRIPTIONS for description in WALL_CONNECTOR_DESCRIPTIONS
), ),
@ -446,6 +450,7 @@ class TessieEnergyLiveSensorEntity(TessieEnergyEntity, SensorEntity):
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
self.entity_description = description self.entity_description = description
assert data.live_coordinator is not None
super().__init__(data, data.live_coordinator, description.key) super().__init__(data, data.live_coordinator, description.key)
def _async_update_attrs(self) -> None: def _async_update_attrs(self) -> None: