core/homeassistant/components/tessie/__init__.py

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)