Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Energy History to Tesla Fleet #126878

Merged
merged 39 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6b0b084
Add "Grid Status" and "Storm Watch active" values.
JEMcats Sep 14, 2024
fed5846
Adding Sensors For Energy History
JEMcats Sep 15, 2024
ba8400d
Update Naming
JEMcats Sep 15, 2024
3095a24
updated requirements
JEMcats Sep 15, 2024
3d209e9
Do suggested changes.
JEMcats Sep 15, 2024
39af01b
Change sensors from Measurement to Total.
JEMcats Sep 15, 2024
a084f55
Update coordinator.py
JEMcats Sep 16, 2024
1f713e4
Update coordinator.py
JEMcats Sep 16, 2024
7480afb
Update requirements, change formatting, change history update interva…
JEMcats Sep 16, 2024
1b4aff2
Add more details to Energy History.
JEMcats Sep 17, 2024
7c97870
Add Tests
Bre77 Sep 21, 2024
aa72cda
Add snapshot
Bre77 Sep 21, 2024
d8bd6b2
Update coordinator
Bre77 Sep 21, 2024
5b2af99
Change strings
Bre77 Sep 21, 2024
7069dbe
Rework icons
Bre77 Sep 21, 2024
f38ea93
Add sensors
Bre77 Sep 21, 2024
a5a954b
Update snapshot
Bre77 Sep 21, 2024
c73c3f2
Add rate limit test
Bre77 Sep 21, 2024
78e2d74
Add suggested_display_precision back
Bre77 Sep 22, 2024
9f17ac9
Add better polling
Bre77 Oct 5, 2024
fba4d0a
small cleanup
Bre77 Oct 5, 2024
7b7bd58
fix requirements
Bre77 Oct 5, 2024
b8430b9
Fix test
Bre77 Oct 5, 2024
2c05455
Island status review feedback
Bre77 Oct 24, 2024
24088b9
Merge upstream
Bre77 Oct 24, 2024
25a316a
Update snapshots
Bre77 Oct 24, 2024
d91b3ae
Refix Grid status
Bre77 Oct 24, 2024
6d3cb87
Undo snapshot change
Bre77 Oct 24, 2024
26d489f
Merge branch 'dev' into jemcats
Bre77 Nov 7, 2024
08504c5
ruff
Bre77 Nov 14, 2024
f48e19f
Merge branch 'dev' into jemcats
Bre77 Dec 18, 2024
ee468e5
Remove some changes from PR
Bre77 Dec 18, 2024
89601a3
Revert "Remove some changes from PR"
Bre77 Dec 18, 2024
b17fde1
revert unrelated changes
Bre77 Dec 18, 2024
010cc03
update snapshot
Bre77 Dec 18, 2024
649b44e
Revert icons.json
Bre77 Dec 18, 2024
575998a
Fix snapshot with translations
Bre77 Dec 18, 2024
ee78b6b
Update homeassistant/components/tesla_fleet/coordinator.py
Bre77 Jan 17, 2025
78a4e3a
ruff
Bre77 Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions homeassistant/components/tesla_fleet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from .config_flow import OAuth2FlowHandler
from .const import DOMAIN, LOGGER, MODELS
from .coordinator import (
TeslaFleetEnergySiteHistoryCoordinator,
TeslaFleetEnergySiteInfoCoordinator,
TeslaFleetEnergySiteLiveCoordinator,
TeslaFleetVehicleDataCoordinator,
Expand Down Expand Up @@ -175,9 +176,11 @@ async def _refresh_token() -> str:
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
Expand Down Expand Up @@ -210,6 +213,7 @@ async def _refresh_token() -> str:
TeslaFleetEnergyData(
api=api,
live_coordinator=live_coordinator,
history_coordinator=history_coordinator,
info_coordinator=info_coordinator,
id=site_id,
device=device,
Expand Down
24 changes: 24 additions & 0 deletions homeassistant/components/tesla_fleet/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,30 @@
"Y": "Model Y",
}

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."""
Expand Down
64 changes: 62 additions & 2 deletions homeassistant/components/tesla_fleet/coordinator.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -19,14 +21,15 @@
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 = 90
VEHICLE_INTERVAL = timedelta(seconds=VEHICLE_INTERVAL_SECONDS)
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,
Expand Down Expand Up @@ -182,6 +185,63 @@ async def _async_update_data(self) -> 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."""

updated_once: bool
Bre77 marked this conversation as resolved.
Show resolved Hide resolved

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."""

Expand Down
18 changes: 18 additions & 0 deletions homeassistant/components/tesla_fleet/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from .const import DOMAIN
from .coordinator import (
TeslaFleetEnergySiteHistoryCoordinator,
TeslaFleetEnergySiteInfoCoordinator,
TeslaFleetEnergySiteLiveCoordinator,
TeslaFleetVehicleDataCoordinator,
Expand All @@ -24,6 +25,7 @@ class TeslaFleetEntity(
CoordinatorEntity[
TeslaFleetVehicleDataCoordinator
| TeslaFleetEnergySiteLiveCoordinator
| TeslaFleetEnergySiteHistoryCoordinator
| TeslaFleetEnergySiteInfoCoordinator
]
):
Expand All @@ -37,6 +39,7 @@ def __init__(
self,
coordinator: TeslaFleetVehicleDataCoordinator
| TeslaFleetEnergySiteLiveCoordinator
| TeslaFleetEnergySiteHistoryCoordinator
| TeslaFleetEnergySiteInfoCoordinator,
api: VehicleSpecific | EnergySpecific,
key: str,
Expand Down Expand Up @@ -139,6 +142,21 @@ def __init__(
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."""

Expand Down
71 changes: 68 additions & 3 deletions homeassistant/components/tesla_fleet/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,80 @@
"wall_connector_state": {
"default": "mdi:ev-station"
},
"storm_mode_active": {
"default": "mdi:weather-lightning"
},
"island_status": {
"default": "mdi:help-circle",
"state": {
"on_grid": "mdi:transmission-tower",
"off_grid": "mdi:transmission-tower-off",
"off_grid_unintentional": "mdi:transmission-tower-off",
"island_status_unknown": "mdi:help-circle",
"off_grid_intentional": "mdi:account-cancel"
"off_grid_intentional": "mdi:account-cancel",
"off_grid_unintentional": "mdi:account-alert"
}
},
"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": {
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/tesla_fleet/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from homeassistant.helpers.device_registry import DeviceInfo

from .coordinator import (
TeslaFleetEnergySiteHistoryCoordinator,
TeslaFleetEnergySiteInfoCoordinator,
TeslaFleetEnergySiteLiveCoordinator,
TeslaFleetVehicleDataCoordinator,
Expand Down Expand Up @@ -44,6 +45,7 @@ class TeslaFleetEnergyData:

api: EnergySpecific
live_coordinator: TeslaFleetEnergySiteLiveCoordinator
history_coordinator: TeslaFleetEnergySiteHistoryCoordinator
info_coordinator: TeslaFleetEnergySiteInfoCoordinator
id: int
device: DeviceInfo
Loading