diff --git a/homeassistant/components/russound_rio/media_player.py b/homeassistant/components/russound_rio/media_player.py index 62981262e3247..346f4903f6a91 100644 --- a/homeassistant/components/russound_rio/media_player.py +++ b/homeassistant/components/russound_rio/media_player.py @@ -2,6 +2,7 @@ from __future__ import annotations +import datetime as dt import logging from typing import TYPE_CHECKING @@ -57,6 +58,7 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity): | MediaPlayerEntityFeature.TURN_ON | MediaPlayerEntityFeature.TURN_OFF | MediaPlayerEntityFeature.SELECT_SOURCE + | MediaPlayerEntityFeature.SEEK ) def __init__( @@ -138,6 +140,21 @@ def media_image_url(self) -> str | None: """Image url of current playing media.""" return self._source.cover_art_url + @property + def media_duration(self) -> int | None: + """Duration of the current media.""" + return self._source.track_time + + @property + def media_position(self) -> int | None: + """Position of the current media.""" + return self._source.play_time + + @property + def media_position_updated_at(self) -> dt.datetime: + """Last time the media position was updated.""" + return self._source.position_last_updated + @property def volume_level(self) -> float: """Volume level of the media player (0..1). @@ -199,3 +216,8 @@ async def async_mute_volume(self, mute: bool) -> None: if mute != self.is_volume_muted: await self._zone.toggle_mute() + + @command + async def async_media_seek(self, position: float) -> None: + """Seek to a position in the current media.""" + await self._zone.set_seek_time(int(position)) diff --git a/tests/components/russound_rio/conftest.py b/tests/components/russound_rio/conftest.py index bf6884e09fb25..2516bd816505d 100644 --- a/tests/components/russound_rio/conftest.py +++ b/tests/components/russound_rio/conftest.py @@ -78,6 +78,7 @@ def mock_russound_client() -> Generator[AsyncMock]: zone.mute = AsyncMock() zone.unmute = AsyncMock() zone.toggle_mute = AsyncMock() + zone.set_seek_time = AsyncMock() client.controllers = { 1: Controller( diff --git a/tests/components/russound_rio/test_media_player.py b/tests/components/russound_rio/test_media_player.py index 5a6420da0006a..d0c18a9b1e7a1 100644 --- a/tests/components/russound_rio/test_media_player.py +++ b/tests/components/russound_rio/test_media_player.py @@ -9,6 +9,7 @@ from homeassistant.components.media_player import ( ATTR_INPUT_SOURCE, + ATTR_MEDIA_SEEK_POSITION, ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, DOMAIN as MP_DOMAIN, @@ -16,6 +17,7 @@ ) from homeassistant.const import ( ATTR_ENTITY_ID, + SERVICE_MEDIA_SEEK, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, @@ -232,3 +234,22 @@ async def test_power_service( await hass.services.async_call(MP_DOMAIN, SERVICE_TURN_OFF, data, blocking=True) mock_russound_client.controllers[1].zones[1].zone_off.assert_called_once() + + +async def test_media_seek( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_russound_client: AsyncMock, +) -> None: + """Test media seek service.""" + await setup_integration(hass, mock_config_entry) + + await hass.services.async_call( + MP_DOMAIN, + SERVICE_MEDIA_SEEK, + {ATTR_ENTITY_ID: ENTITY_ID_ZONE_1, ATTR_MEDIA_SEEK_POSITION: 100}, + ) + + mock_russound_client.controllers[1].zones[1].set_seek_time.assert_called_once_with( + 100 + )