From c8d9b83b24107b35b68abe1ab8c61f005c9b239b Mon Sep 17 00:00:00 2001 From: ochlocracy <5885236+ochlocracy@users.noreply.github.com> Date: Mon, 3 Feb 2020 17:20:39 -0500 Subject: [PATCH] Update StepSpeaker and Speaker interfaces in Alexa (#31444) * Yield only one Speaker interface. * Yield PowerController only is supported. * Revert "Yield PowerController only is supported." This reverts commit c0dbf7e4 * Add Alexa.Speaker interface properties. * Refactor tests for Alexa.Speaker and Alexa.StepSpeaker. * Code Smell Change. * Fix R1705: Unnecessary "elif" after "return". --- .../components/alexa/capabilities.py | 38 +++ homeassistant/components/alexa/entities.py | 7 +- tests/components/alexa/test_smart_home.py | 221 ++++++++++-------- 3 files changed, 165 insertions(+), 101 deletions(-) diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index eb1474aed7e..eba59e92cd4 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -1,5 +1,6 @@ """Alexa capabilities.""" import logging +import math from homeassistant.components import ( cover, @@ -645,6 +646,43 @@ class AlexaSpeaker(AlexaCapability): """Return the Alexa API name of this interface.""" return "Alexa.Speaker" + def properties_supported(self): + """Return what properties this entity supports.""" + properties = [{"name": "volume"}] + + supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + if supported & media_player.SUPPORT_VOLUME_MUTE: + properties.append({"name": "muted"}) + + return properties + + def properties_proactively_reported(self): + """Return True if properties asynchronously reported.""" + return True + + def properties_retrievable(self): + """Return True if properties can be retrieved.""" + return True + + def get_property(self, name): + """Read and return a property.""" + if name == "volume": + current_level = self.entity.attributes.get( + media_player.ATTR_MEDIA_VOLUME_LEVEL + ) + try: + current = math.floor(int(current_level * 100)) + except ZeroDivisionError: + current = 0 + return current + + if name == "muted": + return bool( + self.entity.attributes.get(media_player.ATTR_MEDIA_VOLUME_MUTED) + ) + + return None + class AlexaStepSpeaker(AlexaCapability): """Implements Alexa.StepSpeaker. diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index 254cec44553..8801936b35b 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -508,12 +508,7 @@ class MediaPlayerCapabilities(AlexaEntity): supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) if supported & media_player.const.SUPPORT_VOLUME_SET: yield AlexaSpeaker(self.entity) - - step_volume_features = ( - media_player.const.SUPPORT_VOLUME_MUTE - | media_player.const.SUPPORT_VOLUME_STEP - ) - if supported & step_volume_features: + elif supported & media_player.const.SUPPORT_VOLUME_STEP: yield AlexaStepSpeaker(self.entity) playback_features = ( diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index ca6b1e1ccb6..3e8e87f62c9 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -16,6 +16,7 @@ from homeassistant.components.media_player.const import ( SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_STEP, ) import homeassistant.components.vacuum as vacuum from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT @@ -904,7 +905,6 @@ async def test_media_player(hass): "Alexa.PlaybackStateReporter", "Alexa.PowerController", "Alexa.Speaker", - "Alexa.StepSpeaker", ) playback_capability = get_capability(capabilities, "Alexa.PlaybackController") @@ -958,93 +958,6 @@ async def test_media_player(hass): hass, ) - call, _ = await assert_request_calls_service( - "Alexa.Speaker", - "SetVolume", - "media_player#test", - "media_player.volume_set", - hass, - payload={"volume": 50}, - ) - assert call.data["volume_level"] == 0.5 - - call, _ = await assert_request_calls_service( - "Alexa.Speaker", - "SetMute", - "media_player#test", - "media_player.volume_mute", - hass, - payload={"mute": True}, - ) - assert call.data["is_volume_muted"] - - call, _, = await assert_request_calls_service( - "Alexa.Speaker", - "SetMute", - "media_player#test", - "media_player.volume_mute", - hass, - payload={"mute": False}, - ) - assert not call.data["is_volume_muted"] - - await assert_percentage_changes( - hass, - [(0.7, "-5"), (0.8, "5"), (0, "-80")], - "Alexa.Speaker", - "AdjustVolume", - "media_player#test", - "volume", - "media_player.volume_set", - "volume_level", - ) - - call, _ = await assert_request_calls_service( - "Alexa.StepSpeaker", - "SetMute", - "media_player#test", - "media_player.volume_mute", - hass, - payload={"mute": True}, - ) - assert call.data["is_volume_muted"] - - call, _, = await assert_request_calls_service( - "Alexa.StepSpeaker", - "SetMute", - "media_player#test", - "media_player.volume_mute", - hass, - payload={"mute": False}, - ) - assert not call.data["is_volume_muted"] - - call, _ = await assert_request_calls_service( - "Alexa.StepSpeaker", - "AdjustVolume", - "media_player#test", - "media_player.volume_up", - hass, - payload={"volumeSteps": 1, "volumeStepsDefault": False}, - ) - - call, _ = await assert_request_calls_service( - "Alexa.StepSpeaker", - "AdjustVolume", - "media_player#test", - "media_player.volume_down", - hass, - payload={"volumeSteps": -1, "volumeStepsDefault": False}, - ) - - call, _ = await assert_request_calls_service( - "Alexa.StepSpeaker", - "AdjustVolume", - "media_player#test", - "media_player.volume_up", - hass, - payload={"volumeSteps": 10, "volumeStepsDefault": True}, - ) call, _ = await assert_request_calls_service( "Alexa.ChannelController", "ChangeChannel", @@ -1140,7 +1053,6 @@ async def test_media_player_power(hass): "Alexa.PowerController", "Alexa.SeekController", "Alexa.Speaker", - "Alexa.StepSpeaker", ) await assert_request_calls_service( @@ -1265,22 +1177,141 @@ async def test_media_player_inputs(hass): async def test_media_player_speaker(hass): - """Test media player discovery with device class speaker.""" + """Test media player with speaker interface.""" device = ( - "media_player.test", + "media_player.test_speaker", "off", { - "friendly_name": "Test media player", - "supported_features": 51765, + "friendly_name": "Test media player speaker", + "supported_features": SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET, "volume_level": 0.75, "device_class": "speaker", }, ) appliance = await discovery_test(device, hass) - assert appliance["endpointId"] == "media_player#test" + assert appliance["endpointId"] == "media_player#test_speaker" assert appliance["displayCategories"][0] == "SPEAKER" - assert appliance["friendlyName"] == "Test media player" + assert appliance["friendlyName"] == "Test media player speaker" + + capabilities = assert_endpoint_capabilities( + appliance, + "Alexa", + "Alexa.EndpointHealth", + "Alexa.PowerController", + "Alexa.Speaker", + ) + + speaker_capability = get_capability(capabilities, "Alexa.Speaker") + properties = speaker_capability["properties"] + assert {"name": "volume"} in properties["supported"] + assert {"name": "muted"} in properties["supported"] + + call, _ = await assert_request_calls_service( + "Alexa.Speaker", + "SetVolume", + "media_player#test_speaker", + "media_player.volume_set", + hass, + payload={"volume": 50}, + ) + assert call.data["volume_level"] == 0.5 + + call, _ = await assert_request_calls_service( + "Alexa.Speaker", + "SetMute", + "media_player#test_speaker", + "media_player.volume_mute", + hass, + payload={"mute": True}, + ) + assert call.data["is_volume_muted"] + + call, _, = await assert_request_calls_service( + "Alexa.Speaker", + "SetMute", + "media_player#test_speaker", + "media_player.volume_mute", + hass, + payload={"mute": False}, + ) + assert not call.data["is_volume_muted"] + + await assert_percentage_changes( + hass, + [(0.7, "-5"), (0.8, "5"), (0, "-80")], + "Alexa.Speaker", + "AdjustVolume", + "media_player#test_speaker", + "volume", + "media_player.volume_set", + "volume_level", + ) + + +async def test_media_player_step_speaker(hass): + """Test media player with step speaker interface.""" + device = ( + "media_player.test_step_speaker", + "off", + { + "friendly_name": "Test media player step speaker", + "supported_features": SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_STEP, + "device_class": "speaker", + }, + ) + appliance = await discovery_test(device, hass) + + assert appliance["endpointId"] == "media_player#test_step_speaker" + assert appliance["displayCategories"][0] == "SPEAKER" + assert appliance["friendlyName"] == "Test media player step speaker" + + call, _ = await assert_request_calls_service( + "Alexa.StepSpeaker", + "SetMute", + "media_player#test_step_speaker", + "media_player.volume_mute", + hass, + payload={"mute": True}, + ) + assert call.data["is_volume_muted"] + + call, _, = await assert_request_calls_service( + "Alexa.StepSpeaker", + "SetMute", + "media_player#test_step_speaker", + "media_player.volume_mute", + hass, + payload={"mute": False}, + ) + assert not call.data["is_volume_muted"] + + call, _ = await assert_request_calls_service( + "Alexa.StepSpeaker", + "AdjustVolume", + "media_player#test_step_speaker", + "media_player.volume_up", + hass, + payload={"volumeSteps": 1, "volumeStepsDefault": False}, + ) + + call, _ = await assert_request_calls_service( + "Alexa.StepSpeaker", + "AdjustVolume", + "media_player#test_step_speaker", + "media_player.volume_down", + hass, + payload={"volumeSteps": -1, "volumeStepsDefault": False}, + ) + + call, _ = await assert_request_calls_service( + "Alexa.StepSpeaker", + "AdjustVolume", + "media_player#test_step_speaker", + "media_player.volume_up", + hass, + payload={"volumeSteps": 10, "volumeStepsDefault": True}, + ) async def test_media_player_seek(hass):