Implement EqualizerController in Alexa for media_player. (#30159)
This commit is contained in:
parent
4ea42c2479
commit
25f78dd1a9
5 changed files with 248 additions and 1 deletions
|
@ -120,7 +120,20 @@ class AlexaCapability:
|
|||
|
||||
@staticmethod
|
||||
def configuration():
|
||||
"""Return the configuration object."""
|
||||
"""Return the configuration object.
|
||||
|
||||
Applicable to the ThermostatController, SecurityControlPanel, ModeController, RangeController,
|
||||
and EventDetectionSensor.
|
||||
"""
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def configurations():
|
||||
"""Return the configurations object.
|
||||
|
||||
The plural configurations object is different that the singular configuration object.
|
||||
Applicable to EqualizerController interface.
|
||||
"""
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
|
@ -177,6 +190,11 @@ class AlexaCapability:
|
|||
if configuration:
|
||||
result["configuration"] = configuration
|
||||
|
||||
# The plural configurations object is different than the singular configuration object above.
|
||||
configurations = self.configurations()
|
||||
if configurations:
|
||||
result["configurations"] = configurations
|
||||
|
||||
semantics = self.semantics()
|
||||
if semantics:
|
||||
result["semantics"] = semantics
|
||||
|
@ -1356,3 +1374,55 @@ class AlexaEventDetectionSensor(AlexaCapability):
|
|||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class AlexaEqualizerController(AlexaCapability):
|
||||
"""Implements Alexa.EqualizerController.
|
||||
|
||||
https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-equalizercontroller.html
|
||||
"""
|
||||
|
||||
def name(self):
|
||||
"""Return the Alexa API name of this interface."""
|
||||
return "Alexa.EqualizerController"
|
||||
|
||||
def properties_supported(self):
|
||||
"""Return what properties this entity supports.
|
||||
|
||||
Either bands, mode or both can be specified. Only mode is supported at this time.
|
||||
"""
|
||||
return [{"name": "mode"}]
|
||||
|
||||
def get_property(self, name):
|
||||
"""Read and return a property."""
|
||||
if name != "mode":
|
||||
raise UnsupportedProperty(name)
|
||||
|
||||
sound_mode = self.entity.attributes.get(media_player.ATTR_SOUND_MODE)
|
||||
if sound_mode and sound_mode.upper() in (
|
||||
"MOVIE",
|
||||
"MUSIC",
|
||||
"NIGHT",
|
||||
"SPORT",
|
||||
"TV",
|
||||
):
|
||||
return sound_mode.upper()
|
||||
|
||||
return None
|
||||
|
||||
def configurations(self):
|
||||
"""Return the sound modes supported in the configurations object.
|
||||
|
||||
Valid Values for modes are: MOVIE, MUSIC, NIGHT, SPORT, TV.
|
||||
"""
|
||||
configurations = None
|
||||
sound_mode_list = self.entity.attributes.get(media_player.ATTR_SOUND_MODE_LIST)
|
||||
if sound_mode_list:
|
||||
supported_sound_modes = []
|
||||
for sound_mode in sound_mode_list:
|
||||
if sound_mode.upper() in ("MOVIE", "MUSIC", "NIGHT", "SPORT", "TV"):
|
||||
supported_sound_modes.append({"name": sound_mode.upper()})
|
||||
|
||||
configurations = {"modes": {"supported": supported_sound_modes}}
|
||||
|
||||
return configurations
|
||||
|
|
|
@ -196,3 +196,11 @@ class Inputs:
|
|||
"video3": "VIDEO 3",
|
||||
"xbox": "XBOX",
|
||||
}
|
||||
|
||||
VALID_SOUND_MODE_MAP = {
|
||||
"movie": "MOVIE",
|
||||
"music": "MUSIC",
|
||||
"night": "NIGHT",
|
||||
"sport": "SPORT",
|
||||
"tv": "TV",
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ from .capabilities import (
|
|||
AlexaContactSensor,
|
||||
AlexaDoorbellEventSource,
|
||||
AlexaEndpointHealth,
|
||||
AlexaEqualizerController,
|
||||
AlexaEventDetectionSensor,
|
||||
AlexaInputController,
|
||||
AlexaLockController,
|
||||
|
@ -522,6 +523,9 @@ class MediaPlayerCapabilities(AlexaEntity):
|
|||
if supported & media_player.const.SUPPORT_PLAY_MEDIA:
|
||||
yield AlexaChannelController(self.entity)
|
||||
|
||||
if supported & media_player.const.SUPPORT_SELECT_SOUND_MODE:
|
||||
yield AlexaEqualizerController(self.entity)
|
||||
|
||||
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||
yield Alexa(self.hass)
|
||||
|
||||
|
|
|
@ -1352,3 +1352,43 @@ async def async_api_seek(hass, config, directive, context):
|
|||
return directive.response(
|
||||
name="StateReport", namespace="Alexa.SeekController", payload=payload
|
||||
)
|
||||
|
||||
|
||||
@HANDLERS.register(("Alexa.EqualizerController", "SetMode"))
|
||||
async def async_api_set_eq_mode(hass, config, directive, context):
|
||||
"""Process a SetMode request for EqualizerController."""
|
||||
mode = directive.payload["mode"]
|
||||
entity = directive.entity
|
||||
data = {ATTR_ENTITY_ID: entity.entity_id}
|
||||
|
||||
sound_mode_list = entity.attributes.get(media_player.const.ATTR_SOUND_MODE_LIST)
|
||||
if sound_mode_list and mode.lower() in sound_mode_list:
|
||||
data[media_player.const.ATTR_SOUND_MODE] = mode.lower()
|
||||
else:
|
||||
msg = "failed to map sound mode {} to a mode on {}".format(
|
||||
mode, entity.entity_id
|
||||
)
|
||||
raise AlexaInvalidValueError(msg)
|
||||
|
||||
await hass.services.async_call(
|
||||
entity.domain,
|
||||
media_player.SERVICE_SELECT_SOUND_MODE,
|
||||
data,
|
||||
blocking=False,
|
||||
context=context,
|
||||
)
|
||||
|
||||
return directive.response()
|
||||
|
||||
|
||||
@HANDLERS.register(("Alexa.EqualizerController", "AdjustBands"))
|
||||
@HANDLERS.register(("Alexa.EqualizerController", "ResetBands"))
|
||||
@HANDLERS.register(("Alexa.EqualizerController", "SetBands"))
|
||||
async def async_api_bands_directive(hass, config, directive, context):
|
||||
"""Handle an AdjustBands, ResetBands, SetBands request.
|
||||
|
||||
Only mode directives are currently supported for the EqualizerController.
|
||||
"""
|
||||
# Currently bands directives are not supported.
|
||||
msg = "Entity does not support directive"
|
||||
raise AlexaInvalidDirectiveError(msg)
|
||||
|
|
|
@ -9,6 +9,7 @@ from homeassistant.components.media_player.const import (
|
|||
SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
SUPPORT_SEEK,
|
||||
SUPPORT_SELECT_SOUND_MODE,
|
||||
SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_TURN_OFF,
|
||||
|
@ -2912,3 +2913,127 @@ async def test_input_number_float(hass):
|
|||
"value",
|
||||
instance="input_number.value",
|
||||
)
|
||||
|
||||
|
||||
async def test_media_player_eq_modes(hass):
|
||||
"""Test media player discovery with sound mode list."""
|
||||
device = (
|
||||
"media_player.test",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Test media player",
|
||||
"supported_features": SUPPORT_SELECT_SOUND_MODE,
|
||||
"sound_mode": "tv",
|
||||
"sound_mode_list": ["movie", "music", "night", "sport", "tv", "rocknroll"],
|
||||
},
|
||||
)
|
||||
appliance = await discovery_test(device, hass)
|
||||
|
||||
assert appliance["endpointId"] == "media_player#test"
|
||||
assert appliance["friendlyName"] == "Test media player"
|
||||
|
||||
capabilities = assert_endpoint_capabilities(
|
||||
appliance,
|
||||
"Alexa",
|
||||
"Alexa.EqualizerController",
|
||||
"Alexa.PowerController",
|
||||
"Alexa.EndpointHealth",
|
||||
)
|
||||
|
||||
eq_capability = get_capability(capabilities, "Alexa.EqualizerController")
|
||||
assert eq_capability is not None
|
||||
assert "modes" in eq_capability["configurations"]
|
||||
|
||||
eq_modes = eq_capability["configurations"]["modes"]
|
||||
assert {"name": "rocknroll"} not in eq_modes["supported"]
|
||||
assert {"name": "ROCKNROLL"} not in eq_modes["supported"]
|
||||
|
||||
for mode in ("MOVIE", "MUSIC", "NIGHT", "SPORT", "TV"):
|
||||
assert {"name": mode} in eq_modes["supported"]
|
||||
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.EqualizerController",
|
||||
"SetMode",
|
||||
"media_player#test",
|
||||
"media_player.select_sound_mode",
|
||||
hass,
|
||||
payload={"mode": mode},
|
||||
)
|
||||
assert call.data["sound_mode"] == mode.lower()
|
||||
|
||||
|
||||
async def test_media_player_sound_mode_list_none(hass):
|
||||
"""Test EqualizerController bands directive not supported."""
|
||||
device = (
|
||||
"media_player.test",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Test media player",
|
||||
"supported_features": SUPPORT_SELECT_SOUND_MODE,
|
||||
"sound_mode": "unknown",
|
||||
"sound_mode_list": None,
|
||||
},
|
||||
)
|
||||
appliance = await discovery_test(device, hass)
|
||||
assert appliance["endpointId"] == "media_player#test"
|
||||
assert appliance["friendlyName"] == "Test media player"
|
||||
|
||||
|
||||
async def test_media_player_eq_bands_not_supported(hass):
|
||||
"""Test EqualizerController bands directive not supported."""
|
||||
device = (
|
||||
"media_player.test_bands",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Test media player",
|
||||
"supported_features": SUPPORT_SELECT_SOUND_MODE,
|
||||
"sound_mode": "tv",
|
||||
"sound_mode_list": ["movie", "music", "night", "sport", "tv", "rocknroll"],
|
||||
},
|
||||
)
|
||||
await discovery_test(device, hass)
|
||||
|
||||
context = Context()
|
||||
|
||||
# Test for SetBands Error
|
||||
request = get_new_request(
|
||||
"Alexa.EqualizerController", "SetBands", "media_player#test_bands"
|
||||
)
|
||||
request["directive"]["payload"] = {"bands": [{"name": "BASS", "value": -2}]}
|
||||
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
||||
|
||||
assert "event" in msg
|
||||
msg = msg["event"]
|
||||
assert msg["header"]["name"] == "ErrorResponse"
|
||||
assert msg["header"]["namespace"] == "Alexa"
|
||||
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
||||
|
||||
# Test for AdjustBands Error
|
||||
request = get_new_request(
|
||||
"Alexa.EqualizerController", "AdjustBands", "media_player#test_bands"
|
||||
)
|
||||
request["directive"]["payload"] = {
|
||||
"bands": [{"name": "BASS", "levelDelta": 3, "levelDirection": "UP"}]
|
||||
}
|
||||
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
||||
|
||||
assert "event" in msg
|
||||
msg = msg["event"]
|
||||
assert msg["header"]["name"] == "ErrorResponse"
|
||||
assert msg["header"]["namespace"] == "Alexa"
|
||||
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
||||
|
||||
# Test for ResetBands Error
|
||||
request = get_new_request(
|
||||
"Alexa.EqualizerController", "ResetBands", "media_player#test_bands"
|
||||
)
|
||||
request["directive"]["payload"] = {
|
||||
"bands": [{"name": "BASS", "levelDelta": 3, "levelDirection": "UP"}]
|
||||
}
|
||||
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
||||
|
||||
assert "event" in msg
|
||||
msg = msg["event"]
|
||||
assert msg["header"]["name"] == "ErrorResponse"
|
||||
assert msg["header"]["namespace"] == "Alexa"
|
||||
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
||||
|
|
Loading…
Add table
Reference in a new issue