mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add humidifier intents (#37335)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
2e4b4dc188
commit
7c9ef39ef6
127
homeassistant/components/humidifier/intent.py
Normal file
127
homeassistant/components/humidifier/intent.py
Normal file
@ -0,0 +1,127 @@
|
||||
"""Intents for the humidifier integration."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import intent
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import (
|
||||
ATTR_AVAILABLE_MODES,
|
||||
ATTR_HUMIDITY,
|
||||
ATTR_MODE,
|
||||
DOMAIN,
|
||||
SERVICE_SET_HUMIDITY,
|
||||
SERVICE_SET_MODE,
|
||||
SERVICE_TURN_ON,
|
||||
SUPPORT_MODES,
|
||||
)
|
||||
|
||||
INTENT_HUMIDITY = "HassHumidifierSetpoint"
|
||||
INTENT_MODE = "HassHumidifierMode"
|
||||
|
||||
|
||||
async def async_setup_intents(hass: HomeAssistant) -> None:
|
||||
"""Set up the humidifier intents."""
|
||||
hass.helpers.intent.async_register(HumidityHandler())
|
||||
hass.helpers.intent.async_register(SetModeHandler())
|
||||
|
||||
|
||||
class HumidityHandler(intent.IntentHandler):
|
||||
"""Handle set humidity intents."""
|
||||
|
||||
intent_type = INTENT_HUMIDITY
|
||||
slot_schema = {
|
||||
vol.Required("name"): cv.string,
|
||||
vol.Required("humidity"): vol.All(vol.Coerce(int), vol.Range(0, 100)),
|
||||
}
|
||||
|
||||
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||
"""Handle the hass intent."""
|
||||
hass = intent_obj.hass
|
||||
slots = self.async_validate_slots(intent_obj.slots)
|
||||
state = hass.helpers.intent.async_match_state(
|
||||
slots["name"]["value"],
|
||||
[state for state in hass.states.async_all() if state.domain == DOMAIN],
|
||||
)
|
||||
|
||||
service_data = {ATTR_ENTITY_ID: state.entity_id}
|
||||
|
||||
humidity = slots["humidity"]["value"]
|
||||
|
||||
if state.state == STATE_OFF:
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_TURN_ON, service_data, context=intent_obj.context
|
||||
)
|
||||
speech = f"Turned {state.name} on and set humidity to {humidity}%"
|
||||
else:
|
||||
speech = f"The {state.name} is set to {humidity}%"
|
||||
|
||||
service_data[ATTR_HUMIDITY] = humidity
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_HUMIDITY,
|
||||
service_data,
|
||||
context=intent_obj.context,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
response = intent_obj.create_response()
|
||||
|
||||
response.async_set_speech(speech)
|
||||
return response
|
||||
|
||||
|
||||
class SetModeHandler(intent.IntentHandler):
|
||||
"""Handle set humidity intents."""
|
||||
|
||||
intent_type = INTENT_MODE
|
||||
slot_schema = {
|
||||
vol.Required("name"): cv.string,
|
||||
vol.Required("mode"): cv.string,
|
||||
}
|
||||
|
||||
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||
"""Handle the hass intent."""
|
||||
hass = intent_obj.hass
|
||||
slots = self.async_validate_slots(intent_obj.slots)
|
||||
state = hass.helpers.intent.async_match_state(
|
||||
slots["name"]["value"],
|
||||
[state for state in hass.states.async_all() if state.domain == DOMAIN],
|
||||
)
|
||||
|
||||
service_data = {ATTR_ENTITY_ID: state.entity_id}
|
||||
|
||||
intent.async_test_feature(state, SUPPORT_MODES, "modes")
|
||||
mode = slots["mode"]["value"]
|
||||
|
||||
if mode not in state.attributes.get(ATTR_AVAILABLE_MODES, []):
|
||||
raise intent.IntentHandleError(
|
||||
f"Entity {state.name} does not support {mode} mode"
|
||||
)
|
||||
|
||||
if state.state == STATE_OFF:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
service_data,
|
||||
context=intent_obj.context,
|
||||
blocking=True,
|
||||
)
|
||||
speech = f"Turned {state.name} on and set {mode} mode"
|
||||
else:
|
||||
speech = f"The mode for {state.name} is set to {mode}"
|
||||
|
||||
service_data[ATTR_MODE] = mode
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_MODE,
|
||||
service_data,
|
||||
context=intent_obj.context,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
response = intent_obj.create_response()
|
||||
|
||||
response.async_set_speech(speech)
|
||||
return response
|
208
tests/components/humidifier/test_intent.py
Normal file
208
tests/components/humidifier/test_intent.py
Normal file
@ -0,0 +1,208 @@
|
||||
"""Tests for the humidifier intents."""
|
||||
from homeassistant.components.humidifier import (
|
||||
ATTR_AVAILABLE_MODES,
|
||||
ATTR_HUMIDITY,
|
||||
ATTR_MODE,
|
||||
DOMAIN,
|
||||
SERVICE_SET_HUMIDITY,
|
||||
SERVICE_SET_MODE,
|
||||
intent,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
SERVICE_TURN_ON,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.helpers.intent import IntentHandleError
|
||||
|
||||
from tests.common import async_mock_service
|
||||
|
||||
|
||||
async def test_intent_set_humidity(hass):
|
||||
"""Test the set humidity intent."""
|
||||
hass.states.async_set(
|
||||
"humidifier.bedroom_humidifier", STATE_ON, {ATTR_HUMIDITY: 40}
|
||||
)
|
||||
humidity_calls = async_mock_service(hass, DOMAIN, SERVICE_SET_HUMIDITY)
|
||||
turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
await intent.async_setup_intents(hass)
|
||||
|
||||
result = await hass.helpers.intent.async_handle(
|
||||
"test",
|
||||
intent.INTENT_HUMIDITY,
|
||||
{"name": {"value": "Bedroom humidifier"}, "humidity": {"value": "50"}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result.speech["plain"]["speech"] == "The bedroom humidifier is set to 50%"
|
||||
|
||||
assert len(turn_on_calls) == 0
|
||||
assert len(humidity_calls) == 1
|
||||
call = humidity_calls[0]
|
||||
assert call.domain == DOMAIN
|
||||
assert call.service == SERVICE_SET_HUMIDITY
|
||||
assert call.data.get(ATTR_ENTITY_ID) == "humidifier.bedroom_humidifier"
|
||||
assert call.data.get(ATTR_HUMIDITY) == 50
|
||||
|
||||
|
||||
async def test_intent_set_humidity_and_turn_on(hass):
|
||||
"""Test the set humidity intent for turned off humidifier."""
|
||||
hass.states.async_set(
|
||||
"humidifier.bedroom_humidifier", STATE_OFF, {ATTR_HUMIDITY: 40}
|
||||
)
|
||||
humidity_calls = async_mock_service(hass, DOMAIN, SERVICE_SET_HUMIDITY)
|
||||
turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
await intent.async_setup_intents(hass)
|
||||
|
||||
result = await hass.helpers.intent.async_handle(
|
||||
"test",
|
||||
intent.INTENT_HUMIDITY,
|
||||
{"name": {"value": "Bedroom humidifier"}, "humidity": {"value": "50"}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
result.speech["plain"]["speech"]
|
||||
== "Turned bedroom humidifier on and set humidity to 50%"
|
||||
)
|
||||
|
||||
assert len(turn_on_calls) == 1
|
||||
call = turn_on_calls[0]
|
||||
assert call.domain == DOMAIN
|
||||
assert call.service == SERVICE_TURN_ON
|
||||
assert call.data.get(ATTR_ENTITY_ID) == "humidifier.bedroom_humidifier"
|
||||
assert len(humidity_calls) == 1
|
||||
call = humidity_calls[0]
|
||||
assert call.domain == DOMAIN
|
||||
assert call.service == SERVICE_SET_HUMIDITY
|
||||
assert call.data.get(ATTR_ENTITY_ID) == "humidifier.bedroom_humidifier"
|
||||
assert call.data.get(ATTR_HUMIDITY) == 50
|
||||
|
||||
|
||||
async def test_intent_set_mode(hass):
|
||||
"""Test the set mode intent."""
|
||||
hass.states.async_set(
|
||||
"humidifier.bedroom_humidifier",
|
||||
STATE_ON,
|
||||
{
|
||||
ATTR_HUMIDITY: 40,
|
||||
ATTR_SUPPORTED_FEATURES: 1,
|
||||
ATTR_AVAILABLE_MODES: ["home", "away"],
|
||||
ATTR_MODE: "home",
|
||||
},
|
||||
)
|
||||
mode_calls = async_mock_service(hass, DOMAIN, SERVICE_SET_MODE)
|
||||
turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
await intent.async_setup_intents(hass)
|
||||
|
||||
result = await hass.helpers.intent.async_handle(
|
||||
"test",
|
||||
intent.INTENT_MODE,
|
||||
{"name": {"value": "Bedroom humidifier"}, "mode": {"value": "away"}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
result.speech["plain"]["speech"]
|
||||
== "The mode for bedroom humidifier is set to away"
|
||||
)
|
||||
|
||||
assert len(turn_on_calls) == 0
|
||||
assert len(mode_calls) == 1
|
||||
call = mode_calls[0]
|
||||
assert call.domain == DOMAIN
|
||||
assert call.service == SERVICE_SET_MODE
|
||||
assert call.data.get(ATTR_ENTITY_ID) == "humidifier.bedroom_humidifier"
|
||||
assert call.data.get(ATTR_MODE) == "away"
|
||||
|
||||
|
||||
async def test_intent_set_mode_and_turn_on(hass):
|
||||
"""Test the set mode intent."""
|
||||
hass.states.async_set(
|
||||
"humidifier.bedroom_humidifier",
|
||||
STATE_OFF,
|
||||
{
|
||||
ATTR_HUMIDITY: 40,
|
||||
ATTR_SUPPORTED_FEATURES: 1,
|
||||
ATTR_AVAILABLE_MODES: ["home", "away"],
|
||||
ATTR_MODE: "home",
|
||||
},
|
||||
)
|
||||
mode_calls = async_mock_service(hass, DOMAIN, SERVICE_SET_MODE)
|
||||
turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||
await intent.async_setup_intents(hass)
|
||||
|
||||
result = await hass.helpers.intent.async_handle(
|
||||
"test",
|
||||
intent.INTENT_MODE,
|
||||
{"name": {"value": "Bedroom humidifier"}, "mode": {"value": "away"}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
result.speech["plain"]["speech"]
|
||||
== "Turned bedroom humidifier on and set away mode"
|
||||
)
|
||||
|
||||
assert len(turn_on_calls) == 1
|
||||
call = turn_on_calls[0]
|
||||
assert call.domain == DOMAIN
|
||||
assert call.service == SERVICE_TURN_ON
|
||||
assert call.data.get(ATTR_ENTITY_ID) == "humidifier.bedroom_humidifier"
|
||||
assert len(mode_calls) == 1
|
||||
call = mode_calls[0]
|
||||
assert call.domain == DOMAIN
|
||||
assert call.service == SERVICE_SET_MODE
|
||||
assert call.data.get(ATTR_ENTITY_ID) == "humidifier.bedroom_humidifier"
|
||||
assert call.data.get(ATTR_MODE) == "away"
|
||||
|
||||
|
||||
async def test_intent_set_mode_tests_feature(hass):
|
||||
"""Test the set mode intent where modes are not supported."""
|
||||
hass.states.async_set(
|
||||
"humidifier.bedroom_humidifier", STATE_ON, {ATTR_HUMIDITY: 40}
|
||||
)
|
||||
mode_calls = async_mock_service(hass, DOMAIN, SERVICE_SET_MODE)
|
||||
await intent.async_setup_intents(hass)
|
||||
|
||||
try:
|
||||
await hass.helpers.intent.async_handle(
|
||||
"test",
|
||||
intent.INTENT_MODE,
|
||||
{"name": {"value": "Bedroom humidifier"}, "mode": {"value": "away"}},
|
||||
)
|
||||
assert False, "handling intent should have raised"
|
||||
except IntentHandleError as err:
|
||||
assert str(err) == "Entity bedroom humidifier does not support modes"
|
||||
|
||||
assert len(mode_calls) == 0
|
||||
|
||||
|
||||
async def test_intent_set_unknown_mode(hass):
|
||||
"""Test the set mode intent for unsupported mode."""
|
||||
hass.states.async_set(
|
||||
"humidifier.bedroom_humidifier",
|
||||
STATE_ON,
|
||||
{
|
||||
ATTR_HUMIDITY: 40,
|
||||
ATTR_SUPPORTED_FEATURES: 1,
|
||||
ATTR_AVAILABLE_MODES: ["home", "away"],
|
||||
ATTR_MODE: "home",
|
||||
},
|
||||
)
|
||||
mode_calls = async_mock_service(hass, DOMAIN, SERVICE_SET_MODE)
|
||||
await intent.async_setup_intents(hass)
|
||||
|
||||
try:
|
||||
await hass.helpers.intent.async_handle(
|
||||
"test",
|
||||
intent.INTENT_MODE,
|
||||
{"name": {"value": "Bedroom humidifier"}, "mode": {"value": "eco"}},
|
||||
)
|
||||
assert False, "handling intent should have raised"
|
||||
except IntentHandleError as err:
|
||||
assert str(err) == "Entity bedroom humidifier does not support eco mode"
|
||||
|
||||
assert len(mode_calls) == 0
|
Loading…
x
Reference in New Issue
Block a user