mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
parent
dc1928f3eb
commit
3fea4efb9f
@ -275,11 +275,11 @@ class GroupManager:
|
|||||||
|
|
||||||
player_id_to_entity_id_map = self.entity_id_map
|
player_id_to_entity_id_map = self.entity_id_map
|
||||||
for group in groups.values():
|
for group in groups.values():
|
||||||
leader_entity_id = player_id_to_entity_id_map.get(group.leader.player_id)
|
leader_entity_id = player_id_to_entity_id_map.get(group.lead_player_id)
|
||||||
member_entity_ids = [
|
member_entity_ids = [
|
||||||
player_id_to_entity_id_map[member.player_id]
|
player_id_to_entity_id_map[member]
|
||||||
for member in group.members
|
for member in group.member_player_ids
|
||||||
if member.player_id in player_id_to_entity_id_map
|
if member in player_id_to_entity_id_map
|
||||||
]
|
]
|
||||||
# Make sure the group leader is always the first element
|
# Make sure the group leader is always the first element
|
||||||
group_info = [leader_entity_id, *member_entity_ids]
|
group_info = [leader_entity_id, *member_entity_ids]
|
||||||
@ -422,7 +422,7 @@ class SourceManager:
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if index is not None:
|
if index is not None:
|
||||||
await player.play_favorite(index)
|
await player.play_preset_station(index)
|
||||||
return
|
return
|
||||||
|
|
||||||
input_source = next(
|
input_source = next(
|
||||||
@ -434,7 +434,7 @@ class SourceManager:
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if input_source is not None:
|
if input_source is not None:
|
||||||
await player.play_input_source(input_source)
|
await player.play_input_source(input_source.media_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.error("Unknown source: %s", source)
|
_LOGGER.error("Unknown source: %s", source)
|
||||||
@ -447,7 +447,7 @@ class SourceManager:
|
|||||||
(
|
(
|
||||||
input_source.name
|
input_source.name
|
||||||
for input_source in self.inputs
|
for input_source in self.inputs
|
||||||
if input_source.input_name == now_playing_media.media_id
|
if input_source.media_id == now_playing_media.media_id
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/heos",
|
"documentation": "https://www.home-assistant.io/integrations/heos",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["pyheos"],
|
"loggers": ["pyheos"],
|
||||||
"requirements": ["pyheos==0.8.0"],
|
"requirements": ["pyheos==0.9.0"],
|
||||||
"single_config_entry": true,
|
"single_config_entry": true,
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
|
@ -47,9 +47,9 @@ BASE_SUPPORTED_FEATURES = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
PLAY_STATE_TO_STATE = {
|
PLAY_STATE_TO_STATE = {
|
||||||
heos_const.PLAY_STATE_PLAY: MediaPlayerState.PLAYING,
|
heos_const.PlayState.PLAY: MediaPlayerState.PLAYING,
|
||||||
heos_const.PLAY_STATE_STOP: MediaPlayerState.IDLE,
|
heos_const.PlayState.STOP: MediaPlayerState.IDLE,
|
||||||
heos_const.PLAY_STATE_PAUSE: MediaPlayerState.PAUSED,
|
heos_const.PlayState.PAUSE: MediaPlayerState.PAUSED,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONTROL_TO_SUPPORT = {
|
CONTROL_TO_SUPPORT = {
|
||||||
@ -61,11 +61,11 @@ CONTROL_TO_SUPPORT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HA_HEOS_ENQUEUE_MAP = {
|
HA_HEOS_ENQUEUE_MAP = {
|
||||||
None: heos_const.ADD_QUEUE_REPLACE_AND_PLAY,
|
None: heos_const.AddCriteriaType.REPLACE_AND_PLAY,
|
||||||
MediaPlayerEnqueue.ADD: heos_const.ADD_QUEUE_ADD_TO_END,
|
MediaPlayerEnqueue.ADD: heos_const.AddCriteriaType.ADD_TO_END,
|
||||||
MediaPlayerEnqueue.REPLACE: heos_const.ADD_QUEUE_REPLACE_AND_PLAY,
|
MediaPlayerEnqueue.REPLACE: heos_const.AddCriteriaType.REPLACE_AND_PLAY,
|
||||||
MediaPlayerEnqueue.NEXT: heos_const.ADD_QUEUE_PLAY_NEXT,
|
MediaPlayerEnqueue.NEXT: heos_const.AddCriteriaType.PLAY_NEXT,
|
||||||
MediaPlayerEnqueue.PLAY: heos_const.ADD_QUEUE_PLAY_NOW,
|
MediaPlayerEnqueue.PLAY: heos_const.AddCriteriaType.PLAY_NOW,
|
||||||
}
|
}
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -268,7 +268,7 @@ class HeosMediaPlayer(MediaPlayerEntity):
|
|||||||
)
|
)
|
||||||
if index is None:
|
if index is None:
|
||||||
raise ValueError(f"Invalid favorite '{media_id}'")
|
raise ValueError(f"Invalid favorite '{media_id}'")
|
||||||
await self._player.play_favorite(index)
|
await self._player.play_preset_station(index)
|
||||||
return
|
return
|
||||||
|
|
||||||
raise ValueError(f"Unsupported media type '{media_type}'")
|
raise ValueError(f"Unsupported media type '{media_type}'")
|
||||||
|
@ -1977,7 +1977,7 @@ pygti==0.9.4
|
|||||||
pyhaversion==22.8.0
|
pyhaversion==22.8.0
|
||||||
|
|
||||||
# homeassistant.components.heos
|
# homeassistant.components.heos
|
||||||
pyheos==0.8.0
|
pyheos==0.9.0
|
||||||
|
|
||||||
# homeassistant.components.hive
|
# homeassistant.components.hive
|
||||||
pyhiveapi==0.5.16
|
pyhiveapi==0.5.16
|
||||||
|
@ -1606,7 +1606,7 @@ pygti==0.9.4
|
|||||||
pyhaversion==22.8.0
|
pyhaversion==22.8.0
|
||||||
|
|
||||||
# homeassistant.components.heos
|
# homeassistant.components.heos
|
||||||
pyheos==0.8.0
|
pyheos==0.9.0
|
||||||
|
|
||||||
# homeassistant.components.hive
|
# homeassistant.components.hive
|
||||||
pyhiveapi==0.5.16
|
pyhiveapi==0.5.16
|
||||||
|
@ -5,15 +5,7 @@ from __future__ import annotations
|
|||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
from pyheos import (
|
from pyheos import Dispatcher, Heos, HeosGroup, HeosPlayer, MediaItem, const
|
||||||
Dispatcher,
|
|
||||||
Heos,
|
|
||||||
HeosGroup,
|
|
||||||
HeosPlayer,
|
|
||||||
HeosSource,
|
|
||||||
InputSource,
|
|
||||||
const,
|
|
||||||
)
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
@ -124,12 +116,13 @@ def player_fixture(quick_selects):
|
|||||||
player.version = "1.0.0"
|
player.version = "1.0.0"
|
||||||
player.is_muted = False
|
player.is_muted = False
|
||||||
player.available = True
|
player.available = True
|
||||||
player.state = const.PLAY_STATE_STOP
|
player.state = const.PlayState.STOP
|
||||||
player.ip_address = f"127.0.0.{i}"
|
player.ip_address = f"127.0.0.{i}"
|
||||||
player.network = "wired"
|
player.network = "wired"
|
||||||
player.shuffle = False
|
player.shuffle = False
|
||||||
player.repeat = const.REPEAT_OFF
|
player.repeat = const.RepeatType.OFF
|
||||||
player.volume = 25
|
player.volume = 25
|
||||||
|
player.now_playing_media = Mock()
|
||||||
player.now_playing_media.supported_controls = const.CONTROLS_ALL
|
player.now_playing_media.supported_controls = const.CONTROLS_ALL
|
||||||
player.now_playing_media.album_id = 1
|
player.now_playing_media.album_id = 1
|
||||||
player.now_playing_media.queue_id = 1
|
player.now_playing_media.queue_id = 1
|
||||||
@ -151,34 +144,52 @@ def player_fixture(quick_selects):
|
|||||||
@pytest.fixture(name="group")
|
@pytest.fixture(name="group")
|
||||||
def group_fixture(players):
|
def group_fixture(players):
|
||||||
"""Create a HEOS group consisting of two players."""
|
"""Create a HEOS group consisting of two players."""
|
||||||
group = Mock(HeosGroup)
|
group = HeosGroup(
|
||||||
group.leader = players[1]
|
name="Group", group_id=999, lead_player_id=1, member_player_ids=[2]
|
||||||
group.members = [players[2]]
|
)
|
||||||
group.group_id = 999
|
|
||||||
return {group.group_id: group}
|
return {group.group_id: group}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="favorites")
|
@pytest.fixture(name="favorites")
|
||||||
def favorites_fixture() -> dict[int, HeosSource]:
|
def favorites_fixture() -> dict[int, MediaItem]:
|
||||||
"""Create favorites fixture."""
|
"""Create favorites fixture."""
|
||||||
station = Mock(HeosSource)
|
station = MediaItem(
|
||||||
station.type = const.TYPE_STATION
|
source_id=const.MUSIC_SOURCE_PANDORA,
|
||||||
station.name = "Today's Hits Radio"
|
name="Today's Hits Radio",
|
||||||
station.media_id = "123456789"
|
media_id="123456789",
|
||||||
radio = Mock(HeosSource)
|
type=const.MediaType.STATION,
|
||||||
radio.type = const.TYPE_STATION
|
playable=True,
|
||||||
radio.name = "Classical MPR (Classical Music)"
|
browsable=False,
|
||||||
radio.media_id = "s1234"
|
image_url="",
|
||||||
|
heos=None,
|
||||||
|
)
|
||||||
|
radio = MediaItem(
|
||||||
|
source_id=const.MUSIC_SOURCE_TUNEIN,
|
||||||
|
name="Classical MPR (Classical Music)",
|
||||||
|
media_id="s1234",
|
||||||
|
type=const.MediaType.STATION,
|
||||||
|
playable=True,
|
||||||
|
browsable=False,
|
||||||
|
image_url="",
|
||||||
|
heos=None,
|
||||||
|
)
|
||||||
return {1: station, 2: radio}
|
return {1: station, 2: radio}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="input_sources")
|
@pytest.fixture(name="input_sources")
|
||||||
def input_sources_fixture() -> Sequence[InputSource]:
|
def input_sources_fixture() -> Sequence[MediaItem]:
|
||||||
"""Create a set of input sources for testing."""
|
"""Create a set of input sources for testing."""
|
||||||
source = Mock(InputSource)
|
source = MediaItem(
|
||||||
source.player_id = 1
|
source_id=1,
|
||||||
source.input_name = const.INPUT_AUX_IN_1
|
name="HEOS Drive - Line In 1",
|
||||||
source.name = "HEOS Drive - Line In 1"
|
media_id=const.INPUT_AUX_IN_1,
|
||||||
|
type=const.MediaType.STATION,
|
||||||
|
playable=True,
|
||||||
|
browsable=False,
|
||||||
|
image_url="",
|
||||||
|
heos=None,
|
||||||
|
)
|
||||||
return [source]
|
return [source]
|
||||||
|
|
||||||
|
|
||||||
@ -240,11 +251,17 @@ def quick_selects_fixture() -> dict[int, str]:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="playlists")
|
@pytest.fixture(name="playlists")
|
||||||
def playlists_fixture() -> Sequence[HeosSource]:
|
def playlists_fixture() -> Sequence[MediaItem]:
|
||||||
"""Create favorites fixture."""
|
"""Create favorites fixture."""
|
||||||
playlist = Mock(HeosSource)
|
playlist = MediaItem(
|
||||||
playlist.type = const.TYPE_PLAYLIST
|
source_id=const.MUSIC_SOURCE_PLAYLISTS,
|
||||||
playlist.name = "Awesome Music"
|
name="Awesome Music",
|
||||||
|
type=const.MediaType.PLAYLIST,
|
||||||
|
playable=True,
|
||||||
|
browsable=True,
|
||||||
|
image_url="",
|
||||||
|
heos=None,
|
||||||
|
)
|
||||||
return [playlist]
|
return [playlist]
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ async def test_updates_from_signals(
|
|||||||
player = controller.players[1]
|
player = controller.players[1]
|
||||||
|
|
||||||
# Test player does not update for other players
|
# Test player does not update for other players
|
||||||
player.state = const.PLAY_STATE_PLAY
|
player.state = const.PlayState.PLAY
|
||||||
player.heos.dispatcher.send(
|
player.heos.dispatcher.send(
|
||||||
const.SIGNAL_PLAYER_EVENT, 2, const.EVENT_PLAYER_STATE_CHANGED
|
const.SIGNAL_PLAYER_EVENT, 2, const.EVENT_PLAYER_STATE_CHANGED
|
||||||
)
|
)
|
||||||
@ -124,7 +124,7 @@ async def test_updates_from_signals(
|
|||||||
assert state.state == STATE_IDLE
|
assert state.state == STATE_IDLE
|
||||||
|
|
||||||
# Test player_update standard events
|
# Test player_update standard events
|
||||||
player.state = const.PLAY_STATE_PLAY
|
player.state = const.PlayState.PLAY
|
||||||
player.heos.dispatcher.send(
|
player.heos.dispatcher.send(
|
||||||
const.SIGNAL_PLAYER_EVENT, player.player_id, const.EVENT_PLAYER_STATE_CHANGED
|
const.SIGNAL_PLAYER_EVENT, player.player_id, const.EVENT_PLAYER_STATE_CHANGED
|
||||||
)
|
)
|
||||||
@ -241,7 +241,7 @@ async def test_updates_from_players_changed(
|
|||||||
async_dispatcher_connect(hass, SIGNAL_HEOS_UPDATED, set_signal)
|
async_dispatcher_connect(hass, SIGNAL_HEOS_UPDATED, set_signal)
|
||||||
|
|
||||||
assert hass.states.get("media_player.test_player").state == STATE_IDLE
|
assert hass.states.get("media_player.test_player").state == STATE_IDLE
|
||||||
player.state = const.PLAY_STATE_PLAY
|
player.state = const.PlayState.PLAY
|
||||||
player.heos.dispatcher.send(
|
player.heos.dispatcher.send(
|
||||||
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED, change_data
|
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED, change_data
|
||||||
)
|
)
|
||||||
@ -551,7 +551,7 @@ async def test_select_favorite(
|
|||||||
{ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name},
|
{ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_favorite.assert_called_once_with(1)
|
player.play_preset_station.assert_called_once_with(1)
|
||||||
# Test state is matched by station name
|
# Test state is matched by station name
|
||||||
player.now_playing_media.station = favorite.name
|
player.now_playing_media.station = favorite.name
|
||||||
player.heos.dispatcher.send(
|
player.heos.dispatcher.send(
|
||||||
@ -576,7 +576,7 @@ async def test_select_radio_favorite(
|
|||||||
{ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name},
|
{ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_favorite.assert_called_once_with(2)
|
player.play_preset_station.assert_called_once_with(2)
|
||||||
# Test state is matched by album id
|
# Test state is matched by album id
|
||||||
player.now_playing_media.station = "Classical"
|
player.now_playing_media.station = "Classical"
|
||||||
player.now_playing_media.album_id = favorite.media_id
|
player.now_playing_media.album_id = favorite.media_id
|
||||||
@ -601,14 +601,14 @@ async def test_select_radio_favorite_command_error(
|
|||||||
player = controller.players[1]
|
player = controller.players[1]
|
||||||
# Test set radio preset
|
# Test set radio preset
|
||||||
favorite = favorites[2]
|
favorite = favorites[2]
|
||||||
player.play_favorite.side_effect = CommandFailedError(None, "Failure", 1)
|
player.play_preset_station.side_effect = CommandFailedError(None, "Failure", 1)
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
MEDIA_PLAYER_DOMAIN,
|
MEDIA_PLAYER_DOMAIN,
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
{ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name},
|
{ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_favorite.assert_called_once_with(2)
|
player.play_preset_station.assert_called_once_with(2)
|
||||||
assert "Unable to select source: Failure (1)" in caplog.text
|
assert "Unable to select source: Failure (1)" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@ -629,7 +629,7 @@ async def test_select_input_source(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_input_source.assert_called_once_with(input_source)
|
player.play_input_source.assert_called_once_with(input_source.media_id)
|
||||||
# Test state is matched by media id
|
# Test state is matched by media id
|
||||||
player.now_playing_media.source_id = const.MUSIC_SOURCE_AUX_INPUT
|
player.now_playing_media.source_id = const.MUSIC_SOURCE_AUX_INPUT
|
||||||
player.now_playing_media.media_id = const.INPUT_AUX_IN_1
|
player.now_playing_media.media_id = const.INPUT_AUX_IN_1
|
||||||
@ -681,7 +681,7 @@ async def test_select_input_command_error(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_input_source.assert_called_once_with(input_source)
|
player.play_input_source.assert_called_once_with(input_source.media_id)
|
||||||
assert "Unable to select source: Failure (1)" in caplog.text
|
assert "Unable to select source: Failure (1)" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@ -831,7 +831,7 @@ async def test_play_media_playlist(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.add_to_queue.assert_called_once_with(
|
player.add_to_queue.assert_called_once_with(
|
||||||
playlist, const.ADD_QUEUE_REPLACE_AND_PLAY
|
playlist, const.AddCriteriaType.REPLACE_AND_PLAY
|
||||||
)
|
)
|
||||||
# Play with enqueuing
|
# Play with enqueuing
|
||||||
player.add_to_queue.reset_mock()
|
player.add_to_queue.reset_mock()
|
||||||
@ -846,7 +846,9 @@ async def test_play_media_playlist(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.add_to_queue.assert_called_once_with(playlist, const.ADD_QUEUE_ADD_TO_END)
|
player.add_to_queue.assert_called_once_with(
|
||||||
|
playlist, const.AddCriteriaType.ADD_TO_END
|
||||||
|
)
|
||||||
# Invalid name
|
# Invalid name
|
||||||
player.add_to_queue.reset_mock()
|
player.add_to_queue.reset_mock()
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
@ -888,9 +890,9 @@ async def test_play_media_favorite(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_favorite.assert_called_once_with(index)
|
player.play_preset_station.assert_called_once_with(index)
|
||||||
# Play by name
|
# Play by name
|
||||||
player.play_favorite.reset_mock()
|
player.play_preset_station.reset_mock()
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
MEDIA_PLAYER_DOMAIN,
|
MEDIA_PLAYER_DOMAIN,
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
@ -901,9 +903,9 @@ async def test_play_media_favorite(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
player.play_favorite.assert_called_once_with(index)
|
player.play_preset_station.assert_called_once_with(index)
|
||||||
# Invalid name
|
# Invalid name
|
||||||
player.play_favorite.reset_mock()
|
player.play_preset_station.reset_mock()
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
MEDIA_PLAYER_DOMAIN,
|
MEDIA_PLAYER_DOMAIN,
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
@ -914,7 +916,7 @@ async def test_play_media_favorite(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert player.play_favorite.call_count == 0
|
assert player.play_preset_station.call_count == 0
|
||||||
assert "Unable to play media: Invalid favorite 'Invalid'" in caplog.text
|
assert "Unable to play media: Invalid favorite 'Invalid'" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user