mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Fix when live status is blank in Telsemetry (#130408)
This commit is contained in:
parent
fba1b4be5b
commit
dae87db244
@ -7,6 +7,7 @@ from typing import Final
|
|||||||
from tesla_fleet_api import EnergySpecific, Teslemetry, VehicleSpecific
|
from tesla_fleet_api import EnergySpecific, Teslemetry, VehicleSpecific
|
||||||
from tesla_fleet_api.const import Scope
|
from tesla_fleet_api.const import Scope
|
||||||
from tesla_fleet_api.exceptions import (
|
from tesla_fleet_api.exceptions import (
|
||||||
|
Forbidden,
|
||||||
InvalidToken,
|
InvalidToken,
|
||||||
SubscriptionRequired,
|
SubscriptionRequired,
|
||||||
TeslaFleetError,
|
TeslaFleetError,
|
||||||
@ -163,10 +164,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
|||||||
serial_number=str(site_id),
|
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(
|
energysites.append(
|
||||||
TeslemetryEnergyData(
|
TeslemetryEnergyData(
|
||||||
api=api,
|
api=api,
|
||||||
live_coordinator=TeslemetryEnergySiteLiveCoordinator(hass, api),
|
live_coordinator=(
|
||||||
|
TeslemetryEnergySiteLiveCoordinator(hass, api, live_status)
|
||||||
|
if isinstance(live_status, dict)
|
||||||
|
else None
|
||||||
|
),
|
||||||
info_coordinator=TeslemetryEnergySiteInfoCoordinator(
|
info_coordinator=TeslemetryEnergySiteInfoCoordinator(
|
||||||
hass, api, product
|
hass, api, product
|
||||||
),
|
),
|
||||||
@ -187,10 +200,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
|||||||
vehicle.coordinator.async_config_entry_first_refresh()
|
vehicle.coordinator.async_config_entry_first_refresh()
|
||||||
for vehicle in vehicles
|
for vehicle in vehicles
|
||||||
),
|
),
|
||||||
*(
|
|
||||||
energysite.live_coordinator.async_config_entry_first_refresh()
|
|
||||||
for energysite in energysites
|
|
||||||
),
|
|
||||||
*(
|
*(
|
||||||
energysite.info_coordinator.async_config_entry_first_refresh()
|
energysite.info_coordinator.async_config_entry_first_refresh()
|
||||||
for energysite in energysites
|
for energysite in energysites
|
||||||
|
@ -193,6 +193,7 @@ async def async_setup_entry(
|
|||||||
( # Energy Site Live
|
( # Energy Site Live
|
||||||
TeslemetryEnergyLiveBinarySensorEntity(energysite, description)
|
TeslemetryEnergyLiveBinarySensorEntity(energysite, description)
|
||||||
for energysite in entry.runtime_data.energysites
|
for energysite in entry.runtime_data.energysites
|
||||||
|
if energysite.live_coordinator
|
||||||
for description in ENERGY_LIVE_DESCRIPTIONS
|
for description in ENERGY_LIVE_DESCRIPTIONS
|
||||||
if energysite.info_coordinator.data.get("components_battery")
|
if energysite.info_coordinator.data.get("components_battery")
|
||||||
),
|
),
|
||||||
|
@ -69,7 +69,9 @@ class TeslemetryVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
class TeslemetryEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
class TeslemetryEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
"""Class to manage fetching energy site live status from the Teslemetry API."""
|
"""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."""
|
"""Initialize Teslemetry Energy Site Live coordinator."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
@ -79,6 +81,12 @@ class TeslemetryEnergySiteLiveCoordinator(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 Teslemetry API."""
|
"""Update energy site data using Teslemetry API."""
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -136,6 +136,8 @@ class TeslemetryEnergyLiveEntity(TeslemetryEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize common aspects of a Teslemetry Energy Site Live entity."""
|
"""Initialize common aspects of a Teslemetry Energy Site Live entity."""
|
||||||
|
|
||||||
|
assert data.live_coordinator
|
||||||
|
|
||||||
self.api = data.api
|
self.api = data.api
|
||||||
self._attr_unique_id = f"{data.id}-{key}"
|
self._attr_unique_id = f"{data.id}-{key}"
|
||||||
self._attr_device_info = data.device
|
self._attr_device_info = data.device
|
||||||
@ -195,6 +197,8 @@ class TeslemetryWallConnectorEntity(TeslemetryEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize common aspects of a Teslemetry entity."""
|
"""Initialize common aspects of a Teslemetry entity."""
|
||||||
|
|
||||||
|
assert data.live_coordinator
|
||||||
|
|
||||||
self.api = data.api
|
self.api = data.api
|
||||||
self.din = din
|
self.din = din
|
||||||
self._attr_unique_id = f"{data.id}-{din}-{key}"
|
self._attr_unique_id = f"{data.id}-{din}-{key}"
|
||||||
|
@ -50,7 +50,7 @@ class TeslemetryEnergyData:
|
|||||||
"""Data for a vehicle in the Teslemetry integration."""
|
"""Data for a vehicle in the Teslemetry integration."""
|
||||||
|
|
||||||
api: EnergySpecific
|
api: EnergySpecific
|
||||||
live_coordinator: TeslemetryEnergySiteLiveCoordinator
|
live_coordinator: TeslemetryEnergySiteLiveCoordinator | None
|
||||||
info_coordinator: TeslemetryEnergySiteInfoCoordinator
|
info_coordinator: TeslemetryEnergySiteInfoCoordinator
|
||||||
history_coordinator: TeslemetryEnergyHistoryCoordinator | None
|
history_coordinator: TeslemetryEnergyHistoryCoordinator | None
|
||||||
id: int
|
id: int
|
||||||
|
@ -523,6 +523,7 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Teslemetry sensor platform from a config entry."""
|
"""Set up the Teslemetry sensor platform from a config entry."""
|
||||||
|
|
||||||
entities: list[SensorEntity] = []
|
entities: list[SensorEntity] = []
|
||||||
for vehicle in entry.runtime_data.vehicles:
|
for vehicle in entry.runtime_data.vehicles:
|
||||||
for description in VEHICLE_DESCRIPTIONS:
|
for description in VEHICLE_DESCRIPTIONS:
|
||||||
@ -551,6 +552,7 @@ async def async_setup_entry(
|
|||||||
entities.extend(
|
entities.extend(
|
||||||
TeslemetryEnergyLiveSensorEntity(energysite, description)
|
TeslemetryEnergyLiveSensorEntity(energysite, description)
|
||||||
for energysite in entry.runtime_data.energysites
|
for energysite in entry.runtime_data.energysites
|
||||||
|
if energysite.live_coordinator
|
||||||
for description in ENERGY_LIVE_DESCRIPTIONS
|
for description in ENERGY_LIVE_DESCRIPTIONS
|
||||||
if description.key in energysite.live_coordinator.data
|
if description.key in energysite.live_coordinator.data
|
||||||
)
|
)
|
||||||
@ -558,6 +560,7 @@ async def async_setup_entry(
|
|||||||
entities.extend(
|
entities.extend(
|
||||||
TeslemetryWallConnectorSensorEntity(energysite, din, description)
|
TeslemetryWallConnectorSensorEntity(energysite, din, description)
|
||||||
for energysite in entry.runtime_data.energysites
|
for energysite in entry.runtime_data.energysites
|
||||||
|
if energysite.live_coordinator
|
||||||
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
|
||||||
)
|
)
|
||||||
|
@ -179,3 +179,14 @@ async def test_vehicle_stream(
|
|||||||
|
|
||||||
state = hass.states.get("binary_sensor.test_status")
|
state = hass.states.get("binary_sensor.test_status")
|
||||||
assert state.state == STATE_OFF
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user