Fix when live status is blank in Telsemetry (#130408)

This commit is contained in:
Brett Adams 2025-01-13 21:44:36 +10:00 committed by GitHub
parent fba1b4be5b
commit dae87db244
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 46 additions and 8 deletions

View File

@ -7,6 +7,7 @@ from typing import Final
from tesla_fleet_api import EnergySpecific, Teslemetry, VehicleSpecific
from tesla_fleet_api.const import Scope
from tesla_fleet_api.exceptions import (
Forbidden,
InvalidToken,
SubscriptionRequired,
TeslaFleetError,
@ -163,10 +164,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
serial_number=str(site_id),
)
# Check live status endpoint works before creating its coordinator
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(
TeslemetryEnergyData(
api=api,
live_coordinator=TeslemetryEnergySiteLiveCoordinator(hass, api),
live_coordinator=(
TeslemetryEnergySiteLiveCoordinator(hass, api, live_status)
if isinstance(live_status, dict)
else None
),
info_coordinator=TeslemetryEnergySiteInfoCoordinator(
hass, api, product
),
@ -187,10 +200,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
vehicle.coordinator.async_config_entry_first_refresh()
for vehicle in vehicles
),
*(
energysite.live_coordinator.async_config_entry_first_refresh()
for energysite in energysites
),
*(
energysite.info_coordinator.async_config_entry_first_refresh()
for energysite in energysites

View File

@ -193,6 +193,7 @@ async def async_setup_entry(
( # Energy Site Live
TeslemetryEnergyLiveBinarySensorEntity(energysite, description)
for energysite in entry.runtime_data.energysites
if energysite.live_coordinator
for description in ENERGY_LIVE_DESCRIPTIONS
if energysite.info_coordinator.data.get("components_battery")
),

View File

@ -69,7 +69,9 @@ class TeslemetryVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
class TeslemetryEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching energy site live status from the Teslemetry API."""
def __init__(self, hass: HomeAssistant, api: EnergySpecific) -> None:
updated_once: bool
def __init__(self, hass: HomeAssistant, api: EnergySpecific, data: dict) -> None:
"""Initialize Teslemetry Energy Site Live coordinator."""
super().__init__(
hass,
@ -79,6 +81,12 @@ class TeslemetryEnergySiteLiveCoordinator(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 Teslemetry API."""

View File

@ -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

View File

@ -136,6 +136,8 @@ class TeslemetryEnergyLiveEntity(TeslemetryEntity):
) -> None:
"""Initialize common aspects of a Teslemetry Energy Site Live entity."""
assert data.live_coordinator
self.api = data.api
self._attr_unique_id = f"{data.id}-{key}"
self._attr_device_info = data.device
@ -195,6 +197,8 @@ class TeslemetryWallConnectorEntity(TeslemetryEntity):
) -> None:
"""Initialize common aspects of a Teslemetry entity."""
assert data.live_coordinator
self.api = data.api
self.din = din
self._attr_unique_id = f"{data.id}-{din}-{key}"

View File

@ -50,7 +50,7 @@ class TeslemetryEnergyData:
"""Data for a vehicle in the Teslemetry integration."""
api: EnergySpecific
live_coordinator: TeslemetryEnergySiteLiveCoordinator
live_coordinator: TeslemetryEnergySiteLiveCoordinator | None
info_coordinator: TeslemetryEnergySiteInfoCoordinator
history_coordinator: TeslemetryEnergyHistoryCoordinator | None
id: int

View File

@ -523,6 +523,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Teslemetry sensor platform from a config entry."""
entities: list[SensorEntity] = []
for vehicle in entry.runtime_data.vehicles:
for description in VEHICLE_DESCRIPTIONS:
@ -551,6 +552,7 @@ async def async_setup_entry(
entities.extend(
TeslemetryEnergyLiveSensorEntity(energysite, description)
for energysite in entry.runtime_data.energysites
if energysite.live_coordinator
for description in ENERGY_LIVE_DESCRIPTIONS
if description.key in energysite.live_coordinator.data
)
@ -558,6 +560,7 @@ async def async_setup_entry(
entities.extend(
TeslemetryWallConnectorSensorEntity(energysite, din, description)
for energysite in entry.runtime_data.energysites
if energysite.live_coordinator
for din in energysite.live_coordinator.data.get("wall_connectors", {})
for description in WALL_CONNECTOR_DESCRIPTIONS
)

View File

@ -179,3 +179,14 @@ async def test_vehicle_stream(
state = hass.states.get("binary_sensor.test_status")
assert state.state == STATE_OFF
async def test_no_live_status(
hass: HomeAssistant,
mock_live_status: AsyncMock,
) -> None:
"""Test coordinator refresh with an error."""
mock_live_status.side_effect = AsyncMock({"response": ""})
await setup_platform(hass)
assert hass.states.get("sensor.energy_site_grid_power") is None