mirror of
https://github.com/home-assistant/core.git
synced 2025-12-09 17:38:51 +00:00
Add HassStopMoving intent for covers and valves (#155267)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,7 @@ from homeassistant.components.cover import (
|
|||||||
SERVICE_CLOSE_COVER,
|
SERVICE_CLOSE_COVER,
|
||||||
SERVICE_OPEN_COVER,
|
SERVICE_OPEN_COVER,
|
||||||
SERVICE_SET_COVER_POSITION,
|
SERVICE_SET_COVER_POSITION,
|
||||||
|
SERVICE_STOP_COVER,
|
||||||
CoverDeviceClass,
|
CoverDeviceClass,
|
||||||
)
|
)
|
||||||
from homeassistant.components.http.data_validator import RequestDataValidator
|
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||||
@@ -38,6 +39,7 @@ from homeassistant.components.valve import (
|
|||||||
SERVICE_CLOSE_VALVE,
|
SERVICE_CLOSE_VALVE,
|
||||||
SERVICE_OPEN_VALVE,
|
SERVICE_OPEN_VALVE,
|
||||||
SERVICE_SET_VALVE_POSITION,
|
SERVICE_SET_VALVE_POSITION,
|
||||||
|
SERVICE_STOP_VALVE,
|
||||||
ValveDeviceClass,
|
ValveDeviceClass,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@@ -143,6 +145,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
NevermindIntentHandler(),
|
NevermindIntentHandler(),
|
||||||
)
|
)
|
||||||
intent.async_register(hass, SetPositionIntentHandler())
|
intent.async_register(hass, SetPositionIntentHandler())
|
||||||
|
intent.async_register(hass, StopMovingIntentHandler())
|
||||||
intent.async_register(hass, StartTimerIntentHandler())
|
intent.async_register(hass, StartTimerIntentHandler())
|
||||||
intent.async_register(hass, CancelTimerIntentHandler())
|
intent.async_register(hass, CancelTimerIntentHandler())
|
||||||
intent.async_register(hass, CancelAllTimersIntentHandler())
|
intent.async_register(hass, CancelAllTimersIntentHandler())
|
||||||
@@ -433,6 +436,31 @@ class SetPositionIntentHandler(intent.DynamicServiceIntentHandler):
|
|||||||
raise intent.IntentHandleError(f"Domain not supported: {state.domain}")
|
raise intent.IntentHandleError(f"Domain not supported: {state.domain}")
|
||||||
|
|
||||||
|
|
||||||
|
class StopMovingIntentHandler(intent.DynamicServiceIntentHandler):
|
||||||
|
"""Intent handler for stopping covers and valves."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Create stop moving handler."""
|
||||||
|
super().__init__(
|
||||||
|
intent.INTENT_STOP_MOVING,
|
||||||
|
description="Stops a moving device or entity",
|
||||||
|
platforms={COVER_DOMAIN, VALVE_DOMAIN},
|
||||||
|
device_classes={CoverDeviceClass, ValveDeviceClass},
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_domain_and_service(
|
||||||
|
self, intent_obj: intent.Intent, state: State
|
||||||
|
) -> tuple[str, str]:
|
||||||
|
"""Get the domain and service name to call."""
|
||||||
|
if state.domain == COVER_DOMAIN:
|
||||||
|
return (COVER_DOMAIN, SERVICE_STOP_COVER)
|
||||||
|
|
||||||
|
if state.domain == VALVE_DOMAIN:
|
||||||
|
return (VALVE_DOMAIN, SERVICE_STOP_VALVE)
|
||||||
|
|
||||||
|
raise intent.IntentHandleError(f"Domain not supported: {state.domain}")
|
||||||
|
|
||||||
|
|
||||||
class GetCurrentDateIntentHandler(intent.IntentHandler):
|
class GetCurrentDateIntentHandler(intent.IntentHandler):
|
||||||
"""Gets the current date."""
|
"""Gets the current date."""
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ INTENT_TOGGLE = "HassToggle"
|
|||||||
INTENT_GET_STATE = "HassGetState"
|
INTENT_GET_STATE = "HassGetState"
|
||||||
INTENT_NEVERMIND = "HassNevermind"
|
INTENT_NEVERMIND = "HassNevermind"
|
||||||
INTENT_SET_POSITION = "HassSetPosition"
|
INTENT_SET_POSITION = "HassSetPosition"
|
||||||
|
INTENT_STOP_MOVING = "HassStopMoving"
|
||||||
INTENT_START_TIMER = "HassStartTimer"
|
INTENT_START_TIMER = "HassStartTimer"
|
||||||
INTENT_CANCEL_TIMER = "HassCancelTimer"
|
INTENT_CANCEL_TIMER = "HassCancelTimer"
|
||||||
INTENT_CANCEL_ALL_TIMERS = "HassCancelAllTimers"
|
INTENT_CANCEL_ALL_TIMERS = "HassCancelAllTimers"
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
"""Tests for Intent component."""
|
"""Tests for Intent component."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.button import SERVICE_PRESS
|
from homeassistant.components.button import SERVICE_PRESS
|
||||||
from homeassistant.components.cover import SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER
|
from homeassistant.components.cover import (
|
||||||
|
DOMAIN as COVER_DOMAIN,
|
||||||
|
SERVICE_CLOSE_COVER,
|
||||||
|
SERVICE_OPEN_COVER,
|
||||||
|
SERVICE_STOP_COVER,
|
||||||
|
CoverState,
|
||||||
|
)
|
||||||
from homeassistant.components.lock import SERVICE_LOCK, SERVICE_UNLOCK
|
from homeassistant.components.lock import SERVICE_LOCK, SERVICE_UNLOCK
|
||||||
from homeassistant.components.valve import SERVICE_CLOSE_VALVE, SERVICE_OPEN_VALVE
|
from homeassistant.components.valve import (
|
||||||
|
DOMAIN as VALVE_DOMAIN,
|
||||||
|
SERVICE_CLOSE_VALVE,
|
||||||
|
SERVICE_OPEN_VALVE,
|
||||||
|
SERVICE_STOP_VALVE,
|
||||||
|
ValveState,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
@@ -594,3 +608,66 @@ async def test_intents_respond_intent(hass: HomeAssistant) -> None:
|
|||||||
hass, "test", intent.INTENT_RESPOND, {"response": {"value": "Hello World"}}
|
hass, "test", intent.INTENT_RESPOND, {"response": {"value": "Hello World"}}
|
||||||
)
|
)
|
||||||
assert response.speech["plain"]["speech"] == "Hello World"
|
assert response.speech["plain"]["speech"] == "Hello World"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_stop_moving_valve(hass: HomeAssistant) -> None:
|
||||||
|
"""Test HassStopMoving intent for valves."""
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
|
entity_id = f"{VALVE_DOMAIN}.test_valve"
|
||||||
|
hass.states.async_set(entity_id, ValveState.OPEN)
|
||||||
|
calls = async_mock_service(hass, VALVE_DOMAIN, SERVICE_STOP_VALVE)
|
||||||
|
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", intent.INTENT_STOP_MOVING, {"name": {"value": "test valve"}}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
assert len(calls) == 1
|
||||||
|
call = calls[0]
|
||||||
|
assert call.domain == VALVE_DOMAIN
|
||||||
|
assert call.service == SERVICE_STOP_VALVE
|
||||||
|
assert call.data == {"entity_id": entity_id}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("slots"),
|
||||||
|
[
|
||||||
|
({"name": {"value": "test cover"}}),
|
||||||
|
({"device_class": {"value": "shade"}}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_stop_moving_cover(hass: HomeAssistant, slots: dict[str, Any]) -> None:
|
||||||
|
"""Test HassStopMoving intent for covers."""
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
|
entity_id = f"{COVER_DOMAIN}.test_cover"
|
||||||
|
hass.states.async_set(
|
||||||
|
entity_id, CoverState.OPEN, attributes={"device_class": "shade"}
|
||||||
|
)
|
||||||
|
calls = async_mock_service(hass, COVER_DOMAIN, SERVICE_STOP_COVER)
|
||||||
|
|
||||||
|
response = await intent.async_handle(hass, "test", intent.INTENT_STOP_MOVING, slots)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
assert len(calls) == 1
|
||||||
|
call = calls[0]
|
||||||
|
assert call.domain == COVER_DOMAIN
|
||||||
|
assert call.service == SERVICE_STOP_COVER
|
||||||
|
assert call.data == {"entity_id": entity_id}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_stop_moving_intent_unsupported_domain(hass: HomeAssistant) -> None:
|
||||||
|
"""Test that HassStopMoving intent fails with unsupported domain."""
|
||||||
|
assert await async_setup_component(hass, "homeassistant", {})
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
|
# Can't stop lights
|
||||||
|
hass.states.async_set("light.test_light", "on")
|
||||||
|
|
||||||
|
with pytest.raises(intent.IntentHandleError):
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", intent.INTENT_STOP_MOVING, {"name": {"value": "test light"}}
|
||||||
|
)
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ async def test_assist_api_tools(
|
|||||||
"HassTurnOn",
|
"HassTurnOn",
|
||||||
"HassTurnOff",
|
"HassTurnOff",
|
||||||
"HassSetPosition",
|
"HassSetPosition",
|
||||||
|
"HassStopMoving",
|
||||||
"HassStartTimer",
|
"HassStartTimer",
|
||||||
"HassCancelTimer",
|
"HassCancelTimer",
|
||||||
"HassCancelAllTimers",
|
"HassCancelAllTimers",
|
||||||
|
|||||||
Reference in New Issue
Block a user