Code quality improvements for Teslemetry (#123444)

This commit is contained in:
Brett Adams 2024-09-27 23:06:09 +10:00 committed by GitHub
parent 66ab90b518
commit cad87f51a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 218 additions and 124 deletions

View File

@ -107,6 +107,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
device=device, device=device,
) )
) )
elif "energy_site_id" in product and Scope.ENERGY_DEVICE_DATA in scopes: elif "energy_site_id" in product and Scope.ENERGY_DEVICE_DATA in scopes:
site_id = product["energy_site_id"] site_id = product["energy_site_id"]
if not ( if not (

View File

@ -120,7 +120,8 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Set the climate state to on.""" """Set the climate state to on."""
self.raise_for_scope()
self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.auto_conditioning_start()) await handle_vehicle_command(self.api.auto_conditioning_start())
@ -129,7 +130,8 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Set the climate state to off.""" """Set the climate state to off."""
self.raise_for_scope()
self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.auto_conditioning_stop()) await handle_vehicle_command(self.api.auto_conditioning_stop())
@ -261,10 +263,11 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the climate temperature.""" """Set the climate temperature."""
if not (temp := kwargs.get(ATTR_TEMPERATURE)): self.raise_for_scope(Scope.VEHICLE_CMDS)
return
if (cop_mode := TEMP_LEVELS.get(temp)) is None: if (temp := kwargs.get(ATTR_TEMPERATURE)) is None or (
cop_mode := TEMP_LEVELS.get(temp)
) is None:
raise ServiceValidationError( raise ServiceValidationError(
translation_domain=DOMAIN, translation_domain=DOMAIN,
translation_key="invalid_cop_temp", translation_key="invalid_cop_temp",
@ -297,7 +300,7 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the climate mode and state.""" """Set the climate mode and state."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await self._async_set_cop(hvac_mode) await self._async_set_cop(hvac_mode)
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -52,7 +52,6 @@ class TeslemetryVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching data from the Teslemetry API.""" """Class to manage fetching data from the Teslemetry API."""
updated_once: bool updated_once: bool
pre2021: bool
last_active: datetime last_active: datetime
def __init__( def __init__(

View File

@ -79,7 +79,7 @@ class TeslemetryWindowEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Vent windows.""" """Vent windows."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command( await handle_vehicle_command(
self.api.window_control(command=WindowCommand.VENT) self.api.window_control(command=WindowCommand.VENT)
@ -89,7 +89,7 @@ class TeslemetryWindowEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close windows.""" """Close windows."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command( await handle_vehicle_command(
self.api.window_control(command=WindowCommand.CLOSE) self.api.window_control(command=WindowCommand.CLOSE)
@ -122,7 +122,7 @@ class TeslemetryChargePortEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open charge port.""" """Open charge port."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CHARGING_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -130,7 +130,7 @@ class TeslemetryChargePortEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close charge port.""" """Close charge port."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CHARGING_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -157,7 +157,7 @@ class TeslemetryFrontTrunkEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open front trunk.""" """Open front trunk."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -193,7 +193,7 @@ class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open rear trunk.""" """Open rear trunk."""
if self.is_closed is not False: if self.is_closed is not False:
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -202,7 +202,7 @@ class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close rear trunk.""" """Close rear trunk."""
if self.is_closed is not True: if self.is_closed is not True:
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -240,7 +240,7 @@ class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open sunroof.""" """Open sunroof."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.VENT)) await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.VENT))
self._attr_is_closed = False self._attr_is_closed = False
@ -248,7 +248,7 @@ class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close sunroof.""" """Close sunroof."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.CLOSE)) await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.CLOSE))
self._attr_is_closed = True self._attr_is_closed = True
@ -256,7 +256,7 @@ class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverEntity):
async def async_stop_cover(self, **kwargs: Any) -> None: async def async_stop_cover(self, **kwargs: Any) -> None:
"""Close sunroof.""" """Close sunroof."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.STOP)) await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.STOP))
self._attr_is_closed = False self._attr_is_closed = False

View File

@ -4,6 +4,7 @@ from abc import abstractmethod
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.const import Scope
from homeassistant.exceptions import ServiceValidationError from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@ -31,6 +32,7 @@ class TeslemetryEntity(
"""Parent class for all Teslemetry entities.""" """Parent class for all Teslemetry entities."""
_attr_has_entity_name = True _attr_has_entity_name = True
scoped: bool
def __init__( def __init__(
self, self,
@ -38,12 +40,10 @@ class TeslemetryEntity(
| TeslemetryEnergyHistoryCoordinator | TeslemetryEnergyHistoryCoordinator
| TeslemetryEnergySiteLiveCoordinator | TeslemetryEnergySiteLiveCoordinator
| TeslemetryEnergySiteInfoCoordinator, | TeslemetryEnergySiteInfoCoordinator,
api: VehicleSpecific | EnergySpecific,
key: str, key: str,
) -> None: ) -> None:
"""Initialize common aspects of a Teslemetry entity.""" """Initialize common aspects of a Teslemetry entity."""
super().__init__(coordinator) super().__init__(coordinator)
self.api = api
self.key = key self.key = key
self._attr_translation_key = self.key self._attr_translation_key = self.key
self._async_update_attrs() self._async_update_attrs()
@ -87,16 +87,22 @@ class TeslemetryEntity(
def _async_update_attrs(self) -> None: def _async_update_attrs(self) -> None:
"""Update the attributes of the entity.""" """Update the attributes of the entity."""
def raise_for_scope(self): def raise_for_scope(self, scope: Scope):
"""Raise an error if a scope is not available.""" """Raise an error if a scope is not available."""
if not self.scoped: if not self.scoped:
raise ServiceValidationError("Missing required scope") raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="missing_scope",
translation_placeholders={"scope": scope},
)
class TeslemetryVehicleEntity(TeslemetryEntity): class TeslemetryVehicleEntity(TeslemetryEntity):
"""Parent class for Teslemetry Vehicle entities.""" """Parent class for Teslemetry Vehicle entities."""
_last_update: int = 0 _last_update: int = 0
api: VehicleSpecific
vehicle: TeslemetryVehicleData
def __init__( def __init__(
self, self,
@ -105,11 +111,11 @@ class TeslemetryVehicleEntity(TeslemetryEntity):
) -> None: ) -> None:
"""Initialize common aspects of a Teslemetry entity.""" """Initialize common aspects of a Teslemetry entity."""
self._attr_unique_id = f"{data.vin}-{key}" self.api = data.api
self.vehicle = data self.vehicle = data
self._attr_unique_id = f"{data.vin}-{key}"
self._attr_device_info = data.device self._attr_device_info = data.device
super().__init__(data.coordinator, data.api, key) super().__init__(data.coordinator, key)
@property @property
def _value(self) -> Any | None: def _value(self) -> Any | None:
@ -124,31 +130,39 @@ class TeslemetryVehicleEntity(TeslemetryEntity):
class TeslemetryEnergyLiveEntity(TeslemetryEntity): class TeslemetryEnergyLiveEntity(TeslemetryEntity):
"""Parent class for Teslemetry Energy Site Live entities.""" """Parent class for Teslemetry Energy Site Live entities."""
api: EnergySpecific
def __init__( def __init__(
self, self,
data: TeslemetryEnergyData, data: TeslemetryEnergyData,
key: str, key: str,
) -> None: ) -> None:
"""Initialize common aspects of a Teslemetry Energy Site Live entity.""" """Initialize common aspects of a Teslemetry Energy Site Live entity."""
self.api = data.api
self._attr_unique_id = f"{data.id}-{key}" self._attr_unique_id = f"{data.id}-{key}"
self._attr_device_info = data.device self._attr_device_info = data.device
super().__init__(data.live_coordinator, data.api, key) super().__init__(data.live_coordinator, key)
class TeslemetryEnergyInfoEntity(TeslemetryEntity): class TeslemetryEnergyInfoEntity(TeslemetryEntity):
"""Parent class for Teslemetry Energy Site Info Entities.""" """Parent class for Teslemetry Energy Site Info Entities."""
api: EnergySpecific
def __init__( def __init__(
self, self,
data: TeslemetryEnergyData, data: TeslemetryEnergyData,
key: str, key: str,
) -> None: ) -> None:
"""Initialize common aspects of a Teslemetry Energy Site Info entity.""" """Initialize common aspects of a Teslemetry Energy Site Info entity."""
self.api = data.api
self._attr_unique_id = f"{data.id}-{key}" self._attr_unique_id = f"{data.id}-{key}"
self._attr_device_info = data.device self._attr_device_info = data.device
super().__init__(data.info_coordinator, data.api, key) super().__init__(data.info_coordinator, key)
class TeslemetryEnergyHistoryEntity(TeslemetryEntity): class TeslemetryEnergyHistoryEntity(TeslemetryEntity):
@ -160,18 +174,19 @@ class TeslemetryEnergyHistoryEntity(TeslemetryEntity):
key: str, key: str,
) -> None: ) -> None:
"""Initialize common aspects of a Teslemetry Energy Site Info entity.""" """Initialize common aspects of a Teslemetry Energy Site Info entity."""
self.api = data.api
self._attr_unique_id = f"{data.id}-{key}" self._attr_unique_id = f"{data.id}-{key}"
self._attr_device_info = data.device self._attr_device_info = data.device
super().__init__(data.history_coordinator, data.api, key) super().__init__(data.history_coordinator, key)
class TeslemetryWallConnectorEntity( class TeslemetryWallConnectorEntity(TeslemetryEntity):
TeslemetryEntity, CoordinatorEntity[TeslemetryEnergySiteLiveCoordinator]
):
"""Parent class for Teslemetry Wall Connector Entities.""" """Parent class for Teslemetry Wall Connector Entities."""
_attr_has_entity_name = True _attr_has_entity_name = True
api: EnergySpecific
def __init__( def __init__(
self, self,
@ -180,6 +195,8 @@ class TeslemetryWallConnectorEntity(
key: str, key: str,
) -> None: ) -> None:
"""Initialize common aspects of a Teslemetry entity.""" """Initialize common aspects of a Teslemetry entity."""
self.api = data.api
self.din = din self.din = din
self._attr_unique_id = f"{data.id}-{din}-{key}" self._attr_unique_id = f"{data.id}-{din}-{key}"
@ -200,7 +217,7 @@ class TeslemetryWallConnectorEntity(
model=model, model=model,
) )
super().__init__(data.live_coordinator, data.api, key) super().__init__(data.live_coordinator, key)
@property @property
def _value(self) -> int: def _value(self) -> int:

View File

@ -7,7 +7,7 @@ from tesla_fleet_api.exceptions import TeslaFleetError
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from .const import LOGGER, TeslemetryState from .const import DOMAIN, LOGGER, TeslemetryState
async def wake_up_vehicle(vehicle) -> None: async def wake_up_vehicle(vehicle) -> None:
@ -22,12 +22,19 @@ async def wake_up_vehicle(vehicle) -> None:
cmd = await vehicle.api.vehicle() cmd = await vehicle.api.vehicle()
state = cmd["response"]["state"] state = cmd["response"]["state"]
except TeslaFleetError as e: except TeslaFleetError as e:
raise HomeAssistantError(str(e)) from e raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="wake_up_failed",
translation_placeholders={"message": e.message},
) from e
vehicle.coordinator.data["state"] = state vehicle.coordinator.data["state"] = state
if state != TeslemetryState.ONLINE: if state != TeslemetryState.ONLINE:
times += 1 times += 1
if times >= 4: # Give up after 30 seconds total if times >= 4: # Give up after 30 seconds total
raise HomeAssistantError("Could not wake up vehicle") raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="wake_up_timeout",
)
await asyncio.sleep(times * 5) await asyncio.sleep(times * 5)
@ -36,18 +43,26 @@ async def handle_command(command) -> dict[str, Any]:
try: try:
result = await command result = await command
except TeslaFleetError as e: except TeslaFleetError as e:
raise HomeAssistantError(f"Teslemetry command failed, {e.message}") from e raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="command_exception",
translation_placeholders={"message": e.message},
) from e
LOGGER.debug("Command result: %s", result) LOGGER.debug("Command result: %s", result)
return result return result
async def handle_vehicle_command(command) -> dict[str, Any]: async def handle_vehicle_command(command) -> Any:
"""Handle a vehicle command.""" """Handle a vehicle command."""
result = await handle_command(command) result = await handle_command(command)
if (response := result.get("response")) is None: if (response := result.get("response")) is None:
if error := result.get("error"): if error := result.get("error"):
# No response with error # No response with error
raise HomeAssistantError(error) raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="command_error",
translation_placeholders={"error": error},
)
# No response without error (unexpected) # No response without error (unexpected)
raise HomeAssistantError(f"Unknown response: {response}") raise HomeAssistantError(f"Unknown response: {response}")
if (result := response.get("result")) is not True: if (result := response.get("result")) is not True:
@ -56,8 +71,14 @@ async def handle_vehicle_command(command) -> dict[str, Any]:
# Reason is acceptable # Reason is acceptable
return result return result
# Result of false with reason # Result of false with reason
raise HomeAssistantError(reason) raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="command_reason",
translation_placeholders={"reason": reason},
)
# Result of false without reason (unexpected) # Result of false without reason (unexpected)
raise HomeAssistantError("Command failed with no reason") raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="command_no_result"
)
# Response with result of true # Response with result of true
return result return result

View File

@ -53,7 +53,7 @@ class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity):
async def async_lock(self, **kwargs: Any) -> None: async def async_lock(self, **kwargs: Any) -> None:
"""Lock the doors.""" """Lock the doors."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.door_lock()) await handle_vehicle_command(self.api.door_lock())
self._attr_is_locked = True self._attr_is_locked = True
@ -61,7 +61,7 @@ class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity):
async def async_unlock(self, **kwargs: Any) -> None: async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the doors.""" """Unlock the doors."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.door_unlock()) await handle_vehicle_command(self.api.door_unlock())
self._attr_is_locked = False self._attr_is_locked = False
@ -96,7 +96,7 @@ class TeslemetryCableLockEntity(TeslemetryVehicleEntity, LockEntity):
async def async_unlock(self, **kwargs: Any) -> None: async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock charge cable lock.""" """Unlock charge cable lock."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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

View File

@ -115,7 +115,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
async def async_set_volume_level(self, volume: float) -> None: async def async_set_volume_level(self, volume: float) -> None:
"""Set volume level, range 0..1.""" """Set volume level, range 0..1."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command( await handle_vehicle_command(
self.api.adjust_volume(int(volume * self._volume_max)) self.api.adjust_volume(int(volume * self._volume_max))
@ -126,7 +126,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
async def async_media_play(self) -> None: async def async_media_play(self) -> None:
"""Send play command.""" """Send play command."""
if self.state != MediaPlayerState.PLAYING: if self.state != MediaPlayerState.PLAYING:
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -135,7 +135,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
async def async_media_pause(self) -> None: async def async_media_pause(self) -> None:
"""Send pause command.""" """Send pause command."""
if self.state == MediaPlayerState.PLAYING: if self.state == MediaPlayerState.PLAYING:
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -143,12 +143,12 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity):
async def async_media_next_track(self) -> None: async def async_media_next_track(self) -> None:
"""Send next track command.""" """Send next track command."""
self.raise_for_scope() self.raise_for_scope(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.media_prev_track()) await handle_vehicle_command(self.api.media_prev_track())

View File

@ -164,7 +164,7 @@ class TeslemetryVehicleNumberEntity(TeslemetryVehicleEntity, NumberEntity):
async def async_set_native_value(self, value: float) -> None: async def async_set_native_value(self, value: float) -> None:
"""Set new value.""" """Set new value."""
value = int(value) value = int(value)
self.raise_for_scope() self.raise_for_scope(self.entity_description.scopes[0])
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -200,7 +200,7 @@ class TeslemetryEnergyInfoNumberSensorEntity(TeslemetryEnergyInfoEntity, NumberE
async def async_set_native_value(self, value: float) -> None: async def async_set_native_value(self, value: float) -> None:
"""Set new value.""" """Set new value."""
value = int(value) value = int(value)
self.raise_for_scope() self.raise_for_scope(Scope.ENERGY_CMDS)
await 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()

View File

@ -144,7 +144,7 @@ class TeslemetrySeatHeaterSelectEntity(TeslemetryVehicleEntity, 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(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
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
@ -189,7 +189,7 @@ class TeslemetryWheelHeaterSelectEntity(TeslemetryVehicleEntity, 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(Scope.VEHICLE_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
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
@ -226,7 +226,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(Scope.ENERGY_CMDS)
await 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()
@ -256,7 +256,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(Scope.ENERGY_CMDS)
await 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)
) )

View File

@ -567,8 +567,26 @@
"no_energy_site_data_for_device": { "no_energy_site_data_for_device": {
"message": "No energy site data for device ID: {device_id}" "message": "No energy site data for device ID: {device_id}"
}, },
"command_exception": {
"message": "Command returned exception: {message}"
},
"command_error": { "command_error": {
"message": "Command returned error: {error}" "message": "Command returned error: {error}"
},
"command_reason": {
"message": "Command was rejected: {reason}"
},
"command_no_result": {
"message": "Command had no result"
},
"wake_up_failed": {
"message": "Failed to wake up vehicle: {message}"
},
"wake_up_timeout": {
"message": "Timed out trying to wake up vehicle"
},
"missing_scope": {
"message": "Missing required scope: {scope}"
} }
}, },
"services": { "services": {

View File

@ -157,7 +157,7 @@ class TeslemetryVehicleSwitchEntity(TeslemetryVehicleEntity, TeslemetrySwitchEnt
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(self.entity_description.scopes[0])
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -165,7 +165,7 @@ class TeslemetryVehicleSwitchEntity(TeslemetryVehicleEntity, TeslemetrySwitchEnt
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(self.entity_description.scopes[0])
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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
@ -207,7 +207,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(Scope.ENERGY_CMDS)
await 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
@ -218,7 +218,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(Scope.ENERGY_CMDS)
await 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
@ -249,14 +249,14 @@ 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(Scope.ENERGY_CMDS)
await 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(Scope.ENERGY_CMDS)
await 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()

View File

@ -103,7 +103,7 @@ class TeslemetryUpdateEntity(TeslemetryVehicleEntity, UpdateEntity):
self, version: str | None, backup: bool, **kwargs: Any self, version: str | None, backup: bool, **kwargs: Any
) -> None: ) -> None:
"""Install an update.""" """Install an update."""
self.raise_for_scope() self.raise_for_scope(Scope.ENERGY_CMDS)
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_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

View File

@ -2,7 +2,7 @@
from unittest.mock import patch from unittest.mock import patch
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.teslemetry.const import DOMAIN from homeassistant.components.teslemetry.const import DOMAIN
from homeassistant.const import Platform from homeassistant.const import Platform

View File

@ -1,4 +1,10 @@
# serializer version: 1 # serializer version: 1
# name: test_asleep_or_offline[HomeAssistantError]
'Timed out trying to wake up vehicle'
# ---
# name: test_asleep_or_offline[InvalidCommand]
'Failed to wake up vehicle: The data request or command is unknown.'
# ---
# name: test_climate[climate.test_cabin_overheat_protection-entry] # name: test_climate[climate.test_cabin_overheat_protection-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -499,3 +505,6 @@
'state': 'unknown', 'state': 'unknown',
}) })
# --- # ---
# name: test_invalid_error[error]
'Command returned exception: The data request or command is unknown.'
# ---

View File

@ -1,8 +1,10 @@
"""Test the Teslemetry binary sensor platform.""" """Test the Teslemetry binary sensor platform."""
from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL
@ -33,7 +35,7 @@ async def test_binary_sensor_refresh(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
) -> None: ) -> None:
"""Tests that the binary sensor entities are correct.""" """Tests that the binary sensor entities are correct."""
@ -51,7 +53,7 @@ async def test_binary_sensor_refresh(
async def test_binary_sensor_offline( async def test_binary_sensor_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the binary sensor entities are correct when offline.""" """Tests that the binary sensor entities are correct when offline."""

View File

@ -3,7 +3,7 @@
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.const import ATTR_ENTITY_ID, Platform

View File

@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import InvalidCommand, VehicleOffline from tesla_fleet_api.exceptions import InvalidCommand, VehicleOffline
from homeassistant.components.climate import ( from homeassistant.components.climate import (
@ -196,7 +196,7 @@ async def test_climate_alt(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the climate entity is correct.""" """Tests that the climate entity is correct."""
@ -210,7 +210,7 @@ async def test_climate_offline(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the climate entity is correct.""" """Tests that the climate entity is correct."""
@ -219,7 +219,7 @@ async def test_climate_offline(
assert_entities(hass, entry.entry_id, entity_registry, snapshot) assert_entities(hass, entry.entry_id, entity_registry, snapshot)
async def test_invalid_error(hass: HomeAssistant) -> None: async def test_invalid_error(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Tests service error is handled.""" """Tests service error is handled."""
await setup_platform(hass, platforms=[Platform.CLIMATE]) await setup_platform(hass, platforms=[Platform.CLIMATE])
@ -239,10 +239,7 @@ async def test_invalid_error(hass: HomeAssistant) -> None:
blocking=True, blocking=True,
) )
mock_on.assert_called_once() mock_on.assert_called_once()
assert ( assert str(error.value) == snapshot(name="error")
str(error.value)
== "Teslemetry command failed, The data request or command is unknown."
)
@pytest.mark.parametrize("response", COMMAND_ERRORS) @pytest.mark.parametrize("response", COMMAND_ERRORS)
@ -291,10 +288,11 @@ async def test_ignored_error(
@pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_asleep_or_offline( async def test_asleep_or_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
mock_wake_up, mock_wake_up: AsyncMock,
mock_vehicle, mock_vehicle: AsyncMock,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Tests asleep is handled.""" """Tests asleep is handled."""
@ -320,7 +318,7 @@ async def test_asleep_or_offline(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert str(error.value) == "The data request or command is unknown." assert str(error.value) == snapshot(name="InvalidCommand")
mock_wake_up.assert_called_once() mock_wake_up.assert_called_once()
mock_wake_up.side_effect = None mock_wake_up.side_effect = None
@ -339,7 +337,7 @@ async def test_asleep_or_offline(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert str(error.value) == "Could not wake up vehicle" assert str(error.value) == snapshot(name="HomeAssistantError")
mock_wake_up.assert_called_once() mock_wake_up.assert_called_once()
mock_vehicle.assert_called() mock_vehicle.assert_called()

View File

@ -1,6 +1,6 @@
"""Test the Teslemetry config flow.""" """Test the Teslemetry config flow."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
from aiohttp import ClientConnectionError from aiohttp import ClientConnectionError
import pytest import pytest
@ -60,7 +60,10 @@ async def test_form(
], ],
) )
async def test_form_errors( async def test_form_errors(
hass: HomeAssistant, side_effect, error, mock_metadata hass: HomeAssistant,
side_effect: TeslaFleetError,
error: dict[str, str],
mock_metadata: AsyncMock,
) -> None: ) -> None:
"""Test errors are handled.""" """Test errors are handled."""
@ -86,7 +89,7 @@ async def test_form_errors(
assert result3["type"] is FlowResultType.CREATE_ENTRY assert result3["type"] is FlowResultType.CREATE_ENTRY
async def test_reauth(hass: HomeAssistant, mock_metadata) -> None: async def test_reauth(hass: HomeAssistant, mock_metadata: AsyncMock) -> None:
"""Test reauth flow.""" """Test reauth flow."""
mock_entry = MockConfigEntry( mock_entry = MockConfigEntry(
@ -127,7 +130,10 @@ async def test_reauth(hass: HomeAssistant, mock_metadata) -> None:
], ],
) )
async def test_reauth_errors( async def test_reauth_errors(
hass: HomeAssistant, mock_metadata, side_effect, error hass: HomeAssistant,
mock_metadata: AsyncMock,
side_effect: TeslaFleetError,
error: dict[str, str],
) -> None: ) -> None:
"""Test reauth flows that fail.""" """Test reauth flows that fail."""
@ -178,7 +184,7 @@ async def test_unique_id_abort(
assert result2["type"] is FlowResultType.ABORT assert result2["type"] is FlowResultType.ABORT
async def test_migrate_from_1_1(hass: HomeAssistant, mock_metadata) -> None: async def test_migrate_from_1_1(hass: HomeAssistant, mock_metadata: AsyncMock) -> None:
"""Test config migration.""" """Test config migration."""
mock_entry = MockConfigEntry( mock_entry = MockConfigEntry(
@ -199,7 +205,9 @@ async def test_migrate_from_1_1(hass: HomeAssistant, mock_metadata) -> None:
assert entry.unique_id == METADATA["uid"] assert entry.unique_id == METADATA["uid"]
async def test_migrate_error_from_1_1(hass: HomeAssistant, mock_metadata) -> None: async def test_migrate_error_from_1_1(
hass: HomeAssistant, mock_metadata: AsyncMock
) -> None:
"""Test config migration handles errors.""" """Test config migration handles errors."""
mock_metadata.side_effect = TeslaFleetError mock_metadata.side_effect = TeslaFleetError
@ -220,7 +228,9 @@ async def test_migrate_error_from_1_1(hass: HomeAssistant, mock_metadata) -> Non
assert entry.state is ConfigEntryState.MIGRATION_ERROR assert entry.state is ConfigEntryState.MIGRATION_ERROR
async def test_migrate_error_from_future(hass: HomeAssistant, mock_metadata) -> None: async def test_migrate_error_from_future(
hass: HomeAssistant, mock_metadata: AsyncMock
) -> None:
"""Test a future version isn't migrated.""" """Test a future version isn't migrated."""
mock_metadata.side_effect = TeslaFleetError mock_metadata.side_effect = TeslaFleetError

View File

@ -1,9 +1,9 @@
"""Test the Teslemetry cover platform.""" """Test the Teslemetry cover platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.cover import ( from homeassistant.components.cover import (
@ -43,7 +43,7 @@ async def test_cover_alt(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the cover entities are correct with alternate values.""" """Tests that the cover entities are correct with alternate values."""
@ -57,7 +57,7 @@ async def test_cover_noscope(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_metadata, mock_metadata: AsyncMock,
) -> None: ) -> None:
"""Tests that the cover entities are correct without scopes.""" """Tests that the cover entities are correct without scopes."""
@ -68,7 +68,7 @@ async def test_cover_noscope(
async def test_cover_offline( async def test_cover_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the cover entities are correct when offline.""" """Tests that the cover entities are correct when offline."""

View File

@ -1,6 +1,6 @@
"""Test the Teslemetry device tracker platform.""" """Test the Teslemetry device tracker platform."""
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.const import STATE_UNKNOWN, Platform from homeassistant.const import STATE_UNKNOWN, Platform

View File

@ -4,7 +4,7 @@ from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import ( from tesla_fleet_api.exceptions import (
InvalidToken, InvalidToken,
SubscriptionRequired, SubscriptionRequired,
@ -48,7 +48,10 @@ async def test_load_unload(hass: HomeAssistant) -> None:
@pytest.mark.parametrize(("side_effect", "state"), ERRORS) @pytest.mark.parametrize(("side_effect", "state"), ERRORS)
async def test_init_error( async def test_init_error(
hass: HomeAssistant, mock_products, side_effect, state hass: HomeAssistant,
mock_products: AsyncMock,
side_effect: TeslaFleetError,
state: ConfigEntryState,
) -> None: ) -> None:
"""Test init with errors.""" """Test init with errors."""
@ -86,7 +89,7 @@ async def test_vehicle_refresh_asleep(
async def test_vehicle_refresh_offline( async def test_vehicle_refresh_offline(
hass: HomeAssistant, mock_vehicle_data, freezer: FrozenDateTimeFactory hass: HomeAssistant, mock_vehicle_data: AsyncMock, freezer: FrozenDateTimeFactory
) -> None: ) -> None:
"""Test coordinator refresh with an error.""" """Test coordinator refresh with an error."""
entry = await setup_platform(hass, [Platform.CLIMATE]) entry = await setup_platform(hass, [Platform.CLIMATE])
@ -103,7 +106,10 @@ async def test_vehicle_refresh_offline(
@pytest.mark.parametrize(("side_effect", "state"), ERRORS) @pytest.mark.parametrize(("side_effect", "state"), ERRORS)
async def test_vehicle_refresh_error( async def test_vehicle_refresh_error(
hass: HomeAssistant, mock_vehicle_data, side_effect, state hass: HomeAssistant,
mock_vehicle_data: AsyncMock,
side_effect: TeslaFleetError,
state: ConfigEntryState,
) -> None: ) -> None:
"""Test coordinator refresh with an error.""" """Test coordinator refresh with an error."""
mock_vehicle_data.side_effect = side_effect mock_vehicle_data.side_effect = side_effect
@ -112,7 +118,7 @@ async def test_vehicle_refresh_error(
async def test_vehicle_sleep( async def test_vehicle_sleep(
hass: HomeAssistant, mock_vehicle_data, freezer: FrozenDateTimeFactory hass: HomeAssistant, mock_vehicle_data: AsyncMock, freezer: FrozenDateTimeFactory
) -> None: ) -> None:
"""Test coordinator refresh with an error.""" """Test coordinator refresh with an error."""
await setup_platform(hass, [Platform.CLIMATE]) await setup_platform(hass, [Platform.CLIMATE])
@ -171,7 +177,10 @@ async def test_vehicle_sleep(
# Test Energy Live Coordinator # Test Energy Live Coordinator
@pytest.mark.parametrize(("side_effect", "state"), ERRORS) @pytest.mark.parametrize(("side_effect", "state"), ERRORS)
async def test_energy_live_refresh_error( async def test_energy_live_refresh_error(
hass: HomeAssistant, mock_live_status, side_effect, state hass: HomeAssistant,
mock_live_status: AsyncMock,
side_effect: TeslaFleetError,
state: ConfigEntryState,
) -> None: ) -> None:
"""Test coordinator refresh with an error.""" """Test coordinator refresh with an error."""
mock_live_status.side_effect = side_effect mock_live_status.side_effect = side_effect
@ -182,7 +191,10 @@ async def test_energy_live_refresh_error(
# Test Energy Site Coordinator # Test Energy Site Coordinator
@pytest.mark.parametrize(("side_effect", "state"), ERRORS) @pytest.mark.parametrize(("side_effect", "state"), ERRORS)
async def test_energy_site_refresh_error( async def test_energy_site_refresh_error(
hass: HomeAssistant, mock_site_info, side_effect, state hass: HomeAssistant,
mock_site_info: AsyncMock,
side_effect: TeslaFleetError,
state: ConfigEntryState,
) -> None: ) -> None:
"""Test coordinator refresh with an error.""" """Test coordinator refresh with an error."""
mock_site_info.side_effect = side_effect mock_site_info.side_effect = side_effect

View File

@ -1,9 +1,9 @@
"""Test the Teslemetry lock platform.""" """Test the Teslemetry lock platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.lock import ( from homeassistant.components.lock import (
@ -34,7 +34,7 @@ async def test_lock(
async def test_lock_offline( async def test_lock_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the lock entities are correct when offline.""" """Tests that the lock entities are correct when offline."""

View File

@ -1,8 +1,8 @@
"""Test the Teslemetry media player platform.""" """Test the Teslemetry media player platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
@ -38,7 +38,7 @@ async def test_media_player_alt(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the media player entities are correct.""" """Tests that the media player entities are correct."""
@ -49,7 +49,7 @@ async def test_media_player_alt(
async def test_media_player_offline( async def test_media_player_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the media player entities are correct when offline.""" """Tests that the media player entities are correct when offline."""
@ -63,7 +63,7 @@ async def test_media_player_noscope(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_metadata, mock_metadata: AsyncMock,
) -> None: ) -> None:
"""Tests that the media player entities are correct without required scope.""" """Tests that the media player entities are correct without required scope."""

View File

@ -1,9 +1,9 @@
"""Test the Teslemetry number platform.""" """Test the Teslemetry number platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.number import ( from homeassistant.components.number import (
@ -33,7 +33,7 @@ async def test_number(
async def test_number_offline( async def test_number_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the number entities are correct when offline.""" """Tests that the number entities are correct when offline."""
@ -44,7 +44,9 @@ async def test_number_offline(
@pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_number_services(hass: HomeAssistant, mock_vehicle_data) -> None: async def test_number_services(
hass: HomeAssistant, mock_vehicle_data: AsyncMock
) -> None:
"""Tests that the number services work.""" """Tests that the number services work."""
mock_vehicle_data.return_value = VEHICLE_DATA_ALT mock_vehicle_data.return_value = VEHICLE_DATA_ALT
await setup_platform(hass, [Platform.NUMBER]) await setup_platform(hass, [Platform.NUMBER])

View File

@ -1,9 +1,9 @@
"""Test the Teslemetry select platform.""" """Test the Teslemetry select platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.const import EnergyExportMode, EnergyOperationMode from tesla_fleet_api.const import EnergyExportMode, EnergyOperationMode
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
@ -35,7 +35,7 @@ async def test_select(
async def test_select_offline( async def test_select_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the select entities are correct when offline.""" """Tests that the select entities are correct when offline."""

View File

@ -1,8 +1,10 @@
"""Test the Teslemetry sensor platform.""" """Test the Teslemetry sensor platform."""
from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL
from homeassistant.const import Platform from homeassistant.const import Platform
@ -21,7 +23,7 @@ async def test_sensors(
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the sensor entities are correct.""" """Tests that the sensor entities are correct."""

View File

@ -1,9 +1,9 @@
"""Test the Teslemetry switch platform.""" """Test the Teslemetry switch platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.switch import ( from homeassistant.components.switch import (
@ -40,7 +40,7 @@ async def test_switch_alt(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the switch entities are correct.""" """Tests that the switch entities are correct."""
@ -51,7 +51,7 @@ async def test_switch_alt(
async def test_switch_offline( async def test_switch_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the switch entities are correct when offline.""" """Tests that the switch entities are correct when offline."""

View File

@ -1,10 +1,10 @@
"""Test the Teslemetry update platform.""" """Test the Teslemetry update platform."""
import copy import copy
from unittest.mock import patch from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
from syrupy import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import VehicleOffline from tesla_fleet_api.exceptions import VehicleOffline
from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL
@ -35,7 +35,7 @@ async def test_update_alt(
hass: HomeAssistant, hass: HomeAssistant,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the update entities are correct.""" """Tests that the update entities are correct."""
@ -46,7 +46,7 @@ async def test_update_alt(
async def test_update_offline( async def test_update_offline(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
) -> None: ) -> None:
"""Tests that the update entities are correct when offline.""" """Tests that the update entities are correct when offline."""
@ -58,7 +58,7 @@ async def test_update_offline(
async def test_update_services( async def test_update_services(
hass: HomeAssistant, hass: HomeAssistant,
mock_vehicle_data, mock_vehicle_data: AsyncMock,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None: