From 3fea4efb9fdb2647d39910809883a01df82e9f37 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Wed, 8 Jan 2025 02:36:02 -0600 Subject: [PATCH] Update pyheos to 0.9.0 (#134947) Bump pyheos --- homeassistant/components/heos/__init__.py | 14 ++-- homeassistant/components/heos/manifest.json | 2 +- homeassistant/components/heos/media_player.py | 18 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/heos/conftest.py | 83 +++++++++++-------- tests/components/heos/test_media_player.py | 34 ++++---- 7 files changed, 87 insertions(+), 68 deletions(-) diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index d983f3c2547..9fd276c244e 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -275,11 +275,11 @@ class GroupManager: player_id_to_entity_id_map = self.entity_id_map 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 = [ - player_id_to_entity_id_map[member.player_id] - for member in group.members - if member.player_id in player_id_to_entity_id_map + player_id_to_entity_id_map[member] + for member in group.member_player_ids + if member in player_id_to_entity_id_map ] # Make sure the group leader is always the first element group_info = [leader_entity_id, *member_entity_ids] @@ -422,7 +422,7 @@ class SourceManager: None, ) if index is not None: - await player.play_favorite(index) + await player.play_preset_station(index) return input_source = next( @@ -434,7 +434,7 @@ class SourceManager: None, ) if input_source is not None: - await player.play_input_source(input_source) + await player.play_input_source(input_source.media_id) return _LOGGER.error("Unknown source: %s", source) @@ -447,7 +447,7 @@ class SourceManager: ( input_source.name 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, ) diff --git a/homeassistant/components/heos/manifest.json b/homeassistant/components/heos/manifest.json index 20694196e82..d14ad71ff49 100644 --- a/homeassistant/components/heos/manifest.json +++ b/homeassistant/components/heos/manifest.json @@ -6,7 +6,7 @@ "documentation": "https://www.home-assistant.io/integrations/heos", "iot_class": "local_push", "loggers": ["pyheos"], - "requirements": ["pyheos==0.8.0"], + "requirements": ["pyheos==0.9.0"], "single_config_entry": true, "ssdp": [ { diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index adbeadbc24f..924dcbe6b92 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -47,9 +47,9 @@ BASE_SUPPORTED_FEATURES = ( ) PLAY_STATE_TO_STATE = { - heos_const.PLAY_STATE_PLAY: MediaPlayerState.PLAYING, - heos_const.PLAY_STATE_STOP: MediaPlayerState.IDLE, - heos_const.PLAY_STATE_PAUSE: MediaPlayerState.PAUSED, + heos_const.PlayState.PLAY: MediaPlayerState.PLAYING, + heos_const.PlayState.STOP: MediaPlayerState.IDLE, + heos_const.PlayState.PAUSE: MediaPlayerState.PAUSED, } CONTROL_TO_SUPPORT = { @@ -61,11 +61,11 @@ CONTROL_TO_SUPPORT = { } HA_HEOS_ENQUEUE_MAP = { - None: heos_const.ADD_QUEUE_REPLACE_AND_PLAY, - MediaPlayerEnqueue.ADD: heos_const.ADD_QUEUE_ADD_TO_END, - MediaPlayerEnqueue.REPLACE: heos_const.ADD_QUEUE_REPLACE_AND_PLAY, - MediaPlayerEnqueue.NEXT: heos_const.ADD_QUEUE_PLAY_NEXT, - MediaPlayerEnqueue.PLAY: heos_const.ADD_QUEUE_PLAY_NOW, + None: heos_const.AddCriteriaType.REPLACE_AND_PLAY, + MediaPlayerEnqueue.ADD: heos_const.AddCriteriaType.ADD_TO_END, + MediaPlayerEnqueue.REPLACE: heos_const.AddCriteriaType.REPLACE_AND_PLAY, + MediaPlayerEnqueue.NEXT: heos_const.AddCriteriaType.PLAY_NEXT, + MediaPlayerEnqueue.PLAY: heos_const.AddCriteriaType.PLAY_NOW, } _LOGGER = logging.getLogger(__name__) @@ -268,7 +268,7 @@ class HeosMediaPlayer(MediaPlayerEntity): ) if index is None: raise ValueError(f"Invalid favorite '{media_id}'") - await self._player.play_favorite(index) + await self._player.play_preset_station(index) return raise ValueError(f"Unsupported media type '{media_type}'") diff --git a/requirements_all.txt b/requirements_all.txt index a932e4d2a02..55072438060 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1977,7 +1977,7 @@ pygti==0.9.4 pyhaversion==22.8.0 # homeassistant.components.heos -pyheos==0.8.0 +pyheos==0.9.0 # homeassistant.components.hive pyhiveapi==0.5.16 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 003864c9c8e..2ebabbfa942 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1606,7 +1606,7 @@ pygti==0.9.4 pyhaversion==22.8.0 # homeassistant.components.heos -pyheos==0.8.0 +pyheos==0.9.0 # homeassistant.components.hive pyhiveapi==0.5.16 diff --git a/tests/components/heos/conftest.py b/tests/components/heos/conftest.py index 6451f5bc69e..eec74d2dd18 100644 --- a/tests/components/heos/conftest.py +++ b/tests/components/heos/conftest.py @@ -5,15 +5,7 @@ from __future__ import annotations from collections.abc import Sequence from unittest.mock import Mock, patch -from pyheos import ( - Dispatcher, - Heos, - HeosGroup, - HeosPlayer, - HeosSource, - InputSource, - const, -) +from pyheos import Dispatcher, Heos, HeosGroup, HeosPlayer, MediaItem, const import pytest import pytest_asyncio @@ -124,12 +116,13 @@ def player_fixture(quick_selects): player.version = "1.0.0" player.is_muted = False player.available = True - player.state = const.PLAY_STATE_STOP + player.state = const.PlayState.STOP player.ip_address = f"127.0.0.{i}" player.network = "wired" player.shuffle = False - player.repeat = const.REPEAT_OFF + player.repeat = const.RepeatType.OFF player.volume = 25 + player.now_playing_media = Mock() player.now_playing_media.supported_controls = const.CONTROLS_ALL player.now_playing_media.album_id = 1 player.now_playing_media.queue_id = 1 @@ -151,34 +144,52 @@ def player_fixture(quick_selects): @pytest.fixture(name="group") def group_fixture(players): """Create a HEOS group consisting of two players.""" - group = Mock(HeosGroup) - group.leader = players[1] - group.members = [players[2]] - group.group_id = 999 + group = HeosGroup( + name="Group", group_id=999, lead_player_id=1, member_player_ids=[2] + ) + return {group.group_id: group} @pytest.fixture(name="favorites") -def favorites_fixture() -> dict[int, HeosSource]: +def favorites_fixture() -> dict[int, MediaItem]: """Create favorites fixture.""" - station = Mock(HeosSource) - station.type = const.TYPE_STATION - station.name = "Today's Hits Radio" - station.media_id = "123456789" - radio = Mock(HeosSource) - radio.type = const.TYPE_STATION - radio.name = "Classical MPR (Classical Music)" - radio.media_id = "s1234" + station = MediaItem( + source_id=const.MUSIC_SOURCE_PANDORA, + name="Today's Hits Radio", + media_id="123456789", + type=const.MediaType.STATION, + playable=True, + browsable=False, + 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} @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.""" - source = Mock(InputSource) - source.player_id = 1 - source.input_name = const.INPUT_AUX_IN_1 - source.name = "HEOS Drive - Line In 1" + source = MediaItem( + source_id=1, + 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] @@ -240,11 +251,17 @@ def quick_selects_fixture() -> dict[int, str]: @pytest.fixture(name="playlists") -def playlists_fixture() -> Sequence[HeosSource]: +def playlists_fixture() -> Sequence[MediaItem]: """Create favorites fixture.""" - playlist = Mock(HeosSource) - playlist.type = const.TYPE_PLAYLIST - playlist.name = "Awesome Music" + playlist = MediaItem( + source_id=const.MUSIC_SOURCE_PLAYLISTS, + name="Awesome Music", + type=const.MediaType.PLAYLIST, + playable=True, + browsable=True, + image_url="", + heos=None, + ) return [playlist] diff --git a/tests/components/heos/test_media_player.py b/tests/components/heos/test_media_player.py index 355cb47a0d9..155c425b91e 100644 --- a/tests/components/heos/test_media_player.py +++ b/tests/components/heos/test_media_player.py @@ -115,7 +115,7 @@ async def test_updates_from_signals( player = controller.players[1] # Test player does not update for other players - player.state = const.PLAY_STATE_PLAY + player.state = const.PlayState.PLAY player.heos.dispatcher.send( 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 # Test player_update standard events - player.state = const.PLAY_STATE_PLAY + player.state = const.PlayState.PLAY player.heos.dispatcher.send( 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) 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( 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}, 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 player.now_playing_media.station = favorite.name 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}, 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 player.now_playing_media.station = "Classical" 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] # Test set radio preset 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( MEDIA_PLAYER_DOMAIN, SERVICE_SELECT_SOURCE, {ATTR_ENTITY_ID: "media_player.test_player", ATTR_INPUT_SOURCE: favorite.name}, 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 @@ -629,7 +629,7 @@ async def test_select_input_source( }, 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 player.now_playing_media.source_id = const.MUSIC_SOURCE_AUX_INPUT player.now_playing_media.media_id = const.INPUT_AUX_IN_1 @@ -681,7 +681,7 @@ async def test_select_input_command_error( }, 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 @@ -831,7 +831,7 @@ async def test_play_media_playlist( blocking=True, ) player.add_to_queue.assert_called_once_with( - playlist, const.ADD_QUEUE_REPLACE_AND_PLAY + playlist, const.AddCriteriaType.REPLACE_AND_PLAY ) # Play with enqueuing player.add_to_queue.reset_mock() @@ -846,7 +846,9 @@ async def test_play_media_playlist( }, 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 player.add_to_queue.reset_mock() await hass.services.async_call( @@ -888,9 +890,9 @@ async def test_play_media_favorite( }, blocking=True, ) - player.play_favorite.assert_called_once_with(index) + player.play_preset_station.assert_called_once_with(index) # Play by name - player.play_favorite.reset_mock() + player.play_preset_station.reset_mock() await hass.services.async_call( MEDIA_PLAYER_DOMAIN, SERVICE_PLAY_MEDIA, @@ -901,9 +903,9 @@ async def test_play_media_favorite( }, blocking=True, ) - player.play_favorite.assert_called_once_with(index) + player.play_preset_station.assert_called_once_with(index) # Invalid name - player.play_favorite.reset_mock() + player.play_preset_station.reset_mock() await hass.services.async_call( MEDIA_PLAYER_DOMAIN, SERVICE_PLAY_MEDIA, @@ -914,7 +916,7 @@ async def test_play_media_favorite( }, 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