Add humidifier intents (#37335)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Shulyaka 2020-07-17 21:20:34 +03:00 committed by GitHub
parent 2e4b4dc188
commit 7c9ef39ef6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 335 additions and 0 deletions

View 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

View 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