1
mirror of https://github.com/home-assistant/core synced 2024-09-15 17:29:45 +02:00

Implement EqualizerController in Alexa for media_player. (#30159)

This commit is contained in:
ochlocracy 2019-12-24 17:06:39 -05:00 committed by GitHub
parent 4ea42c2479
commit 25f78dd1a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 248 additions and 1 deletions

View File

@ -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

View File

@ -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",
}

View File

@ -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)

View File

@ -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)

View File

@ -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"