mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +00:00
Add move queue item HEOS entity service (#142301)
This commit is contained in:
parent
52143155e7
commit
660cbc136f
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
ATTR_PASSWORD = "password"
|
ATTR_PASSWORD = "password"
|
||||||
ATTR_USERNAME = "username"
|
ATTR_USERNAME = "username"
|
||||||
|
ATTR_DESTINATION_POSITION = "destination_position"
|
||||||
ATTR_QUEUE_IDS = "queue_ids"
|
ATTR_QUEUE_IDS = "queue_ids"
|
||||||
DOMAIN = "heos"
|
DOMAIN = "heos"
|
||||||
ENTRY_TITLE = "HEOS System"
|
ENTRY_TITLE = "HEOS System"
|
||||||
@ -9,6 +10,7 @@ SERVICE_GET_QUEUE = "get_queue"
|
|||||||
SERVICE_GROUP_VOLUME_SET = "group_volume_set"
|
SERVICE_GROUP_VOLUME_SET = "group_volume_set"
|
||||||
SERVICE_GROUP_VOLUME_DOWN = "group_volume_down"
|
SERVICE_GROUP_VOLUME_DOWN = "group_volume_down"
|
||||||
SERVICE_GROUP_VOLUME_UP = "group_volume_up"
|
SERVICE_GROUP_VOLUME_UP = "group_volume_up"
|
||||||
|
SERVICE_MOVE_QUEUE_ITEM = "move_queue_item"
|
||||||
SERVICE_REMOVE_FROM_QUEUE = "remove_from_queue"
|
SERVICE_REMOVE_FROM_QUEUE = "remove_from_queue"
|
||||||
SERVICE_SIGN_IN = "sign_in"
|
SERVICE_SIGN_IN = "sign_in"
|
||||||
SERVICE_SIGN_OUT = "sign_out"
|
SERVICE_SIGN_OUT = "sign_out"
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
"remove_from_queue": {
|
"remove_from_queue": {
|
||||||
"service": "mdi:playlist-remove"
|
"service": "mdi:playlist-remove"
|
||||||
},
|
},
|
||||||
|
"move_queue_item": {
|
||||||
|
"service": "mdi:playlist-edit"
|
||||||
|
},
|
||||||
"group_volume_set": {
|
"group_volume_set": {
|
||||||
"service": "mdi:volume-medium"
|
"service": "mdi:volume-medium"
|
||||||
},
|
},
|
||||||
|
@ -479,6 +479,13 @@ class HeosMediaPlayer(CoordinatorEntity[HeosCoordinator], MediaPlayerEntity):
|
|||||||
"""Remove items from the queue."""
|
"""Remove items from the queue."""
|
||||||
await self._player.remove_from_queue(queue_ids)
|
await self._player.remove_from_queue(queue_ids)
|
||||||
|
|
||||||
|
@catch_action_error("move queue item")
|
||||||
|
async def async_move_queue_item(
|
||||||
|
self, queue_ids: list[int], destination_position: int
|
||||||
|
) -> None:
|
||||||
|
"""Move items in the queue."""
|
||||||
|
await self._player.move_queue_item(queue_ids, destination_position)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return True if the device is available."""
|
"""Return True if the device is available."""
|
||||||
|
@ -19,6 +19,7 @@ from homeassistant.helpers import (
|
|||||||
from homeassistant.helpers.typing import VolDictType, VolSchemaType
|
from homeassistant.helpers.typing import VolDictType, VolSchemaType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
ATTR_DESTINATION_POSITION,
|
||||||
ATTR_PASSWORD,
|
ATTR_PASSWORD,
|
||||||
ATTR_QUEUE_IDS,
|
ATTR_QUEUE_IDS,
|
||||||
ATTR_USERNAME,
|
ATTR_USERNAME,
|
||||||
@ -27,6 +28,7 @@ from .const import (
|
|||||||
SERVICE_GROUP_VOLUME_DOWN,
|
SERVICE_GROUP_VOLUME_DOWN,
|
||||||
SERVICE_GROUP_VOLUME_SET,
|
SERVICE_GROUP_VOLUME_SET,
|
||||||
SERVICE_GROUP_VOLUME_UP,
|
SERVICE_GROUP_VOLUME_UP,
|
||||||
|
SERVICE_MOVE_QUEUE_ITEM,
|
||||||
SERVICE_REMOVE_FROM_QUEUE,
|
SERVICE_REMOVE_FROM_QUEUE,
|
||||||
SERVICE_SIGN_IN,
|
SERVICE_SIGN_IN,
|
||||||
SERVICE_SIGN_OUT,
|
SERVICE_SIGN_OUT,
|
||||||
@ -87,6 +89,16 @@ REMOVE_FROM_QUEUE_SCHEMA: Final[VolDictType] = {
|
|||||||
GROUP_VOLUME_SET_SCHEMA: Final[VolDictType] = {
|
GROUP_VOLUME_SET_SCHEMA: Final[VolDictType] = {
|
||||||
vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float
|
vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float
|
||||||
}
|
}
|
||||||
|
MOVE_QEUEUE_ITEM_SCHEMA: Final[VolDictType] = {
|
||||||
|
vol.Required(ATTR_QUEUE_IDS): vol.All(
|
||||||
|
cv.ensure_list,
|
||||||
|
[vol.All(vol.Coerce(int), vol.Range(min=1, max=1000))],
|
||||||
|
vol.Unique(),
|
||||||
|
),
|
||||||
|
vol.Required(ATTR_DESTINATION_POSITION): vol.All(
|
||||||
|
vol.Coerce(int), vol.Range(min=1, max=1000)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
MEDIA_PLAYER_ENTITY_SERVICES: Final = (
|
MEDIA_PLAYER_ENTITY_SERVICES: Final = (
|
||||||
# Player queue services
|
# Player queue services
|
||||||
@ -96,6 +108,9 @@ MEDIA_PLAYER_ENTITY_SERVICES: Final = (
|
|||||||
EntityServiceDescription(
|
EntityServiceDescription(
|
||||||
SERVICE_REMOVE_FROM_QUEUE, "async_remove_from_queue", REMOVE_FROM_QUEUE_SCHEMA
|
SERVICE_REMOVE_FROM_QUEUE, "async_remove_from_queue", REMOVE_FROM_QUEUE_SCHEMA
|
||||||
),
|
),
|
||||||
|
EntityServiceDescription(
|
||||||
|
SERVICE_MOVE_QUEUE_ITEM, "async_move_queue_item", MOVE_QEUEUE_ITEM_SCHEMA
|
||||||
|
),
|
||||||
# Group volume services
|
# Group volume services
|
||||||
EntityServiceDescription(
|
EntityServiceDescription(
|
||||||
SERVICE_GROUP_VOLUME_SET,
|
SERVICE_GROUP_VOLUME_SET,
|
||||||
|
@ -17,6 +17,26 @@ remove_from_queue:
|
|||||||
multiple: true
|
multiple: true
|
||||||
type: number
|
type: number
|
||||||
|
|
||||||
|
move_queue_item:
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
integration: heos
|
||||||
|
domain: media_player
|
||||||
|
fields:
|
||||||
|
queue_ids:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
multiple: true
|
||||||
|
type: number
|
||||||
|
destination_position:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 1
|
||||||
|
max: 1000
|
||||||
|
step: 1
|
||||||
|
|
||||||
group_volume_set:
|
group_volume_set:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
|
@ -100,6 +100,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"move_queue_item": {
|
||||||
|
"name": "Move queue item",
|
||||||
|
"description": "Move one or more items within the play queue.",
|
||||||
|
"fields": {
|
||||||
|
"queue_ids": {
|
||||||
|
"name": "Queue IDs",
|
||||||
|
"description": "The IDs (indexes) of the items in the queue to move."
|
||||||
|
},
|
||||||
|
"destination_position": {
|
||||||
|
"name": "Destination position",
|
||||||
|
"description": "The position index in the queue to move the items to."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"group_volume_down": {
|
"group_volume_down": {
|
||||||
"name": "Turn down group volume",
|
"name": "Turn down group volume",
|
||||||
"description": "Turns down the group volume."
|
"description": "Turns down the group volume."
|
||||||
|
@ -39,6 +39,7 @@ class MockHeos(Heos):
|
|||||||
self.player_clear_queue: AsyncMock = AsyncMock()
|
self.player_clear_queue: AsyncMock = AsyncMock()
|
||||||
self.player_get_queue: AsyncMock = AsyncMock()
|
self.player_get_queue: AsyncMock = AsyncMock()
|
||||||
self.player_get_quick_selects: AsyncMock = AsyncMock()
|
self.player_get_quick_selects: AsyncMock = AsyncMock()
|
||||||
|
self.player_move_queue_item: AsyncMock = AsyncMock()
|
||||||
self.player_play_next: AsyncMock = AsyncMock()
|
self.player_play_next: AsyncMock = AsyncMock()
|
||||||
self.player_play_previous: AsyncMock = AsyncMock()
|
self.player_play_previous: AsyncMock = AsyncMock()
|
||||||
self.player_play_queue: AsyncMock = AsyncMock()
|
self.player_play_queue: AsyncMock = AsyncMock()
|
||||||
|
@ -27,12 +27,14 @@ from syrupy.assertion import SnapshotAssertion
|
|||||||
from syrupy.filters import props
|
from syrupy.filters import props
|
||||||
|
|
||||||
from homeassistant.components.heos.const import (
|
from homeassistant.components.heos.const import (
|
||||||
|
ATTR_DESTINATION_POSITION,
|
||||||
ATTR_QUEUE_IDS,
|
ATTR_QUEUE_IDS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_GET_QUEUE,
|
SERVICE_GET_QUEUE,
|
||||||
SERVICE_GROUP_VOLUME_DOWN,
|
SERVICE_GROUP_VOLUME_DOWN,
|
||||||
SERVICE_GROUP_VOLUME_SET,
|
SERVICE_GROUP_VOLUME_SET,
|
||||||
SERVICE_GROUP_VOLUME_UP,
|
SERVICE_GROUP_VOLUME_UP,
|
||||||
|
SERVICE_MOVE_QUEUE_ITEM,
|
||||||
SERVICE_REMOVE_FROM_QUEUE,
|
SERVICE_REMOVE_FROM_QUEUE,
|
||||||
)
|
)
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
@ -1784,3 +1786,45 @@ async def test_remove_from_queue(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
controller.player_remove_from_queue.assert_called_once_with(1, [1, 2])
|
controller.player_remove_from_queue.assert_called_once_with(1, [1, 2])
|
||||||
|
|
||||||
|
|
||||||
|
async def test_move_queue_item_queue(
|
||||||
|
hass: HomeAssistant, config_entry: MockConfigEntry, controller: MockHeos
|
||||||
|
) -> None:
|
||||||
|
"""Test the move queue service."""
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_MOVE_QUEUE_ITEM,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.test_player",
|
||||||
|
ATTR_QUEUE_IDS: [1, "2"],
|
||||||
|
ATTR_DESTINATION_POSITION: 10,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
controller.player_move_queue_item.assert_called_once_with(1, [1, 2], 10)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_move_queue_item_queue_error_raises(
|
||||||
|
hass: HomeAssistant, config_entry: MockConfigEntry, controller: MockHeos
|
||||||
|
) -> None:
|
||||||
|
"""Test move queue raises error when failed."""
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
controller.player_move_queue_item.side_effect = HeosError("error")
|
||||||
|
with pytest.raises(
|
||||||
|
HomeAssistantError,
|
||||||
|
match=re.escape("Unable to move queue item: error"),
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_MOVE_QUEUE_ITEM,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.test_player",
|
||||||
|
ATTR_QUEUE_IDS: [1, "2"],
|
||||||
|
ATTR_DESTINATION_POSITION: 10,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user