Skip to content

Commit

Permalink
Add streaming sensors to Teslemetry (#132783)
Browse files Browse the repository at this point in the history
Co-authored-by: Joost Lekkerkerker <[email protected]>
  • Loading branch information
Bre77 and joostlek authored Jan 9, 2025
1 parent cabdae9 commit b6c0257
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 118 deletions.
16 changes: 16 additions & 0 deletions homeassistant/components/teslemetry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
create_handle_vehicle_stream(vin, coordinator),
{"vin": vin},
)
firmware = vehicle_metadata[vin].get("firmware", "Unknown")

vehicles.append(
TeslemetryVehicleData(
api=api,
config_entry=entry,
coordinator=coordinator,
stream=stream,
vin=vin,
firmware=firmware,
device=device,
remove_listener=remove_listener,
)
Expand Down Expand Up @@ -179,6 +182,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -

# Run all first refreshes
await asyncio.gather(
*(async_setup_stream(hass, entry, vehicle) for vehicle in vehicles),
*(
vehicle.coordinator.async_config_entry_first_refresh()
for vehicle in vehicles
Expand Down Expand Up @@ -265,3 +269,15 @@ def handle_vehicle_stream(data: dict) -> None:
coordinator.async_set_updated_data(coordinator.data)

return handle_vehicle_stream


async def async_setup_stream(
hass: HomeAssistant, entry: ConfigEntry, vehicle: TeslemetryVehicleData
):
"""Set up the stream for a vehicle."""

vehicle_stream = vehicle.stream.get_vehicle(vehicle.vin)
await vehicle_stream.get_config()
entry.async_create_background_task(
hass, vehicle_stream.prefer_typed(True), f"Prefer typed for {vehicle.vin}"
)
53 changes: 48 additions & 5 deletions homeassistant/components/teslemetry/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

from tesla_fleet_api import EnergySpecific, VehicleSpecific
from tesla_fleet_api.const import Scope
from teslemetry_stream import Signal

from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
Expand Down Expand Up @@ -73,11 +75,6 @@ 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()
Expand Down Expand Up @@ -236,3 +233,49 @@ def exists(self) -> bool:
return self.key in self.coordinator.data.get("wall_connectors", {}).get(
self.din, {}
)


class TeslemetryVehicleStreamEntity(Entity):
"""Parent class for Teslemetry Vehicle Stream entities."""

_attr_has_entity_name = True

def __init__(
self, data: TeslemetryVehicleData, key: str, streaming_key: Signal
) -> None:
"""Initialize common aspects of a Teslemetry entity."""
self.streaming_key = streaming_key
self.vehicle = data

self.api = data.api
self.stream = data.stream
self.vin = data.vin
self.add_field = data.stream.get_vehicle(self.vin).add_field

self._attr_translation_key = key
self._attr_unique_id = f"{data.vin}-{key}"
self._attr_device_info = data.device

async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
self.async_on_remove(
self.stream.async_add_listener(
self._handle_stream_update,
{"vin": self.vin, "data": {self.streaming_key: None}},
)
)
self.vehicle.config_entry.async_create_background_task(
self.hass,
self.add_field(self.streaming_key),
f"Adding field {self.streaming_key.value} to {self.vehicle.vin}",
)

def _handle_stream_update(self, data: dict[str, Any]) -> None:
"""Handle updated data from the stream."""
self._async_value_from_stream(data["data"][self.streaming_key])
self.async_write_ha_state()

def _async_value_from_stream(self, value: Any) -> None:
"""Update the entity with the latest value from the stream."""
raise NotImplementedError
2 changes: 1 addition & 1 deletion homeassistant/components/teslemetry/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/teslemetry",
"iot_class": "cloud_polling",
"loggers": ["tesla-fleet-api"],
"requirements": ["tesla-fleet-api==0.9.2", "teslemetry-stream==0.4.2"]
"requirements": ["tesla-fleet-api==0.9.2", "teslemetry-stream==0.5.3"]
}
5 changes: 4 additions & 1 deletion homeassistant/components/teslemetry/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from tesla_fleet_api.const import Scope
from teslemetry_stream import TeslemetryStream

from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceInfo

from .coordinator import (
Expand All @@ -34,12 +35,14 @@ class TeslemetryVehicleData:
"""Data for a vehicle in the Teslemetry integration."""

api: VehicleSpecific
config_entry: ConfigEntry
coordinator: TeslemetryVehicleDataCoordinator
stream: TeslemetryStream
vin: str
wakelock = asyncio.Lock()
firmware: str
device: DeviceInfo
remove_listener: Callable
wakelock = asyncio.Lock()


@dataclass
Expand Down
Loading

0 comments on commit b6c0257

Please sign in to comment.