From 6540e013f3bf7620b3a71284b345607c216bd4b4 Mon Sep 17 00:00:00 2001 From: Peter Meiser Date: Tue, 1 Sep 2026 09:28:30 +0200 Subject: [PATCH] Fix Music Assistant shared player icons (#180979) --- .../music_assistant/media_player.py | 29 ++++++++++++++++++- .../music_assistant/test_media_player.py | 27 +++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/music_assistant/media_player.py b/homeassistant/components/music_assistant/media_player.py index 01a80d163d8a..a43656ced570 100644 --- a/homeassistant/components/music_assistant/media_player.py +++ b/homeassistant/components/music_assistant/media_player.py @@ -108,6 +108,33 @@ REPEAT_MODE_MAPPING_TO_HA = { # UNKNOWN is intentionally not mapped - will return None } +MASS_ICON_TO_MDI: Mapping[str, str] = { + "bluetooth": "mdi:bluetooth", + "car": "mdi:car", + "cast": "mdi:cast", + "headphones": "mdi:headphones", + "laptop": "mdi:laptop", + "monitor": "mdi:monitor", + "radio": "mdi:radio", + "smartphone": "mdi:cellphone", + "soundbar": "mdi:soundbar", + "speaker": "mdi:speaker", + "speakers": "mdi:speaker-multiple", + "sun": "mdi:white-balance-sunny", + "tablet": "mdi:tablet", + "tv": "mdi:television", + "vinyl": "mdi:record-player", +} + + +def _get_mdi_icon(icon: str) -> str: + """Return an MDI icon for a Music Assistant icon.""" + if icon.startswith("mdi:"): + return icon + if icon.startswith("mdi-"): + return icon.replace("mdi-", "mdi:", 1) + return MASS_ICON_TO_MDI.get(icon, "mdi:speaker") + async def async_setup_entry( hass: HomeAssistant, @@ -136,7 +163,7 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity): def __init__(self, mass: MusicAssistantClient, player_id: str) -> None: """Initialize MediaPlayer entity.""" super().__init__(mass, player_id) - self._attr_icon = self.player.icon.replace("mdi-", "mdi:") + self._attr_icon = _get_mdi_icon(self.player.icon) self._set_supported_features() self._attr_device_class = MediaPlayerDeviceClass.SPEAKER self._source_list_mapping: dict[str, str] = {} diff --git a/tests/components/music_assistant/test_media_player.py b/tests/components/music_assistant/test_media_player.py index 5de1b9dad5cc..b3e7e549654b 100644 --- a/tests/components/music_assistant/test_media_player.py +++ b/tests/components/music_assistant/test_media_player.py @@ -52,6 +52,7 @@ from homeassistant.components.music_assistant.const import ( ATTR_USERNAME, DOMAIN, ) +from homeassistant.components.music_assistant.media_player import MusicAssistantPlayer from homeassistant.components.music_assistant.services import ( SERVICE_GET_QUEUE, SERVICE_PLAY_ANNOUNCEMENT, @@ -81,6 +82,7 @@ from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import entity_registry as er from .common import ( + create_players_from_fixture, setup_integration_from_fixtures, snapshot_music_assistant_entities, trigger_subscription_callback, @@ -96,6 +98,31 @@ MOCK_TRACK = Track( ) +@pytest.mark.parametrize( + ("mass_icon", "mdi_icon"), + [ + pytest.param("speaker", "mdi:speaker", id="speaker"), + pytest.param("speakers", "mdi:speaker-multiple", id="speakers"), + pytest.param("tv", "mdi:television", id="tv"), + pytest.param("smartphone", "mdi:cellphone", id="smartphone"), + pytest.param("google-nest", "mdi:speaker", id="fallback"), + pytest.param("mdi-speaker", "mdi:speaker", id="legacy-mdi-dash"), + pytest.param("mdi:speaker", "mdi:speaker", id="legacy-mdi-colon"), + ], +) +def test_player_icon( + music_assistant_client: MagicMock, mass_icon: str, mdi_icon: str +) -> None: + """Test Music Assistant player icon mapping.""" + player = create_players_from_fixture()[0] + player.icon = mass_icon + music_assistant_client.players._players[player.player_id] = player + + entity = MusicAssistantPlayer(music_assistant_client, player.player_id) + + assert entity.icon == mdi_icon + + async def test_media_player( hass: HomeAssistant, entity_registry: er.EntityRegistry,