-
-
Notifications
You must be signed in to change notification settings - Fork 30.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add number platform to Tesla Fleet (#125985)
* Add number platform * Actually add the umber platform files
- Loading branch information
Showing
5 changed files
with
576 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
"""Number platform for Tesla Fleet integration.""" | ||
|
||
from __future__ import annotations | ||
|
||
from collections.abc import Awaitable, Callable | ||
from dataclasses import dataclass | ||
from itertools import chain | ||
from typing import Any | ||
|
||
from tesla_fleet_api import EnergySpecific, VehicleSpecific | ||
from tesla_fleet_api.const import Scope | ||
|
||
from homeassistant.components.number import ( | ||
NumberDeviceClass, | ||
NumberEntity, | ||
NumberEntityDescription, | ||
NumberMode, | ||
) | ||
from homeassistant.const import PERCENTAGE, PRECISION_WHOLE, UnitOfElectricCurrent | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
from homeassistant.helpers.icon import icon_for_battery_level | ||
|
||
from . import TeslaFleetConfigEntry | ||
from .entity import TeslaFleetEnergyInfoEntity, TeslaFleetVehicleEntity | ||
from .helpers import handle_command, handle_vehicle_command | ||
from .models import TeslaFleetEnergyData, TeslaFleetVehicleData | ||
|
||
PARALLEL_UPDATES = 0 | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class TeslaFleetNumberVehicleEntityDescription(NumberEntityDescription): | ||
"""Describes TeslaFleet Number entity.""" | ||
|
||
func: Callable[[VehicleSpecific, float], Awaitable[Any]] | ||
native_min_value: float | ||
native_max_value: float | ||
min_key: str | None = None | ||
max_key: str | ||
scopes: list[Scope] | ||
|
||
|
||
VEHICLE_DESCRIPTIONS: tuple[TeslaFleetNumberVehicleEntityDescription, ...] = ( | ||
TeslaFleetNumberVehicleEntityDescription( | ||
key="charge_state_charge_current_request", | ||
native_step=PRECISION_WHOLE, | ||
native_min_value=0, | ||
native_max_value=32, | ||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, | ||
device_class=NumberDeviceClass.CURRENT, | ||
mode=NumberMode.AUTO, | ||
max_key="charge_state_charge_current_request_max", | ||
func=lambda api, value: api.set_charging_amps(value), | ||
scopes=[Scope.VEHICLE_CHARGING_CMDS], | ||
), | ||
TeslaFleetNumberVehicleEntityDescription( | ||
key="charge_state_charge_limit_soc", | ||
native_step=PRECISION_WHOLE, | ||
native_min_value=50, | ||
native_max_value=100, | ||
native_unit_of_measurement=PERCENTAGE, | ||
device_class=NumberDeviceClass.BATTERY, | ||
mode=NumberMode.AUTO, | ||
min_key="charge_state_charge_limit_soc_min", | ||
max_key="charge_state_charge_limit_soc_max", | ||
func=lambda api, value: api.set_charge_limit(value), | ||
scopes=[Scope.VEHICLE_CHARGING_CMDS, Scope.VEHICLE_CMDS], | ||
), | ||
) | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class TeslaFleetNumberBatteryEntityDescription(NumberEntityDescription): | ||
"""Describes TeslaFleet Number entity.""" | ||
|
||
func: Callable[[EnergySpecific, float], Awaitable[Any]] | ||
requires: str | None = None | ||
|
||
|
||
ENERGY_INFO_DESCRIPTIONS: tuple[TeslaFleetNumberBatteryEntityDescription, ...] = ( | ||
TeslaFleetNumberBatteryEntityDescription( | ||
key="backup_reserve_percent", | ||
func=lambda api, value: api.backup(int(value)), | ||
requires="components_battery", | ||
), | ||
TeslaFleetNumberBatteryEntityDescription( | ||
key="off_grid_vehicle_charging_reserve_percent", | ||
func=lambda api, value: api.off_grid_vehicle_charging_reserve(int(value)), | ||
requires="components_off_grid_vehicle_charging_reserve_supported", | ||
), | ||
) | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, | ||
entry: TeslaFleetConfigEntry, | ||
async_add_entities: AddEntitiesCallback, | ||
) -> None: | ||
"""Set up the TeslaFleet number platform from a config entry.""" | ||
|
||
async_add_entities( | ||
chain( | ||
( # Add vehicle entities | ||
TeslaFleetVehicleNumberEntity( | ||
vehicle, | ||
description, | ||
entry.runtime_data.scopes, | ||
) | ||
for vehicle in entry.runtime_data.vehicles | ||
for description in VEHICLE_DESCRIPTIONS | ||
), | ||
( # Add energy site entities | ||
TeslaFleetEnergyInfoNumberSensorEntity( | ||
energysite, | ||
description, | ||
entry.runtime_data.scopes, | ||
) | ||
for energysite in entry.runtime_data.energysites | ||
for description in ENERGY_INFO_DESCRIPTIONS | ||
if description.requires is None | ||
or energysite.info_coordinator.data.get(description.requires) | ||
), | ||
) | ||
) | ||
|
||
|
||
class TeslaFleetVehicleNumberEntity(TeslaFleetVehicleEntity, NumberEntity): | ||
"""Vehicle number entity base class.""" | ||
|
||
entity_description: TeslaFleetNumberVehicleEntityDescription | ||
|
||
def __init__( | ||
self, | ||
data: TeslaFleetVehicleData, | ||
description: TeslaFleetNumberVehicleEntityDescription, | ||
scopes: list[Scope], | ||
) -> None: | ||
"""Initialize the number entity.""" | ||
self.scoped = any(scope in scopes for scope in description.scopes) | ||
self.entity_description = description | ||
super().__init__( | ||
data, | ||
description.key, | ||
) | ||
|
||
def _async_update_attrs(self) -> None: | ||
"""Update the attributes of the entity.""" | ||
self._attr_native_value = self._value | ||
|
||
if (min_key := self.entity_description.min_key) is not None: | ||
self._attr_native_min_value = self.get_number( | ||
min_key, | ||
self.entity_description.native_min_value, | ||
) | ||
else: | ||
self._attr_native_min_value = self.entity_description.native_min_value | ||
|
||
self._attr_native_max_value = self.get_number( | ||
self.entity_description.max_key, | ||
self.entity_description.native_max_value, | ||
) | ||
|
||
async def async_set_native_value(self, value: float) -> None: | ||
"""Set new value.""" | ||
value = int(value) | ||
self.raise_for_read_only(self.entity_description.scopes[0]) | ||
await self.wake_up_if_asleep() | ||
await handle_vehicle_command(self.entity_description.func(self.api, value)) | ||
self._attr_native_value = value | ||
self.async_write_ha_state() | ||
|
||
|
||
class TeslaFleetEnergyInfoNumberSensorEntity(TeslaFleetEnergyInfoEntity, NumberEntity): | ||
"""Energy info number entity base class.""" | ||
|
||
entity_description: TeslaFleetNumberBatteryEntityDescription | ||
_attr_native_step = PRECISION_WHOLE | ||
_attr_native_min_value = 0 | ||
_attr_native_max_value = 100 | ||
_attr_device_class = NumberDeviceClass.BATTERY | ||
_attr_native_unit_of_measurement = PERCENTAGE | ||
|
||
def __init__( | ||
self, | ||
data: TeslaFleetEnergyData, | ||
description: TeslaFleetNumberBatteryEntityDescription, | ||
scopes: list[Scope], | ||
) -> None: | ||
"""Initialize the number entity.""" | ||
self.scoped = Scope.ENERGY_CMDS in scopes | ||
self.entity_description = description | ||
super().__init__(data, description.key) | ||
|
||
def _async_update_attrs(self) -> None: | ||
"""Update the attributes of the entity.""" | ||
self._attr_native_value = self._value | ||
self._attr_icon = icon_for_battery_level(self.native_value) | ||
|
||
async def async_set_native_value(self, value: float) -> None: | ||
"""Set new value.""" | ||
value = int(value) | ||
self.raise_for_read_only(Scope.ENERGY_CMDS) | ||
await handle_command(self.entity_description.func(self.api, value)) | ||
self._attr_native_value = value | ||
self.async_write_ha_state() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.