Add get_queue action for Sonos (#124707)
* initial commit * use constants * use constants * update typing * add queue fixture * remove blank line * update docstring * update icons * use list comprehension
This commit is contained in:
parent
57a73d1b1b
commit
5824d06fd7
8 changed files with 129 additions and 3 deletions
|
@ -64,6 +64,9 @@
|
|||
},
|
||||
"update_alarm": {
|
||||
"service": "mdi:alarm"
|
||||
},
|
||||
"get_queue": {
|
||||
"service": "mdi:queue-first-in-last-out"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ from soco.core import (
|
|||
PLAY_MODE_BY_MEANING,
|
||||
PLAY_MODES,
|
||||
)
|
||||
from soco.data_structures import DidlFavorite
|
||||
from soco.data_structures import DidlFavorite, DidlMusicTrack
|
||||
from soco.ms_data_structures import MusicServiceItem
|
||||
from sonos_websocket.exception import SonosWebsocketError
|
||||
import voluptuous as vol
|
||||
|
@ -22,8 +22,12 @@ import voluptuous as vol
|
|||
from homeassistant.components import media_source, spotify
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_INPUT_SOURCE,
|
||||
ATTR_MEDIA_ALBUM_NAME,
|
||||
ATTR_MEDIA_ANNOUNCE,
|
||||
ATTR_MEDIA_ARTIST,
|
||||
ATTR_MEDIA_CONTENT_ID,
|
||||
ATTR_MEDIA_ENQUEUE,
|
||||
ATTR_MEDIA_TITLE,
|
||||
BrowseMedia,
|
||||
MediaPlayerDeviceClass,
|
||||
MediaPlayerEnqueue,
|
||||
|
@ -38,7 +42,7 @@ from homeassistant.components.plex import PLEX_URI_SCHEME
|
|||
from homeassistant.components.plex.services import process_plex_payload
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TIME
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse, callback
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform, service
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
@ -88,6 +92,7 @@ SERVICE_CLEAR_TIMER = "clear_sleep_timer"
|
|||
SERVICE_UPDATE_ALARM = "update_alarm"
|
||||
SERVICE_PLAY_QUEUE = "play_queue"
|
||||
SERVICE_REMOVE_FROM_QUEUE = "remove_from_queue"
|
||||
SERVICE_GET_QUEUE = "get_queue"
|
||||
|
||||
ATTR_SLEEP_TIME = "sleep_time"
|
||||
ATTR_ALARM_ID = "alarm_id"
|
||||
|
@ -190,6 +195,13 @@ async def async_setup_entry(
|
|||
"remove_from_queue",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_GET_QUEUE,
|
||||
None,
|
||||
"get_queue",
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
|
||||
class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
||||
"""Representation of a Sonos entity."""
|
||||
|
@ -741,6 +753,20 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
|||
"""Remove item from the queue."""
|
||||
self.coordinator.soco.remove_from_queue(queue_position)
|
||||
|
||||
@soco_error()
|
||||
def get_queue(self) -> list[dict]:
|
||||
"""Get the queue."""
|
||||
queue: list[DidlMusicTrack] = self.coordinator.soco.get_queue(max_items=0)
|
||||
return [
|
||||
{
|
||||
ATTR_MEDIA_TITLE: track.title,
|
||||
ATTR_MEDIA_ALBUM_NAME: track.album,
|
||||
ATTR_MEDIA_ARTIST: track.creator,
|
||||
ATTR_MEDIA_CONTENT_ID: track.get_uri(),
|
||||
}
|
||||
for track in queue
|
||||
]
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return entity specific state attributes."""
|
||||
|
|
|
@ -63,6 +63,11 @@ remove_from_queue:
|
|||
max: 10000
|
||||
mode: box
|
||||
|
||||
get_queue:
|
||||
target:
|
||||
entity:
|
||||
domain: media_player
|
||||
|
||||
update_alarm:
|
||||
target:
|
||||
device:
|
||||
|
|
|
@ -172,6 +172,10 @@
|
|||
"description": "Enable or disable including grouped rooms."
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_queue": {
|
||||
"name": "Get queue",
|
||||
"description": "Returns the contents of the queue."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
|
|
|
@ -10,7 +10,12 @@ from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
|||
import pytest
|
||||
from soco import SoCo
|
||||
from soco.alarms import Alarms
|
||||
from soco.data_structures import DidlFavorite, DidlPlaylistContainer, SearchResult
|
||||
from soco.data_structures import (
|
||||
DidlFavorite,
|
||||
DidlMusicTrack,
|
||||
DidlPlaylistContainer,
|
||||
SearchResult,
|
||||
)
|
||||
from soco.events_base import Event as SonosEvent
|
||||
|
||||
from homeassistant.components import ssdp, zeroconf
|
||||
|
@ -185,6 +190,7 @@ class SoCoMockFactory:
|
|||
battery_info,
|
||||
alarm_clock,
|
||||
sonos_playlists: SearchResult,
|
||||
sonos_queue: list[DidlMusicTrack],
|
||||
) -> None:
|
||||
"""Initialize the mock factory."""
|
||||
self.mock_list: dict[str, MockSoCo] = {}
|
||||
|
@ -194,6 +200,7 @@ class SoCoMockFactory:
|
|||
self.battery_info = battery_info
|
||||
self.alarm_clock = alarm_clock
|
||||
self.sonos_playlists = sonos_playlists
|
||||
self.sonos_queue = sonos_queue
|
||||
|
||||
def cache_mock(
|
||||
self, mock_soco: MockSoCo, ip_address: str, name: str = "Zone A"
|
||||
|
@ -207,6 +214,7 @@ class SoCoMockFactory:
|
|||
mock_soco.get_current_track_info.return_value = self.current_track_info
|
||||
mock_soco.music_source_from_uri = SoCo.music_source_from_uri
|
||||
mock_soco.get_sonos_playlists.return_value = self.sonos_playlists
|
||||
mock_soco.get_queue.return_value = self.sonos_queue
|
||||
my_speaker_info = self.speaker_info.copy()
|
||||
my_speaker_info["zone_name"] = name
|
||||
my_speaker_info["uid"] = mock_soco.uid
|
||||
|
@ -277,6 +285,7 @@ def soco_factory(
|
|||
alarm_clock,
|
||||
sonos_playlists: SearchResult,
|
||||
sonos_websocket,
|
||||
sonos_queue: list[DidlMusicTrack],
|
||||
):
|
||||
"""Create factory for instantiating SoCo mocks."""
|
||||
factory = SoCoMockFactory(
|
||||
|
@ -286,6 +295,7 @@ def soco_factory(
|
|||
battery_info,
|
||||
alarm_clock,
|
||||
sonos_playlists,
|
||||
sonos_queue=sonos_queue,
|
||||
)
|
||||
with (
|
||||
patch("homeassistant.components.sonos.SoCo", new=factory.get_mock),
|
||||
|
@ -370,6 +380,13 @@ def sonos_playlists_fixture() -> SearchResult:
|
|||
return SearchResult(playlists_list, "sonos_playlists", 1, 1, 0)
|
||||
|
||||
|
||||
@pytest.fixture(name="sonos_queue")
|
||||
def sonos_queue() -> list[DidlMusicTrack]:
|
||||
"""Create sonos queue fixture."""
|
||||
queue = load_json_value_fixture("sonos_queue.json", "sonos")
|
||||
return [DidlMusicTrack.from_dict(track) for track in queue]
|
||||
|
||||
|
||||
class MockMusicServiceItem:
|
||||
"""Mocks a Soco MusicServiceItem."""
|
||||
|
||||
|
|
30
tests/components/sonos/fixtures/sonos_queue.json
Normal file
30
tests/components/sonos/fixtures/sonos_queue.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"title": "Something",
|
||||
"album": "Abbey Road",
|
||||
"creator": "The Beatles",
|
||||
"item_id": "Q:0/1",
|
||||
"parent_id": "Q:0",
|
||||
"original_track_number": 3,
|
||||
"resources": [
|
||||
{
|
||||
"uri": "x-file-cifs://192.168.42.10/music/The%20Beatles/Abbey%20Road/03%20Something.mp3",
|
||||
"protocol_info": "file:*:audio/mpegurl:*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Come Together",
|
||||
"album": "Abbey Road",
|
||||
"creator": "The Beatles",
|
||||
"item_id": "Q:0/2",
|
||||
"parent_id": "Q:0",
|
||||
"original_track_number": 1,
|
||||
"resources": [
|
||||
{
|
||||
"uri": "x-file-cifs://192.168.42.10/music/The%20Beatles/Abbey%20Road/01%20Come%20Together.mp3",
|
||||
"protocol_info": "file:*:audio/mpegurl:*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -56,3 +56,21 @@
|
|||
'state': 'idle',
|
||||
})
|
||||
# ---
|
||||
# name: test_media_get_queue
|
||||
dict({
|
||||
'media_player.zone_a': list([
|
||||
dict({
|
||||
'media_album_name': 'Abbey Road',
|
||||
'media_artist': 'The Beatles',
|
||||
'media_content_id': 'x-file-cifs://192.168.42.10/music/The%20Beatles/Abbey%20Road/03%20Something.mp3',
|
||||
'media_title': 'Something',
|
||||
}),
|
||||
dict({
|
||||
'media_album_name': 'Abbey Road',
|
||||
'media_artist': 'The Beatles',
|
||||
'media_content_id': 'x-file-cifs://192.168.42.10/music/The%20Beatles/Abbey%20Road/01%20Come%20Together.mp3',
|
||||
'media_title': 'Come Together',
|
||||
}),
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
|
|
|
@ -32,6 +32,7 @@ from homeassistant.components.sonos.const import (
|
|||
)
|
||||
from homeassistant.components.sonos.media_player import (
|
||||
LONG_SERVICE_TIMEOUT,
|
||||
SERVICE_GET_QUEUE,
|
||||
SERVICE_RESTORE,
|
||||
SERVICE_SNAPSHOT,
|
||||
VOLUME_INCREMENT,
|
||||
|
@ -1121,3 +1122,25 @@ async def test_play_media_announce(
|
|||
blocking=True,
|
||||
)
|
||||
assert sonos_websocket.play_clip.call_count == 1
|
||||
|
||||
|
||||
async def test_media_get_queue(
|
||||
hass: HomeAssistant,
|
||||
soco: MockSoCo,
|
||||
async_autosetup_sonos,
|
||||
soco_factory,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test getting the media queue."""
|
||||
soco_mock = soco_factory.mock_list.get("192.168.42.2")
|
||||
result = await hass.services.async_call(
|
||||
SONOS_DOMAIN,
|
||||
SERVICE_GET_QUEUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.zone_a",
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
soco_mock.get_queue.assert_called_with(max_items=0)
|
||||
assert result == snapshot
|
||||
|
|
Loading…
Add table
Reference in a new issue