Fail if targeting all devices in the house in service intent handler (#117930)
* Fail if targeting all devices in the house * Update homeassistant/helpers/intent.py --------- Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
eeeb5b2725
commit
f99ec87338
3 changed files with 75 additions and 2 deletions
|
@ -243,6 +243,19 @@ class MatchTargetsConstraints:
|
|||
allow_duplicate_names: bool = False
|
||||
"""True if entities with duplicate names are allowed in result."""
|
||||
|
||||
@property
|
||||
def has_constraints(self) -> bool:
|
||||
"""Returns True if at least one constraint is set (ignores assistant)."""
|
||||
return bool(
|
||||
self.name
|
||||
or self.area_name
|
||||
or self.floor_name
|
||||
or self.domains
|
||||
or self.device_classes
|
||||
or self.features
|
||||
or self.states
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MatchTargetsPreferences:
|
||||
|
@ -766,6 +779,15 @@ class IntentHandler:
|
|||
return f"<{self.__class__.__name__} - {self.intent_type}>"
|
||||
|
||||
|
||||
def non_empty_string(value: Any) -> str:
|
||||
"""Coerce value to string and fail if string is empty or whitespace."""
|
||||
value_str = cv.string(value)
|
||||
if not value_str.strip():
|
||||
raise vol.Invalid("string value is empty")
|
||||
|
||||
return value_str
|
||||
|
||||
|
||||
class DynamicServiceIntentHandler(IntentHandler):
|
||||
"""Service Intent handler registration (dynamic).
|
||||
|
||||
|
@ -817,7 +839,7 @@ class DynamicServiceIntentHandler(IntentHandler):
|
|||
def slot_schema(self) -> dict:
|
||||
"""Return a slot schema."""
|
||||
slot_schema = {
|
||||
vol.Any("name", "area", "floor"): cv.string,
|
||||
vol.Any("name", "area", "floor"): non_empty_string,
|
||||
vol.Optional("domain"): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional("device_class"): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional("preferred_area_id"): cv.string,
|
||||
|
@ -892,6 +914,10 @@ class DynamicServiceIntentHandler(IntentHandler):
|
|||
features=self.required_features,
|
||||
states=self.required_states,
|
||||
)
|
||||
if not match_constraints.has_constraints:
|
||||
# Fail if attempting to target all devices in the house
|
||||
raise IntentHandleError("Service handler cannot target all devices")
|
||||
|
||||
match_preferences = MatchTargetsPreferences(
|
||||
area_id=slots.get("preferred_area_id", {}).get("value"),
|
||||
floor_id=slots.get("preferred_floor_id", {}).get("value"),
|
||||
|
|
|
@ -236,7 +236,12 @@ async def test_turn_on_all(hass: HomeAssistant) -> None:
|
|||
hass.states.async_set("light.test_light_2", "off")
|
||||
calls = async_mock_service(hass, "light", SERVICE_TURN_ON)
|
||||
|
||||
await intent.async_handle(hass, "test", "HassTurnOn", {"name": {"value": "all"}})
|
||||
await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
"HassTurnOn",
|
||||
{"name": {"value": "all"}, "domain": {"value": "light"}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# All lights should be on now
|
||||
|
|
|
@ -771,3 +771,45 @@ async def test_service_intent_handler_required_domains(hass: HomeAssistant) -> N
|
|||
"TestType",
|
||||
slots={"name": {"value": "bedroom"}, "domain": {"value": "switch"}},
|
||||
)
|
||||
|
||||
|
||||
async def test_service_handler_empty_strings(hass: HomeAssistant) -> None:
|
||||
"""Test that passing empty strings for filters fails in ServiceIntentHandler."""
|
||||
handler = intent.ServiceIntentHandler(
|
||||
"TestType", "light", "turn_on", "Turned {} on"
|
||||
)
|
||||
intent.async_register(hass, handler)
|
||||
|
||||
for slot_name in ("name", "area", "floor"):
|
||||
# Empty string
|
||||
with pytest.raises(intent.InvalidSlotInfo):
|
||||
await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
"TestType",
|
||||
slots={slot_name: {"value": ""}},
|
||||
)
|
||||
|
||||
# Whitespace
|
||||
with pytest.raises(intent.InvalidSlotInfo):
|
||||
await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
"TestType",
|
||||
slots={slot_name: {"value": " "}},
|
||||
)
|
||||
|
||||
|
||||
async def test_service_handler_no_filter(hass: HomeAssistant) -> None:
|
||||
"""Test that targeting all devices in the house fails."""
|
||||
handler = intent.ServiceIntentHandler(
|
||||
"TestType", "light", "turn_on", "Turned {} on"
|
||||
)
|
||||
intent.async_register(hass, handler)
|
||||
|
||||
with pytest.raises(intent.IntentHandleError):
|
||||
await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
"TestType",
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue