Add Select entity support to Google Assistant (#51997)

This commit is contained in:
Franck Nijhof 2021-06-18 23:30:46 +02:00 committed by GitHub
parent 0ca199d8d0
commit 655f797f67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 5 deletions

View File

@ -15,6 +15,7 @@ from homeassistant.components import (
media_player, media_player,
scene, scene,
script, script,
select,
sensor, sensor,
switch, switch,
vacuum, vacuum,
@ -39,6 +40,8 @@ CONF_PRIVATE_KEY = "private_key"
DEFAULT_EXPOSE_BY_DEFAULT = True DEFAULT_EXPOSE_BY_DEFAULT = True
DEFAULT_EXPOSED_DOMAINS = [ DEFAULT_EXPOSED_DOMAINS = [
"alarm_control_panel",
"binary_sensor",
"climate", "climate",
"cover", "cover",
"fan", "fan",
@ -47,15 +50,14 @@ DEFAULT_EXPOSED_DOMAINS = [
"input_boolean", "input_boolean",
"input_select", "input_select",
"light", "light",
"lock",
"media_player", "media_player",
"scene", "scene",
"script", "script",
"select",
"sensor",
"switch", "switch",
"vacuum", "vacuum",
"lock",
"binary_sensor",
"sensor",
"alarm_control_panel",
] ]
PREFIX_TYPES = "action.devices.types." PREFIX_TYPES = "action.devices.types."
@ -117,6 +119,7 @@ EVENT_QUERY_RECEIVED = "google_assistant_query"
EVENT_SYNC_RECEIVED = "google_assistant_sync" EVENT_SYNC_RECEIVED = "google_assistant_sync"
DOMAIN_TO_GOOGLE_TYPES = { DOMAIN_TO_GOOGLE_TYPES = {
alarm_control_panel.DOMAIN: TYPE_ALARM,
camera.DOMAIN: TYPE_CAMERA, camera.DOMAIN: TYPE_CAMERA,
climate.DOMAIN: TYPE_THERMOSTAT, climate.DOMAIN: TYPE_THERMOSTAT,
cover.DOMAIN: TYPE_BLINDS, cover.DOMAIN: TYPE_BLINDS,
@ -130,9 +133,9 @@ DOMAIN_TO_GOOGLE_TYPES = {
media_player.DOMAIN: TYPE_SETTOP, media_player.DOMAIN: TYPE_SETTOP,
scene.DOMAIN: TYPE_SCENE, scene.DOMAIN: TYPE_SCENE,
script.DOMAIN: TYPE_SCENE, script.DOMAIN: TYPE_SCENE,
select.DOMAIN: TYPE_SENSOR,
switch.DOMAIN: TYPE_SWITCH, switch.DOMAIN: TYPE_SWITCH,
vacuum.DOMAIN: TYPE_VACUUM, vacuum.DOMAIN: TYPE_VACUUM,
alarm_control_panel.DOMAIN: TYPE_ALARM,
} }
DEVICE_CLASS_TO_GOOGLE_TYPES = { DEVICE_CLASS_TO_GOOGLE_TYPES = {

View File

@ -17,6 +17,7 @@ from homeassistant.components import (
media_player, media_player,
scene, scene,
script, script,
select,
sensor, sensor,
switch, switch,
vacuum, vacuum,
@ -1384,6 +1385,9 @@ class ModesTrait(_Trait):
if domain == input_select.DOMAIN: if domain == input_select.DOMAIN:
return True return True
if domain == select.DOMAIN:
return True
if domain == humidifier.DOMAIN and features & humidifier.SUPPORT_MODES: if domain == humidifier.DOMAIN and features & humidifier.SUPPORT_MODES:
return True return True
@ -1427,6 +1431,7 @@ class ModesTrait(_Trait):
(fan.DOMAIN, fan.ATTR_PRESET_MODES, "preset mode"), (fan.DOMAIN, fan.ATTR_PRESET_MODES, "preset mode"),
(media_player.DOMAIN, media_player.ATTR_SOUND_MODE_LIST, "sound mode"), (media_player.DOMAIN, media_player.ATTR_SOUND_MODE_LIST, "sound mode"),
(input_select.DOMAIN, input_select.ATTR_OPTIONS, "option"), (input_select.DOMAIN, input_select.ATTR_OPTIONS, "option"),
(select.DOMAIN, select.ATTR_OPTIONS, "option"),
(humidifier.DOMAIN, humidifier.ATTR_AVAILABLE_MODES, "mode"), (humidifier.DOMAIN, humidifier.ATTR_AVAILABLE_MODES, "mode"),
(light.DOMAIN, light.ATTR_EFFECT_LIST, "effect"), (light.DOMAIN, light.ATTR_EFFECT_LIST, "effect"),
): ):
@ -1459,6 +1464,8 @@ class ModesTrait(_Trait):
mode_settings["sound mode"] = attrs.get(media_player.ATTR_SOUND_MODE) mode_settings["sound mode"] = attrs.get(media_player.ATTR_SOUND_MODE)
elif self.state.domain == input_select.DOMAIN: elif self.state.domain == input_select.DOMAIN:
mode_settings["option"] = self.state.state mode_settings["option"] = self.state.state
elif self.state.domain == select.DOMAIN:
mode_settings["option"] = self.state.state
elif self.state.domain == humidifier.DOMAIN: elif self.state.domain == humidifier.DOMAIN:
if ATTR_MODE in attrs: if ATTR_MODE in attrs:
mode_settings["mode"] = attrs.get(ATTR_MODE) mode_settings["mode"] = attrs.get(ATTR_MODE)
@ -1503,6 +1510,20 @@ class ModesTrait(_Trait):
) )
return return
if self.state.domain == select.DOMAIN:
option = settings["option"]
await self.hass.services.async_call(
select.DOMAIN,
select.SERVICE_SELECT_OPTION,
{
ATTR_ENTITY_ID: self.state.entity_id,
select.ATTR_OPTION: option,
},
blocking=True,
context=data.context,
)
return
if self.state.domain == humidifier.DOMAIN: if self.state.domain == humidifier.DOMAIN:
requested_mode = settings["mode"] requested_mode = settings["mode"]
await self.hass.services.async_call( await self.hass.services.async_call(

View File

@ -41,6 +41,7 @@ BASE_PLATFORMS = {
"notify", "notify",
"remote", "remote",
"scene", "scene",
"select",
"sensor", "sensor",
"switch", "switch",
"tts", "tts",

View File

@ -18,6 +18,7 @@ from homeassistant.components import (
media_player, media_player,
scene, scene,
script, script,
select,
sensor, sensor,
switch, switch,
vacuum, vacuum,
@ -1799,6 +1800,80 @@ async def test_modes_input_select(hass):
assert calls[0].data == {"entity_id": "input_select.bla", "option": "xyz"} assert calls[0].data == {"entity_id": "input_select.bla", "option": "xyz"}
async def test_modes_select(hass):
"""Test Select Mode trait."""
assert helpers.get_google_type(select.DOMAIN, None) is not None
assert trait.ModesTrait.supported(select.DOMAIN, None, None, None)
trt = trait.ModesTrait(
hass,
State("select.bla", "unavailable"),
BASIC_CONFIG,
)
assert trt.sync_attributes() == {"availableModes": []}
trt = trait.ModesTrait(
hass,
State(
"select.bla",
"abc",
attributes={select.ATTR_OPTIONS: ["abc", "123", "xyz"]},
),
BASIC_CONFIG,
)
attribs = trt.sync_attributes()
assert attribs == {
"availableModes": [
{
"name": "option",
"name_values": [
{
"name_synonym": ["option", "setting", "mode", "value"],
"lang": "en",
}
],
"settings": [
{
"setting_name": "abc",
"setting_values": [{"setting_synonym": ["abc"], "lang": "en"}],
},
{
"setting_name": "123",
"setting_values": [{"setting_synonym": ["123"], "lang": "en"}],
},
{
"setting_name": "xyz",
"setting_values": [{"setting_synonym": ["xyz"], "lang": "en"}],
},
],
"ordered": False,
}
]
}
assert trt.query_attributes() == {
"currentModeSettings": {"option": "abc"},
"on": True,
}
assert trt.can_execute(
trait.COMMAND_MODES,
params={"updateModeSettings": {"option": "xyz"}},
)
calls = async_mock_service(hass, select.DOMAIN, select.SERVICE_SELECT_OPTION)
await trt.execute(
trait.COMMAND_MODES,
BASIC_DATA,
{"updateModeSettings": {"option": "xyz"}},
{},
)
assert len(calls) == 1
assert calls[0].data == {"entity_id": "select.bla", "option": "xyz"}
async def test_modes_humidifier(hass): async def test_modes_humidifier(hass):
"""Test Humidifier Mode trait.""" """Test Humidifier Mode trait."""
assert helpers.get_google_type(humidifier.DOMAIN, None) is not None assert helpers.get_google_type(humidifier.DOMAIN, None) is not None