diff --git a/homeassistant/components/music_assistant/entity.py b/homeassistant/components/music_assistant/entity.py index c12bf107fc027..4c12d33556973 100644 --- a/homeassistant/components/music_assistant/entity.py +++ b/homeassistant/components/music_assistant/entity.py @@ -38,6 +38,20 @@ def __init__(self, mass: MusicAssistantClient, player_id: str) -> None: name=self.player.name, configuration_url=f"{mass.server_url}/#/settings/editplayer/{player_id}", ) + # Forward MA player's hardware connections (set by per-protocol + # providers — bluetooth bridges, AirPlay, WiiM, …) verbatim into + # HA's device-registry input. The connection-type strings come + # from the provider; HA's registry treats any string as a valid + # connection type and merges devices across integrations that + # declare the same tuple. ``getattr`` keeps the integration + # working with older ``music_assistant_models`` releases that + # don't yet have the field. Only set the key when non-empty so + # players with no advertised hardware identity don't get a + # spurious empty ``connections`` set in their HA device record. + if mass_connections := set( + getattr(self.player.device_info, "connections", None) or set() + ): + self._attr_device_info["connections"] = mass_connections async def async_added_to_hass(self) -> None: """Register callbacks.""" diff --git a/tests/components/music_assistant/fixtures/players.json b/tests/components/music_assistant/fixtures/players.json index 2ddf5a718553b..80f1548f6318c 100644 --- a/tests/components/music_assistant/fixtures/players.json +++ b/tests/components/music_assistant/fixtures/players.json @@ -9,7 +9,11 @@ "device_info": { "model": "Test Model", "address": "192.168.1.1", - "manufacturer": "Test Manufacturer" + "manufacturer": "Test Manufacturer", + "connections": [ + ["mac", "00:00:00:00:00:01"], + ["bluetooth", "aa:bb:cc:dd:ee:ff"] + ] }, "supported_features": [ "volume_set", diff --git a/tests/components/music_assistant/test_init.py b/tests/components/music_assistant/test_init.py index 94f6f176964ff..735a2809c7e2e 100644 --- a/tests/components/music_assistant/test_init.py +++ b/tests/components/music_assistant/test_init.py @@ -4,6 +4,8 @@ from music_assistant_models.enums import EventType from music_assistant_models.errors import ActionUnavailable, AuthenticationRequired +from music_assistant_models.player import DeviceInfo as MassDeviceInfo +import pytest from homeassistant.components.music_assistant.const import ( ATTR_CONF_EXPOSE_PLAYER_TO_HA, @@ -211,3 +213,41 @@ async def test_authentication_required_addon_no_reauth( issue_reg = ir.async_get(hass) issue_id = f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}" assert issue_reg.async_get_issue("homeassistant", issue_id) is None + + +@pytest.mark.skipif( + "connections" not in getattr(MassDeviceInfo, "__dataclass_fields__", {}), + reason=( + "music_assistant_models.player.DeviceInfo.connections not yet " + "available — requires music-assistant/models#215 release + a " + "music-assistant-client bump in this integration's manifest." + ), +) +async def test_device_info_forwards_player_connections( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + music_assistant_client: MagicMock, +) -> None: + """Test that the player's device_info.connections is forwarded into HA's device_registry. + + Provider-side connection identifiers (BT MAC, Wi-Fi MAC, Zigbee EUI-64, + UPnP UUID, etc.) are surfaced in the MA player's + ``device_info.connections`` set; HA's MA integration must forward them + so HA's device_registry can dedupe device cards across integrations + that know the same hardware. + """ + await setup_integration_from_fixtures(hass, music_assistant_client) + await hass.async_block_till_done() + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + # Look up the specific player's device by its identifier rather than + # by ``async_entries_for_config_entry`` index (the fixture has multiple + # players and ordering is not part of the API). + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, "00:00:00:00:00:01")} + ) + assert device_entry is not None + assert device_entry.config_entries == {config_entry.entry_id} + # The fixture's first player declares two connections: a Wi-Fi MAC + # and a Bluetooth MAC. Both must round-trip into the HA device. + assert ("mac", "00:00:00:00:00:01") in device_entry.connections + assert ("bluetooth", "aa:bb:cc:dd:ee:ff") in device_entry.connections