mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Add Energy History to Tesla Fleet (#126878)
Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: JEMcats <hurst-status09@icloud.com> Co-authored-by: JEMcats <jakobmattheis@icloud.com>
This commit is contained in:
parent
b4f4b06f29
commit
689d7d3cd9
@ -36,6 +36,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
||||
|
||||
from .const import DOMAIN, LOGGER, MODELS
|
||||
from .coordinator import (
|
||||
TeslaFleetEnergySiteHistoryCoordinator,
|
||||
TeslaFleetEnergySiteInfoCoordinator,
|
||||
TeslaFleetEnergySiteLiveCoordinator,
|
||||
TeslaFleetVehicleDataCoordinator,
|
||||
@ -176,9 +177,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) -
|
||||
api = EnergySpecific(tesla.energy, site_id)
|
||||
|
||||
live_coordinator = TeslaFleetEnergySiteLiveCoordinator(hass, api)
|
||||
history_coordinator = TeslaFleetEnergySiteHistoryCoordinator(hass, api)
|
||||
info_coordinator = TeslaFleetEnergySiteInfoCoordinator(hass, api, product)
|
||||
|
||||
await live_coordinator.async_config_entry_first_refresh()
|
||||
await history_coordinator.async_config_entry_first_refresh()
|
||||
await info_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# Create energy site model
|
||||
@ -211,6 +214,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) -
|
||||
TeslaFleetEnergyData(
|
||||
api=api,
|
||||
live_coordinator=live_coordinator,
|
||||
history_coordinator=history_coordinator,
|
||||
info_coordinator=info_coordinator,
|
||||
id=site_id,
|
||||
device=device,
|
||||
|
@ -37,6 +37,30 @@ MODELS = {
|
||||
"T": "Tesla Semi",
|
||||
}
|
||||
|
||||
ENERGY_HISTORY_FIELDS = [
|
||||
"solar_energy_exported",
|
||||
"generator_energy_exported",
|
||||
"grid_energy_imported",
|
||||
"grid_services_energy_imported",
|
||||
"grid_services_energy_exported",
|
||||
"grid_energy_exported_from_solar",
|
||||
"grid_energy_exported_from_generator",
|
||||
"grid_energy_exported_from_battery",
|
||||
"battery_energy_exported",
|
||||
"battery_energy_imported_from_grid",
|
||||
"battery_energy_imported_from_solar",
|
||||
"battery_energy_imported_from_generator",
|
||||
"consumer_energy_imported_from_grid",
|
||||
"consumer_energy_imported_from_solar",
|
||||
"consumer_energy_imported_from_battery",
|
||||
"consumer_energy_imported_from_generator",
|
||||
"total_home_usage",
|
||||
"total_battery_charge",
|
||||
"total_battery_discharge",
|
||||
"total_solar_generation",
|
||||
"total_grid_energy_exported",
|
||||
]
|
||||
|
||||
|
||||
class TeslaFleetState(StrEnum):
|
||||
"""Teslemetry Vehicle States."""
|
||||
|
@ -1,10 +1,12 @@
|
||||
"""Tesla Fleet Data Coordinator."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from random import randint
|
||||
from time import time
|
||||
from typing import Any
|
||||
|
||||
from tesla_fleet_api import EnergySpecific, VehicleSpecific
|
||||
from tesla_fleet_api.const import VehicleDataEndpoint
|
||||
from tesla_fleet_api.const import TeslaEnergyPeriod, VehicleDataEndpoint
|
||||
from tesla_fleet_api.exceptions import (
|
||||
InvalidToken,
|
||||
LoginRequired,
|
||||
@ -19,7 +21,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import LOGGER, TeslaFleetState
|
||||
from .const import ENERGY_HISTORY_FIELDS, LOGGER, TeslaFleetState
|
||||
|
||||
VEHICLE_INTERVAL_SECONDS = 300
|
||||
VEHICLE_INTERVAL = timedelta(seconds=VEHICLE_INTERVAL_SECONDS)
|
||||
@ -27,6 +29,7 @@ VEHICLE_WAIT = timedelta(minutes=15)
|
||||
|
||||
ENERGY_INTERVAL_SECONDS = 60
|
||||
ENERGY_INTERVAL = timedelta(seconds=ENERGY_INTERVAL_SECONDS)
|
||||
ENERGY_HISTORY_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
ENDPOINTS = [
|
||||
VehicleDataEndpoint.CHARGE_STATE,
|
||||
@ -182,6 +185,61 @@ class TeslaFleetEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]])
|
||||
return data
|
||||
|
||||
|
||||
class TeslaFleetEnergySiteHistoryCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
"""Class to manage fetching energy site history import and export from the Tesla Fleet API."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, api: EnergySpecific) -> None:
|
||||
"""Initialize Tesla Fleet Energy Site History coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
LOGGER,
|
||||
name=f"Tesla Fleet Energy History {api.energy_site_id}",
|
||||
update_interval=timedelta(seconds=300),
|
||||
)
|
||||
self.api = api
|
||||
self.data = {}
|
||||
self.updated_once = False
|
||||
|
||||
async def async_config_entry_first_refresh(self) -> None:
|
||||
"""Set up the data coordinator."""
|
||||
await super().async_config_entry_first_refresh()
|
||||
|
||||
# Calculate seconds until next 5 minute period plus a random delay
|
||||
delta = randint(310, 330) - (int(time()) % 300)
|
||||
self.logger.debug("Scheduling next %s refresh in %s seconds", self.name, delta)
|
||||
self.update_interval = timedelta(seconds=delta)
|
||||
self._schedule_refresh()
|
||||
self.update_interval = ENERGY_HISTORY_INTERVAL
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Update energy site history data using Tesla Fleet API."""
|
||||
|
||||
try:
|
||||
data = (await self.api.energy_history(TeslaEnergyPeriod.DAY))["response"]
|
||||
except RateLimited as e:
|
||||
LOGGER.warning(
|
||||
"%s rate limited, will retry in %s seconds",
|
||||
self.name,
|
||||
e.data.get("after"),
|
||||
)
|
||||
if "after" in e.data:
|
||||
self.update_interval = timedelta(seconds=int(e.data["after"]))
|
||||
return self.data
|
||||
except (InvalidToken, OAuthExpired, LoginRequired) as e:
|
||||
raise ConfigEntryAuthFailed from e
|
||||
except TeslaFleetError as e:
|
||||
raise UpdateFailed(e.message) from e
|
||||
self.updated_once = True
|
||||
|
||||
# Add all time periods together
|
||||
output = {key: 0 for key in ENERGY_HISTORY_FIELDS}
|
||||
for period in data.get("time_series", []):
|
||||
for key in ENERGY_HISTORY_FIELDS:
|
||||
output[key] += period.get(key, 0)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
class TeslaFleetEnergySiteInfoCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
"""Class to manage fetching energy site info from the TeslaFleet API."""
|
||||
|
||||
|
@ -12,6 +12,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import (
|
||||
TeslaFleetEnergySiteHistoryCoordinator,
|
||||
TeslaFleetEnergySiteInfoCoordinator,
|
||||
TeslaFleetEnergySiteLiveCoordinator,
|
||||
TeslaFleetVehicleDataCoordinator,
|
||||
@ -24,6 +25,7 @@ class TeslaFleetEntity(
|
||||
CoordinatorEntity[
|
||||
TeslaFleetVehicleDataCoordinator
|
||||
| TeslaFleetEnergySiteLiveCoordinator
|
||||
| TeslaFleetEnergySiteHistoryCoordinator
|
||||
| TeslaFleetEnergySiteInfoCoordinator
|
||||
]
|
||||
):
|
||||
@ -37,6 +39,7 @@ class TeslaFleetEntity(
|
||||
self,
|
||||
coordinator: TeslaFleetVehicleDataCoordinator
|
||||
| TeslaFleetEnergySiteLiveCoordinator
|
||||
| TeslaFleetEnergySiteHistoryCoordinator
|
||||
| TeslaFleetEnergySiteInfoCoordinator,
|
||||
api: VehicleSpecific | EnergySpecific,
|
||||
key: str,
|
||||
@ -139,6 +142,21 @@ class TeslaFleetEnergyLiveEntity(TeslaFleetEntity):
|
||||
super().__init__(data.live_coordinator, data.api, key)
|
||||
|
||||
|
||||
class TeslaFleetEnergyHistoryEntity(TeslaFleetEntity):
|
||||
"""Parent class for TeslaFleet Energy Site History entities."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: TeslaFleetEnergyData,
|
||||
key: str,
|
||||
) -> None:
|
||||
"""Initialize common aspects of a Tesla Fleet Energy Site History entity."""
|
||||
self._attr_unique_id = f"{data.id}-{key}"
|
||||
self._attr_device_info = data.device
|
||||
|
||||
super().__init__(data.history_coordinator, data.api, key)
|
||||
|
||||
|
||||
class TeslaFleetEnergyInfoEntity(TeslaFleetEntity):
|
||||
"""Parent class for TeslaFleet Energy Site Info entities."""
|
||||
|
||||
|
@ -232,6 +232,69 @@
|
||||
"island_status_unknown": "mdi:help-circle",
|
||||
"off_grid_intentional": "mdi:account-cancel"
|
||||
}
|
||||
},
|
||||
"total_home_usage": {
|
||||
"default": "mdi:home-lightning-bolt"
|
||||
},
|
||||
"total_battery_charge": {
|
||||
"default": "mdi:battery-arrow-up"
|
||||
},
|
||||
"total_battery_discharge": {
|
||||
"default": "mdi:battery-arrow-down"
|
||||
},
|
||||
"total_solar_production": {
|
||||
"default": "mdi:solar-power-variant"
|
||||
},
|
||||
"grid_energy_imported": {
|
||||
"default": "mdi:transmission-tower-import"
|
||||
},
|
||||
"total_grid_energy_exported": {
|
||||
"default": "mdi:transmission-tower-export"
|
||||
},
|
||||
"solar_energy_exported": {
|
||||
"default": "mdi:solar-power-variant"
|
||||
},
|
||||
"generator_energy_exported": {
|
||||
"default": "mdi:generator-stationary"
|
||||
},
|
||||
"grid_services_energy_imported": {
|
||||
"default": "mdi:transmission-tower-import"
|
||||
},
|
||||
"grid_services_energy_exported": {
|
||||
"default": "mdi:transmission-tower-export"
|
||||
},
|
||||
"grid_energy_exported_from_solar": {
|
||||
"default": "mdi:solar-power"
|
||||
},
|
||||
"grid_energy_exported_from_generator": {
|
||||
"default": "mdi:generator-stationary"
|
||||
},
|
||||
"grid_energy_exported_from_battery": {
|
||||
"default": "mdi:battery-arrow-down"
|
||||
},
|
||||
"battery_energy_exported": {
|
||||
"default": "mdi:battery-arrow-down"
|
||||
},
|
||||
"battery_energy_imported_from_grid": {
|
||||
"default": "mdi:transmission-tower-import"
|
||||
},
|
||||
"battery_energy_imported_from_solar": {
|
||||
"default": "mdi:solar-power"
|
||||
},
|
||||
"battery_energy_imported_from_generator": {
|
||||
"default": "mdi:generator-stationary"
|
||||
},
|
||||
"consumer_energy_imported_from_grid": {
|
||||
"default": "mdi:transmission-tower-import"
|
||||
},
|
||||
"consumer_energy_imported_from_solar": {
|
||||
"default": "mdi:solar-power"
|
||||
},
|
||||
"consumer_energy_imported_from_battery": {
|
||||
"default": "mdi:home-battery"
|
||||
},
|
||||
"consumer_energy_imported_from_generator": {
|
||||
"default": "mdi:generator-stationary"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
@ -11,6 +11,7 @@ from tesla_fleet_api.const import Scope
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
|
||||
from .coordinator import (
|
||||
TeslaFleetEnergySiteHistoryCoordinator,
|
||||
TeslaFleetEnergySiteInfoCoordinator,
|
||||
TeslaFleetEnergySiteLiveCoordinator,
|
||||
TeslaFleetVehicleDataCoordinator,
|
||||
@ -44,6 +45,7 @@ class TeslaFleetEnergyData:
|
||||
|
||||
api: EnergySpecific
|
||||
live_coordinator: TeslaFleetEnergySiteLiveCoordinator
|
||||
history_coordinator: TeslaFleetEnergySiteHistoryCoordinator
|
||||
info_coordinator: TeslaFleetEnergySiteInfoCoordinator
|
||||
id: int
|
||||
device: DeviceInfo
|
||||
|
@ -35,8 +35,9 @@ from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.variance import ignore_variance
|
||||
|
||||
from . import TeslaFleetConfigEntry
|
||||
from .const import TeslaFleetState
|
||||
from .const import ENERGY_HISTORY_FIELDS, TeslaFleetState
|
||||
from .entity import (
|
||||
TeslaFleetEnergyHistoryEntity,
|
||||
TeslaFleetEnergyInfoEntity,
|
||||
TeslaFleetEnergyLiveEntity,
|
||||
TeslaFleetVehicleEntity,
|
||||
@ -415,6 +416,21 @@ WALL_CONNECTOR_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
|
||||
),
|
||||
)
|
||||
|
||||
ENERGY_HISTORY_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = tuple(
|
||||
SensorEntityDescription(
|
||||
key=key,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
suggested_display_precision=2,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=(
|
||||
key.startswith("total") or key == "grid_energy_imported"
|
||||
),
|
||||
)
|
||||
for key in ENERGY_HISTORY_FIELDS
|
||||
)
|
||||
|
||||
ENERGY_INFO_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="vpp_backup_reserve_percent",
|
||||
@ -450,6 +466,13 @@ async def async_setup_entry(
|
||||
for description in ENERGY_LIVE_DESCRIPTIONS
|
||||
if description.key in energysite.live_coordinator.data
|
||||
),
|
||||
( # Add energy site history
|
||||
TeslaFleetEnergyHistorySensorEntity(energysite, description)
|
||||
for energysite in entry.runtime_data.energysites
|
||||
for description in ENERGY_HISTORY_DESCRIPTIONS
|
||||
if energysite.info_coordinator.data.get("components_battery")
|
||||
or energysite.info_coordinator.data.get("components_solar")
|
||||
),
|
||||
( # Add wall connectors
|
||||
TeslaFleetWallConnectorSensorEntity(energysite, wc["din"], description)
|
||||
for energysite in entry.runtime_data.energysites
|
||||
@ -540,7 +563,25 @@ class TeslaFleetEnergyLiveSensorEntity(TeslaFleetEnergyLiveEntity, SensorEntity)
|
||||
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update the attributes of the sensor."""
|
||||
self._attr_available = not self.is_none
|
||||
self._attr_native_value = self._value
|
||||
|
||||
|
||||
class TeslaFleetEnergyHistorySensorEntity(TeslaFleetEnergyHistoryEntity, SensorEntity):
|
||||
"""Base class for Tesla Fleet energy site metric sensors."""
|
||||
|
||||
entity_description: SensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: TeslaFleetEnergyData,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self.entity_description = description
|
||||
super().__init__(data, description.key)
|
||||
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update the attributes of the sensor."""
|
||||
self._attr_native_value = self._value
|
||||
|
||||
|
||||
|
@ -424,6 +424,9 @@
|
||||
"off_grid_intentional": "Disconnected intentionally"
|
||||
}
|
||||
},
|
||||
"storm_mode_active": {
|
||||
"name": "Storm Watch active"
|
||||
},
|
||||
"vehicle_state_tpms_pressure_fl": {
|
||||
"name": "Tire pressure front left"
|
||||
},
|
||||
@ -453,6 +456,69 @@
|
||||
},
|
||||
"wall_connector_state": {
|
||||
"name": "State code"
|
||||
},
|
||||
"solar_energy_exported": {
|
||||
"name": "Solar exported"
|
||||
},
|
||||
"generator_energy_exported": {
|
||||
"name": "Generator exported"
|
||||
},
|
||||
"grid_energy_imported": {
|
||||
"name": "Grid imported"
|
||||
},
|
||||
"grid_services_energy_imported": {
|
||||
"name": "Grid services imported"
|
||||
},
|
||||
"grid_services_energy_exported": {
|
||||
"name": "Grid services exported"
|
||||
},
|
||||
"grid_energy_exported_from_solar": {
|
||||
"name": "Grid exported from solar"
|
||||
},
|
||||
"grid_energy_exported_from_generator": {
|
||||
"name": "Grid exported from generator"
|
||||
},
|
||||
"grid_energy_exported_from_battery": {
|
||||
"name": "Grid exported from battery"
|
||||
},
|
||||
"battery_energy_exported": {
|
||||
"name": "Battery exported"
|
||||
},
|
||||
"battery_energy_imported_from_grid": {
|
||||
"name": "Battery imported from grid"
|
||||
},
|
||||
"battery_energy_imported_from_solar": {
|
||||
"name": "Battery imported from solar"
|
||||
},
|
||||
"battery_energy_imported_from_generator": {
|
||||
"name": "Battery imported from generator"
|
||||
},
|
||||
"consumer_energy_imported_from_grid": {
|
||||
"name": "Consumer imported from grid"
|
||||
},
|
||||
"consumer_energy_imported_from_solar": {
|
||||
"name": "Consumer imported from solar"
|
||||
},
|
||||
"consumer_energy_imported_from_battery": {
|
||||
"name": "Consumer imported from battery"
|
||||
},
|
||||
"consumer_energy_imported_from_generator": {
|
||||
"name": "Consumer imported from generator"
|
||||
},
|
||||
"total_home_usage": {
|
||||
"name": "Home usage"
|
||||
},
|
||||
"total_battery_charge": {
|
||||
"name": "Battery charged"
|
||||
},
|
||||
"total_battery_discharge": {
|
||||
"name": "Battery discharged"
|
||||
},
|
||||
"total_solar_generation": {
|
||||
"name": "Solar generated"
|
||||
},
|
||||
"total_grid_energy_exported": {
|
||||
"name": "Grid exported"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
@ -15,6 +15,7 @@ from homeassistant.components.tesla_fleet.const import DOMAIN, SCOPES
|
||||
|
||||
from .const import (
|
||||
COMMAND_OK,
|
||||
ENERGY_HISTORY,
|
||||
LIVE_STATUS,
|
||||
PRODUCTS,
|
||||
SITE_INFO,
|
||||
@ -177,6 +178,16 @@ def mock_request():
|
||||
yield mock_request
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_energy_history():
|
||||
"""Mock Teslemetry Energy Specific site_info method."""
|
||||
with patch(
|
||||
"homeassistant.components.teslemetry.EnergySpecific.energy_history",
|
||||
return_value=ENERGY_HISTORY,
|
||||
) as mock_live_status:
|
||||
yield mock_live_status
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_signed_command() -> Generator[AsyncMock]:
|
||||
"""Mock Tesla Fleet Api signed_command method."""
|
||||
|
@ -11,6 +11,7 @@ PRODUCTS = load_json_object_fixture("products.json", DOMAIN)
|
||||
VEHICLE_DATA = load_json_object_fixture("vehicle_data.json", DOMAIN)
|
||||
VEHICLE_DATA_ALT = load_json_object_fixture("vehicle_data_alt.json", DOMAIN)
|
||||
LIVE_STATUS = load_json_object_fixture("live_status.json", DOMAIN)
|
||||
ENERGY_HISTORY = load_json_object_fixture("energy_history.json", DOMAIN)
|
||||
SITE_INFO = load_json_object_fixture("site_info.json", DOMAIN)
|
||||
|
||||
COMMAND_OK = {"response": {"result": True, "reason": ""}}
|
||||
|
45
tests/components/tesla_fleet/fixtures/energy_history.json
Normal file
45
tests/components/tesla_fleet/fixtures/energy_history.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"response": {
|
||||
"period": "day",
|
||||
"time_series": [
|
||||
{
|
||||
"timestamp": "2023-06-01T01:00:00-07:00",
|
||||
"solar_energy_exported": 70940,
|
||||
"generator_energy_exported": 0,
|
||||
"grid_energy_imported": 521,
|
||||
"grid_services_energy_imported": 17.53125,
|
||||
"grid_services_energy_exported": 3.80859375,
|
||||
"grid_energy_exported_from_solar": 43660,
|
||||
"grid_energy_exported_from_generator": 0,
|
||||
"grid_energy_exported_from_battery": 19,
|
||||
"battery_energy_exported": 10030,
|
||||
"battery_energy_imported_from_grid": 80,
|
||||
"battery_energy_imported_from_solar": 16800,
|
||||
"battery_energy_imported_from_generator": 0,
|
||||
"consumer_energy_imported_from_grid": 441,
|
||||
"consumer_energy_imported_from_solar": 10480,
|
||||
"consumer_energy_imported_from_battery": 10011,
|
||||
"consumer_energy_imported_from_generator": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "2023-06-01T01:05:00-07:00",
|
||||
"solar_energy_exported": 140940,
|
||||
"generator_energy_exported": 1,
|
||||
"grid_energy_imported": 1021,
|
||||
"grid_services_energy_imported": 27.53125,
|
||||
"grid_services_energy_exported": 6.80859375,
|
||||
"grid_energy_exported_from_solar": 83660,
|
||||
"grid_energy_exported_from_generator": 0,
|
||||
"grid_energy_exported_from_battery": 29,
|
||||
"battery_energy_exported": 20030,
|
||||
"battery_energy_imported_from_grid": 0,
|
||||
"battery_energy_imported_from_solar": 26800,
|
||||
"battery_energy_imported_from_generator": 0,
|
||||
"consumer_energy_imported_from_grid": 841,
|
||||
"consumer_energy_imported_from_solar": 20480,
|
||||
"consumer_energy_imported_from_battery": 20011,
|
||||
"consumer_energy_imported_from_generator": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@ from tesla_fleet_api.exceptions import (
|
||||
|
||||
from homeassistant.components.tesla_fleet.const import AUTHORIZE_URL
|
||||
from homeassistant.components.tesla_fleet.coordinator import (
|
||||
ENERGY_HISTORY_INTERVAL,
|
||||
ENERGY_INTERVAL,
|
||||
ENERGY_INTERVAL_SECONDS,
|
||||
VEHICLE_INTERVAL,
|
||||
@ -317,6 +318,21 @@ async def test_energy_site_refresh_error(
|
||||
assert normal_config_entry.state is state
|
||||
|
||||
|
||||
# Test Energy History Coordinator
|
||||
@pytest.mark.parametrize(("side_effect", "state"), ERRORS)
|
||||
async def test_energy_history_refresh_error(
|
||||
hass: HomeAssistant,
|
||||
normal_config_entry: MockConfigEntry,
|
||||
mock_energy_history: AsyncMock,
|
||||
side_effect: TeslaFleetError,
|
||||
state: ConfigEntryState,
|
||||
) -> None:
|
||||
"""Test coordinator refresh with an error."""
|
||||
mock_energy_history.side_effect = side_effect
|
||||
await setup_platform(hass, normal_config_entry)
|
||||
assert normal_config_entry.state is state
|
||||
|
||||
|
||||
async def test_energy_live_refresh_ratelimited(
|
||||
hass: HomeAssistant,
|
||||
normal_config_entry: MockConfigEntry,
|
||||
@ -379,6 +395,39 @@ async def test_energy_info_refresh_ratelimited(
|
||||
assert mock_site_info.call_count == 3
|
||||
|
||||
|
||||
async def test_energy_history_refresh_ratelimited(
|
||||
hass: HomeAssistant,
|
||||
normal_config_entry: MockConfigEntry,
|
||||
mock_energy_history: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test coordinator refresh handles 429."""
|
||||
|
||||
await setup_platform(hass, normal_config_entry)
|
||||
|
||||
mock_energy_history.side_effect = RateLimited(
|
||||
{"after": int(ENERGY_HISTORY_INTERVAL.total_seconds() + 10)}
|
||||
)
|
||||
freezer.tick(ENERGY_HISTORY_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_energy_history.call_count == 2
|
||||
|
||||
freezer.tick(ENERGY_HISTORY_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Should not call for another 10 seconds
|
||||
assert mock_energy_history.call_count == 2
|
||||
|
||||
freezer.tick(ENERGY_HISTORY_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_energy_history.call_count == 3
|
||||
|
||||
|
||||
async def test_init_region_issue(
|
||||
hass: HomeAssistant,
|
||||
normal_config_entry: MockConfigEntry,
|
||||
|
Loading…
x
Reference in New Issue
Block a user