diff --git a/homeassistant/components/indevolt/coordinator.py b/homeassistant/components/indevolt/coordinator.py index f6f2a18cb4a7..fba667f86854 100644 --- a/homeassistant/components/indevolt/coordinator.py +++ b/homeassistant/components/indevolt/coordinator.py @@ -17,6 +17,7 @@ from homeassistant.const import CONF_HOST, CONF_MODEL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( @@ -39,6 +40,7 @@ class IndevoltCoordinator(DataUpdateCoordinator[dict[str, Any]]): friendly_name: str config_entry: IndevoltConfigEntry firmware_version: str | None + mac_address: str | None serial_number: str device_model: str generation: int @@ -74,8 +76,9 @@ class IndevoltCoordinator(DataUpdateCoordinator[dict[str, Any]]): # Cache device information device_data = config_data.get("device", {}) - self.firmware_version = device_data.get("fw") + raw_mac = device_data.get("mac") + self.mac_address = format_mac(raw_mac) if raw_mac else None async def _async_update_data(self) -> dict[str, Any]: """Fetch raw JSON data from the device.""" diff --git a/homeassistant/components/indevolt/diagnostics.py b/homeassistant/components/indevolt/diagnostics.py index 8047701c8b7f..c2de67fb5730 100644 --- a/homeassistant/components/indevolt/diagnostics.py +++ b/homeassistant/components/indevolt/diagnostics.py @@ -25,6 +25,12 @@ TO_REDACT = { } +def _redact_mac(mac_address: str) -> str: + """Redact the device-specific part of a MAC address (keep OUI, used for discovery).""" + parts = mac_address.split(":") + return ":".join([*parts[:3], "XX", "XX", "XX"]) + + async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: IndevoltConfigEntry ) -> dict[str, Any]: @@ -36,6 +42,9 @@ async def async_get_config_entry_diagnostics( "generation": coordinator.generation, "serial_number": coordinator.serial_number, "firmware_version": coordinator.firmware_version, + "mac_address": _redact_mac(coordinator.mac_address) + if coordinator.mac_address + else None, } return { diff --git a/homeassistant/components/indevolt/entity.py b/homeassistant/components/indevolt/entity.py index da87036a33a3..de4b5a209c8d 100644 --- a/homeassistant/components/indevolt/entity.py +++ b/homeassistant/components/indevolt/entity.py @@ -1,6 +1,6 @@ """Base entity for Indevolt integration.""" -from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN @@ -21,8 +21,12 @@ class IndevoltEntity(CoordinatorEntity[IndevoltCoordinator]): def device_info(self) -> DeviceInfo: """Return device information for registry.""" coordinator = self.coordinator + connections: set[tuple[str, str]] = set() + if coordinator.mac_address: + connections.add((CONNECTION_NETWORK_MAC, coordinator.mac_address)) return DeviceInfo( identifiers={(DOMAIN, coordinator.serial_number)}, + connections=connections, manufacturer="INDEVOLT", serial_number=coordinator.serial_number, model=coordinator.device_model, diff --git a/tests/components/indevolt/conftest.py b/tests/components/indevolt/conftest.py index e91fe6c963a9..11a0fac750e7 100644 --- a/tests/components/indevolt/conftest.py +++ b/tests/components/indevolt/conftest.py @@ -19,21 +19,26 @@ ALT_TEST_HOST = "192.168.1.101" TEST_PORT = 8080 TEST_DEVICE_SN_GEN1 = "BK1600-12345678" TEST_DEVICE_SN_GEN2 = "SolidFlex2000-87654321" -TEST_FW_VERSION = "1.2.3" +TEST_MODEL_GEN1 = "BK1600" +TEST_MODEL_GEN2 = "CMS-SF2000" -# Map device fixture names to generation and fixture files +# Create DeviceInfo per generation DEVICE_MAPPING = { 1: { - "device": "BK1600", + "device": TEST_MODEL_GEN1, "generation": 1, "sn": TEST_DEVICE_SN_GEN1, "host": ALT_TEST_HOST, + "mac": "aa:bb:cc:11:22:33", + "fw": "1.2.3", }, 2: { - "device": "CMS-SF2000", + "device": TEST_MODEL_GEN2, "generation": 2, "sn": TEST_DEVICE_SN_GEN2, "host": TEST_HOST, + "mac": "aa:bb:cc:44:55:66", + "fw": "1.2.3", }, } @@ -121,7 +126,8 @@ def mock_indevolt(generation: int) -> Generator[AsyncMock]: "sn": device_info["sn"], "type": device_info["device"], "generation": device_info["generation"], - "fw": TEST_FW_VERSION, + "fw": device_info["fw"], + "mac": device_info["mac"], } } diff --git a/tests/components/indevolt/snapshots/test_diagnostics.ambr b/tests/components/indevolt/snapshots/test_diagnostics.ambr index 0df488c530db..28e91d6d6b1a 100644 --- a/tests/components/indevolt/snapshots/test_diagnostics.ambr +++ b/tests/components/indevolt/snapshots/test_diagnostics.ambr @@ -39,6 +39,7 @@ 'device': dict({ 'firmware_version': '1.2.3', 'generation': 1, + 'mac_address': 'aa:bb:cc:XX:XX:XX', 'model': 'BK1600', 'serial_number': '**REDACTED**', }), @@ -137,6 +138,7 @@ 'device': dict({ 'firmware_version': '1.2.3', 'generation': 2, + 'mac_address': 'aa:bb:cc:XX:XX:XX', 'model': 'CMS-SF2000', 'serial_number': '**REDACTED**', }), diff --git a/tests/components/indevolt/test_init.py b/tests/components/indevolt/test_init.py index e39f8e479e97..3f495e6d99ae 100644 --- a/tests/components/indevolt/test_init.py +++ b/tests/components/indevolt/test_init.py @@ -6,8 +6,11 @@ import pytest from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from . import setup_integration +from .conftest import DEVICE_MAPPING from tests.common import MockConfigEntry @@ -30,6 +33,31 @@ async def test_load_unload( assert mock_config_entry.state is ConfigEntryState.NOT_LOADED +@pytest.mark.parametrize("generation", [1, 2], indirect=True) +async def test_device_info( + hass: HomeAssistant, + mock_indevolt: AsyncMock, + mock_config_entry: MockConfigEntry, + generation: int, + device_registry: dr.DeviceRegistry, +) -> None: + """Test that device info is correctly registered in the device registry.""" + await setup_integration(hass, mock_config_entry) + + device_info = DEVICE_MAPPING[generation] + device_entry = device_registry.async_get_device( + connections={(CONNECTION_NETWORK_MAC, device_info["mac"])} + ) + + assert device_entry is not None + assert device_entry.manufacturer == "INDEVOLT" + assert device_entry.model == device_info["device"] + assert device_entry.serial_number == device_info["sn"] + assert device_entry.sw_version == device_info["fw"] + assert device_entry.hw_version == str(device_info["generation"]) + assert (CONNECTION_NETWORK_MAC, device_info["mac"]) in device_entry.connections + + @pytest.mark.parametrize("generation", [2], indirect=True) async def test_load_failure( hass: HomeAssistant, mock_indevolt: AsyncMock, mock_config_entry: MockConfigEntry