177 lines
6.1 KiB
Python
177 lines
6.1 KiB
Python
"""Device tracker platform for Teslemetry integration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
|
|
from teslemetry_stream import TeslemetryStreamVehicle
|
|
from teslemetry_stream.const import TeslaLocation
|
|
|
|
from homeassistant.components.device_tracker.config_entry import (
|
|
TrackerEntity,
|
|
TrackerEntityDescription,
|
|
)
|
|
from homeassistant.const import STATE_HOME
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.restore_state import RestoreEntity
|
|
|
|
from . import TeslemetryConfigEntry
|
|
from .entity import TeslemetryVehicleEntity, TeslemetryVehicleStreamEntity
|
|
from .models import TeslemetryVehicleData
|
|
|
|
PARALLEL_UPDATES = 0
|
|
|
|
|
|
@dataclass(frozen=True, kw_only=True)
|
|
class TeslemetryDeviceTrackerEntityDescription(TrackerEntityDescription):
|
|
"""Describe a Teslemetry device tracker entity."""
|
|
|
|
value_listener: Callable[
|
|
[TeslemetryStreamVehicle, Callable[[TeslaLocation | None], None]],
|
|
Callable[[], None],
|
|
]
|
|
name_listener: (
|
|
Callable[
|
|
[TeslemetryStreamVehicle, Callable[[str | None], None]], Callable[[], None]
|
|
]
|
|
| None
|
|
) = None
|
|
streaming_firmware: str
|
|
polling_prefix: str | None = None
|
|
|
|
|
|
DESCRIPTIONS: tuple[TeslemetryDeviceTrackerEntityDescription, ...] = (
|
|
TeslemetryDeviceTrackerEntityDescription(
|
|
key="location",
|
|
polling_prefix="drive_state",
|
|
value_listener=lambda x, y: x.listen_Location(y),
|
|
streaming_firmware="2024.26",
|
|
),
|
|
TeslemetryDeviceTrackerEntityDescription(
|
|
key="route",
|
|
polling_prefix="drive_state_active_route",
|
|
value_listener=lambda x, y: x.listen_DestinationLocation(y),
|
|
name_listener=lambda x, y: x.listen_DestinationName(y),
|
|
streaming_firmware="2024.26",
|
|
),
|
|
TeslemetryDeviceTrackerEntityDescription(
|
|
key="origin",
|
|
value_listener=lambda x, y: x.listen_OriginLocation(y),
|
|
streaming_firmware="2024.26",
|
|
entity_registry_enabled_default=False,
|
|
),
|
|
)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: TeslemetryConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the Teslemetry device tracker platform from a config entry."""
|
|
|
|
entities: list[
|
|
TeslemetryPollingDeviceTrackerEntity | TeslemetryStreamingDeviceTrackerEntity
|
|
] = []
|
|
for vehicle in entry.runtime_data.vehicles:
|
|
for description in DESCRIPTIONS:
|
|
if vehicle.api.pre2021 or vehicle.firmware < description.streaming_firmware:
|
|
if description.polling_prefix:
|
|
entities.append(
|
|
TeslemetryPollingDeviceTrackerEntity(vehicle, description)
|
|
)
|
|
else:
|
|
entities.append(
|
|
TeslemetryStreamingDeviceTrackerEntity(vehicle, description)
|
|
)
|
|
|
|
async_add_entities(entities)
|
|
|
|
|
|
class TeslemetryPollingDeviceTrackerEntity(TeslemetryVehicleEntity, TrackerEntity):
|
|
"""Base class for Teslemetry Tracker Entities."""
|
|
|
|
entity_description: TeslemetryDeviceTrackerEntityDescription
|
|
|
|
def __init__(
|
|
self,
|
|
vehicle: TeslemetryVehicleData,
|
|
description: TeslemetryDeviceTrackerEntityDescription,
|
|
) -> None:
|
|
"""Initialize the device tracker."""
|
|
self.entity_description = description
|
|
super().__init__(vehicle, description.key)
|
|
|
|
def _async_update_attrs(self) -> None:
|
|
"""Update the attributes of the entity."""
|
|
self._attr_latitude = self.get(
|
|
f"{self.entity_description.polling_prefix}_latitude"
|
|
)
|
|
self._attr_longitude = self.get(
|
|
f"{self.entity_description.polling_prefix}_longitude"
|
|
)
|
|
self._attr_location_name = self.get(
|
|
f"{self.entity_description.polling_prefix}_destination"
|
|
)
|
|
if self._attr_location_name == "Home":
|
|
self._attr_location_name = STATE_HOME
|
|
self._attr_available = (
|
|
self._attr_latitude is not None and self._attr_longitude is not None
|
|
)
|
|
|
|
|
|
class TeslemetryStreamingDeviceTrackerEntity(
|
|
TeslemetryVehicleStreamEntity, TrackerEntity, RestoreEntity
|
|
):
|
|
"""Base class for Teslemetry Tracker Entities."""
|
|
|
|
entity_description: TeslemetryDeviceTrackerEntityDescription
|
|
|
|
def __init__(
|
|
self,
|
|
vehicle: TeslemetryVehicleData,
|
|
description: TeslemetryDeviceTrackerEntityDescription,
|
|
) -> None:
|
|
"""Initialize the device tracker."""
|
|
self.entity_description = description
|
|
super().__init__(vehicle, description.key)
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Handle entity which will be added."""
|
|
await super().async_added_to_hass()
|
|
if (state := await self.async_get_last_state()) is not None:
|
|
self._attr_state = state.state
|
|
self._attr_latitude = state.attributes.get("latitude")
|
|
self._attr_longitude = state.attributes.get("longitude")
|
|
self._attr_location_name = state.attributes.get("location_name")
|
|
self.async_on_remove(
|
|
self.entity_description.value_listener(
|
|
self.vehicle.stream_vehicle, self._location_callback
|
|
)
|
|
)
|
|
if self.entity_description.name_listener:
|
|
self.async_on_remove(
|
|
self.entity_description.name_listener(
|
|
self.vehicle.stream_vehicle, self._name_callback
|
|
)
|
|
)
|
|
|
|
def _location_callback(self, location: TeslaLocation | None) -> None:
|
|
"""Update the value of the entity."""
|
|
if location is None:
|
|
self._attr_available = False
|
|
else:
|
|
self._attr_available = True
|
|
self._attr_latitude = location.latitude
|
|
self._attr_longitude = location.longitude
|
|
self.async_write_ha_state()
|
|
|
|
def _name_callback(self, name: str | None) -> None:
|
|
"""Update the value of the entity."""
|
|
self._attr_location_name = name
|
|
if self._attr_location_name == "Home":
|
|
self._attr_location_name = STATE_HOME
|
|
self.async_write_ha_state()
|