mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Improve BMW translations (#133236)
This commit is contained in:
parent
ebc8ca8419
commit
8953ac1357
@ -16,7 +16,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import BMWConfigEntry
|
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
|
||||||
from .entity import BMWBaseEntity
|
from .entity import BMWBaseEntity
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -55,7 +55,6 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
|
|||||||
BMWButtonEntityDescription(
|
BMWButtonEntityDescription(
|
||||||
key="deactivate_air_conditioning",
|
key="deactivate_air_conditioning",
|
||||||
translation_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(),
|
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
|
||||||
is_available=lambda vehicle: vehicle.is_remote_climate_stop_enabled,
|
is_available=lambda vehicle: vehicle.is_remote_climate_stop_enabled,
|
||||||
),
|
),
|
||||||
@ -111,6 +110,10 @@ class BMWButton(BMWBaseEntity, ButtonEntity):
|
|||||||
try:
|
try:
|
||||||
await self.entity_description.remote_function(self.vehicle)
|
await self.entity_description.remote_function(self.vehicle)
|
||||||
except MyBMWAPIError as ex:
|
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()
|
self.coordinator.async_update_listeners()
|
||||||
|
@ -22,7 +22,13 @@ from homeassistant.exceptions import ConfigEntryAuthFailed
|
|||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
from homeassistant.util.ssl import get_default_context
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -57,7 +63,7 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
config_entry=config_entry,
|
config_entry=config_entry,
|
||||||
name=f"{DOMAIN}-{config_entry.data[CONF_USERNAME]}",
|
name=f"{BMW_DOMAIN}-{config_entry.data[CONF_USERNAME]}",
|
||||||
update_interval=timedelta(
|
update_interval=timedelta(
|
||||||
seconds=SCAN_INTERVALS[config_entry.data[CONF_REGION]]
|
seconds=SCAN_INTERVALS[config_entry.data[CONF_REGION]]
|
||||||
),
|
),
|
||||||
@ -75,18 +81,29 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||||||
except MyBMWCaptchaMissingError as err:
|
except MyBMWCaptchaMissingError as err:
|
||||||
# If a captcha is required (user/password login flow), always trigger the reauth flow
|
# If a captcha is required (user/password login flow), always trigger the reauth flow
|
||||||
raise ConfigEntryAuthFailed(
|
raise ConfigEntryAuthFailed(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=BMW_DOMAIN,
|
||||||
translation_key="missing_captcha",
|
translation_key="missing_captcha",
|
||||||
) from err
|
) from err
|
||||||
except MyBMWAuthError as err:
|
except MyBMWAuthError as err:
|
||||||
# Allow one retry interval before raising AuthFailed to avoid flaky API issues
|
# Allow one retry interval before raising AuthFailed to avoid flaky API issues
|
||||||
if self.last_update_success:
|
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
|
# Clear refresh token and trigger reauth if previous update failed as well
|
||||||
self._update_config_entry_refresh_token(None)
|
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:
|
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:
|
if self.account.refresh_token != old_refresh_token:
|
||||||
self._update_config_entry_refresh_token(self.account.refresh_token)
|
self._update_config_entry_refresh_token(self.account.refresh_token)
|
||||||
|
@ -49,7 +49,7 @@ class BMWDeviceTracker(BMWBaseEntity, TrackerEntity):
|
|||||||
|
|
||||||
_attr_force_update = False
|
_attr_force_update = False
|
||||||
_attr_translation_key = "car"
|
_attr_translation_key = "car"
|
||||||
_attr_icon = "mdi:car"
|
_attr_name = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -58,9 +58,7 @@ class BMWDeviceTracker(BMWBaseEntity, TrackerEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Tracker."""
|
"""Initialize the Tracker."""
|
||||||
super().__init__(coordinator, vehicle)
|
super().__init__(coordinator, vehicle)
|
||||||
|
|
||||||
self._attr_unique_id = vehicle.vin
|
self._attr_unique_id = vehicle.vin
|
||||||
self._attr_name = None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, Any]:
|
def extra_state_attributes(self) -> dict[str, Any]:
|
||||||
|
@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import BMWConfigEntry
|
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
|
||||||
from .coordinator import BMWDataUpdateCoordinator
|
from .coordinator import BMWDataUpdateCoordinator
|
||||||
from .entity import BMWBaseEntity
|
from .entity import BMWBaseEntity
|
||||||
|
|
||||||
@ -70,7 +70,11 @@ class BMWLock(BMWBaseEntity, LockEntity):
|
|||||||
# Set the state to unknown if the command fails
|
# Set the state to unknown if the command fails
|
||||||
self._attr_is_locked = None
|
self._attr_is_locked = None
|
||||||
self.async_write_ha_state()
|
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:
|
finally:
|
||||||
# Always update the listeners to get the latest state
|
# Always update the listeners to get the latest state
|
||||||
self.coordinator.async_update_listeners()
|
self.coordinator.async_update_listeners()
|
||||||
@ -90,7 +94,11 @@ class BMWLock(BMWBaseEntity, LockEntity):
|
|||||||
# Set the state to unknown if the command fails
|
# Set the state to unknown if the command fails
|
||||||
self._attr_is_locked = None
|
self._attr_is_locked = None
|
||||||
self.async_write_ha_state()
|
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:
|
finally:
|
||||||
# Always update the listeners to get the latest state
|
# Always update the listeners to get the latest state
|
||||||
self.coordinator.async_update_listeners()
|
self.coordinator.async_update_listeners()
|
||||||
|
@ -20,7 +20,7 @@ from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from . import DOMAIN, BMWConfigEntry
|
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
|
||||||
|
|
||||||
PARALLEL_UPDATES = 1
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ class BMWNotificationService(BaseNotificationService):
|
|||||||
|
|
||||||
except (vol.Invalid, TypeError, ValueError) as ex:
|
except (vol.Invalid, TypeError, ValueError) as ex:
|
||||||
raise ServiceValidationError(
|
raise ServiceValidationError(
|
||||||
translation_domain=DOMAIN,
|
translation_domain=BMW_DOMAIN,
|
||||||
translation_key="invalid_poi",
|
translation_key="invalid_poi",
|
||||||
translation_placeholders={
|
translation_placeholders={
|
||||||
"poi_exception": str(ex),
|
"poi_exception": str(ex),
|
||||||
@ -106,4 +106,8 @@ class BMWNotificationService(BaseNotificationService):
|
|||||||
try:
|
try:
|
||||||
await vehicle.remote_services.trigger_send_poi(poi)
|
await vehicle.remote_services.trigger_send_poi(poi)
|
||||||
except MyBMWAPIError as ex:
|
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
|
||||||
|
@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import BMWConfigEntry
|
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
|
||||||
from .coordinator import BMWDataUpdateCoordinator
|
from .coordinator import BMWDataUpdateCoordinator
|
||||||
from .entity import BMWBaseEntity
|
from .entity import BMWBaseEntity
|
||||||
|
|
||||||
@ -109,6 +109,10 @@ class BMWNumber(BMWBaseEntity, NumberEntity):
|
|||||||
try:
|
try:
|
||||||
await self.entity_description.remote_service(self.vehicle, value)
|
await self.entity_description.remote_service(self.vehicle, value)
|
||||||
except MyBMWAPIError as ex:
|
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()
|
self.coordinator.async_update_listeners()
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import BMWConfigEntry
|
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
|
||||||
from .coordinator import BMWDataUpdateCoordinator
|
from .coordinator import BMWDataUpdateCoordinator
|
||||||
from .entity import BMWBaseEntity
|
from .entity import BMWBaseEntity
|
||||||
|
|
||||||
@ -123,6 +123,10 @@ class BMWSelect(BMWBaseEntity, SelectEntity):
|
|||||||
try:
|
try:
|
||||||
await self.entity_description.remote_service(self.vehicle, option)
|
await self.entity_description.remote_service(self.vehicle, option)
|
||||||
except MyBMWAPIError as ex:
|
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()
|
self.coordinator.async_update_listeners()
|
||||||
|
@ -2,11 +2,16 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"description": "Enter your MyBMW/MINI Connected credentials.",
|
"description": "Connect to your MyBMW/MINI Connected account to retrieve vehicle data.",
|
||||||
"data": {
|
"data": {
|
||||||
"username": "[%key:common::config_flow::data::username%]",
|
"username": "[%key:common::config_flow::data::username%]",
|
||||||
"password": "[%key:common::config_flow::data::password%]",
|
"password": "[%key:common::config_flow::data::password%]",
|
||||||
"region": "ConnectedDrive Region"
|
"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": {
|
"captcha": {
|
||||||
@ -23,6 +28,9 @@
|
|||||||
"description": "Update your MyBMW/MINI Connected password for account `{username}` in region `{region}`.",
|
"description": "Update your MyBMW/MINI Connected password for account `{username}` in region `{region}`.",
|
||||||
"data": {
|
"data": {
|
||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
|
},
|
||||||
|
"data_description": {
|
||||||
|
"password": "[%key:component::bmw_connected_drive::config::step::user::data_description::password%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -41,7 +49,10 @@
|
|||||||
"step": {
|
"step": {
|
||||||
"account_options": {
|
"account_options": {
|
||||||
"data": {
|
"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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,6 +94,9 @@
|
|||||||
"activate_air_conditioning": {
|
"activate_air_conditioning": {
|
||||||
"name": "Activate air conditioning"
|
"name": "Activate air conditioning"
|
||||||
},
|
},
|
||||||
|
"deactivate_air_conditioning": {
|
||||||
|
"name": "Deactivate air conditioning"
|
||||||
|
},
|
||||||
"find_vehicle": {
|
"find_vehicle": {
|
||||||
"name": "Find vehicle"
|
"name": "Find vehicle"
|
||||||
}
|
}
|
||||||
@ -220,6 +234,15 @@
|
|||||||
},
|
},
|
||||||
"missing_captcha": {
|
"missing_captcha": {
|
||||||
"message": "Login requires captcha validation"
|
"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}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import BMWConfigEntry
|
from . import DOMAIN as BMW_DOMAIN, BMWConfigEntry
|
||||||
from .coordinator import BMWDataUpdateCoordinator
|
from .coordinator import BMWDataUpdateCoordinator
|
||||||
from .entity import BMWBaseEntity
|
from .entity import BMWBaseEntity
|
||||||
|
|
||||||
@ -111,8 +111,11 @@ class BMWSwitch(BMWBaseEntity, SwitchEntity):
|
|||||||
try:
|
try:
|
||||||
await self.entity_description.remote_service_on(self.vehicle)
|
await self.entity_description.remote_service_on(self.vehicle)
|
||||||
except MyBMWAPIError as ex:
|
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()
|
self.coordinator.async_update_listeners()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
@ -120,6 +123,9 @@ class BMWSwitch(BMWBaseEntity, SwitchEntity):
|
|||||||
try:
|
try:
|
||||||
await self.entity_description.remote_service_off(self.vehicle)
|
await self.entity_description.remote_service_off(self.vehicle)
|
||||||
except MyBMWAPIError as ex:
|
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()
|
self.coordinator.async_update_listeners()
|
||||||
|
@ -48,6 +48,11 @@ FIXTURE_CONFIG_ENTRY = {
|
|||||||
"unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_USERNAME]}",
|
"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:
|
async def setup_mocked_integration(hass: HomeAssistant) -> MockConfigEntry:
|
||||||
"""Mock a fully setup config entry and all components based on fixtures."""
|
"""Mock a fully setup config entry and all components based on fixtures."""
|
||||||
|
@ -13,7 +13,11 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
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
|
from tests.common import snapshot_platform
|
||||||
|
|
||||||
@ -81,11 +85,13 @@ async def test_service_call_fail(
|
|||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
RemoteServices,
|
RemoteServices,
|
||||||
"trigger_remote_service",
|
"trigger_remote_service",
|
||||||
AsyncMock(side_effect=MyBMWRemoteServiceError),
|
AsyncMock(
|
||||||
|
side_effect=MyBMWRemoteServiceError("HTTPStatusError: 502 Bad Gateway")
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError, match=REMOTE_SERVICE_EXC_TRANSLATION):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"button",
|
"button",
|
||||||
"press",
|
"press",
|
||||||
|
@ -16,7 +16,12 @@ from homeassistant.exceptions import HomeAssistantError
|
|||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.util import dt as dt_util
|
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.common import snapshot_platform
|
||||||
from tests.components.recorder.common import async_wait_recording_done
|
from tests.components.recorder.common import async_wait_recording_done
|
||||||
@ -118,11 +123,11 @@ async def test_service_call_fail(
|
|||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
RemoteServices,
|
RemoteServices,
|
||||||
"trigger_remote_service",
|
"trigger_remote_service",
|
||||||
AsyncMock(side_effect=MyBMWRemoteServiceError),
|
AsyncMock(side_effect=MyBMWRemoteServiceError(REMOTE_SERVICE_EXC_REASON)),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError, match=REMOTE_SERVICE_EXC_TRANSLATION):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"lock",
|
"lock",
|
||||||
service,
|
service,
|
||||||
|
@ -11,7 +11,11 @@ import respx
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||||
|
|
||||||
from . import check_remote_service_call, setup_mocked_integration
|
from . import (
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
check_remote_service_call,
|
||||||
|
setup_mocked_integration,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_legacy_notify_service_simple(
|
async def test_legacy_notify_service_simple(
|
||||||
@ -68,21 +72,21 @@ async def test_legacy_notify_service_simple(
|
|||||||
{
|
{
|
||||||
"latitude": POI_DATA.get("lat"),
|
"latitude": POI_DATA.get("lat"),
|
||||||
},
|
},
|
||||||
"Invalid data for point of interest: required key not provided @ data['longitude']",
|
r"Invalid data for point of interest: required key not provided @ data\['longitude'\]",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"latitude": POI_DATA.get("lat"),
|
"latitude": POI_DATA.get("lat"),
|
||||||
"longitude": "text",
|
"longitude": "text",
|
||||||
},
|
},
|
||||||
"Invalid data for point of interest: invalid longitude for dictionary value @ data['longitude']",
|
r"Invalid data for point of interest: invalid longitude for dictionary value @ data\['longitude'\]",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"latitude": POI_DATA.get("lat"),
|
"latitude": POI_DATA.get("lat"),
|
||||||
"longitude": 9999,
|
"longitude": 9999,
|
||||||
},
|
},
|
||||||
"Invalid data for point of interest: invalid longitude for dictionary value @ data['longitude']",
|
r"Invalid data for point of interest: invalid longitude for dictionary value @ data\['longitude'\]",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -96,7 +100,7 @@ async def test_service_call_invalid_input(
|
|||||||
# Setup component
|
# Setup component
|
||||||
assert await setup_mocked_integration(hass)
|
assert await setup_mocked_integration(hass)
|
||||||
|
|
||||||
with pytest.raises(ServiceValidationError) as exc:
|
with pytest.raises(ServiceValidationError, match=exc_translation):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"notify",
|
"notify",
|
||||||
"bmw_connected_drive_ix_xdrive50",
|
"bmw_connected_drive_ix_xdrive50",
|
||||||
@ -106,7 +110,6 @@ async def test_service_call_invalid_input(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert str(exc.value) == exc_translation
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("bmw_fixture")
|
@pytest.mark.usefixtures("bmw_fixture")
|
||||||
@ -132,11 +135,11 @@ async def test_service_call_fail(
|
|||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
RemoteServices,
|
RemoteServices,
|
||||||
"trigger_remote_service",
|
"trigger_remote_service",
|
||||||
AsyncMock(side_effect=raised),
|
AsyncMock(side_effect=raised("HTTPStatusError: 502 Bad Gateway")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(expected):
|
with pytest.raises(expected, match=REMOTE_SERVICE_EXC_TRANSLATION):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"notify",
|
"notify",
|
||||||
"bmw_connected_drive_ix_xdrive50",
|
"bmw_connected_drive_ix_xdrive50",
|
||||||
|
@ -13,7 +13,12 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
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.common import snapshot_platform
|
||||||
|
|
||||||
@ -89,7 +94,10 @@ async def test_service_call_invalid_input(
|
|||||||
old_value = hass.states.get(entity_id).state
|
old_value = hass.states.get(entity_id).state
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(
|
||||||
|
ValueError,
|
||||||
|
match="Target SoC must be an integer between 20 and 100 that is a multiple of 5.",
|
||||||
|
):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"number",
|
"number",
|
||||||
"set_value",
|
"set_value",
|
||||||
@ -102,17 +110,32 @@ async def test_service_call_invalid_input(
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("bmw_fixture")
|
@pytest.mark.usefixtures("bmw_fixture")
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("raised", "expected"),
|
("raised", "expected", "exc_translation"),
|
||||||
[
|
[
|
||||||
(MyBMWRemoteServiceError, HomeAssistantError),
|
(
|
||||||
(MyBMWAPIError, HomeAssistantError),
|
MyBMWRemoteServiceError(REMOTE_SERVICE_EXC_REASON),
|
||||||
(ValueError, ValueError),
|
HomeAssistantError,
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
MyBMWAPIError(REMOTE_SERVICE_EXC_REASON),
|
||||||
|
HomeAssistantError,
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueError(
|
||||||
|
"Target SoC must be an integer between 20 and 100 that is a multiple of 5."
|
||||||
|
),
|
||||||
|
ValueError,
|
||||||
|
"Target SoC must be an integer between 20 and 100 that is a multiple of 5.",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_service_call_fail(
|
async def test_service_call_fail(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
raised: Exception,
|
raised: Exception,
|
||||||
expected: Exception,
|
expected: Exception,
|
||||||
|
exc_translation: str,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test exception handling."""
|
"""Test exception handling."""
|
||||||
@ -130,7 +153,7 @@ async def test_service_call_fail(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(expected):
|
with pytest.raises(expected, match=exc_translation):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"number",
|
"number",
|
||||||
"set_value",
|
"set_value",
|
||||||
|
@ -16,7 +16,12 @@ from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.translation import async_get_translations
|
from homeassistant.helpers.translation import async_get_translations
|
||||||
|
|
||||||
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.common import snapshot_platform
|
||||||
|
|
||||||
@ -105,7 +110,10 @@ async def test_service_call_invalid_input(
|
|||||||
old_value = hass.states.get(entity_id).state
|
old_value = hass.states.get(entity_id).state
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(ServiceValidationError):
|
with pytest.raises(
|
||||||
|
ServiceValidationError,
|
||||||
|
match=f"Option {value} is not valid for entity {entity_id}",
|
||||||
|
):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"select",
|
"select",
|
||||||
"select_option",
|
"select_option",
|
||||||
@ -118,17 +126,32 @@ async def test_service_call_invalid_input(
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("bmw_fixture")
|
@pytest.mark.usefixtures("bmw_fixture")
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("raised", "expected"),
|
("raised", "expected", "exc_translation"),
|
||||||
[
|
[
|
||||||
(MyBMWRemoteServiceError, HomeAssistantError),
|
(
|
||||||
(MyBMWAPIError, HomeAssistantError),
|
MyBMWRemoteServiceError(REMOTE_SERVICE_EXC_REASON),
|
||||||
(ServiceValidationError, ServiceValidationError),
|
HomeAssistantError,
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
MyBMWAPIError(REMOTE_SERVICE_EXC_REASON),
|
||||||
|
HomeAssistantError,
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ServiceValidationError(
|
||||||
|
"Option 17 is not valid for entity select.i4_edrive40_ac_charging_limit"
|
||||||
|
),
|
||||||
|
ServiceValidationError,
|
||||||
|
"Option 17 is not valid for entity select.i4_edrive40_ac_charging_limit",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_service_call_fail(
|
async def test_service_call_fail(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
raised: Exception,
|
raised: Exception,
|
||||||
expected: Exception,
|
expected: Exception,
|
||||||
|
exc_translation: str,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test exception handling."""
|
"""Test exception handling."""
|
||||||
@ -146,7 +169,7 @@ async def test_service_call_fail(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(expected):
|
with pytest.raises(expected, match=exc_translation):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"select",
|
"select",
|
||||||
"select_option",
|
"select_option",
|
||||||
|
@ -13,7 +13,12 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
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.common import snapshot_platform
|
||||||
|
|
||||||
@ -75,17 +80,25 @@ async def test_service_call_success(
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("bmw_fixture")
|
@pytest.mark.usefixtures("bmw_fixture")
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("raised", "expected"),
|
("raised", "expected", "exc_translation"),
|
||||||
[
|
[
|
||||||
(MyBMWRemoteServiceError, HomeAssistantError),
|
(
|
||||||
(MyBMWAPIError, HomeAssistantError),
|
MyBMWRemoteServiceError(REMOTE_SERVICE_EXC_REASON),
|
||||||
(ValueError, ValueError),
|
HomeAssistantError,
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
MyBMWAPIError(REMOTE_SERVICE_EXC_REASON),
|
||||||
|
HomeAssistantError,
|
||||||
|
REMOTE_SERVICE_EXC_TRANSLATION,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_service_call_fail(
|
async def test_service_call_fail(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
raised: Exception,
|
raised: Exception,
|
||||||
expected: Exception,
|
expected: Exception,
|
||||||
|
exc_translation: str,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test exception handling."""
|
"""Test exception handling."""
|
||||||
@ -107,7 +120,7 @@ async def test_service_call_fail(
|
|||||||
assert hass.states.get(entity_id).state == old_value
|
assert hass.states.get(entity_id).state == old_value
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(expected):
|
with pytest.raises(expected, match=exc_translation):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"switch",
|
"switch",
|
||||||
"turn_on",
|
"turn_on",
|
||||||
@ -122,7 +135,7 @@ async def test_service_call_fail(
|
|||||||
assert hass.states.get(entity_id).state == old_value
|
assert hass.states.get(entity_id).state == old_value
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
with pytest.raises(expected):
|
with pytest.raises(expected, match=exc_translation):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"switch",
|
"switch",
|
||||||
"turn_off",
|
"turn_off",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user