Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions homeassistant/components/music_assistant/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
6 changes: 5 additions & 1 deletion tests/components/music_assistant/fixtures/players.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
40 changes: 40 additions & 0 deletions tests/components/music_assistant/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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."
),
)
Comment thread
trudenboy marked this conversation as resolved.
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()
Comment thread
trudenboy marked this conversation as resolved.
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).
Comment thread
trudenboy marked this conversation as resolved.
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