Add mute support to Russound RIO (#134118)

pull/134130/head
Noah Husby 2024-12-28 03:22:13 -05:00 committed by GitHub
parent 6edf06f8a4
commit aceb1b39ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 78 additions and 11 deletions

View File

@ -3,9 +3,6 @@
import asyncio
from aiorussound import CommandError
from aiorussound.const import FeatureFlag
from homeassistant.components.media_player import MediaPlayerEntityFeature
DOMAIN = "russound_rio"
@ -15,7 +12,3 @@ RUSSOUND_RIO_EXCEPTIONS = (
TimeoutError,
asyncio.CancelledError,
)
MP_FEATURES_BY_FLAG = {
FeatureFlag.COMMANDS_ZONE_MUTE_OFF_ON: MediaPlayerEntityFeature.VOLUME_MUTE
}

View File

@ -22,7 +22,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import RussoundConfigEntry
from .const import MP_FEATURES_BY_FLAG
from .entity import RussoundBaseEntity, command
_LOGGER = logging.getLogger(__name__)
@ -54,6 +53,7 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
_attr_supported_features = (
MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.TURN_ON
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.SELECT_SOURCE
@ -69,9 +69,6 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
self._sources = sources
self._attr_name = _zone.name
self._attr_unique_id = f"{self._primary_mac_address}-{_zone.device_str}"
for flag, feature in MP_FEATURES_BY_FLAG.items():
if flag in self._client.supported_features:
self._attr_supported_features |= feature
@property
def _zone(self) -> ZoneControlSurface:
@ -150,6 +147,11 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
"""
return self._zone.volume / 50.0
@property
def is_volume_muted(self) -> bool:
"""Return whether zone is muted."""
return self._zone.is_mute
@command
async def async_turn_off(self) -> None:
"""Turn off the zone."""
@ -184,3 +186,16 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
async def async_volume_down(self) -> None:
"""Step the volume down."""
await self._zone.volume_down()
@command
async def async_mute_volume(self, mute: bool) -> None:
"""Mute the media player."""
if FeatureFlag.COMMANDS_ZONE_MUTE_OFF_ON in self._client.supported_features:
if mute:
await self._zone.mute()
else:
await self._zone.unmute()
return
if mute != self.is_volume_muted:
await self._zone.toggle_mute()

View File

@ -75,6 +75,9 @@ def mock_russound_client() -> Generator[AsyncMock]:
zone.zone_on = AsyncMock()
zone.zone_off = AsyncMock()
zone.select_source = AsyncMock()
zone.mute = AsyncMock()
zone.unmute = AsyncMock()
zone.toggle_mute = AsyncMock()
client.controllers = {
1: Controller(

View File

@ -2,6 +2,7 @@
from unittest.mock import AsyncMock
from aiorussound.const import FeatureFlag
from aiorussound.exceptions import CommandError
from aiorussound.models import PlayStatus
import pytest
@ -9,6 +10,7 @@ import pytest
from homeassistant.components.media_player import (
ATTR_INPUT_SOURCE,
ATTR_MEDIA_VOLUME_LEVEL,
ATTR_MEDIA_VOLUME_MUTED,
DOMAIN as MP_DOMAIN,
SERVICE_SELECT_SOURCE,
)
@ -17,6 +19,7 @@ from homeassistant.const import (
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
SERVICE_VOLUME_DOWN,
SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_SET,
SERVICE_VOLUME_UP,
STATE_BUFFERING,
@ -106,6 +109,59 @@ async def test_media_volume(
)
async def test_volume_mute(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_russound_client: AsyncMock,
) -> None:
"""Test mute service."""
await setup_integration(hass, mock_config_entry)
# Test mute (w/ toggle mute support)
await hass.services.async_call(
MP_DOMAIN,
SERVICE_VOLUME_MUTE,
{ATTR_ENTITY_ID: ENTITY_ID_ZONE_1, ATTR_MEDIA_VOLUME_MUTED: True},
blocking=True,
)
mock_russound_client.controllers[1].zones[1].toggle_mute.assert_called_once()
mock_russound_client.controllers[1].zones[1].toggle_mute.reset_mock()
mock_russound_client.controllers[1].zones[1].is_mute = True
# Test mute when already muted (w/ toggle mute support)
await hass.services.async_call(
MP_DOMAIN,
SERVICE_VOLUME_MUTE,
{ATTR_ENTITY_ID: ENTITY_ID_ZONE_1, ATTR_MEDIA_VOLUME_MUTED: True},
blocking=True,
)
mock_russound_client.controllers[1].zones[1].toggle_mute.assert_not_called()
mock_russound_client.supported_features = [FeatureFlag.COMMANDS_ZONE_MUTE_OFF_ON]
# Test mute (w/ dedicated commands)
await hass.services.async_call(
MP_DOMAIN,
SERVICE_VOLUME_MUTE,
{ATTR_ENTITY_ID: ENTITY_ID_ZONE_1, ATTR_MEDIA_VOLUME_MUTED: True},
blocking=True,
)
mock_russound_client.controllers[1].zones[1].mute.assert_called_once()
# Test unmute (w/ dedicated commands)
await hass.services.async_call(
MP_DOMAIN,
SERVICE_VOLUME_MUTE,
{ATTR_ENTITY_ID: ENTITY_ID_ZONE_1, ATTR_MEDIA_VOLUME_MUTED: False},
blocking=True,
)
mock_russound_client.controllers[1].zones[1].unmute.assert_called_once()
@pytest.mark.parametrize(
("source_name", "source_id"),
[