From 423674c0c98e053b6680b3641bc522b6ffd6e4de Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sun, 16 Jan 2022 22:18:46 +0100 Subject: [PATCH] Register MAC connection for Elgato devices (#64201) * Register MAC connection for Elgato devices * Add tests, fix name --- homeassistant/components/elgato/button.py | 9 ++++++--- homeassistant/components/elgato/config_flow.py | 7 +++++-- homeassistant/components/elgato/entity.py | 10 ++++++++-- homeassistant/components/elgato/light.py | 11 ++++++++--- tests/components/elgato/conftest.py | 8 ++++++-- tests/components/elgato/test_button.py | 18 +++++++++++++++++- tests/components/elgato/test_config_flow.py | 6 ++++-- tests/components/elgato/test_light.py | 17 ++++++++++++++++- 8 files changed, 70 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/elgato/button.py b/homeassistant/components/elgato/button.py index 5baff347fdf..f2cfc2b8673 100644 --- a/homeassistant/components/elgato/button.py +++ b/homeassistant/components/elgato/button.py @@ -7,6 +7,7 @@ from elgato import Elgato, ElgatoError, Info from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_MAC from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -25,15 +26,17 @@ async def async_setup_entry( ) -> None: """Set up Elgato button based on a config entry.""" data: HomeAssistantElgatoData = hass.data[DOMAIN][entry.entry_id] - async_add_entities([ElgatoIdentifyButton(data.client, data.info)]) + async_add_entities( + [ElgatoIdentifyButton(data.client, data.info, entry.data.get(CONF_MAC))] + ) class ElgatoIdentifyButton(ElgatoEntity, ButtonEntity): """Defines an Elgato identify button.""" - def __init__(self, client: Elgato, info: Info) -> None: + def __init__(self, client: Elgato, info: Info, mac: str | None) -> None: """Initialize the button entity.""" - super().__init__(client, info) + super().__init__(client, info, mac) self.entity_description = ButtonEntityDescription( key="identify", name="Identify", diff --git a/homeassistant/components/elgato/config_flow.py b/homeassistant/components/elgato/config_flow.py index 27883bbcfb5..7abf570ba3e 100644 --- a/homeassistant/components/elgato/config_flow.py +++ b/homeassistant/components/elgato/config_flow.py @@ -8,7 +8,7 @@ import voluptuous as vol from homeassistant.components import zeroconf from homeassistant.config_entries import ConfigFlow -from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PORT from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -24,6 +24,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN): host: str port: int serial_number: str + mac: str | None = None async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -47,6 +48,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Handle zeroconf discovery.""" self.host = discovery_info.host + self.mac = discovery_info.properties.get("id") self.port = discovery_info.port or 9123 try: @@ -89,6 +91,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN): data={ CONF_HOST: self.host, CONF_PORT: self.port, + CONF_MAC: self.mac, }, ) @@ -107,7 +110,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN): info.serial_number, raise_on_progress=raise_on_progress ) self._abort_if_unique_id_configured( - updates={CONF_HOST: self.host, CONF_PORT: self.port} + updates={CONF_HOST: self.host, CONF_PORT: self.port, CONF_MAC: self.mac} ) self.serial_number = info.serial_number diff --git a/homeassistant/components/elgato/entity.py b/homeassistant/components/elgato/entity.py index e4f7cc440e5..b0c1be2871a 100644 --- a/homeassistant/components/elgato/entity.py +++ b/homeassistant/components/elgato/entity.py @@ -1,7 +1,9 @@ """Base entity for the Elgato integration.""" +from __future__ import annotations from elgato import Elgato, Info +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac from homeassistant.helpers.entity import DeviceInfo, Entity from .const import DOMAIN @@ -10,13 +12,17 @@ from .const import DOMAIN class ElgatoEntity(Entity): """Defines an Elgato entity.""" - def __init__(self, client: Elgato, info: Info) -> None: + def __init__(self, client: Elgato, info: Info, mac: str | None) -> None: """Initialize an Elgato entity.""" self.client = client self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, info.serial_number)}, manufacturer="Elgato", model=info.product_name, - name=info.product_name, + name=info.display_name, sw_version=f"{info.firmware_version} ({info.firmware_build_number})", ) + if mac is not None: + self._attr_device_info["connections"] = { + (CONNECTION_NETWORK_MAC, format_mac(mac)) + } diff --git a/homeassistant/components/elgato/light.py b/homeassistant/components/elgato/light.py index 6f4209f23fe..9d901ff0857 100644 --- a/homeassistant/components/elgato/light.py +++ b/homeassistant/components/elgato/light.py @@ -16,6 +16,7 @@ from homeassistant.components.light import ( LightEntity, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_MAC from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import ( AddEntitiesCallback, @@ -40,7 +41,9 @@ async def async_setup_entry( """Set up Elgato Light based on a config entry.""" data: HomeAssistantElgatoData = hass.data[DOMAIN][entry.entry_id] settings = await data.client.settings() - async_add_entities([ElgatoLight(data.client, data.info, settings)], True) + async_add_entities( + [ElgatoLight(data.client, data.info, entry.data.get(CONF_MAC), settings)], True + ) platform = async_get_current_platform() platform.async_register_entity_service( @@ -53,9 +56,11 @@ async def async_setup_entry( class ElgatoLight(ElgatoEntity, LightEntity): """Defines an Elgato Light.""" - def __init__(self, client: Elgato, info: Info, settings: Settings) -> None: + def __init__( + self, client: Elgato, info: Info, mac: str | None, settings: Settings + ) -> None: """Initialize Elgato Light.""" - super().__init__(client, info) + super().__init__(client, info, mac) self._state: State | None = None min_mired = 143 diff --git a/tests/components/elgato/conftest.py b/tests/components/elgato/conftest.py index 323d9cad7b7..efae0739c7b 100644 --- a/tests/components/elgato/conftest.py +++ b/tests/components/elgato/conftest.py @@ -6,7 +6,7 @@ from elgato import Info, Settings, State import pytest from homeassistant.components.elgato.const import DOMAIN -from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PORT from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, load_fixture @@ -19,7 +19,11 @@ def mock_config_entry() -> MockConfigEntry: return MockConfigEntry( title="CN11A1A00001", domain=DOMAIN, - data={CONF_HOST: "127.0.0.1", CONF_PORT: 9123}, + data={ + CONF_HOST: "127.0.0.1", + CONF_MAC: "AA:BB:CC:DD:EE:FF", + CONF_PORT: 9123, + }, unique_id="CN11A1A00001", ) diff --git a/tests/components/elgato/test_button.py b/tests/components/elgato/test_button.py index 4c29c49daec..f44299285d2 100644 --- a/tests/components/elgato/test_button.py +++ b/tests/components/elgato/test_button.py @@ -5,9 +5,10 @@ from elgato import ElgatoError import pytest from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.components.elgato.const import DOMAIN from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON, STATE_UNKNOWN from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.entity import EntityCategory from tests.common import MockConfigEntry @@ -20,6 +21,7 @@ async def test_button_identify( mock_elgato: MagicMock, ) -> None: """Test the Elgato identify button.""" + device_registry = dr.async_get(hass) entity_registry = er.async_get(hass) state = hass.states.get("button.identify") @@ -32,6 +34,20 @@ async def test_button_identify( assert entry.unique_id == "CN11A1A00001_identify" assert entry.entity_category == EntityCategory.CONFIG + assert entry.device_id + device_entry = device_registry.async_get(entry.device_id) + assert device_entry + assert device_entry.configuration_url is None + assert device_entry.connections == { + (dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:dd:ee:ff") + } + assert device_entry.entry_type is None + assert device_entry.identifiers == {(DOMAIN, "CN11A1A00001")} + assert device_entry.manufacturer == "Elgato" + assert device_entry.model == "Elgato Key Light" + assert device_entry.name == "Frenck" + assert device_entry.sw_version == "1.0.3 (192)" + await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, diff --git a/tests/components/elgato/test_config_flow.py b/tests/components/elgato/test_config_flow.py index 19d227134a0..dffd59cedcc 100644 --- a/tests/components/elgato/test_config_flow.py +++ b/tests/components/elgato/test_config_flow.py @@ -6,7 +6,7 @@ from elgato import ElgatoConnectionError from homeassistant.components import zeroconf from homeassistant.components.elgato.const import DOMAIN from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF -from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SOURCE +from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PORT, CONF_SOURCE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import ( RESULT_TYPE_ABORT, @@ -40,6 +40,7 @@ async def test_full_user_flow_implementation( assert result2.get("title") == "CN11A1A00001" assert result2.get("data") == { CONF_HOST: "127.0.0.1", + CONF_MAC: None, CONF_PORT: 9123, } assert "result" in result2 @@ -63,7 +64,7 @@ async def test_full_zeroconf_flow_implementation( hostname="example.local.", name="mock_name", port=9123, - properties={}, + properties={"id": "AA:BB:CC:DD:EE:FF"}, type="mock_type", ), ) @@ -87,6 +88,7 @@ async def test_full_zeroconf_flow_implementation( assert result2.get("title") == "CN11A1A00001" assert result2.get("data") == { CONF_HOST: "127.0.0.1", + CONF_MAC: "AA:BB:CC:DD:EE:FF", CONF_PORT: 9123, } assert "result" in result2 diff --git a/tests/components/elgato/test_light.py b/tests/components/elgato/test_light.py index a037163d260..b96dfcceea7 100644 --- a/tests/components/elgato/test_light.py +++ b/tests/components/elgato/test_light.py @@ -25,7 +25,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from tests.common import MockConfigEntry @@ -36,6 +36,7 @@ async def test_light_state_temperature( mock_elgato: MagicMock, ) -> None: """Test the creation and values of the Elgato Lights in temperature mode.""" + device_registry = dr.async_get(hass) entity_registry = er.async_get(hass) # First segment of the strip @@ -54,6 +55,20 @@ async def test_light_state_temperature( assert entry assert entry.unique_id == "CN11A1A00001" + assert entry.device_id + device_entry = device_registry.async_get(entry.device_id) + assert device_entry + assert device_entry.configuration_url is None + assert device_entry.connections == { + (dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:dd:ee:ff") + } + assert device_entry.entry_type is None + assert device_entry.identifiers == {(DOMAIN, "CN11A1A00001")} + assert device_entry.manufacturer == "Elgato" + assert device_entry.model == "Elgato Key Light" + assert device_entry.name == "Frenck" + assert device_entry.sw_version == "1.0.3 (192)" + @pytest.mark.parametrize( "mock_elgato", [{"settings": "color", "state": "color"}], indirect=True