mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Refactor helpers and bump Teslemetry (#119246)
This commit is contained in:
parent
04c8a5574a
commit
4a9ebd9af1
@ -102,6 +102,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
|||||||
manufacturer="Tesla",
|
manufacturer="Tesla",
|
||||||
configuration_url="https://teslemetry.com/console",
|
configuration_url="https://teslemetry.com/console",
|
||||||
name=product.get("site_name", "Energy Site"),
|
name=product.get("site_name", "Energy Site"),
|
||||||
|
serial_number=str(site_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
energysites.append(
|
energysites.append(
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryVehicleEntity
|
from .entity import TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_vehicle_command
|
||||||
from .models import TeslemetryVehicleData
|
from .models import TeslemetryVehicleData
|
||||||
|
|
||||||
|
|
||||||
@ -84,4 +85,4 @@ class TeslemetryButtonEntity(TeslemetryVehicleEntity, ButtonEntity):
|
|||||||
"""Press the button."""
|
"""Press the button."""
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
if self.entity_description.func:
|
if self.entity_description.func:
|
||||||
await self.handle_command(self.entity_description.func(self))
|
await handle_vehicle_command(self.entity_description.func(self))
|
||||||
|
@ -26,6 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .const import DOMAIN, TeslemetryClimateSide
|
from .const import DOMAIN, TeslemetryClimateSide
|
||||||
from .entity import TeslemetryVehicleEntity
|
from .entity import TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_vehicle_command
|
||||||
from .models import TeslemetryVehicleData
|
from .models import TeslemetryVehicleData
|
||||||
|
|
||||||
DEFAULT_MIN_TEMP = 15
|
DEFAULT_MIN_TEMP = 15
|
||||||
@ -114,7 +115,7 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
|
|||||||
|
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.auto_conditioning_start())
|
await handle_vehicle_command(self.api.auto_conditioning_start())
|
||||||
|
|
||||||
self._attr_hvac_mode = HVACMode.HEAT_COOL
|
self._attr_hvac_mode = HVACMode.HEAT_COOL
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -124,7 +125,7 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
|
|||||||
|
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.auto_conditioning_stop())
|
await handle_vehicle_command(self.api.auto_conditioning_stop())
|
||||||
|
|
||||||
self._attr_hvac_mode = HVACMode.OFF
|
self._attr_hvac_mode = HVACMode.OFF
|
||||||
self._attr_preset_mode = self._attr_preset_modes[0]
|
self._attr_preset_mode = self._attr_preset_modes[0]
|
||||||
@ -135,7 +136,7 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
|
|||||||
|
|
||||||
if temp := kwargs.get(ATTR_TEMPERATURE):
|
if temp := kwargs.get(ATTR_TEMPERATURE):
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.set_temps(
|
self.api.set_temps(
|
||||||
driver_temp=temp,
|
driver_temp=temp,
|
||||||
passenger_temp=temp,
|
passenger_temp=temp,
|
||||||
@ -159,7 +160,7 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
|
|||||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||||
"""Set the climate preset mode."""
|
"""Set the climate preset mode."""
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.set_climate_keeper_mode(
|
self.api.set_climate_keeper_mode(
|
||||||
climate_keeper_mode=self._attr_preset_modes.index(preset_mode)
|
climate_keeper_mode=self._attr_preset_modes.index(preset_mode)
|
||||||
)
|
)
|
||||||
@ -261,7 +262,7 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
|
|||||||
)
|
)
|
||||||
|
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.set_cop_temp(cop_mode))
|
await handle_vehicle_command(self.api.set_cop_temp(cop_mode))
|
||||||
self._attr_target_temperature = temp
|
self._attr_target_temperature = temp
|
||||||
|
|
||||||
if mode := kwargs.get(ATTR_HVAC_MODE):
|
if mode := kwargs.get(ATTR_HVAC_MODE):
|
||||||
@ -271,15 +272,15 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
|
|||||||
|
|
||||||
async def _async_set_cop(self, hvac_mode: HVACMode) -> None:
|
async def _async_set_cop(self, hvac_mode: HVACMode) -> None:
|
||||||
if hvac_mode == HVACMode.OFF:
|
if hvac_mode == HVACMode.OFF:
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.set_cabin_overheat_protection(on=False, fan_only=False)
|
self.api.set_cabin_overheat_protection(on=False, fan_only=False)
|
||||||
)
|
)
|
||||||
elif hvac_mode == HVACMode.COOL:
|
elif hvac_mode == HVACMode.COOL:
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.set_cabin_overheat_protection(on=True, fan_only=False)
|
self.api.set_cabin_overheat_protection(on=True, fan_only=False)
|
||||||
)
|
)
|
||||||
elif hvac_mode == HVACMode.FAN_ONLY:
|
elif hvac_mode == HVACMode.FAN_ONLY:
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.set_cabin_overheat_protection(on=True, fan_only=True)
|
self.api.set_cabin_overheat_protection(on=True, fan_only=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryVehicleEntity
|
from .entity import TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_vehicle_command
|
||||||
from .models import TeslemetryVehicleData
|
from .models import TeslemetryVehicleData
|
||||||
|
|
||||||
OPEN = 1
|
OPEN = 1
|
||||||
@ -88,7 +89,9 @@ class TeslemetryWindowEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
"""Vent windows."""
|
"""Vent windows."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.window_control(command=WindowCommand.VENT))
|
await handle_vehicle_command(
|
||||||
|
self.api.window_control(command=WindowCommand.VENT)
|
||||||
|
)
|
||||||
self._attr_is_closed = False
|
self._attr_is_closed = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -96,7 +99,9 @@ class TeslemetryWindowEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
"""Close windows."""
|
"""Close windows."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.window_control(command=WindowCommand.CLOSE))
|
await handle_vehicle_command(
|
||||||
|
self.api.window_control(command=WindowCommand.CLOSE)
|
||||||
|
)
|
||||||
self._attr_is_closed = True
|
self._attr_is_closed = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -127,7 +132,7 @@ class TeslemetryChargePortEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
"""Open charge port."""
|
"""Open charge port."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.charge_port_door_open())
|
await handle_vehicle_command(self.api.charge_port_door_open())
|
||||||
self._attr_is_closed = False
|
self._attr_is_closed = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -135,7 +140,7 @@ class TeslemetryChargePortEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
"""Close charge port."""
|
"""Close charge port."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.charge_port_door_close())
|
await handle_vehicle_command(self.api.charge_port_door_close())
|
||||||
self._attr_is_closed = True
|
self._attr_is_closed = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -162,7 +167,7 @@ class TeslemetryFrontTrunkEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
"""Open front trunk."""
|
"""Open front trunk."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.actuate_trunk(Trunk.FRONT))
|
await handle_vehicle_command(self.api.actuate_trunk(Trunk.FRONT))
|
||||||
self._attr_is_closed = False
|
self._attr_is_closed = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -198,7 +203,7 @@ class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
if self.is_closed is not False:
|
if self.is_closed is not False:
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.actuate_trunk(Trunk.REAR))
|
await handle_vehicle_command(self.api.actuate_trunk(Trunk.REAR))
|
||||||
self._attr_is_closed = False
|
self._attr_is_closed = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -207,6 +212,6 @@ class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverEntity):
|
|||||||
if self.is_closed is not True:
|
if self.is_closed is not True:
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.actuate_trunk(Trunk.REAR))
|
await handle_vehicle_command(self.api.actuate_trunk(Trunk.REAR))
|
||||||
self._attr_is_closed = True
|
self._attr_is_closed = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
"""Teslemetry parent entity class."""
|
"""Teslemetry parent entity class."""
|
||||||
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
import asyncio
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from tesla_fleet_api import EnergySpecific, VehicleSpecific
|
from tesla_fleet_api import EnergySpecific, VehicleSpecific
|
||||||
from tesla_fleet_api.exceptions import TeslaFleetError
|
|
||||||
|
|
||||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN, LOGGER, TeslemetryState
|
from .const import DOMAIN
|
||||||
from .coordinator import (
|
from .coordinator import (
|
||||||
TeslemetryEnergySiteInfoCoordinator,
|
TeslemetryEnergySiteInfoCoordinator,
|
||||||
TeslemetryEnergySiteLiveCoordinator,
|
TeslemetryEnergySiteLiveCoordinator,
|
||||||
TeslemetryVehicleDataCoordinator,
|
TeslemetryVehicleDataCoordinator,
|
||||||
)
|
)
|
||||||
|
from .helpers import wake_up_vehicle
|
||||||
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
||||||
|
|
||||||
|
|
||||||
@ -76,15 +75,6 @@ class TeslemetryEntity(
|
|||||||
"""Return True if a specific value is in coordinator data."""
|
"""Return True if a specific value is in coordinator data."""
|
||||||
return self.key in self.coordinator.data
|
return self.key in self.coordinator.data
|
||||||
|
|
||||||
async def handle_command(self, command) -> dict[str, Any]:
|
|
||||||
"""Handle a command."""
|
|
||||||
try:
|
|
||||||
result = await command
|
|
||||||
except TeslaFleetError as e:
|
|
||||||
raise HomeAssistantError(f"Teslemetry command failed, {e.message}") from e
|
|
||||||
LOGGER.debug("Command result: %s", result)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
self._async_update_attrs()
|
self._async_update_attrs()
|
||||||
@ -113,7 +103,7 @@ class TeslemetryVehicleEntity(TeslemetryEntity):
|
|||||||
"""Initialize common aspects of a Teslemetry entity."""
|
"""Initialize common aspects of a Teslemetry entity."""
|
||||||
|
|
||||||
self._attr_unique_id = f"{data.vin}-{key}"
|
self._attr_unique_id = f"{data.vin}-{key}"
|
||||||
self._wakelock = data.wakelock
|
self.vehicle = data
|
||||||
|
|
||||||
self._attr_device_info = data.device
|
self._attr_device_info = data.device
|
||||||
super().__init__(data.coordinator, data.api, key)
|
super().__init__(data.coordinator, data.api, key)
|
||||||
@ -125,44 +115,7 @@ class TeslemetryVehicleEntity(TeslemetryEntity):
|
|||||||
|
|
||||||
async def wake_up_if_asleep(self) -> None:
|
async def wake_up_if_asleep(self) -> None:
|
||||||
"""Wake up the vehicle if its asleep."""
|
"""Wake up the vehicle if its asleep."""
|
||||||
async with self._wakelock:
|
await wake_up_vehicle(self.vehicle)
|
||||||
times = 0
|
|
||||||
while self.coordinator.data["state"] != TeslemetryState.ONLINE:
|
|
||||||
try:
|
|
||||||
if times == 0:
|
|
||||||
cmd = await self.api.wake_up()
|
|
||||||
else:
|
|
||||||
cmd = await self.api.vehicle()
|
|
||||||
state = cmd["response"]["state"]
|
|
||||||
except TeslaFleetError as e:
|
|
||||||
raise HomeAssistantError(str(e)) from e
|
|
||||||
self.coordinator.data["state"] = state
|
|
||||||
if state != TeslemetryState.ONLINE:
|
|
||||||
times += 1
|
|
||||||
if times >= 4: # Give up after 30 seconds total
|
|
||||||
raise HomeAssistantError("Could not wake up vehicle")
|
|
||||||
await asyncio.sleep(times * 5)
|
|
||||||
|
|
||||||
async def handle_command(self, command) -> dict[str, Any]:
|
|
||||||
"""Handle a vehicle command."""
|
|
||||||
result = await super().handle_command(command)
|
|
||||||
if (response := result.get("response")) is None:
|
|
||||||
if error := result.get("error"):
|
|
||||||
# No response with error
|
|
||||||
raise HomeAssistantError(error)
|
|
||||||
# No response without error (unexpected)
|
|
||||||
raise HomeAssistantError(f"Unknown response: {response}")
|
|
||||||
if (result := response.get("result")) is not True:
|
|
||||||
if reason := response.get("reason"):
|
|
||||||
if reason in ("already_set", "not_charging", "requested"):
|
|
||||||
# Reason is acceptable
|
|
||||||
return result
|
|
||||||
# Result of false with reason
|
|
||||||
raise HomeAssistantError(reason)
|
|
||||||
# Result of false without reason (unexpected)
|
|
||||||
raise HomeAssistantError("Command failed with no reason")
|
|
||||||
# Response with result of true
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class TeslemetryEnergyLiveEntity(TeslemetryEntity):
|
class TeslemetryEnergyLiveEntity(TeslemetryEntity):
|
||||||
|
63
homeassistant/components/teslemetry/helpers.py
Normal file
63
homeassistant/components/teslemetry/helpers.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""Teslemetry helper functions."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from tesla_fleet_api.exceptions import TeslaFleetError
|
||||||
|
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
|
from .const import LOGGER, TeslemetryState
|
||||||
|
|
||||||
|
|
||||||
|
async def wake_up_vehicle(vehicle) -> None:
|
||||||
|
"""Wake up a vehicle."""
|
||||||
|
async with vehicle.wakelock:
|
||||||
|
times = 0
|
||||||
|
while vehicle.coordinator.data["state"] != TeslemetryState.ONLINE:
|
||||||
|
try:
|
||||||
|
if times == 0:
|
||||||
|
cmd = await vehicle.api.wake_up()
|
||||||
|
else:
|
||||||
|
cmd = await vehicle.api.vehicle()
|
||||||
|
state = cmd["response"]["state"]
|
||||||
|
except TeslaFleetError as e:
|
||||||
|
raise HomeAssistantError(str(e)) from e
|
||||||
|
vehicle.coordinator.data["state"] = state
|
||||||
|
if state != TeslemetryState.ONLINE:
|
||||||
|
times += 1
|
||||||
|
if times >= 4: # Give up after 30 seconds total
|
||||||
|
raise HomeAssistantError("Could not wake up vehicle")
|
||||||
|
await asyncio.sleep(times * 5)
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_command(command) -> dict[str, Any]:
|
||||||
|
"""Handle a command."""
|
||||||
|
try:
|
||||||
|
result = await command
|
||||||
|
except TeslaFleetError as e:
|
||||||
|
raise HomeAssistantError(f"Teslemetry command failed, {e.message}") from e
|
||||||
|
LOGGER.debug("Command result: %s", result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_vehicle_command(command) -> dict[str, Any]:
|
||||||
|
"""Handle a vehicle command."""
|
||||||
|
result = await handle_command(command)
|
||||||
|
if (response := result.get("response")) is None:
|
||||||
|
if error := result.get("error"):
|
||||||
|
# No response with error
|
||||||
|
raise HomeAssistantError(error)
|
||||||
|
# No response without error (unexpected)
|
||||||
|
raise HomeAssistantError(f"Unknown response: {response}")
|
||||||
|
if (result := response.get("result")) is not True:
|
||||||
|
if reason := response.get("reason"):
|
||||||
|
if reason in ("already_set", "not_charging", "requested"):
|
||||||
|
# Reason is acceptable
|
||||||
|
return result
|
||||||
|
# Result of false with reason
|
||||||
|
raise HomeAssistantError(reason)
|
||||||
|
# Result of false without reason (unexpected)
|
||||||
|
raise HomeAssistantError("Command failed with no reason")
|
||||||
|
# Response with result of true
|
||||||
|
return result
|
@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .entity import TeslemetryVehicleEntity
|
from .entity import TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_vehicle_command
|
||||||
from .models import TeslemetryVehicleData
|
from .models import TeslemetryVehicleData
|
||||||
|
|
||||||
ENGAGED = "Engaged"
|
ENGAGED = "Engaged"
|
||||||
@ -52,7 +53,7 @@ class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity):
|
|||||||
"""Lock the doors."""
|
"""Lock the doors."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.door_lock())
|
await handle_vehicle_command(self.api.door_lock())
|
||||||
self._attr_is_locked = True
|
self._attr_is_locked = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity):
|
|||||||
"""Unlock the doors."""
|
"""Unlock the doors."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.door_unlock())
|
await handle_vehicle_command(self.api.door_unlock())
|
||||||
self._attr_is_locked = False
|
self._attr_is_locked = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -95,6 +96,6 @@ class TeslemetryCableLockEntity(TeslemetryVehicleEntity, LockEntity):
|
|||||||
"""Unlock charge cable lock."""
|
"""Unlock charge cable lock."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.charge_port_door_open())
|
await handle_vehicle_command(self.api.charge_port_door_open())
|
||||||
self._attr_is_locked = False
|
self._attr_is_locked = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/teslemetry",
|
"documentation": "https://www.home-assistant.io/integrations/teslemetry",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["tesla-fleet-api"],
|
"loggers": ["tesla-fleet-api"],
|
||||||
"requirements": ["tesla-fleet-api==0.5.12"]
|
"requirements": ["tesla-fleet-api==0.6.1"]
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryVehicleEntity
|
from .entity import TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_vehicle_command
|
||||||
from .models import TeslemetryVehicleData
|
from .models import TeslemetryVehicleData
|
||||||
|
|
||||||
STATES = {
|
STATES = {
|
||||||
@ -114,7 +115,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
|
|||||||
"""Set volume level, range 0..1."""
|
"""Set volume level, range 0..1."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.adjust_volume(int(volume * self._volume_max))
|
self.api.adjust_volume(int(volume * self._volume_max))
|
||||||
)
|
)
|
||||||
self._attr_volume_level = volume
|
self._attr_volume_level = volume
|
||||||
@ -125,7 +126,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
|
|||||||
if self.state != MediaPlayerState.PLAYING:
|
if self.state != MediaPlayerState.PLAYING:
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.media_toggle_playback())
|
await handle_vehicle_command(self.api.media_toggle_playback())
|
||||||
self._attr_state = MediaPlayerState.PLAYING
|
self._attr_state = MediaPlayerState.PLAYING
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
|
|||||||
if self.state == MediaPlayerState.PLAYING:
|
if self.state == MediaPlayerState.PLAYING:
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.media_toggle_playback())
|
await handle_vehicle_command(self.api.media_toggle_playback())
|
||||||
self._attr_state = MediaPlayerState.PAUSED
|
self._attr_state = MediaPlayerState.PAUSED
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -142,10 +143,10 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
|
|||||||
"""Send next track command."""
|
"""Send next track command."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.media_next_track())
|
await handle_vehicle_command(self.api.media_next_track())
|
||||||
|
|
||||||
async def async_media_previous_track(self) -> None:
|
async def async_media_previous_track(self) -> None:
|
||||||
"""Send previous track command."""
|
"""Send previous track command."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.media_prev_track())
|
await handle_vehicle_command(self.api.media_prev_track())
|
||||||
|
@ -23,6 +23,7 @@ from homeassistant.helpers.icon import icon_for_battery_level
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
|
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_command, handle_vehicle_command
|
||||||
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
||||||
|
|
||||||
|
|
||||||
@ -163,7 +164,7 @@ class TeslemetryVehicleNumberEntity(TeslemetryVehicleEntity, NumberEntity):
|
|||||||
value = int(value)
|
value = int(value)
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.entity_description.func(self.api, value))
|
await handle_vehicle_command(self.entity_description.func(self.api, value))
|
||||||
self._attr_native_value = value
|
self._attr_native_value = value
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -198,6 +199,6 @@ class TeslemetryEnergyInfoNumberSensorEntity(TeslemetryEnergyInfoEntity, NumberE
|
|||||||
"""Set new value."""
|
"""Set new value."""
|
||||||
value = int(value)
|
value = int(value)
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(self.entity_description.func(self.api, value))
|
await handle_command(self.entity_description.func(self.api, value))
|
||||||
self._attr_native_value = value
|
self._attr_native_value = value
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
|
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_command, handle_vehicle_command
|
||||||
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
||||||
|
|
||||||
OFF = "off"
|
OFF = "off"
|
||||||
@ -146,8 +147,8 @@ class TeslemetrySeatHeaterSelectEntity(TeslemetryVehicleEntity, SelectEntity):
|
|||||||
level = self._attr_options.index(option)
|
level = self._attr_options.index(option)
|
||||||
# AC must be on to turn on seat heater
|
# AC must be on to turn on seat heater
|
||||||
if level and not self.get("climate_state_is_climate_on"):
|
if level and not self.get("climate_state_is_climate_on"):
|
||||||
await self.handle_command(self.api.auto_conditioning_start())
|
await handle_vehicle_command(self.api.auto_conditioning_start())
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.remote_seat_heater_request(self.entity_description.position, level)
|
self.api.remote_seat_heater_request(self.entity_description.position, level)
|
||||||
)
|
)
|
||||||
self._attr_current_option = option
|
self._attr_current_option = option
|
||||||
@ -191,8 +192,8 @@ class TeslemetryWheelHeaterSelectEntity(TeslemetryVehicleEntity, SelectEntity):
|
|||||||
level = self._attr_options.index(option)
|
level = self._attr_options.index(option)
|
||||||
# AC must be on to turn on steering wheel heater
|
# AC must be on to turn on steering wheel heater
|
||||||
if level and not self.get("climate_state_is_climate_on"):
|
if level and not self.get("climate_state_is_climate_on"):
|
||||||
await self.handle_command(self.api.auto_conditioning_start())
|
await handle_vehicle_command(self.api.auto_conditioning_start())
|
||||||
await self.handle_command(
|
await handle_vehicle_command(
|
||||||
self.api.remote_steering_wheel_heat_level_request(level)
|
self.api.remote_steering_wheel_heat_level_request(level)
|
||||||
)
|
)
|
||||||
self._attr_current_option = option
|
self._attr_current_option = option
|
||||||
@ -224,7 +225,7 @@ class TeslemetryOperationSelectEntity(TeslemetryEnergyInfoEntity, SelectEntity):
|
|||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the selected option."""
|
"""Change the selected option."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(self.api.operation(option))
|
await handle_command(self.api.operation(option))
|
||||||
self._attr_current_option = option
|
self._attr_current_option = option
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -254,7 +255,7 @@ class TeslemetryExportRuleSelectEntity(TeslemetryEnergyInfoEntity, SelectEntity)
|
|||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the selected option."""
|
"""Change the selected option."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(
|
await handle_command(
|
||||||
self.api.grid_import_export(customer_preferred_export_rule=option)
|
self.api.grid_import_export(customer_preferred_export_rule=option)
|
||||||
)
|
)
|
||||||
self._attr_current_option = option
|
self._attr_current_option = option
|
||||||
|
@ -19,6 +19,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
|
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_command, handle_vehicle_command
|
||||||
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
from .models import TeslemetryEnergyData, TeslemetryVehicleData
|
||||||
|
|
||||||
|
|
||||||
@ -156,7 +157,7 @@ class TeslemetryVehicleSwitchEntity(TeslemetryVehicleEntity, TeslemetrySwitchEnt
|
|||||||
"""Turn on the Switch."""
|
"""Turn on the Switch."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.entity_description.on_func(self.api))
|
await handle_vehicle_command(self.entity_description.on_func(self.api))
|
||||||
self._attr_is_on = True
|
self._attr_is_on = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ class TeslemetryVehicleSwitchEntity(TeslemetryVehicleEntity, TeslemetrySwitchEnt
|
|||||||
"""Turn off the Switch."""
|
"""Turn off the Switch."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.entity_description.off_func(self.api))
|
await handle_vehicle_command(self.entity_description.off_func(self.api))
|
||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -205,7 +206,7 @@ class TeslemetryChargeFromGridSwitchEntity(
|
|||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn on the Switch."""
|
"""Turn on the Switch."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(
|
await handle_command(
|
||||||
self.api.grid_import_export(
|
self.api.grid_import_export(
|
||||||
disallow_charge_from_grid_with_solar_installed=False
|
disallow_charge_from_grid_with_solar_installed=False
|
||||||
)
|
)
|
||||||
@ -216,7 +217,7 @@ class TeslemetryChargeFromGridSwitchEntity(
|
|||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the Switch."""
|
"""Turn off the Switch."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(
|
await handle_command(
|
||||||
self.api.grid_import_export(
|
self.api.grid_import_export(
|
||||||
disallow_charge_from_grid_with_solar_installed=True
|
disallow_charge_from_grid_with_solar_installed=True
|
||||||
)
|
)
|
||||||
@ -247,13 +248,13 @@ class TeslemetryStormModeSwitchEntity(
|
|||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn on the Switch."""
|
"""Turn on the Switch."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(self.api.storm_mode(enabled=True))
|
await handle_command(self.api.storm_mode(enabled=True))
|
||||||
self._attr_is_on = True
|
self._attr_is_on = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the Switch."""
|
"""Turn off the Switch."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.handle_command(self.api.storm_mode(enabled=False))
|
await handle_command(self.api.storm_mode(enabled=False))
|
||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
@ -12,6 +12,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TeslemetryConfigEntry
|
from . import TeslemetryConfigEntry
|
||||||
from .entity import TeslemetryVehicleEntity
|
from .entity import TeslemetryVehicleEntity
|
||||||
|
from .helpers import handle_vehicle_command
|
||||||
from .models import TeslemetryVehicleData
|
from .models import TeslemetryVehicleData
|
||||||
|
|
||||||
AVAILABLE = "available"
|
AVAILABLE = "available"
|
||||||
@ -102,6 +103,6 @@ class TeslemetryUpdateEntity(TeslemetryVehicleEntity, UpdateEntity):
|
|||||||
"""Install an update."""
|
"""Install an update."""
|
||||||
self.raise_for_scope()
|
self.raise_for_scope()
|
||||||
await self.wake_up_if_asleep()
|
await self.wake_up_if_asleep()
|
||||||
await self.handle_command(self.api.schedule_software_update(offset_sec=60))
|
await handle_vehicle_command(self.api.schedule_software_update(offset_sec=60))
|
||||||
self._attr_in_progress = True
|
self._attr_in_progress = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
@ -2707,7 +2707,7 @@ temperusb==1.6.1
|
|||||||
# tensorflow==2.5.0
|
# tensorflow==2.5.0
|
||||||
|
|
||||||
# homeassistant.components.teslemetry
|
# homeassistant.components.teslemetry
|
||||||
tesla-fleet-api==0.5.12
|
tesla-fleet-api==0.6.1
|
||||||
|
|
||||||
# homeassistant.components.powerwall
|
# homeassistant.components.powerwall
|
||||||
tesla-powerwall==0.5.2
|
tesla-powerwall==0.5.2
|
||||||
|
@ -2105,7 +2105,7 @@ temescal==0.5
|
|||||||
temperusb==1.6.1
|
temperusb==1.6.1
|
||||||
|
|
||||||
# homeassistant.components.teslemetry
|
# homeassistant.components.teslemetry
|
||||||
tesla-fleet-api==0.5.12
|
tesla-fleet-api==0.6.1
|
||||||
|
|
||||||
# homeassistant.components.powerwall
|
# homeassistant.components.powerwall
|
||||||
tesla-powerwall==0.5.2
|
tesla-powerwall==0.5.2
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
'model': 'Powerwall 2, Tesla Backup Gateway 2',
|
'model': 'Powerwall 2, Tesla Backup Gateway 2',
|
||||||
'name': 'Energy Site',
|
'name': 'Energy Site',
|
||||||
'name_by_user': None,
|
'name_by_user': None,
|
||||||
'serial_number': None,
|
'serial_number': '123456',
|
||||||
'suggested_area': None,
|
'suggested_area': None,
|
||||||
'sw_version': None,
|
'sw_version': None,
|
||||||
'via_device_id': None,
|
'via_device_id': None,
|
||||||
|
@ -343,7 +343,7 @@ async def test_asleep_or_offline(
|
|||||||
mock_wake_up.return_value = WAKE_UP_ASLEEP
|
mock_wake_up.return_value = WAKE_UP_ASLEEP
|
||||||
mock_vehicle.return_value = WAKE_UP_ASLEEP
|
mock_vehicle.return_value = WAKE_UP_ASLEEP
|
||||||
with (
|
with (
|
||||||
patch("homeassistant.components.teslemetry.entity.asyncio.sleep"),
|
patch("homeassistant.components.teslemetry.helpers.asyncio.sleep"),
|
||||||
pytest.raises(HomeAssistantError) as error,
|
pytest.raises(HomeAssistantError) as error,
|
||||||
):
|
):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user