Improve error handling in Teslemetry (#117336)

* Improvement command handle

* Add test for ignored reasons
pull/117338/head
Brett Adams 2024-05-13 12:37:59 +10:00 committed by GitHub
parent 4d5ae57390
commit af0dd189d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 16 deletions

View File

@ -74,10 +74,9 @@ class TeslemetryEntity(
"""Handle a command."""
try:
result = await command
LOGGER.debug("Command result: %s", result)
except TeslaFleetError as e:
LOGGER.debug("Command error: %s", e.message)
raise HomeAssistantError(f"Teslemetry command failed, {e.message}") from e
LOGGER.debug("Command result: %s", result)
return result
def _handle_coordinator_update(self) -> None:
@ -137,21 +136,20 @@ class TeslemetryVehicleEntity(TeslemetryEntity):
"""Handle a vehicle command."""
result = await super().handle_command(command)
if (response := result.get("response")) is None:
if message := result.get("error"):
if error := result.get("error"):
# No response with error
LOGGER.info("Command failure: %s", message)
raise HomeAssistantError(message)
raise HomeAssistantError(error)
# No response without error (unexpected)
LOGGER.error("Unknown response: %s", response)
raise HomeAssistantError("Unknown response")
if (message := response.get("result")) is not True:
if message := response.get("reason"):
raise HomeAssistantError(f"Unknown response: {response}")
if (result := response.get("result")) is not True:
if reason := response.get("reason"):
if reason in ("already_set", "not_charging", "requested"):
# Reason is acceptable
return result
# Result of false with reason
LOGGER.info("Command failure: %s", message)
raise HomeAssistantError(message)
raise HomeAssistantError(reason)
# Result of false without reason (unexpected)
LOGGER.error("Unknown response: %s", response)
raise HomeAssistantError("Unknown response")
raise HomeAssistantError("Command failed with no reason")
# Response with result of true
return result

View File

@ -18,6 +18,7 @@ SITE_INFO = load_json_object_fixture("site_info.json", DOMAIN)
COMMAND_OK = {"response": {"result": True, "reason": ""}}
COMMAND_REASON = {"response": {"result": False, "reason": "already closed"}}
COMMAND_IGNORED_REASON = {"response": {"result": False, "reason": "already_set"}}
COMMAND_NOREASON = {"response": {"result": False}} # Unexpected
COMMAND_ERROR = {
"response": None,

View File

@ -27,6 +27,7 @@ from homeassistant.helpers import entity_registry as er
from . import assert_entities, setup_platform
from .const import (
COMMAND_ERRORS,
COMMAND_IGNORED_REASON,
METADATA_NOSCOPE,
VEHICLE_DATA_ALT,
WAKE_UP_ASLEEP,
@ -134,8 +135,7 @@ async def test_climate_offline(
assert_entities(hass, entry.entry_id, entity_registry, snapshot)
@pytest.mark.parametrize("response", COMMAND_ERRORS)
async def test_errors(hass: HomeAssistant, response: str) -> None:
async def test_invalid_error(hass: HomeAssistant) -> None:
"""Tests service error is handled."""
await setup_platform(hass, platforms=[Platform.CLIMATE])
@ -157,12 +157,20 @@ async def test_errors(hass: HomeAssistant, response: str) -> None:
mock_on.assert_called_once()
assert error.from_exception == InvalidCommand
@pytest.mark.parametrize("response", COMMAND_ERRORS)
async def test_errors(hass: HomeAssistant, response: str) -> None:
"""Tests service reason is handled."""
await setup_platform(hass, platforms=[Platform.CLIMATE])
entity_id = "climate.test_climate"
with (
patch(
"homeassistant.components.teslemetry.VehicleSpecific.auto_conditioning_start",
return_value=response,
) as mock_on,
pytest.raises(HomeAssistantError) as error,
pytest.raises(HomeAssistantError),
):
await hass.services.async_call(
CLIMATE_DOMAIN,
@ -173,6 +181,26 @@ async def test_errors(hass: HomeAssistant, response: str) -> None:
mock_on.assert_called_once()
async def test_ignored_error(
hass: HomeAssistant,
) -> None:
"""Tests ignored error is handled."""
await setup_platform(hass, [Platform.CLIMATE])
entity_id = "climate.test_climate"
with patch(
"homeassistant.components.teslemetry.VehicleSpecific.auto_conditioning_start",
return_value=COMMAND_IGNORED_REASON,
) as mock_on:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
mock_on.assert_called_once()
async def test_asleep_or_offline(
hass: HomeAssistant,
mock_vehicle_data,