Skip to content

Commit

Permalink
Progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Bre77 committed Dec 14, 2024
1 parent c1be677 commit d864b0f
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 54 deletions.
2 changes: 1 addition & 1 deletion custom_components/teslemetry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
PLATFORMS: Final = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
#Platform.COVER,
Platform.COVER,
Platform.CLIMATE,
#Platform.DEVICE_TRACKER,
#Platform.LOCK,
Expand Down
8 changes: 4 additions & 4 deletions custom_components/teslemetry/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async def async_setup_entry(
TeslemetryPollingClimateEntity(
vehicle, TeslemetryClimateSide.DRIVER, entry.runtime_data.scopes
)
if True or vehicle.api.pre2021 or vehicle.firmware < "2024.44.25"
if vehicle.api.pre2021 or vehicle.firmware < "2030.44.25" # Insufficent streaming data for climate
else TeslemetryStreamingClimateEntity(
vehicle, TeslemetryClimateSide.DRIVER, entry.runtime_data.scopes
)
Expand Down Expand Up @@ -357,7 +357,7 @@ class TeslemetryPollingCabinOverheatProtectionEntity(TeslemetryVehicleEntity, Te
def __init__(
self,
data: TeslemetryVehicleData,
scopes: [Scope],
scopes: list[Scope],
) -> None:
"""Initialize the climate."""

Expand Down Expand Up @@ -400,7 +400,7 @@ class TeslemetryStreamingCabinOverheatProtectionEntity(TeslemetryVehicleComplexS
def __init__(
self,
data: TeslemetryVehicleData,
scopes: [Scope],
scopes: list[Scope],
) -> None:
"""Initialize the climate."""

Expand All @@ -418,7 +418,7 @@ def __init__(
self._attr_supported_features = (
ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
)
if self.get("vehicle_config_cop_user_set_temp_supported"):
if data.coordinator.data.get("vehicle_config_cop_user_set_temp_supported"):
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE

# Scopes
Expand Down
154 changes: 111 additions & 43 deletions custom_components/teslemetry/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

from __future__ import annotations

from itertools import chain
from typing import Any

from tesla_fleet_api.const import WindowCommand, Trunk, Scope, SunRoofCommand
from teslemetry_stream import Signal

from homeassistant.components.cover import (
CoverDeviceClass,
CoverEntity,
Expand All @@ -16,10 +14,16 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from tesla_fleet_api.const import Scope, SunRoofCommand, Trunk, WindowCommand
from teslemetry_stream import Signal

from .entity import TeslemetryVehicleEntity, TeslemetryVehicleStreamEntity, TeslemetryVehicleComplexStreamEntity
from .models import TeslemetryVehicleData
from .entity import (
TeslemetryVehicleComplexStreamEntity,
TeslemetryVehicleEntity,
TeslemetryVehicleStreamEntity,
)
from .helpers import auto_type
from .models import TeslemetryVehicleData

CLOSED = 0
OPEN = 1
Expand All @@ -29,18 +33,38 @@ async def async_setup_entry(
) -> None:
"""Set up the Teslemetry sensor platform from a config entry."""


async_add_entities(
klass(vehicle, entry.runtime_data.scopes)
for vehicle in entry.runtime_data.vehicles
for (klass) in (
TeslemetryPollingWindowEntity if vehicle.pre2021 else TeslemetryStreamingWindowEntity,
TeslemetryChargePortEntity,
TeslemetryFrontTrunkEntity,
TeslemetryRearTrunkEntity,
TeslemetrySunroofEntity,
chain(
(
TeslemetryPollingWindowEntity(vehicle, entry.runtime_data.scopes)
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
else TeslemetryStreamingWindowEntity(vehicle, entry.runtime_data.scopes)
for vehicle in entry.runtime_data.vehicles
),
(
TeslemetryPollingChargePortEntity(vehicle, entry.runtime_data.scopes)
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
else TeslemetryStreamingChargePortEntity(vehicle, entry.runtime_data.scopes)
for vehicle in entry.runtime_data.vehicles
),
(
TeslemetryPollingFrontTrunkEntity(vehicle, entry.runtime_data.scopes)
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
else TeslemetryStreamingFrontTrunkEntity(vehicle, entry.runtime_data.scopes)
for vehicle in entry.runtime_data.vehicles
),
(
TeslemetryPollingRearTrunkEntity(vehicle, entry.runtime_data.scopes)
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
else TeslemetryStreamingRearTrunkEntity(vehicle, entry.runtime_data.scopes)
for vehicle in entry.runtime_data.vehicles
),
(
TeslemetrySunroofEntity(vehicle, entry.runtime_data.scopes)
for vehicle in entry.runtime_data.vehicles
if vehicle.coordinator.data.get("vehicle_config_sun_roof_installed")
)
)

)


Expand Down Expand Up @@ -131,9 +155,9 @@ def _async_data_from_stream(self, data) -> None:
if value := data.get(Signal.RP_WINDOW):
self.rp = value == "WindowStateOpen"

if True in (fd, fp, rd, rp):
if True in (self.fd, self.fp, self.rd, self.rp):
self._attr_is_closed = False
elif None in (fd, fp, rd, rp):
elif None in (self.fd, self.fp, self.rd, self.rp):
self._attr_is_closed = None
else:
self._attr_is_closed = True
Expand All @@ -160,8 +184,9 @@ async def async_close_cover(self, **kwargs: Any) -> None:
self._attr_is_closed = True
self.async_write_ha_state()

class TeslemetryPollingChargePortLatch(TeslemetryVehicleEntity, TeslemetryChargePortEntity):
class TeslemetryPollingChargePortEntity(TeslemetryVehicleEntity, TeslemetryChargePortEntity):
"""Polling cover entity for the charge port."""

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"""Initialize the sensor."""
self.scoped = any(
Expand All @@ -176,11 +201,11 @@ def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"charge_state_charge_port_door_open",
)

def _async_value_from_stream(self, value) -> None:
"""Update the value of the entity."""
self._attr_is_closed = not auto_type(value)
def _async_update_attrs(self) -> None:
"""Update the entity attributes."""
self._attr_is_closed = self.exactly(False)

class TeslemetryStreamingChargePortLatch(TeslemetryVehicleStreamEntity, TeslemetryChargePortEntity):
class TeslemetryStreamingChargePortEntity(TeslemetryVehicleStreamEntity, TeslemetryChargePortEntity):
"""Streaming cover entity for the charge port."""

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
Expand All @@ -200,26 +225,15 @@ def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:

def _async_value_from_stream(self, value) -> None:
"""Update the value of the entity."""
self._attr_is_closed = not auto_type(value)
self._attr_is_closed = value == "ChargePortLatchDisengaged"


class TeslemetryFrontTrunkEntity(TeslemetryVehicleEntity, CoverRestoreEntity):
class TeslemetryFrontTrunkEntity(CoverEntity):
"""Cover entity for the front trunk."""

_attr_device_class = CoverDeviceClass.DOOR
_attr_supported_features = CoverEntityFeature.OPEN

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"""Initialize the sensor."""
self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped:
self._attr_supported_features = CoverEntityFeature(0)
super().__init__(vehicle, "vehicle_state_ft")

def _async_update_attrs(self) -> None:
"""Update the entity attributes."""
self._attr_is_closed = self.exactly(CLOSED)

async def async_open_cover(self, **kwargs: Any) -> None:
"""Open front trunk."""
self.raise_for_scope(Scope.VEHICLE_CMDS)
Expand All @@ -230,24 +244,44 @@ async def async_open_cover(self, **kwargs: Any) -> None:

# In the future this could be extended to add aftermarket close support through a option flow


class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverRestoreEntity):
"""Cover entity for the rear trunk."""

_attr_device_class = CoverDeviceClass.DOOR
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
class TeslemetryPollingFrontTrunkEntity(TeslemetryVehicleEntity, TeslemetryFrontTrunkEntity):
"""Polling cover entity for the front trunk."""

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"""Initialize the sensor."""
self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped:
self._attr_supported_features = CoverEntityFeature(0)
super().__init__(vehicle, "vehicle_state_rt")
super().__init__(vehicle, "vehicle_state_ft")

def _async_update_attrs(self) -> None:
"""Update the entity attributes."""
self._attr_is_closed = self.exactly(CLOSED)

class TeslemetryStreamingFrontTrunkEntity(TeslemetryVehicleStreamEntity, TeslemetryFrontTrunkEntity):
"""Streaming cover entity for the front trunk."""

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"""Initialize the sensor."""
self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped:
self._attr_supported_features = CoverEntityFeature(0)
super().__init__(vehicle, "vehicle_state_ft", Signal.DOOR_STATE)

def _async_value_from_stream(self, value) -> None:
"""Update the entity attributes."""
value = value.get("TrunkFront")
if value is None:
self._attr_is_closed = None
else:
self._attr_is_closed = not value

class TeslemetryRearTrunkEntity(CoverEntity):
"""Cover entity for the rear trunk."""

_attr_device_class = CoverDeviceClass.DOOR
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE

async def async_open_cover(self, **kwargs: Any) -> None:
"""Open rear trunk."""
if self.is_closed is not False:
Expand All @@ -266,7 +300,41 @@ async def async_close_cover(self, **kwargs: Any) -> None:
self._attr_is_closed = True
self.async_write_ha_state()

class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverRestoreEntity):

class TeslemetryPollingRearTrunkEntity(TeslemetryVehicleEntity, TeslemetryRearTrunkEntity):
"""Polling Cover entity for the rear trunk."""

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"""Initialize the sensor."""
self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped:
self._attr_supported_features = CoverEntityFeature(0)
super().__init__(vehicle, "vehicle_state_rt")

def _async_update_attrs(self) -> None:
"""Update the entity attributes."""
self._attr_is_closed = self.exactly(CLOSED)


class TeslemetryStreamingRearTrunkEntity(TeslemetryVehicleStreamEntity, TeslemetryRearTrunkEntity):
"""Polling Cover entity for the rear trunk."""

def __init__(self, vehicle: TeslemetryVehicleData, scopes: list[Scope]) -> None:
"""Initialize the sensor."""
self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped:
self._attr_supported_features = CoverEntityFeature(0)
super().__init__(vehicle, "vehicle_state_rt", Signal.DOOR_STATE)

def _async_value_from_stream(self, value) -> None:
"""Update the entity attributes."""
value = value.get("TrunkRear")
if value is None:
self._attr_is_closed = None
else:
self._attr_is_closed = not value

class TeslemetrySunroofEntity(TeslemetryVehicleEntity, TeslemetryWindowEntity):
"""Cover entity for the sunroof."""

_attr_device_class = CoverDeviceClass.WINDOW
Expand Down
22 changes: 16 additions & 6 deletions custom_components/teslemetry/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .const import DOMAIN, LOGGER

from .coordinator import (
TeslemetryEnergySiteInfoCoordinator,
Expand Down Expand Up @@ -72,7 +72,12 @@ async def async_added_to_hass(self) -> None:

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])

try:
self._async_value_from_stream(data["data"][self.streaming_key])
except Exception as e:
LOGGER.error("Error updating %s: %s", self._attr_translation_key, e)
LOGGER.debug(data)
self.async_write_ha_state()

def _async_value_from_stream(self, value: Any) -> None:
Expand Down Expand Up @@ -114,9 +119,14 @@ async def async_added_to_hass(self) -> None:

def _handle_stream_update(self, data: dict[str, Any]) -> None:
"""Handle updated data from the stream."""
data = {key: data["data"][key] for key in self.streaming_keys if key in data["data"]}
self._async_data_from_stream(data["data"])
self.async_write_ha_state()
#data = {key: data["data"][key] for key in self.streaming_keys if key in data["data"]}
if any(key in data["data"] for key in self.streaming_keys):
try:
self._async_data_from_stream(data["data"])
except Exception as e:
LOGGER.error("Error updating %s: %s", self._attr_translation_key, e)
LOGGER.debug(data)
self.async_write_ha_state()

def _async_data_from_stream(self, data: Any) -> None:
"""Update the entity with the latest value from the stream."""
Expand Down Expand Up @@ -168,7 +178,7 @@ def get_number(self, key: str, default: float) -> float:
return default

def exactly(self, value: Any, key: str | None = None) -> bool | None:
"""Return if a key exactly matches the valug but retain None."""
"""Return if a key exactly matches the value but retain None."""
key = key or self.key
if value is None:
return self.get(key, False) is None
Expand Down

0 comments on commit d864b0f

Please sign in to comment.