Raise if referenced entity does not support service (#68394)
This commit is contained in:
parent
0cbc29caca
commit
8bbbd1947d
10 changed files with 105 additions and 90 deletions
|
@ -220,7 +220,7 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity):
|
||||||
)
|
)
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
async def _async_alarm_arm(self, state, script=None, code=None):
|
async def _async_alarm_arm(self, state, script, code):
|
||||||
"""Arm the panel to specified state with supplied script."""
|
"""Arm the panel to specified state with supplied script."""
|
||||||
optimistic_set = False
|
optimistic_set = False
|
||||||
|
|
||||||
|
@ -228,10 +228,7 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity):
|
||||||
self._state = state
|
self._state = state
|
||||||
optimistic_set = True
|
optimistic_set = True
|
||||||
|
|
||||||
if script is not None:
|
await script.async_run({ATTR_CODE: code}, context=self._context)
|
||||||
await script.async_run({ATTR_CODE: code}, context=self._context)
|
|
||||||
else:
|
|
||||||
_LOGGER.error("No script action defined for %s", state)
|
|
||||||
|
|
||||||
if optimistic_set:
|
if optimistic_set:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -527,7 +527,7 @@ def async_set_service_schema(
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def entity_service_call(
|
async def entity_service_call( # noqa: C901
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
platforms: Iterable[EntityPlatform],
|
platforms: Iterable[EntityPlatform],
|
||||||
func: str | Callable[..., Any],
|
func: str | Callable[..., Any],
|
||||||
|
@ -646,6 +646,12 @@ async def entity_service_call(
|
||||||
for feature_set in required_features
|
for feature_set in required_features
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
|
# 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."
|
||||||
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entities.append(entity)
|
entities.append(entity)
|
||||||
|
|
|
@ -39,6 +39,7 @@ from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er, network
|
from homeassistant.helpers import device_registry as dr, entity_registry as er, network
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@ -1225,15 +1226,18 @@ async def test_entity_control(hass: HomeAssistant):
|
||||||
chromecast.media_controller.pause.assert_called_once_with()
|
chromecast.media_controller.pause.assert_called_once_with()
|
||||||
|
|
||||||
# Media previous
|
# Media previous
|
||||||
await common.async_media_previous_track(hass, entity_id)
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await common.async_media_previous_track(hass, entity_id)
|
||||||
chromecast.media_controller.queue_prev.assert_not_called()
|
chromecast.media_controller.queue_prev.assert_not_called()
|
||||||
|
|
||||||
# Media next
|
# Media next
|
||||||
await common.async_media_next_track(hass, entity_id)
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await common.async_media_next_track(hass, entity_id)
|
||||||
chromecast.media_controller.queue_next.assert_not_called()
|
chromecast.media_controller.queue_next.assert_not_called()
|
||||||
|
|
||||||
# Media seek
|
# Media seek
|
||||||
await common.async_media_seek(hass, 123, entity_id)
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await common.async_media_seek(hass, 123, entity_id)
|
||||||
chromecast.media_controller.seek.assert_not_called()
|
chromecast.media_controller.seek.assert_not_called()
|
||||||
|
|
||||||
# Enable support for queue and seek
|
# Enable support for queue and seek
|
||||||
|
|
|
@ -3,6 +3,7 @@ from dynalite_devices_lib.cover import DynaliteTimeCoverWithTiltDevice
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_FRIENDLY_NAME
|
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_FRIENDLY_NAME
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
ATTR_ARGS,
|
ATTR_ARGS,
|
||||||
|
@ -65,9 +66,10 @@ async def test_cover_without_tilt(hass, mock_device):
|
||||||
"""Test a cover with no tilt."""
|
"""Test a cover with no tilt."""
|
||||||
mock_device.has_tilt = False
|
mock_device.has_tilt = False
|
||||||
await create_entity_from_device(hass, mock_device)
|
await create_entity_from_device(hass, mock_device)
|
||||||
await hass.services.async_call(
|
with pytest.raises(HomeAssistantError):
|
||||||
"cover", "open_cover_tilt", {"entity_id": "cover.name"}, blocking=True
|
await hass.services.async_call(
|
||||||
)
|
"cover", "open_cover_tilt", {"entity_id": "cover.name"}, blocking=True
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
mock_device.async_open_cover_tilt.assert_not_called()
|
mock_device.async_open_cover_tilt.assert_not_called()
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import pytest
|
||||||
|
|
||||||
from homeassistant.components.rfxtrx import DOMAIN
|
from homeassistant.components.rfxtrx import DOMAIN
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, mock_restore_cache
|
from tests.common import MockConfigEntry, mock_restore_cache
|
||||||
from tests.components.rfxtrx.conftest import create_rfx_test_cfg
|
from tests.components.rfxtrx.conftest import create_rfx_test_cfg
|
||||||
|
@ -181,19 +182,21 @@ async def test_rfy_cover(hass, rfxtrx):
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
with pytest.raises(HomeAssistantError):
|
||||||
"cover",
|
await hass.services.async_call(
|
||||||
"open_cover_tilt",
|
"cover",
|
||||||
{"entity_id": "cover.rfy_010203_1"},
|
"open_cover_tilt",
|
||||||
blocking=True,
|
{"entity_id": "cover.rfy_010203_1"},
|
||||||
)
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
with pytest.raises(HomeAssistantError):
|
||||||
"cover",
|
await hass.services.async_call(
|
||||||
"close_cover_tilt",
|
"cover",
|
||||||
{"entity_id": "cover.rfy_010203_1"},
|
"close_cover_tilt",
|
||||||
blocking=True,
|
{"entity_id": "cover.rfy_010203_1"},
|
||||||
)
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert rfxtrx.transport.send.mock_calls == [
|
assert rfxtrx.transport.send.mock_calls == [
|
||||||
call(bytearray(b"\x08\x1a\x00\x00\x01\x02\x03\x01\x00")),
|
call(bytearray(b"\x08\x1a\x00\x00\x01\x02\x03\x01\x00")),
|
||||||
|
|
|
@ -27,6 +27,7 @@ from homeassistant.const import (
|
||||||
STATE_ALARM_TRIGGERED,
|
STATE_ALARM_TRIGGERED,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.entity_component import async_update_entity
|
from homeassistant.helpers.entity_component import async_update_entity
|
||||||
|
|
||||||
|
@ -337,17 +338,24 @@ async def test_sets_with_correct_code(hass, two_part_alarm):
|
||||||
await _test_service_call(
|
await _test_service_call(
|
||||||
hass, SERVICE_ALARM_ARM_NIGHT, "group_arm", SECOND_ENTITY_ID, 1, "C", **code
|
hass, SERVICE_ALARM_ARM_NIGHT, "group_arm", SECOND_ENTITY_ID, 1, "C", **code
|
||||||
)
|
)
|
||||||
await _test_no_service_call(
|
with pytest.raises(HomeAssistantError):
|
||||||
hass, SERVICE_ALARM_ARM_CUSTOM_BYPASS, "partial_arm", FIRST_ENTITY_ID, 0, **code
|
await _test_no_service_call(
|
||||||
)
|
hass,
|
||||||
await _test_no_service_call(
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
hass,
|
"partial_arm",
|
||||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
FIRST_ENTITY_ID,
|
||||||
"partial_arm",
|
0,
|
||||||
SECOND_ENTITY_ID,
|
**code,
|
||||||
1,
|
)
|
||||||
**code,
|
with pytest.raises(HomeAssistantError):
|
||||||
)
|
await _test_no_service_call(
|
||||||
|
hass,
|
||||||
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
|
"partial_arm",
|
||||||
|
SECOND_ENTITY_ID,
|
||||||
|
1,
|
||||||
|
**code,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_sets_with_incorrect_code(hass, two_part_alarm):
|
async def test_sets_with_incorrect_code(hass, two_part_alarm):
|
||||||
|
@ -379,14 +387,21 @@ async def test_sets_with_incorrect_code(hass, two_part_alarm):
|
||||||
await _test_no_service_call(
|
await _test_no_service_call(
|
||||||
hass, SERVICE_ALARM_ARM_NIGHT, "group_arm", SECOND_ENTITY_ID, 1, **code
|
hass, SERVICE_ALARM_ARM_NIGHT, "group_arm", SECOND_ENTITY_ID, 1, **code
|
||||||
)
|
)
|
||||||
await _test_no_service_call(
|
with pytest.raises(HomeAssistantError):
|
||||||
hass, SERVICE_ALARM_ARM_CUSTOM_BYPASS, "partial_arm", FIRST_ENTITY_ID, 0, **code
|
await _test_no_service_call(
|
||||||
)
|
hass,
|
||||||
await _test_no_service_call(
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
hass,
|
"partial_arm",
|
||||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
FIRST_ENTITY_ID,
|
||||||
"partial_arm",
|
0,
|
||||||
SECOND_ENTITY_ID,
|
**code,
|
||||||
1,
|
)
|
||||||
**code,
|
with pytest.raises(HomeAssistantError):
|
||||||
)
|
await _test_no_service_call(
|
||||||
|
hass,
|
||||||
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
|
"partial_arm",
|
||||||
|
SECOND_ENTITY_ID,
|
||||||
|
1,
|
||||||
|
**code,
|
||||||
|
)
|
||||||
|
|
|
@ -60,6 +60,7 @@ from homeassistant.const import (
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
@ -896,9 +897,10 @@ async def test_turn_on_wol(hass: HomeAssistant) -> None:
|
||||||
async def test_turn_on_without_turnon(hass: HomeAssistant, remote: Mock) -> None:
|
async def test_turn_on_without_turnon(hass: HomeAssistant, remote: Mock) -> None:
|
||||||
"""Test turn on."""
|
"""Test turn on."""
|
||||||
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
|
await setup_samsungtv(hass, MOCK_CONFIG_NOTURNON)
|
||||||
assert await hass.services.async_call(
|
with pytest.raises(HomeAssistantError):
|
||||||
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
|
await hass.services.async_call(
|
||||||
)
|
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID_NOTURNON}, True
|
||||||
|
)
|
||||||
# nothing called as not supported feature
|
# nothing called as not supported feature
|
||||||
assert remote.control.call_count == 0
|
assert remote.control.call_count == 0
|
||||||
|
|
||||||
|
|
|
@ -129,38 +129,6 @@ async def test_optimistic_states(hass, start_ha):
|
||||||
assert hass.states.get(TEMPLATE_NAME).state == set_state
|
assert hass.states.get(TEMPLATE_NAME).state == set_state
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count,domain", [(1, "alarm_control_panel")])
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"config",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "{{ states('alarm_control_panel.test') }}",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_no_action_scripts(hass, start_ha):
|
|
||||||
"""Test no action scripts per state."""
|
|
||||||
hass.states.async_set("alarm_control_panel.test", STATE_ALARM_ARMED_AWAY)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
for func, set_state in [
|
|
||||||
(common.async_alarm_arm_away, STATE_ALARM_ARMED_AWAY),
|
|
||||||
(common.async_alarm_arm_home, STATE_ALARM_ARMED_AWAY),
|
|
||||||
(common.async_alarm_arm_night, STATE_ALARM_ARMED_AWAY),
|
|
||||||
(common.async_alarm_disarm, STATE_ALARM_ARMED_AWAY),
|
|
||||||
]:
|
|
||||||
await func(hass, entity_id=TEMPLATE_NAME)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert hass.states.get(TEMPLATE_NAME).state == set_state
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("count,domain", [(0, "alarm_control_panel")])
|
@pytest.mark.parametrize("count,domain", [(0, "alarm_control_panel")])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config,msg",
|
"config,msg",
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
"""The tests for WebOS TV automation triggers."""
|
"""The tests for WebOS TV automation triggers."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import automation
|
from homeassistant.components import automation
|
||||||
from homeassistant.components.webostv import DOMAIN
|
from homeassistant.components.webostv import DOMAIN
|
||||||
from homeassistant.const import SERVICE_RELOAD
|
from homeassistant.const import SERVICE_RELOAD
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.device_registry import async_get as get_dev_reg
|
from homeassistant.helpers.device_registry import async_get as get_dev_reg
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
@ -57,17 +60,18 @@ async def test_webostv_turn_on_trigger_device_id(hass, calls, client):
|
||||||
with patch("homeassistant.config.load_yaml", return_value={}):
|
with patch("homeassistant.config.load_yaml", return_value={}):
|
||||||
await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True)
|
await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||||
|
|
||||||
await hass.services.async_call(
|
calls.clear()
|
||||||
"media_player",
|
|
||||||
"turn_on",
|
with pytest.raises(HomeAssistantError):
|
||||||
{"entity_id": ENTITY_ID},
|
await hass.services.async_call(
|
||||||
blocking=True,
|
"media_player",
|
||||||
)
|
"turn_on",
|
||||||
|
{"entity_id": ENTITY_ID},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(calls) == 1
|
assert len(calls) == 0
|
||||||
assert calls[0].data["some"] == device.id
|
|
||||||
assert calls[0].data["id"] == 0
|
|
||||||
|
|
||||||
|
|
||||||
async def test_webostv_turn_on_trigger_entity_id(hass, calls, client):
|
async def test_webostv_turn_on_trigger_entity_id(hass, calls, client):
|
||||||
|
|
|
@ -552,6 +552,20 @@ async def test_call_with_required_features(hass, mock_entities):
|
||||||
actual = [call[0][0] for call in test_service_mock.call_args_list]
|
actual = [call[0][0] for call in test_service_mock.call_args_list]
|
||||||
assert all(entity in actual for entity in expected)
|
assert all(entity in actual for entity in expected)
|
||||||
|
|
||||||
|
# Test we raise if we target entity ID that does not support the service
|
||||||
|
test_service_mock.reset_mock()
|
||||||
|
with pytest.raises(exceptions.HomeAssistantError):
|
||||||
|
await service.entity_service_call(
|
||||||
|
hass,
|
||||||
|
[Mock(entities=mock_entities)],
|
||||||
|
test_service_mock,
|
||||||
|
ha.ServiceCall(
|
||||||
|
"test_domain", "test_service", {"entity_id": "light.living_room"}
|
||||||
|
),
|
||||||
|
required_features=[SUPPORT_A],
|
||||||
|
)
|
||||||
|
assert test_service_mock.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_call_with_both_required_features(hass, mock_entities):
|
async def test_call_with_both_required_features(hass, mock_entities):
|
||||||
"""Test service calls invoked only if entity has both features."""
|
"""Test service calls invoked only if entity has both features."""
|
||||||
|
|
Loading…
Add table
Reference in a new issue