"""Teslemetry parent entity class.""" from abc import abstractmethod from typing import Any from tesla_fleet_api import EnergySpecific, VehicleSpecific from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN from .coordinator import ( TeslemetryEnergySiteInfoCoordinator, TeslemetryEnergySiteLiveCoordinator, TeslemetryVehicleDataCoordinator, ) from .helpers import wake_up_vehicle from .models import TeslemetryEnergyData, TeslemetryVehicleData class TeslemetryEntity( CoordinatorEntity[ TeslemetryVehicleDataCoordinator | TeslemetryEnergySiteLiveCoordinator | TeslemetryEnergySiteInfoCoordinator ] ): """Parent class for all Teslemetry entities.""" _attr_has_entity_name = True def __init__( self, coordinator: TeslemetryVehicleDataCoordinator | TeslemetryEnergySiteLiveCoordinator | TeslemetryEnergySiteInfoCoordinator, api: VehicleSpecific | EnergySpecific, key: str, ) -> None: """Initialize common aspects of a Teslemetry entity.""" super().__init__(coordinator) self.api = api self.key = key self._attr_translation_key = self.key self._async_update_attrs() @property def available(self) -> bool: """Return if sensor is available.""" return self.coordinator.last_update_success and self._attr_available @property def _value(self) -> Any | None: """Return a specific value from coordinator data.""" return self.coordinator.data.get(self.key) def get(self, key: str, default: Any | None = None) -> Any | None: """Return a specific value from coordinator data.""" return self.coordinator.data.get(key, default) def get_number(self, key: str, default: float) -> float: """Return a specific number from coordinator data.""" if isinstance(value := self.coordinator.data.get(key), (int, float)): return value return default @property def is_none(self) -> bool: """Return if the value is a literal None.""" return self.get(self.key, False) is None @property def has(self) -> bool: """Return True if a specific value is in coordinator data.""" return self.key in self.coordinator.data def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" self._async_update_attrs() self.async_write_ha_state() @abstractmethod def _async_update_attrs(self) -> None: """Update the attributes of the entity.""" def raise_for_scope(self): """Raise an error if a scope is not available.""" if not self.scoped: raise ServiceValidationError("Missing required scope") class TeslemetryVehicleEntity(TeslemetryEntity): """Parent class for Teslemetry Vehicle entities.""" _last_update: int = 0 def __init__( self, data: TeslemetryVehicleData, key: str, ) -> None: """Initialize common aspects of a Teslemetry entity.""" self._attr_unique_id = f"{data.vin}-{key}" self.vehicle = data self._attr_device_info = data.device super().__init__(data.coordinator, data.api, key) @property def _value(self) -> Any | None: """Return a specific value from coordinator data.""" return self.coordinator.data.get(self.key) async def wake_up_if_asleep(self) -> None: """Wake up the vehicle if its asleep.""" await wake_up_vehicle(self.vehicle) class TeslemetryEnergyLiveEntity(TeslemetryEntity): """Parent class for Teslemetry Energy Site Live entities.""" def __init__( self, data: TeslemetryEnergyData, key: str, ) -> None: """Initialize common aspects of a Teslemetry Energy Site Live entity.""" self._attr_unique_id = f"{data.id}-{key}" self._attr_device_info = data.device super().__init__(data.live_coordinator, data.api, key) class TeslemetryEnergyInfoEntity(TeslemetryEntity): """Parent class for Teslemetry Energy Site Info Entities.""" def __init__( self, data: TeslemetryEnergyData, key: str, ) -> None: """Initialize common aspects of a Teslemetry Energy Site Info entity.""" self._attr_unique_id = f"{data.id}-{key}" self._attr_device_info = data.device super().__init__(data.info_coordinator, data.api, key) class TeslemetryWallConnectorEntity( TeslemetryEntity, CoordinatorEntity[TeslemetryEnergySiteLiveCoordinator] ): """Parent class for Teslemetry Wall Connector Entities.""" _attr_has_entity_name = True def __init__( self, data: TeslemetryEnergyData, din: str, key: str, ) -> None: """Initialize common aspects of a Teslemetry entity.""" self.din = din self._attr_unique_id = f"{data.id}-{din}-{key}" # Find the model from the info coordinator model: str | None = None for wc in data.info_coordinator.data.get("components_wall_connectors", []): if wc["din"] == din: model = wc.get("part_name") break self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, din)}, manufacturer="Tesla", configuration_url="https://teslemetry.com/console", name="Wall Connector", via_device=(DOMAIN, str(data.id)), serial_number=din.split("-")[-1], model=model, ) super().__init__(data.live_coordinator, data.api, key) @property def _value(self) -> int: """Return a specific wall connector value from coordinator data.""" return ( self.coordinator.data.get("wall_connectors", {}) .get(self.din, {}) .get(self.key) )