161 lines
5.4 KiB
Python
161 lines
5.4 KiB
Python
"""Tessie integration."""
|
|
|
|
import asyncio
|
|
from http import HTTPStatus
|
|
import logging
|
|
|
|
from aiohttp import ClientError, ClientResponseError
|
|
from tesla_fleet_api import EnergySpecific, Tessie
|
|
from tesla_fleet_api.const import Scope
|
|
from tesla_fleet_api.exceptions import TeslaFleetError
|
|
from tessie_api import get_state_of_all_vehicles
|
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import CONF_ACCESS_TOKEN, Platform
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
from homeassistant.helpers.device_registry import DeviceInfo
|
|
|
|
from .const import DOMAIN, MODELS
|
|
from .coordinator import (
|
|
TessieEnergySiteInfoCoordinator,
|
|
TessieEnergySiteLiveCoordinator,
|
|
TessieStateUpdateCoordinator,
|
|
)
|
|
from .models import TessieData, TessieEnergyData, TessieVehicleData
|
|
|
|
PLATFORMS = [
|
|
Platform.BINARY_SENSOR,
|
|
Platform.BUTTON,
|
|
Platform.CLIMATE,
|
|
Platform.COVER,
|
|
Platform.DEVICE_TRACKER,
|
|
Platform.LOCK,
|
|
Platform.MEDIA_PLAYER,
|
|
Platform.NUMBER,
|
|
Platform.SELECT,
|
|
Platform.SENSOR,
|
|
Platform.SWITCH,
|
|
Platform.UPDATE,
|
|
]
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
type TessieConfigEntry = ConfigEntry[TessieData]
|
|
|
|
|
|
async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bool:
|
|
"""Set up Tessie config."""
|
|
api_key = entry.data[CONF_ACCESS_TOKEN]
|
|
session = async_get_clientsession(hass)
|
|
|
|
try:
|
|
state_of_all_vehicles = await get_state_of_all_vehicles(
|
|
session=session,
|
|
api_key=api_key,
|
|
only_active=True,
|
|
)
|
|
except ClientResponseError as e:
|
|
if e.status == HTTPStatus.UNAUTHORIZED:
|
|
raise ConfigEntryAuthFailed from e
|
|
_LOGGER.error("Setup failed, unable to connect to Tessie: %s", e)
|
|
return False
|
|
except ClientError as e:
|
|
raise ConfigEntryNotReady from e
|
|
|
|
vehicles = [
|
|
TessieVehicleData(
|
|
vin=vehicle["vin"],
|
|
data_coordinator=TessieStateUpdateCoordinator(
|
|
hass,
|
|
api_key=api_key,
|
|
vin=vehicle["vin"],
|
|
data=vehicle["last_state"],
|
|
),
|
|
device=DeviceInfo(
|
|
identifiers={(DOMAIN, vehicle["vin"])},
|
|
manufacturer="Tesla",
|
|
configuration_url="https://my.tessie.com/",
|
|
name=vehicle["last_state"]["display_name"],
|
|
model=MODELS.get(
|
|
vehicle["last_state"]["vehicle_config"]["car_type"],
|
|
vehicle["last_state"]["vehicle_config"]["car_type"],
|
|
),
|
|
sw_version=vehicle["last_state"]["vehicle_state"]["car_version"].split(
|
|
" "
|
|
)[0],
|
|
hw_version=vehicle["last_state"]["vehicle_config"]["driver_assist"],
|
|
serial_number=vehicle["vin"],
|
|
),
|
|
)
|
|
for vehicle in state_of_all_vehicles["results"]
|
|
if vehicle["last_state"] is not None
|
|
]
|
|
|
|
# Energy Sites
|
|
tessie = Tessie(session, api_key)
|
|
energysites: list[TessieEnergyData] = []
|
|
|
|
try:
|
|
scopes = await tessie.scopes()
|
|
except TeslaFleetError as e:
|
|
raise ConfigEntryNotReady from e
|
|
|
|
if Scope.ENERGY_DEVICE_DATA in scopes:
|
|
try:
|
|
products = (await tessie.products())["response"]
|
|
except TeslaFleetError as e:
|
|
raise ConfigEntryNotReady from e
|
|
|
|
for product in products:
|
|
if "energy_site_id" in product:
|
|
site_id = product["energy_site_id"]
|
|
if not (
|
|
product["components"]["battery"]
|
|
or product["components"]["solar"]
|
|
or "wall_connectors" in product["components"]
|
|
):
|
|
_LOGGER.debug(
|
|
"Skipping Energy Site %s as it has no components",
|
|
site_id,
|
|
)
|
|
continue
|
|
|
|
api = EnergySpecific(tessie.energy, site_id)
|
|
energysites.append(
|
|
TessieEnergyData(
|
|
api=api,
|
|
id=site_id,
|
|
live_coordinator=TessieEnergySiteLiveCoordinator(hass, api),
|
|
info_coordinator=TessieEnergySiteInfoCoordinator(hass, api),
|
|
device=DeviceInfo(
|
|
identifiers={(DOMAIN, str(site_id))},
|
|
manufacturer="Tesla",
|
|
name=product.get("site_name", "Energy Site"),
|
|
),
|
|
)
|
|
)
|
|
|
|
# Populate coordinator data before forwarding to platforms
|
|
await asyncio.gather(
|
|
*(
|
|
energysite.live_coordinator.async_config_entry_first_refresh()
|
|
for energysite in energysites
|
|
),
|
|
*(
|
|
energysite.info_coordinator.async_config_entry_first_refresh()
|
|
for energysite in energysites
|
|
),
|
|
)
|
|
|
|
entry.runtime_data = TessieData(vehicles, energysites)
|
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
|
|
|
return True
|
|
|
|
|
|
async def async_unload_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bool:
|
|
"""Unload Tessie Config."""
|
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|