From cad87f51a37da0c01ae81377f7eb43d9ef94506f Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Fri, 27 Sep 2024 23:06:09 +1000 Subject: [PATCH] Code quality improvements for Teslemetry (#123444) --- .../components/teslemetry/__init__.py | 1 + .../components/teslemetry/climate.py | 15 ++++--- .../components/teslemetry/coordinator.py | 1 - homeassistant/components/teslemetry/cover.py | 20 ++++----- homeassistant/components/teslemetry/entity.py | 45 +++++++++++++------ .../components/teslemetry/helpers.py | 37 +++++++++++---- homeassistant/components/teslemetry/lock.py | 6 +-- .../components/teslemetry/media_player.py | 10 ++--- homeassistant/components/teslemetry/number.py | 4 +- homeassistant/components/teslemetry/select.py | 8 ++-- .../components/teslemetry/strings.json | 18 ++++++++ homeassistant/components/teslemetry/switch.py | 12 ++--- homeassistant/components/teslemetry/update.py | 2 +- tests/components/teslemetry/__init__.py | 2 +- .../teslemetry/snapshots/test_climate.ambr | 9 ++++ .../teslemetry/test_binary_sensors.py | 8 ++-- tests/components/teslemetry/test_button.py | 2 +- tests/components/teslemetry/test_climate.py | 24 +++++----- .../components/teslemetry/test_config_flow.py | 24 +++++++--- tests/components/teslemetry/test_cover.py | 10 ++--- .../teslemetry/test_device_tracker.py | 2 +- tests/components/teslemetry/test_init.py | 26 ++++++++--- tests/components/teslemetry/test_lock.py | 6 +-- .../teslemetry/test_media_player.py | 10 ++--- tests/components/teslemetry/test_number.py | 10 +++-- tests/components/teslemetry/test_select.py | 6 +-- tests/components/teslemetry/test_sensor.py | 6 ++- tests/components/teslemetry/test_switch.py | 8 ++-- tests/components/teslemetry/test_update.py | 10 ++--- 29 files changed, 218 insertions(+), 124 deletions(-) diff --git a/homeassistant/components/teslemetry/__init__.py b/homeassistant/components/teslemetry/__init__.py index 3bf19e0a218..ab2e4c04734 100644 --- a/homeassistant/components/teslemetry/__init__.py +++ b/homeassistant/components/teslemetry/__init__.py @@ -107,6 +107,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) - device=device, ) ) + elif "energy_site_id" in product and Scope.ENERGY_DEVICE_DATA in scopes: site_id = product["energy_site_id"] if not ( diff --git a/homeassistant/components/teslemetry/climate.py b/homeassistant/components/teslemetry/climate.py index 9218be4dcb1..5e933d1dbce 100644 --- a/homeassistant/components/teslemetry/climate.py +++ b/homeassistant/components/teslemetry/climate.py @@ -120,7 +120,8 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity): async def async_turn_on(self) -> None: """Set the climate state to on.""" - self.raise_for_scope() + + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.auto_conditioning_start()) @@ -129,7 +130,8 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity): async def async_turn_off(self) -> None: """Set the climate state to off.""" - self.raise_for_scope() + + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() 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: """Set the climate temperature.""" - if not (temp := kwargs.get(ATTR_TEMPERATURE)): - return + self.raise_for_scope(Scope.VEHICLE_CMDS) - 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( translation_domain=DOMAIN, translation_key="invalid_cop_temp", @@ -297,7 +300,7 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """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._async_set_cop(hvac_mode) self.async_write_ha_state() diff --git a/homeassistant/components/teslemetry/coordinator.py b/homeassistant/components/teslemetry/coordinator.py index 1dc61ad2595..4612408e14d 100644 --- a/homeassistant/components/teslemetry/coordinator.py +++ b/homeassistant/components/teslemetry/coordinator.py @@ -52,7 +52,6 @@ class TeslemetryVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]): """Class to manage fetching data from the Teslemetry API.""" updated_once: bool - pre2021: bool last_active: datetime def __init__( diff --git a/homeassistant/components/teslemetry/cover.py b/homeassistant/components/teslemetry/cover.py index 0b6d30b1faf..190f729d99f 100644 --- a/homeassistant/components/teslemetry/cover.py +++ b/homeassistant/components/teslemetry/cover.py @@ -79,7 +79,7 @@ class TeslemetryWindowEntity(TeslemetryVehicleEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Vent windows.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command( self.api.window_control(command=WindowCommand.VENT) @@ -89,7 +89,7 @@ class TeslemetryWindowEntity(TeslemetryVehicleEntity, CoverEntity): async def async_close_cover(self, **kwargs: Any) -> None: """Close windows.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command( self.api.window_control(command=WindowCommand.CLOSE) @@ -122,7 +122,7 @@ class TeslemetryChargePortEntity(TeslemetryVehicleEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Open charge port.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CHARGING_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.charge_port_door_open()) self._attr_is_closed = False @@ -130,7 +130,7 @@ class TeslemetryChargePortEntity(TeslemetryVehicleEntity, CoverEntity): async def async_close_cover(self, **kwargs: Any) -> None: """Close charge port.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CHARGING_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.charge_port_door_close()) self._attr_is_closed = True @@ -157,7 +157,7 @@ class TeslemetryFrontTrunkEntity(TeslemetryVehicleEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Open front trunk.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.actuate_trunk(Trunk.FRONT)) self._attr_is_closed = False @@ -193,7 +193,7 @@ class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Open rear trunk.""" 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 handle_vehicle_command(self.api.actuate_trunk(Trunk.REAR)) self._attr_is_closed = False @@ -202,7 +202,7 @@ class TeslemetryRearTrunkEntity(TeslemetryVehicleEntity, CoverEntity): async def async_close_cover(self, **kwargs: Any) -> None: """Close rear trunk.""" 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 handle_vehicle_command(self.api.actuate_trunk(Trunk.REAR)) self._attr_is_closed = True @@ -240,7 +240,7 @@ class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Open sunroof.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.VENT)) self._attr_is_closed = False @@ -248,7 +248,7 @@ class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverEntity): async def async_close_cover(self, **kwargs: Any) -> None: """Close sunroof.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.CLOSE)) self._attr_is_closed = True @@ -256,7 +256,7 @@ class TeslemetrySunroofEntity(TeslemetryVehicleEntity, CoverEntity): async def async_stop_cover(self, **kwargs: Any) -> None: """Close sunroof.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.sun_roof_control(SunRoofCommand.STOP)) self._attr_is_closed = False diff --git a/homeassistant/components/teslemetry/entity.py b/homeassistant/components/teslemetry/entity.py index 724d9371396..ca40d4d00ce 100644 --- a/homeassistant/components/teslemetry/entity.py +++ b/homeassistant/components/teslemetry/entity.py @@ -4,6 +4,7 @@ from abc import abstractmethod from typing import Any from tesla_fleet_api import EnergySpecific, VehicleSpecific +from tesla_fleet_api.const import Scope from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers.device_registry import DeviceInfo @@ -31,6 +32,7 @@ class TeslemetryEntity( """Parent class for all Teslemetry entities.""" _attr_has_entity_name = True + scoped: bool def __init__( self, @@ -38,12 +40,10 @@ class TeslemetryEntity( | TeslemetryEnergyHistoryCoordinator | TeslemetryEnergySiteLiveCoordinator | TeslemetryEnergySiteInfoCoordinator, - api: VehicleSpecific | EnergySpecific, key: str, ) -> None: """Initialize common aspects of a Teslemetry entity.""" super().__init__(coordinator) - self.api = api self.key = key self._attr_translation_key = self.key self._async_update_attrs() @@ -87,16 +87,22 @@ class TeslemetryEntity( def _async_update_attrs(self) -> None: """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.""" 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): """Parent class for Teslemetry Vehicle entities.""" _last_update: int = 0 + api: VehicleSpecific + vehicle: TeslemetryVehicleData def __init__( self, @@ -105,11 +111,11 @@ class TeslemetryVehicleEntity(TeslemetryEntity): ) -> None: """Initialize common aspects of a Teslemetry entity.""" - self._attr_unique_id = f"{data.vin}-{key}" + self.api = data.api self.vehicle = data - + self._attr_unique_id = f"{data.vin}-{key}" self._attr_device_info = data.device - super().__init__(data.coordinator, data.api, key) + super().__init__(data.coordinator, key) @property def _value(self) -> Any | None: @@ -124,31 +130,39 @@ class TeslemetryVehicleEntity(TeslemetryEntity): class TeslemetryEnergyLiveEntity(TeslemetryEntity): """Parent class for Teslemetry Energy Site Live entities.""" + api: EnergySpecific + def __init__( self, data: TeslemetryEnergyData, key: str, ) -> None: """Initialize common aspects of a Teslemetry Energy Site Live entity.""" + + self.api = data.api self._attr_unique_id = f"{data.id}-{key}" self._attr_device_info = data.device - super().__init__(data.live_coordinator, data.api, key) + super().__init__(data.live_coordinator, key) class TeslemetryEnergyInfoEntity(TeslemetryEntity): """Parent class for Teslemetry Energy Site Info Entities.""" + api: EnergySpecific + def __init__( self, data: TeslemetryEnergyData, key: str, ) -> None: """Initialize common aspects of a Teslemetry Energy Site Info entity.""" + + self.api = data.api self._attr_unique_id = f"{data.id}-{key}" self._attr_device_info = data.device - super().__init__(data.info_coordinator, data.api, key) + super().__init__(data.info_coordinator, key) class TeslemetryEnergyHistoryEntity(TeslemetryEntity): @@ -160,18 +174,19 @@ class TeslemetryEnergyHistoryEntity(TeslemetryEntity): key: str, ) -> None: """Initialize common aspects of a Teslemetry Energy Site Info entity.""" + + self.api = data.api self._attr_unique_id = f"{data.id}-{key}" self._attr_device_info = data.device - super().__init__(data.history_coordinator, data.api, key) + super().__init__(data.history_coordinator, key) -class TeslemetryWallConnectorEntity( - TeslemetryEntity, CoordinatorEntity[TeslemetryEnergySiteLiveCoordinator] -): +class TeslemetryWallConnectorEntity(TeslemetryEntity): """Parent class for Teslemetry Wall Connector Entities.""" _attr_has_entity_name = True + api: EnergySpecific def __init__( self, @@ -180,6 +195,8 @@ class TeslemetryWallConnectorEntity( key: str, ) -> None: """Initialize common aspects of a Teslemetry entity.""" + + self.api = data.api self.din = din self._attr_unique_id = f"{data.id}-{din}-{key}" @@ -200,7 +217,7 @@ class TeslemetryWallConnectorEntity( model=model, ) - super().__init__(data.live_coordinator, data.api, key) + super().__init__(data.live_coordinator, key) @property def _value(self) -> int: diff --git a/homeassistant/components/teslemetry/helpers.py b/homeassistant/components/teslemetry/helpers.py index a8cfa1051f1..4e086008333 100644 --- a/homeassistant/components/teslemetry/helpers.py +++ b/homeassistant/components/teslemetry/helpers.py @@ -7,7 +7,7 @@ from tesla_fleet_api.exceptions import TeslaFleetError from homeassistant.exceptions import HomeAssistantError -from .const import LOGGER, TeslemetryState +from .const import DOMAIN, LOGGER, TeslemetryState async def wake_up_vehicle(vehicle) -> None: @@ -22,12 +22,19 @@ async def wake_up_vehicle(vehicle) -> None: cmd = await vehicle.api.vehicle() state = cmd["response"]["state"] 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 if state != TeslemetryState.ONLINE: times += 1 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) @@ -36,18 +43,26 @@ async def handle_command(command) -> dict[str, Any]: try: result = await command 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) return result -async def handle_vehicle_command(command) -> dict[str, Any]: +async def handle_vehicle_command(command) -> 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) + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="command_error", + translation_placeholders={"error": error}, + ) # No response without error (unexpected) raise HomeAssistantError(f"Unknown response: {response}") if (result := response.get("result")) is not True: @@ -56,8 +71,14 @@ async def handle_vehicle_command(command) -> dict[str, Any]: # Reason is acceptable return result # 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) - raise HomeAssistantError("Command failed with no reason") + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="command_no_result" + ) # Response with result of true return result diff --git a/homeassistant/components/teslemetry/lock.py b/homeassistant/components/teslemetry/lock.py index e23747924f6..0a7a557ed88 100644 --- a/homeassistant/components/teslemetry/lock.py +++ b/homeassistant/components/teslemetry/lock.py @@ -53,7 +53,7 @@ class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity): async def async_lock(self, **kwargs: Any) -> None: """Lock the doors.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.door_lock()) self._attr_is_locked = True @@ -61,7 +61,7 @@ class TeslemetryVehicleLockEntity(TeslemetryVehicleEntity, LockEntity): async def async_unlock(self, **kwargs: Any) -> None: """Unlock the doors.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.door_unlock()) self._attr_is_locked = False @@ -96,7 +96,7 @@ class TeslemetryCableLockEntity(TeslemetryVehicleEntity, LockEntity): async def async_unlock(self, **kwargs: Any) -> None: """Unlock charge cable lock.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.charge_port_door_open()) self._attr_is_locked = False diff --git a/homeassistant/components/teslemetry/media_player.py b/homeassistant/components/teslemetry/media_player.py index b21ba0f733d..e0e144ffe3a 100644 --- a/homeassistant/components/teslemetry/media_player.py +++ b/homeassistant/components/teslemetry/media_player.py @@ -115,7 +115,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity): async def async_set_volume_level(self, volume: float) -> None: """Set volume level, range 0..1.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command( self.api.adjust_volume(int(volume * self._volume_max)) @@ -126,7 +126,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity): async def async_media_play(self) -> None: """Send play command.""" if self.state != MediaPlayerState.PLAYING: - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.media_toggle_playback()) self._attr_state = MediaPlayerState.PLAYING @@ -135,7 +135,7 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity): async def async_media_pause(self) -> None: """Send pause command.""" if self.state == MediaPlayerState.PLAYING: - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.media_toggle_playback()) self._attr_state = MediaPlayerState.PAUSED @@ -143,12 +143,12 @@ class TeslemetryMediaEntity(TeslemetryVehicleEntity, MediaPlayerEntity): async def async_media_next_track(self) -> None: """Send next track command.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.media_next_track()) async def async_media_previous_track(self) -> None: """Send previous track command.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.media_prev_track()) diff --git a/homeassistant/components/teslemetry/number.py b/homeassistant/components/teslemetry/number.py index 8c14c8e4186..9ba9c28b199 100644 --- a/homeassistant/components/teslemetry/number.py +++ b/homeassistant/components/teslemetry/number.py @@ -164,7 +164,7 @@ class TeslemetryVehicleNumberEntity(TeslemetryVehicleEntity, NumberEntity): async def async_set_native_value(self, value: float) -> None: """Set new value.""" value = int(value) - self.raise_for_scope() + self.raise_for_scope(self.entity_description.scopes[0]) await self.wake_up_if_asleep() await handle_vehicle_command(self.entity_description.func(self.api, value)) self._attr_native_value = value @@ -200,7 +200,7 @@ class TeslemetryEnergyInfoNumberSensorEntity(TeslemetryEnergyInfoEntity, NumberE async def async_set_native_value(self, value: float) -> None: """Set new 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)) self._attr_native_value = value self.async_write_ha_state() diff --git a/homeassistant/components/teslemetry/select.py b/homeassistant/components/teslemetry/select.py index 7cbdd4e31d2..192e2b194a8 100644 --- a/homeassistant/components/teslemetry/select.py +++ b/homeassistant/components/teslemetry/select.py @@ -144,7 +144,7 @@ class TeslemetrySeatHeaterSelectEntity(TeslemetryVehicleEntity, SelectEntity): async def async_select_option(self, option: str) -> None: """Change the selected option.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() level = self._attr_options.index(option) # 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: """Change the selected option.""" - self.raise_for_scope() + self.raise_for_scope(Scope.VEHICLE_CMDS) await self.wake_up_if_asleep() level = self._attr_options.index(option) # 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: """Change the selected option.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await handle_command(self.api.operation(option)) self._attr_current_option = option self.async_write_ha_state() @@ -256,7 +256,7 @@ class TeslemetryExportRuleSelectEntity(TeslemetryEnergyInfoEntity, SelectEntity) async def async_select_option(self, option: str) -> None: """Change the selected option.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await handle_command( self.api.grid_import_export(customer_preferred_export_rule=option) ) diff --git a/homeassistant/components/teslemetry/strings.json b/homeassistant/components/teslemetry/strings.json index b7ba06fbce4..005c87571f6 100644 --- a/homeassistant/components/teslemetry/strings.json +++ b/homeassistant/components/teslemetry/strings.json @@ -567,8 +567,26 @@ "no_energy_site_data_for_device": { "message": "No energy site data for device ID: {device_id}" }, + "command_exception": { + "message": "Command returned exception: {message}" + }, "command_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": { diff --git a/homeassistant/components/teslemetry/switch.py b/homeassistant/components/teslemetry/switch.py index 3204d73410f..91ef3074bae 100644 --- a/homeassistant/components/teslemetry/switch.py +++ b/homeassistant/components/teslemetry/switch.py @@ -157,7 +157,7 @@ class TeslemetryVehicleSwitchEntity(TeslemetryVehicleEntity, TeslemetrySwitchEnt async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the Switch.""" - self.raise_for_scope() + self.raise_for_scope(self.entity_description.scopes[0]) await self.wake_up_if_asleep() await handle_vehicle_command(self.entity_description.on_func(self.api)) self._attr_is_on = True @@ -165,7 +165,7 @@ class TeslemetryVehicleSwitchEntity(TeslemetryVehicleEntity, TeslemetrySwitchEnt async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the Switch.""" - self.raise_for_scope() + self.raise_for_scope(self.entity_description.scopes[0]) await self.wake_up_if_asleep() await handle_vehicle_command(self.entity_description.off_func(self.api)) self._attr_is_on = False @@ -207,7 +207,7 @@ class TeslemetryChargeFromGridSwitchEntity( async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the Switch.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await handle_command( self.api.grid_import_export( disallow_charge_from_grid_with_solar_installed=False @@ -218,7 +218,7 @@ class TeslemetryChargeFromGridSwitchEntity( async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the Switch.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await handle_command( self.api.grid_import_export( disallow_charge_from_grid_with_solar_installed=True @@ -249,14 +249,14 @@ class TeslemetryStormModeSwitchEntity( async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the Switch.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await handle_command(self.api.storm_mode(enabled=True)) self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the Switch.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await handle_command(self.api.storm_mode(enabled=False)) self._attr_is_on = False self.async_write_ha_state() diff --git a/homeassistant/components/teslemetry/update.py b/homeassistant/components/teslemetry/update.py index de508fa58d4..1884689ae64 100644 --- a/homeassistant/components/teslemetry/update.py +++ b/homeassistant/components/teslemetry/update.py @@ -103,7 +103,7 @@ class TeslemetryUpdateEntity(TeslemetryVehicleEntity, UpdateEntity): self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install an update.""" - self.raise_for_scope() + self.raise_for_scope(Scope.ENERGY_CMDS) await self.wake_up_if_asleep() await handle_vehicle_command(self.api.schedule_software_update(offset_sec=60)) self._attr_in_progress = True diff --git a/tests/components/teslemetry/__init__.py b/tests/components/teslemetry/__init__.py index c4fbdaf3fbd..b6b9df7eb4b 100644 --- a/tests/components/teslemetry/__init__.py +++ b/tests/components/teslemetry/__init__.py @@ -2,7 +2,7 @@ from unittest.mock import patch -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from homeassistant.components.teslemetry.const import DOMAIN from homeassistant.const import Platform diff --git a/tests/components/teslemetry/snapshots/test_climate.ambr b/tests/components/teslemetry/snapshots/test_climate.ambr index f5a95c7e3f2..9d5e3827ffc 100644 --- a/tests/components/teslemetry/snapshots/test_climate.ambr +++ b/tests/components/teslemetry/snapshots/test_climate.ambr @@ -1,4 +1,10 @@ # 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] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -499,3 +505,6 @@ 'state': 'unknown', }) # --- +# name: test_invalid_error[error] + 'Command returned exception: The data request or command is unknown.' +# --- diff --git a/tests/components/teslemetry/test_binary_sensors.py b/tests/components/teslemetry/test_binary_sensors.py index a7a8c03c174..95fccde5f25 100644 --- a/tests/components/teslemetry/test_binary_sensors.py +++ b/tests/components/teslemetry/test_binary_sensors.py @@ -1,8 +1,10 @@ """Test the Teslemetry binary sensor platform.""" +from unittest.mock import AsyncMock + from freezegun.api import FrozenDateTimeFactory import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL @@ -33,7 +35,7 @@ async def test_binary_sensor_refresh( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, freezer: FrozenDateTimeFactory, ) -> None: """Tests that the binary sensor entities are correct.""" @@ -51,7 +53,7 @@ async def test_binary_sensor_refresh( async def test_binary_sensor_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the binary sensor entities are correct when offline.""" diff --git a/tests/components/teslemetry/test_button.py b/tests/components/teslemetry/test_button.py index a10e3efdff2..04edf668765 100644 --- a/tests/components/teslemetry/test_button.py +++ b/tests/components/teslemetry/test_button.py @@ -3,7 +3,7 @@ from unittest.mock import patch import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.const import ATTR_ENTITY_ID, Platform diff --git a/tests/components/teslemetry/test_climate.py b/tests/components/teslemetry/test_climate.py index 800748f4c77..55f99caa13c 100644 --- a/tests/components/teslemetry/test_climate.py +++ b/tests/components/teslemetry/test_climate.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, patch from freezegun.api import FrozenDateTimeFactory import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import InvalidCommand, VehicleOffline from homeassistant.components.climate import ( @@ -196,7 +196,7 @@ async def test_climate_alt( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the climate entity is correct.""" @@ -210,7 +210,7 @@ async def test_climate_offline( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """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) -async def test_invalid_error(hass: HomeAssistant) -> None: +async def test_invalid_error(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None: """Tests service error is handled.""" await setup_platform(hass, platforms=[Platform.CLIMATE]) @@ -239,10 +239,7 @@ async def test_invalid_error(hass: HomeAssistant) -> None: blocking=True, ) mock_on.assert_called_once() - assert ( - str(error.value) - == "Teslemetry command failed, The data request or command is unknown." - ) + assert str(error.value) == snapshot(name="error") @pytest.mark.parametrize("response", COMMAND_ERRORS) @@ -291,10 +288,11 @@ async def test_ignored_error( @pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_asleep_or_offline( hass: HomeAssistant, - mock_vehicle_data, - mock_wake_up, - mock_vehicle, + mock_vehicle_data: AsyncMock, + mock_wake_up: AsyncMock, + mock_vehicle: AsyncMock, freezer: FrozenDateTimeFactory, + snapshot: SnapshotAssertion, ) -> None: """Tests asleep is handled.""" @@ -320,7 +318,7 @@ async def test_asleep_or_offline( {ATTR_ENTITY_ID: [entity_id]}, 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.side_effect = None @@ -339,7 +337,7 @@ async def test_asleep_or_offline( {ATTR_ENTITY_ID: [entity_id]}, 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_vehicle.assert_called() diff --git a/tests/components/teslemetry/test_config_flow.py b/tests/components/teslemetry/test_config_flow.py index 03e46c6a8be..aeee3a620d4 100644 --- a/tests/components/teslemetry/test_config_flow.py +++ b/tests/components/teslemetry/test_config_flow.py @@ -1,6 +1,6 @@ """Test the Teslemetry config flow.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch from aiohttp import ClientConnectionError import pytest @@ -60,7 +60,10 @@ async def test_form( ], ) 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: """Test errors are handled.""" @@ -86,7 +89,7 @@ async def test_form_errors( 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.""" mock_entry = MockConfigEntry( @@ -127,7 +130,10 @@ async def test_reauth(hass: HomeAssistant, mock_metadata) -> None: ], ) 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: """Test reauth flows that fail.""" @@ -178,7 +184,7 @@ async def test_unique_id_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.""" 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"] -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.""" 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 -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.""" mock_metadata.side_effect = TeslaFleetError diff --git a/tests/components/teslemetry/test_cover.py b/tests/components/teslemetry/test_cover.py index 8d4493ab25f..464f91aabfc 100644 --- a/tests/components/teslemetry/test_cover.py +++ b/tests/components/teslemetry/test_cover.py @@ -1,9 +1,9 @@ """Test the Teslemetry cover platform.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.components.cover import ( @@ -43,7 +43,7 @@ async def test_cover_alt( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the cover entities are correct with alternate values.""" @@ -57,7 +57,7 @@ async def test_cover_noscope( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_metadata, + mock_metadata: AsyncMock, ) -> None: """Tests that the cover entities are correct without scopes.""" @@ -68,7 +68,7 @@ async def test_cover_noscope( async def test_cover_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the cover entities are correct when offline.""" diff --git a/tests/components/teslemetry/test_device_tracker.py b/tests/components/teslemetry/test_device_tracker.py index 55deaefdab5..a3fcd428c66 100644 --- a/tests/components/teslemetry/test_device_tracker.py +++ b/tests/components/teslemetry/test_device_tracker.py @@ -1,6 +1,6 @@ """Test the Teslemetry device tracker platform.""" -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.const import STATE_UNKNOWN, Platform diff --git a/tests/components/teslemetry/test_init.py b/tests/components/teslemetry/test_init.py index b96ef42cd2e..a7afff9e341 100644 --- a/tests/components/teslemetry/test_init.py +++ b/tests/components/teslemetry/test_init.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock from freezegun.api import FrozenDateTimeFactory import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import ( InvalidToken, SubscriptionRequired, @@ -48,7 +48,10 @@ async def test_load_unload(hass: HomeAssistant) -> None: @pytest.mark.parametrize(("side_effect", "state"), ERRORS) async def test_init_error( - hass: HomeAssistant, mock_products, side_effect, state + hass: HomeAssistant, + mock_products: AsyncMock, + side_effect: TeslaFleetError, + state: ConfigEntryState, ) -> None: """Test init with errors.""" @@ -86,7 +89,7 @@ async def test_vehicle_refresh_asleep( async def test_vehicle_refresh_offline( - hass: HomeAssistant, mock_vehicle_data, freezer: FrozenDateTimeFactory + hass: HomeAssistant, mock_vehicle_data: AsyncMock, freezer: FrozenDateTimeFactory ) -> None: """Test coordinator refresh with an error.""" entry = await setup_platform(hass, [Platform.CLIMATE]) @@ -103,7 +106,10 @@ async def test_vehicle_refresh_offline( @pytest.mark.parametrize(("side_effect", "state"), ERRORS) 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: """Test coordinator refresh with an error.""" mock_vehicle_data.side_effect = side_effect @@ -112,7 +118,7 @@ async def test_vehicle_refresh_error( async def test_vehicle_sleep( - hass: HomeAssistant, mock_vehicle_data, freezer: FrozenDateTimeFactory + hass: HomeAssistant, mock_vehicle_data: AsyncMock, freezer: FrozenDateTimeFactory ) -> None: """Test coordinator refresh with an error.""" await setup_platform(hass, [Platform.CLIMATE]) @@ -171,7 +177,10 @@ async def test_vehicle_sleep( # Test Energy Live Coordinator @pytest.mark.parametrize(("side_effect", "state"), ERRORS) 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: """Test coordinator refresh with an error.""" mock_live_status.side_effect = side_effect @@ -182,7 +191,10 @@ async def test_energy_live_refresh_error( # Test Energy Site Coordinator @pytest.mark.parametrize(("side_effect", "state"), ERRORS) 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: """Test coordinator refresh with an error.""" mock_site_info.side_effect = side_effect diff --git a/tests/components/teslemetry/test_lock.py b/tests/components/teslemetry/test_lock.py index bd8e72a1df3..b1460e870f0 100644 --- a/tests/components/teslemetry/test_lock.py +++ b/tests/components/teslemetry/test_lock.py @@ -1,9 +1,9 @@ """Test the Teslemetry lock platform.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.components.lock import ( @@ -34,7 +34,7 @@ async def test_lock( async def test_lock_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the lock entities are correct when offline.""" diff --git a/tests/components/teslemetry/test_media_player.py b/tests/components/teslemetry/test_media_player.py index 8544c11a625..0d30750d10d 100644 --- a/tests/components/teslemetry/test_media_player.py +++ b/tests/components/teslemetry/test_media_player.py @@ -1,8 +1,8 @@ """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 homeassistant.components.media_player import ( @@ -38,7 +38,7 @@ async def test_media_player_alt( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the media player entities are correct.""" @@ -49,7 +49,7 @@ async def test_media_player_alt( async def test_media_player_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the media player entities are correct when offline.""" @@ -63,7 +63,7 @@ async def test_media_player_noscope( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_metadata, + mock_metadata: AsyncMock, ) -> None: """Tests that the media player entities are correct without required scope.""" diff --git a/tests/components/teslemetry/test_number.py b/tests/components/teslemetry/test_number.py index 728d37c4d7c..5df948b475c 100644 --- a/tests/components/teslemetry/test_number.py +++ b/tests/components/teslemetry/test_number.py @@ -1,9 +1,9 @@ """Test the Teslemetry number platform.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.components.number import ( @@ -33,7 +33,7 @@ async def test_number( async def test_number_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """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") -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.""" mock_vehicle_data.return_value = VEHICLE_DATA_ALT await setup_platform(hass, [Platform.NUMBER]) diff --git a/tests/components/teslemetry/test_select.py b/tests/components/teslemetry/test_select.py index 3b1c8c436bf..caf0b9c1deb 100644 --- a/tests/components/teslemetry/test_select.py +++ b/tests/components/teslemetry/test_select.py @@ -1,9 +1,9 @@ """Test the Teslemetry select platform.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.const import EnergyExportMode, EnergyOperationMode from tesla_fleet_api.exceptions import VehicleOffline @@ -35,7 +35,7 @@ async def test_select( async def test_select_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the select entities are correct when offline.""" diff --git a/tests/components/teslemetry/test_sensor.py b/tests/components/teslemetry/test_sensor.py index c5bdd15d712..f0b472a7183 100644 --- a/tests/components/teslemetry/test_sensor.py +++ b/tests/components/teslemetry/test_sensor.py @@ -1,8 +1,10 @@ """Test the Teslemetry sensor platform.""" +from unittest.mock import AsyncMock + from freezegun.api import FrozenDateTimeFactory import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL from homeassistant.const import Platform @@ -21,7 +23,7 @@ async def test_sensors( snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, freezer: FrozenDateTimeFactory, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the sensor entities are correct.""" diff --git a/tests/components/teslemetry/test_switch.py b/tests/components/teslemetry/test_switch.py index 47a2843eb8f..dae3ce6fbf8 100644 --- a/tests/components/teslemetry/test_switch.py +++ b/tests/components/teslemetry/test_switch.py @@ -1,9 +1,9 @@ """Test the Teslemetry switch platform.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.components.switch import ( @@ -40,7 +40,7 @@ async def test_switch_alt( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the switch entities are correct.""" @@ -51,7 +51,7 @@ async def test_switch_alt( async def test_switch_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the switch entities are correct when offline.""" diff --git a/tests/components/teslemetry/test_update.py b/tests/components/teslemetry/test_update.py index 62bbcc94516..f02f09cd19a 100644 --- a/tests/components/teslemetry/test_update.py +++ b/tests/components/teslemetry/test_update.py @@ -1,10 +1,10 @@ """Test the Teslemetry update platform.""" import copy -from unittest.mock import patch +from unittest.mock import AsyncMock, patch from freezegun.api import FrozenDateTimeFactory -from syrupy import SnapshotAssertion +from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import VehicleOffline from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL @@ -35,7 +35,7 @@ async def test_update_alt( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the update entities are correct.""" @@ -46,7 +46,7 @@ async def test_update_alt( async def test_update_offline( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, ) -> None: """Tests that the update entities are correct when offline.""" @@ -58,7 +58,7 @@ async def test_update_offline( async def test_update_services( hass: HomeAssistant, - mock_vehicle_data, + mock_vehicle_data: AsyncMock, freezer: FrozenDateTimeFactory, snapshot: SnapshotAssertion, ) -> None: