mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 08:26:41 +01:00
Add exception translations to Tessie (#166047)
This commit is contained in:
@@ -7,7 +7,7 @@ from http import HTTPStatus
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from aiohttp import ClientResponseError
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from tesla_fleet_api.const import TeslaEnergyPeriod
|
||||
from tesla_fleet_api.exceptions import InvalidToken, MissingToken, TeslaFleetError
|
||||
from tesla_fleet_api.tessie import EnergySite
|
||||
@@ -83,7 +83,15 @@ class TessieStateUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
except ClientResponseError as e:
|
||||
if e.status == HTTPStatus.UNAUTHORIZED:
|
||||
raise ConfigEntryAuthFailed from e
|
||||
raise
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from e
|
||||
except ClientError as e:
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from e
|
||||
return flatten(vehicle)
|
||||
|
||||
|
||||
@@ -123,7 +131,10 @@ class TessieEnergySiteLiveCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
except (InvalidToken, MissingToken) as e:
|
||||
raise ConfigEntryAuthFailed from e
|
||||
except TeslaFleetError as e:
|
||||
raise UpdateFailed(e.message) from e
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from e
|
||||
|
||||
# Convert Wall Connectors from array to dict
|
||||
data["wall_connectors"] = {
|
||||
@@ -159,7 +170,10 @@ class TessieEnergySiteInfoCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
except (InvalidToken, MissingToken) as e:
|
||||
raise ConfigEntryAuthFailed from e
|
||||
except TeslaFleetError as e:
|
||||
raise UpdateFailed(e.message) from e
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from e
|
||||
|
||||
return flatten(data)
|
||||
|
||||
@@ -197,7 +211,10 @@ class TessieEnergyHistoryCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
translation_key="auth_failed",
|
||||
) from e
|
||||
except TeslaFleetError as e:
|
||||
raise UpdateFailed(e.message) from e
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from e
|
||||
|
||||
if (
|
||||
not data
|
||||
|
||||
@@ -4,7 +4,7 @@ from abc import abstractmethod
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientResponseError
|
||||
from aiohttp import ClientError
|
||||
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -103,8 +103,11 @@ class TessieEntity(TessieBaseEntity):
|
||||
api_key=self._api_key,
|
||||
**kargs,
|
||||
)
|
||||
except ClientResponseError as e:
|
||||
raise HomeAssistantError from e
|
||||
except ClientError as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="cannot_connect",
|
||||
) from e
|
||||
if response["result"] is False:
|
||||
name: str = getattr(self, "name", self.entity_id)
|
||||
reason: str = response.get("reason", "unknown")
|
||||
|
||||
@@ -72,13 +72,7 @@ rules:
|
||||
entity-device-class: done
|
||||
entity-disabled-by-default: done
|
||||
entity-translations: done
|
||||
exception-translations:
|
||||
status: todo
|
||||
comment: |
|
||||
Most user-facing exceptions have translations (HomeAssistantError and
|
||||
ServiceValidationError use translation keys from strings.json). Remaining:
|
||||
entity.py raises bare HomeAssistantError for ClientResponseError, and
|
||||
coordinators raise UpdateFailed with untranslated messages.
|
||||
exception-translations: done
|
||||
icon-translations: done
|
||||
reconfiguration-flow: todo
|
||||
repair-issues: todo
|
||||
|
||||
@@ -631,6 +631,9 @@
|
||||
"cable_connected": {
|
||||
"message": "Charge cable is connected."
|
||||
},
|
||||
"cannot_connect": {
|
||||
"message": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"command_failed": {
|
||||
"message": "Command failed, {message}"
|
||||
},
|
||||
|
||||
@@ -22,7 +22,13 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import ERROR_UNKNOWN, TEST_RESPONSE, assert_entities, setup_platform
|
||||
from .common import (
|
||||
ERROR_CONNECTION,
|
||||
ERROR_UNKNOWN,
|
||||
TEST_RESPONSE,
|
||||
assert_entities,
|
||||
setup_platform,
|
||||
)
|
||||
|
||||
|
||||
async def test_climate(
|
||||
@@ -130,6 +136,27 @@ async def test_errors(hass: HomeAssistant) -> None:
|
||||
)
|
||||
mock_set.assert_called_once()
|
||||
assert error.value.__cause__ == ERROR_UNKNOWN
|
||||
assert error.value.translation_domain == "tessie"
|
||||
assert error.value.translation_key == "cannot_connect"
|
||||
|
||||
# Test setting climate on with connection error
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.tessie.climate.stop_climate",
|
||||
side_effect=ERROR_CONNECTION,
|
||||
) as mock_set,
|
||||
pytest.raises(HomeAssistantError) as error,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: [entity_id]},
|
||||
blocking=True,
|
||||
)
|
||||
mock_set.assert_called_once()
|
||||
assert error.value.__cause__ == ERROR_CONNECTION
|
||||
assert error.value.translation_domain == "tessie"
|
||||
assert error.value.translation_key == "cannot_connect"
|
||||
|
||||
# Test setting climate with child presence detection error
|
||||
with (
|
||||
|
||||
@@ -7,6 +7,7 @@ import pytest
|
||||
from tesla_fleet_api.exceptions import Forbidden, InvalidToken, MissingToken
|
||||
|
||||
from homeassistant.components.tessie import PLATFORMS
|
||||
from homeassistant.components.tessie.const import DOMAIN
|
||||
from homeassistant.components.tessie.coordinator import (
|
||||
TESSIE_ENERGY_HISTORY_INTERVAL,
|
||||
TESSIE_FLEET_API_SYNC_INTERVAL,
|
||||
@@ -15,6 +16,7 @@ from homeassistant.components.tessie.coordinator import (
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_ON, STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import UpdateFailed
|
||||
|
||||
from .common import ERROR_AUTH, ERROR_CONNECTION, ERROR_UNKNOWN, setup_platform
|
||||
|
||||
@@ -43,13 +45,17 @@ async def test_coordinator_clienterror(
|
||||
"""Tests that the coordinator handles client errors."""
|
||||
|
||||
mock_get_state.side_effect = ERROR_UNKNOWN
|
||||
await setup_platform(hass, [Platform.BINARY_SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.BINARY_SENSOR])
|
||||
coordinator = entry.runtime_data.vehicles[0].data_coordinator
|
||||
|
||||
freezer.tick(WAIT)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
mock_get_state.assert_called_once()
|
||||
assert hass.states.get("binary_sensor.test_status").state == STATE_UNAVAILABLE
|
||||
assert isinstance(coordinator.last_exception, UpdateFailed)
|
||||
assert coordinator.last_exception.translation_domain == DOMAIN
|
||||
assert coordinator.last_exception.translation_key == "cannot_connect"
|
||||
|
||||
|
||||
async def test_coordinator_auth(
|
||||
@@ -72,12 +78,16 @@ async def test_coordinator_connection(
|
||||
"""Tests that the coordinator handles connection errors."""
|
||||
|
||||
mock_get_state.side_effect = ERROR_CONNECTION
|
||||
await setup_platform(hass, [Platform.BINARY_SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.BINARY_SENSOR])
|
||||
coordinator = entry.runtime_data.vehicles[0].data_coordinator
|
||||
freezer.tick(WAIT)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
mock_get_state.assert_called_once()
|
||||
assert hass.states.get("binary_sensor.test_status").state == STATE_UNAVAILABLE
|
||||
assert isinstance(coordinator.last_exception, UpdateFailed)
|
||||
assert coordinator.last_exception.translation_domain == DOMAIN
|
||||
assert coordinator.last_exception.translation_key == "cannot_connect"
|
||||
|
||||
|
||||
async def test_coordinator_live_error(
|
||||
@@ -85,7 +95,9 @@ async def test_coordinator_live_error(
|
||||
) -> None:
|
||||
"""Tests that the energy live coordinator handles fleet errors."""
|
||||
|
||||
await setup_platform(hass, [Platform.SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.SENSOR])
|
||||
coordinator = entry.runtime_data.energysites[0].live_coordinator
|
||||
assert coordinator is not None
|
||||
|
||||
mock_live_status.reset_mock()
|
||||
mock_live_status.side_effect = Forbidden
|
||||
@@ -94,6 +106,9 @@ async def test_coordinator_live_error(
|
||||
await hass.async_block_till_done()
|
||||
mock_live_status.assert_called_once()
|
||||
assert hass.states.get("sensor.energy_site_solar_power").state == STATE_UNAVAILABLE
|
||||
assert isinstance(coordinator.last_exception, UpdateFailed)
|
||||
assert coordinator.last_exception.translation_domain == DOMAIN
|
||||
assert coordinator.last_exception.translation_key == "cannot_connect"
|
||||
|
||||
|
||||
async def test_coordinator_info_error(
|
||||
@@ -101,7 +116,8 @@ async def test_coordinator_info_error(
|
||||
) -> None:
|
||||
"""Tests that the energy info coordinator handles fleet errors."""
|
||||
|
||||
await setup_platform(hass, [Platform.SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.SENSOR])
|
||||
coordinator = entry.runtime_data.energysites[0].info_coordinator
|
||||
|
||||
mock_site_info.reset_mock()
|
||||
mock_site_info.side_effect = Forbidden
|
||||
@@ -113,6 +129,9 @@ async def test_coordinator_info_error(
|
||||
hass.states.get("sensor.energy_site_vpp_backup_reserve").state
|
||||
== STATE_UNAVAILABLE
|
||||
)
|
||||
assert isinstance(coordinator.last_exception, UpdateFailed)
|
||||
assert coordinator.last_exception.translation_domain == DOMAIN
|
||||
assert coordinator.last_exception.translation_key == "cannot_connect"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -144,7 +163,9 @@ async def test_coordinator_energy_history_error(
|
||||
) -> None:
|
||||
"""Tests that the energy history coordinator handles fleet errors."""
|
||||
|
||||
await setup_platform(hass, [Platform.SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.SENSOR])
|
||||
coordinator = entry.runtime_data.energysites[0].history_coordinator
|
||||
assert coordinator is not None
|
||||
|
||||
mock_energy_history.reset_mock()
|
||||
mock_energy_history.side_effect = Forbidden
|
||||
@@ -155,6 +176,9 @@ async def test_coordinator_energy_history_error(
|
||||
assert (
|
||||
hass.states.get("sensor.energy_site_grid_imported").state == STATE_UNAVAILABLE
|
||||
)
|
||||
assert isinstance(coordinator.last_exception, UpdateFailed)
|
||||
assert coordinator.last_exception.translation_domain == DOMAIN
|
||||
assert coordinator.last_exception.translation_key == "cannot_connect"
|
||||
|
||||
|
||||
async def test_coordinator_energy_history_invalid_data(
|
||||
@@ -162,7 +186,9 @@ async def test_coordinator_energy_history_invalid_data(
|
||||
) -> None:
|
||||
"""Tests that the energy history coordinator handles invalid data."""
|
||||
|
||||
await setup_platform(hass, [Platform.SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.SENSOR])
|
||||
coordinator = entry.runtime_data.energysites[0].history_coordinator
|
||||
assert coordinator is not None
|
||||
|
||||
mock_energy_history.reset_mock()
|
||||
mock_energy_history.side_effect = lambda *a, **kw: {"response": {}}
|
||||
@@ -173,3 +199,6 @@ async def test_coordinator_energy_history_invalid_data(
|
||||
assert (
|
||||
hass.states.get("sensor.energy_site_grid_imported").state == STATE_UNAVAILABLE
|
||||
)
|
||||
assert isinstance(coordinator.last_exception, UpdateFailed)
|
||||
assert coordinator.last_exception.translation_domain == DOMAIN
|
||||
assert coordinator.last_exception.translation_key == "invalid_energy_history_data"
|
||||
|
||||
@@ -96,6 +96,8 @@ async def test_errors(hass: HomeAssistant) -> None:
|
||||
)
|
||||
mock_set.assert_called_once()
|
||||
assert error.value.__cause__ == ERROR_UNKNOWN
|
||||
assert error.value.translation_domain == "tessie"
|
||||
assert error.value.translation_key == "cannot_connect"
|
||||
|
||||
# Test setting cover open with unknown error
|
||||
with (
|
||||
|
||||
@@ -125,6 +125,8 @@ async def test_errors(hass: HomeAssistant) -> None:
|
||||
)
|
||||
mock_set.assert_called_once()
|
||||
assert error.value.__cause__ == ERROR_UNKNOWN
|
||||
assert error.value.translation_domain == "tessie"
|
||||
assert error.value.translation_key == "cannot_connect"
|
||||
|
||||
# Test changing energy select with unknown error
|
||||
with (
|
||||
|
||||
Reference in New Issue
Block a user