Add exception translation for entity action not supported (#131956)
parent
c55a4e9584
commit
3aae9b629f
|
@ -224,6 +224,9 @@
|
|||
"service_not_found": {
|
||||
"message": "Action {domain}.{service} not found."
|
||||
},
|
||||
"service_not_supported": {
|
||||
"message": "Entity {entity_id} does not support action {domain}.{service}."
|
||||
},
|
||||
"service_does_not_support_response": {
|
||||
"message": "An action which does not return responses can't be called with {return_response}."
|
||||
},
|
||||
|
|
|
@ -270,6 +270,25 @@ class ServiceNotFound(ServiceValidationError):
|
|||
self.generate_message = True
|
||||
|
||||
|
||||
class ServiceNotSupported(ServiceValidationError):
|
||||
"""Raised when an entity action is not supported."""
|
||||
|
||||
def __init__(self, domain: str, service: str, entity_id: str) -> None:
|
||||
"""Initialize ServiceNotSupported exception."""
|
||||
super().__init__(
|
||||
translation_domain="homeassistant",
|
||||
translation_key="service_not_supported",
|
||||
translation_placeholders={
|
||||
"domain": domain,
|
||||
"service": service,
|
||||
"entity_id": entity_id,
|
||||
},
|
||||
)
|
||||
self.domain = domain
|
||||
self.service = service
|
||||
self.generate_message = True
|
||||
|
||||
|
||||
class MaxLengthExceeded(HomeAssistantError):
|
||||
"""Raised when a property value has exceeded the max character length."""
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ from homeassistant.core import (
|
|||
)
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
ServiceNotSupported,
|
||||
TemplateError,
|
||||
Unauthorized,
|
||||
UnknownUser,
|
||||
|
@ -986,9 +987,7 @@ async def entity_service_call(
|
|||
):
|
||||
# If entity explicitly referenced, raise an error
|
||||
if referenced is not None and entity.entity_id in referenced.referenced:
|
||||
raise HomeAssistantError(
|
||||
f"Entity {entity.entity_id} does not support this service."
|
||||
)
|
||||
raise ServiceNotSupported(call.domain, call.service, entity.entity_id)
|
||||
|
||||
continue
|
||||
|
||||
|
|
|
@ -20,8 +20,9 @@ from homeassistant.const import (
|
|||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import ServiceNotSupported
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .mocks import (
|
||||
|
@ -453,8 +454,9 @@ async def test_open_throws_hass_service_not_supported_error(
|
|||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test open throws correct error on entity does not support this service error."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
mocked_lock_detail = await _mock_operative_august_lock_detail(hass)
|
||||
await _create_august_with_devices(hass, [mocked_lock_detail])
|
||||
data = {ATTR_ENTITY_ID: "lock.a6697750d607098bae8d6baa11ef8063_name"}
|
||||
with pytest.raises(HomeAssistantError, match="does not support this service"):
|
||||
with pytest.raises(ServiceNotSupported, match="does not support action"):
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_OPEN, data, blocking=True)
|
||||
|
|
|
@ -14,7 +14,8 @@ import voluptuous as vol
|
|||
|
||||
from homeassistant.components.calendar import DOMAIN, SERVICE_GET_EVENTS
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceNotSupported
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .conftest import MockCalendarEntity, MockConfigEntry
|
||||
|
@ -214,8 +215,12 @@ async def test_unsupported_websocket(
|
|||
|
||||
async def test_unsupported_create_event_service(hass: HomeAssistant) -> None:
|
||||
"""Test unsupported service call."""
|
||||
|
||||
with pytest.raises(HomeAssistantError, match="does not support this service"):
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
with pytest.raises(
|
||||
ServiceNotSupported,
|
||||
match="Entity calendar.calendar_1 does not "
|
||||
"support action calendar.create_event",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"create_event",
|
||||
|
|
|
@ -20,7 +20,8 @@ from homeassistant.components.google.const import CONF_CALENDAR_ACCESS
|
|||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceNotSupported
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.dt import UTC, utcnow
|
||||
|
||||
from .conftest import (
|
||||
|
@ -593,7 +594,7 @@ async def test_unsupported_create_event(
|
|||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test create event service call is unsupported for virtual calendars."""
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
mock_calendars_list({"items": [test_api_calendar]})
|
||||
mock_events_list({})
|
||||
assert await component_setup()
|
||||
|
@ -601,8 +602,12 @@ async def test_unsupported_create_event(
|
|||
start_datetime = datetime.datetime.now(tz=zoneinfo.ZoneInfo("America/Regina"))
|
||||
delta = datetime.timedelta(days=3, hours=3)
|
||||
end_datetime = start_datetime + delta
|
||||
entity_id = "calendar.backyard_light"
|
||||
|
||||
with pytest.raises(HomeAssistantError, match="does not support this service"):
|
||||
with pytest.raises(
|
||||
ServiceNotSupported,
|
||||
match=f"Entity {entity_id} does not support action google.create_event",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"create_event",
|
||||
|
@ -613,7 +618,7 @@ async def test_unsupported_create_event(
|
|||
"summary": TEST_EVENT_SUMMARY,
|
||||
"description": TEST_EVENT_DESCRIPTION,
|
||||
},
|
||||
target={"entity_id": "calendar.backyard_light"},
|
||||
target={"entity_id": entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
|
|
@ -8,8 +8,10 @@ import pytest
|
|||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, HomeAssistantError
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceNotSupported
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import (
|
||||
set_node_attribute,
|
||||
|
@ -35,6 +37,8 @@ async def test_vacuum_actions(
|
|||
matter_node: MatterNode,
|
||||
) -> None:
|
||||
"""Test vacuum entity actions."""
|
||||
# Fetch translations
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
entity_id = "vacuum.mock_vacuum"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -96,8 +100,8 @@ async def test_vacuum_actions(
|
|||
# test stop action
|
||||
# stop command is not supported by the vacuum fixture
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match="Entity vacuum.mock_vacuum does not support this service.",
|
||||
ServiceNotSupported,
|
||||
match="Entity vacuum.mock_vacuum does not support action vacuum.stop",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"vacuum",
|
||||
|
|
|
@ -76,7 +76,8 @@ from homeassistant.const import (
|
|||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import ServiceNotSupported
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from . import async_wait_config_entry_reload, setup_samsungtv_entry
|
||||
|
@ -1021,8 +1022,9 @@ async def test_turn_on_wol(hass: HomeAssistant) -> None:
|
|||
|
||||
async def test_turn_on_without_turnon(hass: HomeAssistant, remote: Mock) -> None:
|
||||
"""Test turn on."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
await setup_samsungtv_entry(hass, MOCK_CONFIG)
|
||||
with pytest.raises(HomeAssistantError, match="does not support this service"):
|
||||
with pytest.raises(ServiceNotSupported, match="does not support action"):
|
||||
await hass.services.async_call(
|
||||
MP_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True
|
||||
)
|
||||
|
|
|
@ -24,8 +24,9 @@ from homeassistant.components.lock import (
|
|||
from homeassistant.components.webhook import async_generate_url
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceNotSupported
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import WEBHOOK_ID
|
||||
|
||||
|
@ -113,6 +114,8 @@ async def test_lock_without_pullspring(
|
|||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the tedee lock without pullspring."""
|
||||
# Fetch translations
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
mock_tedee.lock.return_value = None
|
||||
mock_tedee.unlock.return_value = None
|
||||
mock_tedee.open.return_value = None
|
||||
|
@ -131,8 +134,8 @@ async def test_lock_without_pullspring(
|
|||
assert device == snapshot
|
||||
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match="Entity lock.lock_2c3d does not support this service.",
|
||||
ServiceNotSupported,
|
||||
match=f"Entity lock.lock_2c3d does not support action {LOCK_DOMAIN}.{SERVICE_OPEN}",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
LOCK_DOMAIN,
|
||||
|
|
|
@ -24,8 +24,13 @@ from homeassistant.components.climate import (
|
|||
from homeassistant.components.tesla_fleet.coordinator import VEHICLE_INTERVAL
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
ServiceNotSupported,
|
||||
ServiceValidationError,
|
||||
)
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import assert_entities, setup_platform
|
||||
from .const import (
|
||||
|
@ -391,6 +396,7 @@ async def test_climate_noscope(
|
|||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Tests with no command scopes."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
await setup_platform(hass, readonly_config_entry, [Platform.CLIMATE])
|
||||
entity_id = "climate.test_climate"
|
||||
|
||||
|
@ -405,8 +411,9 @@ async def test_climate_noscope(
|
|||
)
|
||||
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match="Entity climate.test_climate does not support this service.",
|
||||
ServiceNotSupported,
|
||||
match="Entity climate.test_climate does not "
|
||||
"support action climate.set_temperature",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
|
|
|
@ -27,7 +27,11 @@ from homeassistant.components.todo import (
|
|||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
ServiceNotSupported,
|
||||
ServiceValidationError,
|
||||
)
|
||||
from homeassistant.helpers import intent
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
@ -941,14 +945,15 @@ async def test_unsupported_service(
|
|||
payload: dict[str, Any] | None,
|
||||
) -> None:
|
||||
"""Test a To-do list that does not support features."""
|
||||
|
||||
# Fetch translations
|
||||
await async_setup_component(hass, "homeassistant", "")
|
||||
entity1 = TodoListEntity()
|
||||
entity1.entity_id = "todo.entity1"
|
||||
await create_mock_platform(hass, [entity1])
|
||||
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match="does not support this service",
|
||||
ServiceNotSupported,
|
||||
match=f"Entity todo.entity1 does not support action {DOMAIN}.{service_name}",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -18,7 +18,7 @@ from homeassistant.const import (
|
|||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import ServiceNotSupported
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
|
@ -29,6 +29,7 @@ from .mocks import (
|
|||
_mock_lock_from_fixture,
|
||||
_mock_lock_with_unlatch,
|
||||
_mock_operative_yale_lock_detail,
|
||||
async_setup_component,
|
||||
)
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
@ -418,8 +419,14 @@ async def test_open_throws_hass_service_not_supported_error(
|
|||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test open throws correct error on entity does not support this service error."""
|
||||
# Fetch translations
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
mocked_lock_detail = await _mock_operative_yale_lock_detail(hass)
|
||||
await _create_yale_with_devices(hass, [mocked_lock_detail])
|
||||
data = {ATTR_ENTITY_ID: "lock.a6697750d607098bae8d6baa11ef8063_name"}
|
||||
with pytest.raises(HomeAssistantError, match="does not support this service"):
|
||||
entity_id = "lock.a6697750d607098bae8d6baa11ef8063_name"
|
||||
data = {ATTR_ENTITY_ID: entity_id}
|
||||
with pytest.raises(
|
||||
ServiceNotSupported,
|
||||
match=f"Entity {entity_id} does not support action {LOCK_DOMAIN}.{SERVICE_OPEN}",
|
||||
):
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_OPEN, data, blocking=True)
|
||||
|
|
|
@ -1274,6 +1274,8 @@ async def test_register_with_mixed_case(hass: HomeAssistant) -> None:
|
|||
|
||||
async def test_call_with_required_features(hass: HomeAssistant, mock_entities) -> None:
|
||||
"""Test service calls invoked only if entity has required features."""
|
||||
# Set up homeassistant component to fetch the translations
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
test_service_mock = AsyncMock(return_value=None)
|
||||
await service.entity_service_call(
|
||||
hass,
|
||||
|
@ -1293,7 +1295,11 @@ async def test_call_with_required_features(hass: HomeAssistant, mock_entities) -
|
|||
|
||||
# Test we raise if we target entity ID that does not support the service
|
||||
test_service_mock.reset_mock()
|
||||
with pytest.raises(exceptions.HomeAssistantError):
|
||||
with pytest.raises(
|
||||
exceptions.ServiceNotSupported,
|
||||
match="Entity light.living_room does not "
|
||||
"support action test_domain.test_service",
|
||||
):
|
||||
await service.entity_service_call(
|
||||
hass,
|
||||
mock_entities,
|
||||
|
|
Loading…
Reference in New Issue