Skip to content

Commit

Permalink
Inheritance rework
Browse files Browse the repository at this point in the history
  • Loading branch information
Bre77 committed Feb 7, 2024
1 parent 43764ad commit 5db5f6d
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 145 deletions.
2 changes: 1 addition & 1 deletion custom_components/teslemetry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
for product in products:
if "vin" in product and Scopes.VEHICLE_DEVICE_DATA in scopes:
# Remove the protobuff 'cached_data' that we do not use to save memory
product.pop('cached_data', None)
product.pop("cached_data", None)
vin = product["vin"]
api = VehicleSpecific(teslemetry.vehicle, vin)
coordinator = TeslemetryVehicleDataCoordinator(hass, api, product)
Expand Down
34 changes: 19 additions & 15 deletions custom_components/teslemetry/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Binary Sensor platform for Teslemetry integration."""
from __future__ import annotations

from itertools import chain
from collections.abc import Callable
from dataclasses import dataclass

Expand Down Expand Up @@ -162,21 +163,24 @@ async def async_setup_entry(
data = hass.data[DOMAIN][entry.entry_id]

async_add_entities(
TeslemetryVehicleBinarySensorEntity(vehicle, description)
for vehicle in data.vehicles
for description in VEHICLE_DESCRIPTIONS
)

async_add_entities(
TeslemetryEnergyLiveBinarySensorEntity(energysite, description)
for energysite in data.energysites
for description in ENERGY_LIVE_DESCRIPTIONS
)

async_add_entities(
TeslemetryEnergyInfoBinarySensorEntity(energysite, description)
for energysite in data.energysites
for description in ENERGY_INFO_DESCRIPTIONS
chain(
# Vehicles
TeslemetryVehicleBinarySensorEntity(vehicle, description)
for vehicle in data.vehicles
for description in VEHICLE_DESCRIPTIONS
),
(
# Energy Site Live
TeslemetryEnergyLiveBinarySensorEntity(energysite, description)
for energysite in data.energysites
for description in ENERGY_LIVE_DESCRIPTIONS
),
(
# Energy Site Info
TeslemetryEnergyInfoBinarySensorEntity(energysite, description)
for energysite in data.energysites
for description in ENERGY_INFO_DESCRIPTIONS
),
)


Expand Down
7 changes: 1 addition & 6 deletions custom_components/teslemetry/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class TeslemetryButtonEntityDescription(ButtonEntityDescription):


DESCRIPTIONS: tuple[TeslemetryButtonEntityDescription, ...] = (
TeslemetryButtonEntityDescription(key="wake"), # Every button also runs wakeup
TeslemetryButtonEntityDescription(key="wake"), # Every button runs wakeup
TeslemetryButtonEntityDescription(
key="flash_lights", func=lambda api: api.flash_lights()
),
Expand Down Expand Up @@ -68,11 +68,6 @@ def __init__(
super().__init__(data, description.key)
self.entity_description = description

@property
def avaliable(self) -> bool:
"""Return if the cover is available."""
return True

async def async_press(self) -> None:
"""Press the button."""
await self.wake_up_if_asleep()
Expand Down
21 changes: 11 additions & 10 deletions custom_components/teslemetry/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
ENERGY_INFO_INTERVAL = timedelta(seconds=300)

ENDPOINTS = [
VehicleDataEndpoints.CHARGE_STATE,
VehicleDataEndpoints.CLIMATE_STATE,
#VehicleDataEndpoints.CLOSURES_STATE,
VehicleDataEndpoints.DRIVE_STATE,
#VehicleDataEndpoints.GUI_SETTINGS,
VehicleDataEndpoints.LOCATION_DATA,
#VehicleDataEndpoints.VEHICLE_CONFIG,
VehicleDataEndpoints.VEHICLE_STATE,
]
VehicleDataEndpoints.CHARGE_STATE,
VehicleDataEndpoints.CLIMATE_STATE,
# VehicleDataEndpoints.CLOSURES_STATE,
VehicleDataEndpoints.DRIVE_STATE,
# VehicleDataEndpoints.GUI_SETTINGS,
VehicleDataEndpoints.LOCATION_DATA,
# VehicleDataEndpoints.VEHICLE_CONFIG,
VehicleDataEndpoints.VEHICLE_STATE,
]
#


def flatten(data: dict[str, Any], parent: str | None = None) -> dict[str, Any]:
"""Flatten the data structure."""
result = {}
Expand Down Expand Up @@ -68,7 +69,7 @@ async def _async_update_data(self) -> dict[str, Any]:
except TeslaFleetError as e:
raise UpdateFailed(e.message) from e

return {**self.data , **flatten(data["response"])}
return {**self.data, **flatten(data["response"])}


class TeslemetryEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
Expand Down
50 changes: 22 additions & 28 deletions custom_components/teslemetry/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,19 @@ def __init__(self, data: TeslemetryVehicleData, scoped) -> None:
if not scoped:
self._attr_supported_features = CoverEntityFeature(0)

@property
def avaliable(self) -> bool:
"""Return if the cover is available."""
return True

@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
return (
self.get("vehicle_state_fd_window") == TeslemetryCoverStates.CLOSED
and self.get("vehicle_state_fp_window") == TeslemetryCoverStates.CLOSED
and self.get("vehicle_state_rd_window") == TeslemetryCoverStates.CLOSED
and self.get("vehicle_state_rp_window") == TeslemetryCoverStates.CLOSED
)
fd = self.get("vehicle_state_fd_window")
fp = self.get("vehicle_state_fp_window")
rd = self.get("vehicle_state_rd_window")
rp = self.get("vehicle_state_rp_window")

if fd or fp or rd or rp == TeslemetryCoverStates.OPEN:
return False
if fd and fp and rd and rp == TeslemetryCoverStates.CLOSED:
return True
return None

async def async_open_cover(self, **kwargs: Any) -> None:
"""Vent windows."""
Expand Down Expand Up @@ -106,11 +105,6 @@ def __init__(self, vehicle: TeslemetryVehicleData, scoped) -> None:
if not scoped:
self._attr_supported_features = CoverEntityFeature(0)

@property
def avaliable(self) -> bool:
"""Return if the cover is available."""
return True

@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
Expand Down Expand Up @@ -145,15 +139,15 @@ def __init__(self, vehicle: TeslemetryVehicleData, scoped) -> None:
if not scoped:
self._attr_supported_features = CoverEntityFeature(0)

@property
def avaliable(self) -> bool:
"""Return if the cover is available."""
return True

@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
return self.get() == TeslemetryCoverStates.CLOSED
value = self.get()
if value == TeslemetryCoverStates.CLOSED:
return True
if value == TeslemetryCoverStates.OPEN:
return False
return None

async def async_open_cover(self, **kwargs: Any) -> None:
"""Open front trunk."""
Expand All @@ -176,15 +170,15 @@ def __init__(self, vehicle: TeslemetryVehicleData, scoped) -> None:
if not scoped:
self._attr_supported_features = CoverEntityFeature(0)

@property
def avaliable(self) -> bool:
"""Return if the cover is available."""
return True

@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
return self.get() == TeslemetryCoverStates.CLOSED
value = self.get()
if value == TeslemetryCoverStates.CLOSED:
return True
if value == TeslemetryCoverStates.OPEN:
return False
return None

async def async_open_cover(self, **kwargs: Any) -> None:
"""Open rear trunk."""
Expand Down
5 changes: 5 additions & 0 deletions custom_components/teslemetry/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,8 @@ def longitude(self) -> float | None:
def latitude(self) -> float | None:
"""Return the latitude of the device tracker."""
return self.get("drive_state_active_route_latitude")

@property
def location_name(self) -> str | None:
"""Return the location of the device tracker."""
return self.get("drive_state_active_route_destination")
13 changes: 5 additions & 8 deletions custom_components/teslemetry/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,14 @@ def __init__(
| TeslemetryEnergySiteLiveCoordinator
| TeslemetryEnergySiteInfoCoordinator,
api: VehicleSpecific | EnergySpecific,
key:str
key: str,
) -> None:
"""Initialize common aspects of a Teslemetry entity."""
super().__init__(coordinator)
self.api = api
self.key = key
self._attr_translation_key = key

@property
def available(self) -> bool:
"""Return if sensor is available."""
return (
self.coordinator.last_update_success and self.key in self.coordinator.data
)

def get(self, key: str | None = None, default: Any | None = None) -> Any:
"""Return a specific value from coordinator data."""
return self.coordinator.data.get(key or self.key, default)
Expand All @@ -60,6 +53,10 @@ def set(self, *args: Any) -> None:
self.coordinator.data[key] = value
self.async_write_ha_state()

def has(self, key: str) -> bool:
"""Return True if a specific value is in coordinator data."""
return key in self.coordinator.data

def raise_for_scope(self):
"""Raise an error if a scope is not available."""
if not self.scoped:
Expand Down
4 changes: 2 additions & 2 deletions custom_components/teslemetry/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ async def async_setup_entry(
async_add_entities(
klass(vehicle, Scopes.VEHICLE_CMDS in data.scopes)
for klass in (
TeslemetryLockEntity,
TeslemetryVehicleLockEntity,
TeslemetryCableLockEntity,
TeslemetrySpeedLimitEntity,
)
for vehicle in data.vehicles
)


class TeslemetryLockEntity(TeslemetryVehicleEntity, LockEntity):
class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity):
"""Lock entity for Teslemetry."""

def __init__(self, data: TeslemetryVehicleData, scoped: bool) -> None:
Expand Down
42 changes: 22 additions & 20 deletions custom_components/teslemetry/number.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Number platform for Teslemetry integration."""
from __future__ import annotations

from itertools import chain
from collections.abc import Callable
from dataclasses import dataclass

Expand Down Expand Up @@ -110,27 +111,28 @@ async def async_setup_entry(
"""Set up the Teslemetry sensor platform from a config entry."""
data = hass.data[DOMAIN][entry.entry_id]

# Add vehicle entities
async_add_entities(
TeslemetryVehicleNumberEntity(
vehicle,
description,
any(scope in data.scopes for scope in description.scopes),
)
for vehicle in data.vehicles
for description in VEHICLE_DESCRIPTIONS
)

# Add energy site entities
async_add_entities(
TeslemetryEnergyInfoNumberSensorEntity(
energysite,
description,
any(scope in data.scopes for scope in description.scopes),
)
for energysite in data.energysites
for description in ENERGY_INFO_DESCRIPTIONS
if description.key in energysite.info_coordinator.data
chain(
# Add vehicle entities
TeslemetryVehicleNumberEntity(
vehicle,
description,
any(scope in data.scopes for scope in description.scopes),
)
for vehicle in data.vehicles
for description in VEHICLE_DESCRIPTIONS
),
(
# Add energy site entities
TeslemetryEnergyInfoNumberSensorEntity(
energysite,
description,
any(scope in data.scopes for scope in description.scopes),
)
for energysite in data.energysites
for description in ENERGY_INFO_DESCRIPTIONS
if description.key in energysite.info_coordinator.data
),
)


Expand Down
50 changes: 40 additions & 10 deletions custom_components/teslemetry/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,52 @@ async def async_setup_entry(
entities = []
for vehicle in data.vehicles:
scoped = Scopes.VEHICLE_CMDS in data.scopes
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_left", scoped))
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_right", scoped))
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_left", scoped
)
)
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_right", scoped
)
)
if vehicle.coordinator.data.get("vehicle_config_rear_seat_heaters"):
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_rear_left", scoped))
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_rear_center", scoped))
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_rear_right", scoped))
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_rear_left", scoped
)
)
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_rear_center", scoped
)
)
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_rear_right", scoped
)
)
if vehicle.coordinator.data.get("vehicle_config_third_row_seats") != "None":
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_third_row_left", scoped))
entities.append(TeslemetrySeatHeaterSelectEntity(vehicle, "climate_state_seat_heater_third_row_right", scoped))
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_third_row_left", scoped
)
)
entities.append(
TeslemetrySeatHeaterSelectEntity(
vehicle, "climate_state_seat_heater_third_row_right", scoped
)
)

for energysite in data.energysites:
for description in ENERGY_INFO_DESCRIPTIONS:
if description.key in energysite.info_coordinator.data:
entities.append(TeslemetryEnergySiteSelectEntity(
energysite, description, Scopes.ENERGY_CMDS in data.scopes
))
entities.append(
TeslemetryEnergySiteSelectEntity(
energysite, description, Scopes.ENERGY_CMDS in data.scopes
)
)

async_add_entities(entities)

Expand Down
Loading

0 comments on commit 5db5f6d

Please sign in to comment.