Skip to content

Commit

Permalink
Improve BMW translations (#133236)
Browse files Browse the repository at this point in the history
  • Loading branch information
rikroe authored Dec 15, 2024
1 parent ebc8ca8 commit 8953ac1
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 64 deletions.
9 changes: 6 additions & 3 deletions homeassistant/components/bmw_connected_drive/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BMWConfigEntry
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
from .entity import BMWBaseEntity

if TYPE_CHECKING:
Expand Down Expand Up @@ -55,7 +55,6 @@ class BMWButtonEntityDescription(ButtonEntityDescription):
BMWButtonEntityDescription(
key="deactivate_air_conditioning",
translation_key="deactivate_air_conditioning",
name="Deactivate air conditioning",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
is_available=lambda vehicle: vehicle.is_remote_climate_stop_enabled,
),
Expand Down Expand Up @@ -111,6 +110,10 @@ async def async_press(self) -> None:
try:
await self.entity_description.remote_function(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex
raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex

self.coordinator.async_update_listeners()
29 changes: 23 additions & 6 deletions homeassistant/components/bmw_connected_drive/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util.ssl import get_default_context

from .const import CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN, SCAN_INTERVALS
from .const import (
CONF_GCID,
CONF_READ_ONLY,
CONF_REFRESH_TOKEN,
DOMAIN as BMW_DOMAIN,
SCAN_INTERVALS,
)

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -57,7 +63,7 @@ def __init__(self, hass: HomeAssistant, *, config_entry: ConfigEntry) -> None:
hass,
_LOGGER,
config_entry=config_entry,
name=f"{DOMAIN}-{config_entry.data[CONF_USERNAME]}",
name=f"{BMW_DOMAIN}-{config_entry.data[CONF_USERNAME]}",
update_interval=timedelta(
seconds=SCAN_INTERVALS[config_entry.data[CONF_REGION]]
),
Expand All @@ -75,18 +81,29 @@ async def _async_update_data(self) -> None:
except MyBMWCaptchaMissingError as err:
# If a captcha is required (user/password login flow), always trigger the reauth flow
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_domain=BMW_DOMAIN,
translation_key="missing_captcha",
) from err
except MyBMWAuthError as err:
# Allow one retry interval before raising AuthFailed to avoid flaky API issues
if self.last_update_success:
raise UpdateFailed(err) from err
raise UpdateFailed(
translation_domain=BMW_DOMAIN,
translation_key="update_failed",
translation_placeholders={"exception": str(err)},
) from err
# Clear refresh token and trigger reauth if previous update failed as well
self._update_config_entry_refresh_token(None)
raise ConfigEntryAuthFailed(err) from err
raise ConfigEntryAuthFailed(
translation_domain=BMW_DOMAIN,
translation_key="invalid_auth",
) from err
except (MyBMWAPIError, RequestError) as err:
raise UpdateFailed(err) from err
raise UpdateFailed(
translation_domain=BMW_DOMAIN,
translation_key="update_failed",
translation_placeholders={"exception": str(err)},
) from err

if self.account.refresh_token != old_refresh_token:
self._update_config_entry_refresh_token(self.account.refresh_token)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class BMWDeviceTracker(BMWBaseEntity, TrackerEntity):

_attr_force_update = False
_attr_translation_key = "car"
_attr_icon = "mdi:car"
_attr_name = None

def __init__(
self,
Expand All @@ -58,9 +58,7 @@ def __init__(
) -> None:
"""Initialize the Tracker."""
super().__init__(coordinator, vehicle)

self._attr_unique_id = vehicle.vin
self._attr_name = None

@property
def extra_state_attributes(self) -> dict[str, Any]:
Expand Down
14 changes: 11 additions & 3 deletions homeassistant/components/bmw_connected_drive/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BMWConfigEntry
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
from .coordinator import BMWDataUpdateCoordinator
from .entity import BMWBaseEntity

Expand Down Expand Up @@ -70,7 +70,11 @@ async def async_lock(self, **kwargs: Any) -> None:
# Set the state to unknown if the command fails
self._attr_is_locked = None
self.async_write_ha_state()
raise HomeAssistantError(ex) from ex
raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex
finally:
# Always update the listeners to get the latest state
self.coordinator.async_update_listeners()
Expand All @@ -90,7 +94,11 @@ async def async_unlock(self, **kwargs: Any) -> None:
# Set the state to unknown if the command fails
self._attr_is_locked = None
self.async_write_ha_state()
raise HomeAssistantError(ex) from ex
raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex
finally:
# Always update the listeners to get the latest state
self.coordinator.async_update_listeners()
Expand Down
10 changes: 7 additions & 3 deletions homeassistant/components/bmw_connected_drive/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from . import DOMAIN, BMWConfigEntry
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry

PARALLEL_UPDATES = 1

Expand Down Expand Up @@ -92,7 +92,7 @@ async def async_send_message(self, message: str = "", **kwargs: Any) -> None:

except (vol.Invalid, TypeError, ValueError) as ex:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_domain=BMW_DOMAIN,
translation_key="invalid_poi",
translation_placeholders={
"poi_exception": str(ex),
Expand All @@ -106,4 +106,8 @@ async def async_send_message(self, message: str = "", **kwargs: Any) -> None:
try:
await vehicle.remote_services.trigger_send_poi(poi)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex
raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex
8 changes: 6 additions & 2 deletions homeassistant/components/bmw_connected_drive/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BMWConfigEntry
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
from .coordinator import BMWDataUpdateCoordinator
from .entity import BMWBaseEntity

Expand Down Expand Up @@ -109,6 +109,10 @@ async def async_set_native_value(self, value: float) -> None:
try:
await self.entity_description.remote_service(self.vehicle, value)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex
raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex

self.coordinator.async_update_listeners()
8 changes: 6 additions & 2 deletions homeassistant/components/bmw_connected_drive/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BMWConfigEntry
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
from .coordinator import BMWDataUpdateCoordinator
from .entity import BMWBaseEntity

Expand Down Expand Up @@ -123,6 +123,10 @@ async def async_select_option(self, option: str) -> None:
try:
await self.entity_description.remote_service(self.vehicle, option)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex
raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex

self.coordinator.async_update_listeners()
27 changes: 25 additions & 2 deletions homeassistant/components/bmw_connected_drive/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
"config": {
"step": {
"user": {
"description": "Enter your MyBMW/MINI Connected credentials.",
"description": "Connect to your MyBMW/MINI Connected account to retrieve vehicle data.",
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"region": "ConnectedDrive Region"
},
"data_description": {
"username": "The email address of your MyBMW/MINI Connected account.",
"password": "The password of your MyBMW/MINI Connected account.",
"region": "The region of your MyBMW/MINI Connected account."
}
},
"captcha": {
Expand All @@ -23,6 +28,9 @@
"description": "Update your MyBMW/MINI Connected password for account `{username}` in region `{region}`.",
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::bmw_connected_drive::config::step::user::data_description::password%]"
}
}
},
Expand All @@ -41,7 +49,10 @@
"step": {
"account_options": {
"data": {
"read_only": "Read-only (only sensors and notify, no execution of services, no lock)"
"read_only": "Read-only mode"
},
"data_description": {
"read_only": "Only retrieve values and send POI data, but don't offer any services that can change the vehicle state."
}
}
}
Expand Down Expand Up @@ -83,6 +94,9 @@
"activate_air_conditioning": {
"name": "Activate air conditioning"
},
"deactivate_air_conditioning": {
"name": "Deactivate air conditioning"
},
"find_vehicle": {
"name": "Find vehicle"
}
Expand Down Expand Up @@ -220,6 +234,15 @@
},
"missing_captcha": {
"message": "Login requires captcha validation"
},
"invalid_auth": {
"message": "[%key:common::config_flow::error::invalid_auth%]"
},
"remote_service_error": {
"message": "Error executing remote service on vehicle. {exception}"
},
"update_failed": {
"message": "Error updating vehicle data. {exception}"
}
}
}
16 changes: 11 additions & 5 deletions homeassistant/components/bmw_connected_drive/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BMWConfigEntry
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
from .coordinator import BMWDataUpdateCoordinator
from .entity import BMWBaseEntity

Expand Down Expand Up @@ -111,15 +111,21 @@ async def async_turn_on(self, **kwargs: Any) -> None:
try:
await self.entity_description.remote_service_on(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex

raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex
self.coordinator.async_update_listeners()

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
try:
await self.entity_description.remote_service_off(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex

raise HomeAssistantError(
translation_domain=BMW_DOMAIN,
translation_key="remote_service_error",
translation_placeholders={"exception": str(ex)},
) from ex
self.coordinator.async_update_listeners()
5 changes: 5 additions & 0 deletions tests/components/bmw_connected_drive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
"unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_USERNAME]}",
}

REMOTE_SERVICE_EXC_REASON = "HTTPStatusError: 502 Bad Gateway"
REMOTE_SERVICE_EXC_TRANSLATION = (
"Error executing remote service on vehicle. HTTPStatusError: 502 Bad Gateway"
)


async def setup_mocked_integration(hass: HomeAssistant) -> MockConfigEntry:
"""Mock a fully setup config entry and all components based on fixtures."""
Expand Down
12 changes: 9 additions & 3 deletions tests/components/bmw_connected_drive/test_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er

from . import check_remote_service_call, setup_mocked_integration
from . import (
REMOTE_SERVICE_EXC_TRANSLATION,
check_remote_service_call,
setup_mocked_integration,
)

from tests.common import snapshot_platform

Expand Down Expand Up @@ -81,11 +85,13 @@ async def test_service_call_fail(
monkeypatch.setattr(
RemoteServices,
"trigger_remote_service",
AsyncMock(side_effect=MyBMWRemoteServiceError),
AsyncMock(
side_effect=MyBMWRemoteServiceError("HTTPStatusError: 502 Bad Gateway")
),
)

# Test
with pytest.raises(HomeAssistantError):
with pytest.raises(HomeAssistantError, match=REMOTE_SERVICE_EXC_TRANSLATION):
await hass.services.async_call(
"button",
"press",
Expand Down
11 changes: 8 additions & 3 deletions tests/components/bmw_connected_drive/test_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt as dt_util

from . import check_remote_service_call, setup_mocked_integration
from . import (
REMOTE_SERVICE_EXC_REASON,
REMOTE_SERVICE_EXC_TRANSLATION,
check_remote_service_call,
setup_mocked_integration,
)

from tests.common import snapshot_platform
from tests.components.recorder.common import async_wait_recording_done
Expand Down Expand Up @@ -118,11 +123,11 @@ async def test_service_call_fail(
monkeypatch.setattr(
RemoteServices,
"trigger_remote_service",
AsyncMock(side_effect=MyBMWRemoteServiceError),
AsyncMock(side_effect=MyBMWRemoteServiceError(REMOTE_SERVICE_EXC_REASON)),
)

# Test
with pytest.raises(HomeAssistantError):
with pytest.raises(HomeAssistantError, match=REMOTE_SERVICE_EXC_TRANSLATION):
await hass.services.async_call(
"lock",
service,
Expand Down
Loading

0 comments on commit 8953ac1

Please sign in to comment.