Add support for InputSelector trait (#35753)
This commit is contained in:
parent
2354d0117b
commit
6fa04aa3e3
4 changed files with 81 additions and 74 deletions
|
@ -61,7 +61,6 @@ YOUTUBE_PLAYER_SUPPORT = (
|
|||
| SUPPORT_PLAY
|
||||
| SUPPORT_SHUFFLE_SET
|
||||
| SUPPORT_SELECT_SOUND_MODE
|
||||
| SUPPORT_SELECT_SOURCE
|
||||
| SUPPORT_SEEK
|
||||
)
|
||||
|
||||
|
@ -397,6 +396,7 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
|
|||
self._cur_episode = 1
|
||||
self._episode_count = 13
|
||||
self._source = "dvd"
|
||||
self._source_list = ["dvd", "youtube"]
|
||||
|
||||
@property
|
||||
def media_content_id(self):
|
||||
|
@ -448,6 +448,11 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
|
|||
"""Return the current input source."""
|
||||
return self._source
|
||||
|
||||
@property
|
||||
def source_list(self):
|
||||
"""List of available sources."""
|
||||
return self._source_list
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag media player features that are supported."""
|
||||
|
|
|
@ -86,6 +86,7 @@ TRAIT_TEMPERATURE_SETTING = f"{PREFIX_TRAITS}TemperatureSetting"
|
|||
TRAIT_LOCKUNLOCK = f"{PREFIX_TRAITS}LockUnlock"
|
||||
TRAIT_FANSPEED = f"{PREFIX_TRAITS}FanSpeed"
|
||||
TRAIT_MODES = f"{PREFIX_TRAITS}Modes"
|
||||
TRAIT_INPUTSELECTOR = f"{PREFIX_TRAITS}InputSelector"
|
||||
TRAIT_OPENCLOSE = f"{PREFIX_TRAITS}OpenClose"
|
||||
TRAIT_VOLUME = f"{PREFIX_TRAITS}Volume"
|
||||
TRAIT_ARMDISARM = f"{PREFIX_TRAITS}ArmDisarm"
|
||||
|
@ -112,6 +113,7 @@ COMMAND_THERMOSTAT_SET_MODE = f"{PREFIX_COMMANDS}ThermostatSetMode"
|
|||
COMMAND_LOCKUNLOCK = f"{PREFIX_COMMANDS}LockUnlock"
|
||||
COMMAND_FANSPEED = f"{PREFIX_COMMANDS}SetFanSpeed"
|
||||
COMMAND_MODES = f"{PREFIX_COMMANDS}SetModes"
|
||||
COMMAND_INPUT = f"{PREFIX_COMMANDS}SetInput"
|
||||
COMMAND_OPENCLOSE = f"{PREFIX_COMMANDS}OpenClose"
|
||||
COMMAND_SET_VOLUME = f"{PREFIX_COMMANDS}setVolume"
|
||||
COMMAND_VOLUME_RELATIVE = f"{PREFIX_COMMANDS}volumeRelative"
|
||||
|
@ -1213,7 +1215,6 @@ class ModesTrait(_Trait):
|
|||
commands = [COMMAND_MODES]
|
||||
|
||||
SYNONYMS = {
|
||||
"input source": ["input source", "input", "source"],
|
||||
"sound mode": ["sound mode", "effects"],
|
||||
"option": ["option", "setting", "mode", "value"],
|
||||
}
|
||||
|
@ -1230,10 +1231,7 @@ class ModesTrait(_Trait):
|
|||
if domain != media_player.DOMAIN:
|
||||
return False
|
||||
|
||||
return (
|
||||
features & media_player.SUPPORT_SELECT_SOURCE
|
||||
or features & media_player.SUPPORT_SELECT_SOUND_MODE
|
||||
)
|
||||
return features & media_player.SUPPORT_SELECT_SOUND_MODE
|
||||
|
||||
def sync_attributes(self):
|
||||
"""Return mode attributes for a sync request."""
|
||||
|
@ -1266,13 +1264,6 @@ class ModesTrait(_Trait):
|
|||
attrs = self.state.attributes
|
||||
modes = []
|
||||
if self.state.domain == media_player.DOMAIN:
|
||||
if media_player.ATTR_INPUT_SOURCE_LIST in attrs:
|
||||
modes.append(
|
||||
_generate(
|
||||
"input source", attrs[media_player.ATTR_INPUT_SOURCE_LIST]
|
||||
)
|
||||
)
|
||||
|
||||
if media_player.ATTR_SOUND_MODE_LIST in attrs:
|
||||
modes.append(
|
||||
_generate("sound mode", attrs[media_player.ATTR_SOUND_MODE_LIST])
|
||||
|
@ -1294,11 +1285,6 @@ class ModesTrait(_Trait):
|
|||
mode_settings = {}
|
||||
|
||||
if self.state.domain == media_player.DOMAIN:
|
||||
if media_player.ATTR_INPUT_SOURCE_LIST in attrs:
|
||||
mode_settings["input source"] = attrs.get(
|
||||
media_player.ATTR_INPUT_SOURCE
|
||||
)
|
||||
|
||||
if media_player.ATTR_SOUND_MODE_LIST in attrs:
|
||||
mode_settings["sound mode"] = attrs.get(media_player.ATTR_SOUND_MODE)
|
||||
elif self.state.domain == input_select.DOMAIN:
|
||||
|
@ -1352,21 +1338,8 @@ class ModesTrait(_Trait):
|
|||
)
|
||||
return
|
||||
|
||||
requested_source = settings.get("input source")
|
||||
sound_mode = settings.get("sound mode")
|
||||
|
||||
if requested_source:
|
||||
await self.hass.services.async_call(
|
||||
media_player.DOMAIN,
|
||||
media_player.SERVICE_SELECT_SOURCE,
|
||||
{
|
||||
ATTR_ENTITY_ID: self.state.entity_id,
|
||||
media_player.ATTR_INPUT_SOURCE: requested_source,
|
||||
},
|
||||
blocking=True,
|
||||
context=data.context,
|
||||
)
|
||||
|
||||
if sound_mode:
|
||||
await self.hass.services.async_call(
|
||||
media_player.DOMAIN,
|
||||
|
@ -1380,6 +1353,61 @@ class ModesTrait(_Trait):
|
|||
)
|
||||
|
||||
|
||||
@register_trait
|
||||
class InputSelectorTrait(_Trait):
|
||||
"""Trait to set modes.
|
||||
|
||||
https://developers.google.com/assistant/smarthome/traits/inputselector
|
||||
"""
|
||||
|
||||
name = TRAIT_INPUTSELECTOR
|
||||
commands = [COMMAND_INPUT]
|
||||
|
||||
SYNONYMS = {}
|
||||
|
||||
@staticmethod
|
||||
def supported(domain, features, device_class):
|
||||
"""Test if state is supported."""
|
||||
if domain == media_player.DOMAIN and (
|
||||
features & media_player.SUPPORT_SELECT_SOURCE
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def sync_attributes(self):
|
||||
"""Return mode attributes for a sync request."""
|
||||
attrs = self.state.attributes
|
||||
inputs = [
|
||||
{"key": source, "names": [{"name_synonym": [source], "lang": "en"}]}
|
||||
for source in attrs.get(media_player.ATTR_INPUT_SOURCE_LIST, [])
|
||||
]
|
||||
|
||||
payload = {"availableInputs": inputs, "orderedInputs": True}
|
||||
|
||||
return payload
|
||||
|
||||
def query_attributes(self):
|
||||
"""Return current modes."""
|
||||
attrs = self.state.attributes
|
||||
return {"currentInput": attrs.get(media_player.ATTR_INPUT_SOURCE, "")}
|
||||
|
||||
async def execute(self, command, data, params, challenge):
|
||||
"""Execute an SetInputSource command."""
|
||||
requested_source = params.get("newInput")
|
||||
|
||||
await self.hass.services.async_call(
|
||||
media_player.DOMAIN,
|
||||
media_player.SERVICE_SELECT_SOURCE,
|
||||
{
|
||||
ATTR_ENTITY_ID: self.state.entity_id,
|
||||
media_player.ATTR_INPUT_SOURCE: requested_source,
|
||||
},
|
||||
blocking=True,
|
||||
context=data.context,
|
||||
)
|
||||
|
||||
|
||||
@register_trait
|
||||
class OpenCloseTrait(_Trait):
|
||||
"""Trait to open and close a cover.
|
||||
|
|
|
@ -190,6 +190,7 @@ DEMO_DEVICES = [
|
|||
"id": "media_player.lounge_room",
|
||||
"name": {"name": "Lounge room"},
|
||||
"traits": [
|
||||
"action.devices.traits.InputSelector",
|
||||
"action.devices.traits.OnOff",
|
||||
"action.devices.traits.Modes",
|
||||
"action.devices.traits.TransportControl",
|
||||
|
|
|
@ -1313,14 +1313,14 @@ async def test_fan_speed(hass):
|
|||
assert calls[0].data == {"entity_id": "fan.living_room_fan", "speed": "medium"}
|
||||
|
||||
|
||||
async def test_modes_media_player(hass):
|
||||
"""Test Media Player Mode trait."""
|
||||
async def test_inputselector(hass):
|
||||
"""Test input selector trait."""
|
||||
assert helpers.get_google_type(media_player.DOMAIN, None) is not None
|
||||
assert trait.ModesTrait.supported(
|
||||
assert trait.InputSelectorTrait.supported(
|
||||
media_player.DOMAIN, media_player.SUPPORT_SELECT_SOURCE, None
|
||||
)
|
||||
|
||||
trt = trait.ModesTrait(
|
||||
trt = trait.InputSelectorTrait(
|
||||
hass,
|
||||
State(
|
||||
"media_player.living_room",
|
||||
|
@ -1340,56 +1340,29 @@ async def test_modes_media_player(hass):
|
|||
|
||||
attribs = trt.sync_attributes()
|
||||
assert attribs == {
|
||||
"availableModes": [
|
||||
"availableInputs": [
|
||||
{"key": "media", "names": [{"name_synonym": ["media"], "lang": "en"}]},
|
||||
{"key": "game", "names": [{"name_synonym": ["game"], "lang": "en"}]},
|
||||
{
|
||||
"name": "input source",
|
||||
"name_values": [
|
||||
{"name_synonym": ["input source", "input", "source"], "lang": "en"}
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"setting_name": "media",
|
||||
"setting_values": [
|
||||
{"setting_synonym": ["media"], "lang": "en"}
|
||||
],
|
||||
},
|
||||
{
|
||||
"setting_name": "game",
|
||||
"setting_values": [{"setting_synonym": ["game"], "lang": "en"}],
|
||||
},
|
||||
{
|
||||
"setting_name": "chromecast",
|
||||
"setting_values": [
|
||||
{"setting_synonym": ["chromecast"], "lang": "en"}
|
||||
],
|
||||
},
|
||||
{
|
||||
"setting_name": "plex",
|
||||
"setting_values": [{"setting_synonym": ["plex"], "lang": "en"}],
|
||||
},
|
||||
],
|
||||
"ordered": False,
|
||||
}
|
||||
]
|
||||
"key": "chromecast",
|
||||
"names": [{"name_synonym": ["chromecast"], "lang": "en"}],
|
||||
},
|
||||
{"key": "plex", "names": [{"name_synonym": ["plex"], "lang": "en"}]},
|
||||
],
|
||||
"orderedInputs": True,
|
||||
}
|
||||
|
||||
assert trt.query_attributes() == {
|
||||
"currentModeSettings": {"input source": "game"},
|
||||
"on": True,
|
||||
"currentInput": "game",
|
||||
}
|
||||
|
||||
assert trt.can_execute(
|
||||
trait.COMMAND_MODES, params={"updateModeSettings": {"input source": "media"}},
|
||||
)
|
||||
assert trt.can_execute(trait.COMMAND_INPUT, params={"newInput": "media"},)
|
||||
|
||||
calls = async_mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOURCE
|
||||
)
|
||||
await trt.execute(
|
||||
trait.COMMAND_MODES,
|
||||
BASIC_DATA,
|
||||
{"updateModeSettings": {"input source": "media"}},
|
||||
{},
|
||||
trait.COMMAND_INPUT, BASIC_DATA, {"newInput": "media"}, {},
|
||||
)
|
||||
|
||||
assert len(calls) == 1
|
||||
|
|
Loading…
Add table
Reference in a new issue