mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Fix intent TurnOn creating stack trace for buttons (#144205)
This commit is contained in:
parent
490bb46a82
commit
8048d2bfb8
@ -10,6 +10,11 @@ from aiohttp import web
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import http, sensor
|
from homeassistant.components import http, sensor
|
||||||
|
from homeassistant.components.button import (
|
||||||
|
DOMAIN as BUTTON_DOMAIN,
|
||||||
|
SERVICE_PRESS as SERVICE_PRESS_BUTTON,
|
||||||
|
ButtonDeviceClass,
|
||||||
|
)
|
||||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
ATTR_POSITION,
|
ATTR_POSITION,
|
||||||
@ -20,6 +25,7 @@ from homeassistant.components.cover import (
|
|||||||
CoverDeviceClass,
|
CoverDeviceClass,
|
||||||
)
|
)
|
||||||
from homeassistant.components.http.data_validator import RequestDataValidator
|
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||||
|
from homeassistant.components.input_button import DOMAIN as INPUT_BUTTON_DOMAIN
|
||||||
from homeassistant.components.lock import (
|
from homeassistant.components.lock import (
|
||||||
DOMAIN as LOCK_DOMAIN,
|
DOMAIN as LOCK_DOMAIN,
|
||||||
SERVICE_LOCK,
|
SERVICE_LOCK,
|
||||||
@ -80,6 +86,7 @@ __all__ = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
ONOFF_DEVICE_CLASSES = {
|
ONOFF_DEVICE_CLASSES = {
|
||||||
|
ButtonDeviceClass,
|
||||||
CoverDeviceClass,
|
CoverDeviceClass,
|
||||||
ValveDeviceClass,
|
ValveDeviceClass,
|
||||||
SwitchDeviceClass,
|
SwitchDeviceClass,
|
||||||
@ -103,7 +110,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
intent.INTENT_TURN_ON,
|
intent.INTENT_TURN_ON,
|
||||||
HOMEASSISTANT_DOMAIN,
|
HOMEASSISTANT_DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
description="Turns on/opens a device or entity. For locks, this performs a 'lock' action. Use for requests like 'turn on', 'activate', 'enable', or 'lock'.",
|
description="Turns on/opens/presses a device or entity. For locks, this performs a 'lock' action. Use for requests like 'turn on', 'activate', 'enable', or 'lock'.",
|
||||||
device_classes=ONOFF_DEVICE_CLASSES,
|
device_classes=ONOFF_DEVICE_CLASSES,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -168,6 +175,25 @@ class OnOffIntentHandler(intent.ServiceIntentHandler):
|
|||||||
"""Call service on entity with handling for special cases."""
|
"""Call service on entity with handling for special cases."""
|
||||||
hass = intent_obj.hass
|
hass = intent_obj.hass
|
||||||
|
|
||||||
|
if state.domain in (BUTTON_DOMAIN, INPUT_BUTTON_DOMAIN):
|
||||||
|
if service != SERVICE_TURN_ON:
|
||||||
|
raise intent.IntentHandleError(
|
||||||
|
f"Entity {state.entity_id} cannot be turned off"
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._run_then_background(
|
||||||
|
hass.async_create_task(
|
||||||
|
hass.services.async_call(
|
||||||
|
state.domain,
|
||||||
|
SERVICE_PRESS_BUTTON,
|
||||||
|
{ATTR_ENTITY_ID: state.entity_id},
|
||||||
|
context=intent_obj.context,
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if state.domain == COVER_DOMAIN:
|
if state.domain == COVER_DOMAIN:
|
||||||
# on = open
|
# on = open
|
||||||
# off = close
|
# off = close
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.cover import SERVICE_OPEN_COVER
|
from homeassistant.components.button import SERVICE_PRESS
|
||||||
from homeassistant.components.lock import SERVICE_LOCK
|
from homeassistant.components.cover import SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER
|
||||||
|
from homeassistant.components.lock import SERVICE_LOCK, SERVICE_UNLOCK
|
||||||
|
from homeassistant.components.valve import SERVICE_CLOSE_VALVE, SERVICE_OPEN_VALVE
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
@ -121,41 +123,130 @@ async def test_turn_on_intent(hass: HomeAssistant) -> None:
|
|||||||
assert call.data == {"entity_id": ["light.test_light"]}
|
assert call.data == {"entity_id": ["light.test_light"]}
|
||||||
|
|
||||||
|
|
||||||
async def test_translated_turn_on_intent(
|
@pytest.mark.parametrize("domain", ["button", "input_button"])
|
||||||
|
async def test_turn_on_intent_button(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry, domain
|
||||||
|
) -> None:
|
||||||
|
"""Test HassTurnOn intent on button domains."""
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
|
button = entity_registry.async_get_or_create(domain, "test", "button_uid")
|
||||||
|
|
||||||
|
hass.states.async_set(button.entity_id, "unknown")
|
||||||
|
button_service_calls = async_mock_service(hass, domain, SERVICE_PRESS)
|
||||||
|
|
||||||
|
with pytest.raises(intent.IntentHandleError):
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassTurnOff", {"name": {"value": button.entity_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassTurnOn", {"name": {"value": button.entity_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(button_service_calls) == 1
|
||||||
|
call = button_service_calls[0]
|
||||||
|
assert call.domain == domain
|
||||||
|
assert call.service == SERVICE_PRESS
|
||||||
|
assert call.data == {"entity_id": button.entity_id}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_turn_on_off_intent_valve(
|
||||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test HassTurnOn intent on domains which don't have the intent."""
|
"""Test HassTurnOn/Off intent on valve domains."""
|
||||||
result = await async_setup_component(hass, "homeassistant", {})
|
assert await async_setup_component(hass, "intent", {})
|
||||||
result = await async_setup_component(hass, "intent", {})
|
|
||||||
await hass.async_block_till_done()
|
valve = entity_registry.async_get_or_create("valve", "test", "valve_uid")
|
||||||
assert result
|
|
||||||
|
hass.states.async_set(valve.entity_id, "closed")
|
||||||
|
open_calls = async_mock_service(hass, "valve", SERVICE_OPEN_VALVE)
|
||||||
|
close_calls = async_mock_service(hass, "valve", SERVICE_CLOSE_VALVE)
|
||||||
|
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassTurnOn", {"name": {"value": valve.entity_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(open_calls) == 1
|
||||||
|
call = open_calls[0]
|
||||||
|
assert call.domain == "valve"
|
||||||
|
assert call.service == SERVICE_OPEN_VALVE
|
||||||
|
assert call.data == {"entity_id": valve.entity_id}
|
||||||
|
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassTurnOff", {"name": {"value": valve.entity_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(close_calls) == 1
|
||||||
|
call = close_calls[0]
|
||||||
|
assert call.domain == "valve"
|
||||||
|
assert call.service == SERVICE_CLOSE_VALVE
|
||||||
|
assert call.data == {"entity_id": valve.entity_id}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_turn_on_off_intent_cover(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test HassTurnOn/Off intent on cover domains."""
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
cover = entity_registry.async_get_or_create("cover", "test", "cover_uid")
|
cover = entity_registry.async_get_or_create("cover", "test", "cover_uid")
|
||||||
lock = entity_registry.async_get_or_create("lock", "test", "lock_uid")
|
|
||||||
|
|
||||||
hass.states.async_set(cover.entity_id, "closed")
|
hass.states.async_set(cover.entity_id, "closed")
|
||||||
hass.states.async_set(lock.entity_id, "unlocked")
|
open_calls = async_mock_service(hass, "cover", SERVICE_OPEN_COVER)
|
||||||
cover_service_calls = async_mock_service(hass, "cover", SERVICE_OPEN_COVER)
|
close_calls = async_mock_service(hass, "cover", SERVICE_CLOSE_COVER)
|
||||||
lock_service_calls = async_mock_service(hass, "lock", SERVICE_LOCK)
|
|
||||||
|
|
||||||
await intent.async_handle(
|
await intent.async_handle(
|
||||||
hass, "test", "HassTurnOn", {"name": {"value": cover.entity_id}}
|
hass, "test", "HassTurnOn", {"name": {"value": cover.entity_id}}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert len(open_calls) == 1
|
||||||
|
call = open_calls[0]
|
||||||
|
assert call.domain == "cover"
|
||||||
|
assert call.service == SERVICE_OPEN_COVER
|
||||||
|
assert call.data == {"entity_id": cover.entity_id}
|
||||||
|
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassTurnOff", {"name": {"value": cover.entity_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(close_calls) == 1
|
||||||
|
call = close_calls[0]
|
||||||
|
assert call.domain == "cover"
|
||||||
|
assert call.service == SERVICE_CLOSE_COVER
|
||||||
|
assert call.data == {"entity_id": cover.entity_id}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_turn_on_off_intent_lock(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test HassTurnOn/Off intent on lock domains."""
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
|
lock = entity_registry.async_get_or_create("lock", "test", "lock_uid")
|
||||||
|
|
||||||
|
hass.states.async_set(lock.entity_id, "locked")
|
||||||
|
unlock_calls = async_mock_service(hass, "lock", SERVICE_UNLOCK)
|
||||||
|
lock_calls = async_mock_service(hass, "lock", SERVICE_LOCK)
|
||||||
|
|
||||||
await intent.async_handle(
|
await intent.async_handle(
|
||||||
hass, "test", "HassTurnOn", {"name": {"value": lock.entity_id}}
|
hass, "test", "HassTurnOn", {"name": {"value": lock.entity_id}}
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(cover_service_calls) == 1
|
assert len(lock_calls) == 1
|
||||||
call = cover_service_calls[0]
|
call = lock_calls[0]
|
||||||
assert call.domain == "cover"
|
|
||||||
assert call.service == "open_cover"
|
|
||||||
assert call.data == {"entity_id": cover.entity_id}
|
|
||||||
|
|
||||||
assert len(lock_service_calls) == 1
|
|
||||||
call = lock_service_calls[0]
|
|
||||||
assert call.domain == "lock"
|
assert call.domain == "lock"
|
||||||
assert call.service == "lock"
|
assert call.service == SERVICE_LOCK
|
||||||
|
assert call.data == {"entity_id": lock.entity_id}
|
||||||
|
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassTurnOff", {"name": {"value": lock.entity_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(unlock_calls) == 1
|
||||||
|
call = unlock_calls[0]
|
||||||
|
assert call.domain == "lock"
|
||||||
|
assert call.service == SERVICE_UNLOCK
|
||||||
assert call.data == {"entity_id": lock.entity_id}
|
assert call.data == {"entity_id": lock.entity_id}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user