From 31c2d22912e953ba35b8e8814ff731c044a5b59b Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Sun, 13 Apr 2025 21:55:16 +1000 Subject: [PATCH] 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 --- homeassistant/components/tessie/__init__.py | 24 ++++++++++++++++--- .../components/tessie/binary_sensor.py | 2 ++ .../components/tessie/coordinator.py | 12 +++++++++- .../components/tessie/diagnostics.py | 4 +++- homeassistant/components/tessie/entity.py | 2 +- homeassistant/components/tessie/models.py | 2 +- homeassistant/components/tessie/sensor.py | 9 +++++-- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/tessie/__init__.py b/homeassistant/components/tessie/__init__.py index e247931e3ba..7fd2729ef03 100644 --- a/homeassistant/components/tessie/__init__.py +++ b/homeassistant/components/tessie/__init__.py @@ -6,7 +6,12 @@ import logging from aiohttp import ClientError, ClientResponseError 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 tessie_api import get_state_of_all_vehicles @@ -124,12 +129,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo continue 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( TessieEnergyData( api=api, id=site_id, - live_coordinator=TessieEnergySiteLiveCoordinator( - hass, entry, api + live_coordinator=( + TessieEnergySiteLiveCoordinator( + hass, entry, api, live_status + ) + if isinstance(live_status, dict) + else None ), info_coordinator=TessieEnergySiteInfoCoordinator( 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() for energysite in energysites + if energysite.live_coordinator is not None ), *( energysite.info_coordinator.async_config_entry_first_refresh() diff --git a/homeassistant/components/tessie/binary_sensor.py b/homeassistant/components/tessie/binary_sensor.py index 515339c3da8..cdf3b0035fc 100644 --- a/homeassistant/components/tessie/binary_sensor.py +++ b/homeassistant/components/tessie/binary_sensor.py @@ -191,6 +191,7 @@ async def async_setup_entry( TessieEnergyLiveBinarySensorEntity(energy, description) for energy in entry.runtime_data.energysites for description in ENERGY_LIVE_DESCRIPTIONS + if energy.live_coordinator is not None ), ( TessieEnergyInfoBinarySensorEntity(vehicle, description) @@ -233,6 +234,7 @@ class TessieEnergyLiveBinarySensorEntity(TessieEnergyEntity, BinarySensorEntity) ) -> None: """Initialize the binary sensor.""" self.entity_description = description + assert data.live_coordinator is not None super().__init__(data, data.live_coordinator, description.key) def _async_update_attrs(self) -> None: diff --git a/homeassistant/components/tessie/coordinator.py b/homeassistant/components/tessie/coordinator.py index 2382595b058..8b6fb639a64 100644 --- a/homeassistant/components/tessie/coordinator.py +++ b/homeassistant/components/tessie/coordinator.py @@ -102,7 +102,11 @@ class TessieEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]): config_entry: TessieConfigEntry def __init__( - self, hass: HomeAssistant, config_entry: TessieConfigEntry, api: EnergySite + self, + hass: HomeAssistant, + config_entry: TessieConfigEntry, + api: EnergySite, + data: dict[str, Any], ) -> None: """Initialize Tessie Energy Site Live coordinator.""" super().__init__( @@ -114,6 +118,12 @@ class TessieEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]): ) 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]: """Update energy site data using Tessie API.""" diff --git a/homeassistant/components/tessie/diagnostics.py b/homeassistant/components/tessie/diagnostics.py index bd2db772b57..21fc208612d 100644 --- a/homeassistant/components/tessie/diagnostics.py +++ b/homeassistant/components/tessie/diagnostics.py @@ -41,7 +41,9 @@ async def async_get_config_entry_diagnostics( ] 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), } for x in entry.runtime_data.energysites diff --git a/homeassistant/components/tessie/entity.py b/homeassistant/components/tessie/entity.py index a2b6d3c9761..fb49d02f42e 100644 --- a/homeassistant/components/tessie/entity.py +++ b/homeassistant/components/tessie/entity.py @@ -155,7 +155,7 @@ class TessieWallConnectorEntity(TessieBaseEntity): via_device=(DOMAIN, str(data.id)), serial_number=din.split("-")[-1], ) - + assert data.live_coordinator super().__init__(data.live_coordinator, key) @property diff --git a/homeassistant/components/tessie/models.py b/homeassistant/components/tessie/models.py index 03652782cfe..5330d2d0bf0 100644 --- a/homeassistant/components/tessie/models.py +++ b/homeassistant/components/tessie/models.py @@ -28,7 +28,7 @@ class TessieEnergyData: """Data for a Energy Site in the Tessie integration.""" api: EnergySite - live_coordinator: TessieEnergySiteLiveCoordinator + live_coordinator: TessieEnergySiteLiveCoordinator | None info_coordinator: TessieEnergySiteInfoCoordinator id: int device: DeviceInfo diff --git a/homeassistant/components/tessie/sensor.py b/homeassistant/components/tessie/sensor.py index e5b476057fa..52accb15575 100644 --- a/homeassistant/components/tessie/sensor.py +++ b/homeassistant/components/tessie/sensor.py @@ -396,12 +396,16 @@ async def async_setup_entry( TessieEnergyLiveSensorEntity(energysite, description) for energysite in entry.runtime_data.energysites for description in ENERGY_LIVE_DESCRIPTIONS - if description.key in energysite.live_coordinator.data - or description.key == "percentage_charged" + if energysite.live_coordinator is not None + and ( + description.key in energysite.live_coordinator.data + or description.key == "percentage_charged" + ) ), ( # Add wall connectors TessieWallConnectorSensorEntity(energysite, din, description) 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 description in WALL_CONNECTOR_DESCRIPTIONS ), @@ -446,6 +450,7 @@ class TessieEnergyLiveSensorEntity(TessieEnergyEntity, SensorEntity): ) -> None: """Initialize the sensor.""" self.entity_description = description + assert data.live_coordinator is not None super().__init__(data, data.live_coordinator, description.key) def _async_update_attrs(self) -> None: