Register MAC connection for Elgato devices (#64201)

* Register MAC connection for Elgato devices

* Add tests, fix name
This commit is contained in:
Franck Nijhof 2022-01-16 22:18:46 +01:00 committed by GitHub
parent 32d4f104ff
commit 423674c0c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 70 additions and 16 deletions

View File

@ -7,6 +7,7 @@ from elgato import Elgato, ElgatoError, Info
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MAC
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -25,15 +26,17 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up Elgato button based on a config entry.""" """Set up Elgato button based on a config entry."""
data: HomeAssistantElgatoData = hass.data[DOMAIN][entry.entry_id] 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): class ElgatoIdentifyButton(ElgatoEntity, ButtonEntity):
"""Defines an Elgato identify button.""" """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.""" """Initialize the button entity."""
super().__init__(client, info) super().__init__(client, info, mac)
self.entity_description = ButtonEntityDescription( self.entity_description = ButtonEntityDescription(
key="identify", key="identify",
name="Identify", name="Identify",

View File

@ -8,7 +8,7 @@ import voluptuous as vol
from homeassistant.components import zeroconf from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow 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.core import callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -24,6 +24,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN):
host: str host: str
port: int port: int
serial_number: str serial_number: str
mac: str | None = None
async def async_step_user( async def async_step_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
@ -47,6 +48,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN):
) -> FlowResult: ) -> FlowResult:
"""Handle zeroconf discovery.""" """Handle zeroconf discovery."""
self.host = discovery_info.host self.host = discovery_info.host
self.mac = discovery_info.properties.get("id")
self.port = discovery_info.port or 9123 self.port = discovery_info.port or 9123
try: try:
@ -89,6 +91,7 @@ class ElgatoFlowHandler(ConfigFlow, domain=DOMAIN):
data={ data={
CONF_HOST: self.host, CONF_HOST: self.host,
CONF_PORT: self.port, 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 info.serial_number, raise_on_progress=raise_on_progress
) )
self._abort_if_unique_id_configured( 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 self.serial_number = info.serial_number

View File

@ -1,7 +1,9 @@
"""Base entity for the Elgato integration.""" """Base entity for the Elgato integration."""
from __future__ import annotations
from elgato import Elgato, Info from elgato import Elgato, Info
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac
from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity import DeviceInfo, Entity
from .const import DOMAIN from .const import DOMAIN
@ -10,13 +12,17 @@ from .const import DOMAIN
class ElgatoEntity(Entity): class ElgatoEntity(Entity):
"""Defines an Elgato 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.""" """Initialize an Elgato entity."""
self.client = client self.client = client
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, info.serial_number)}, identifiers={(DOMAIN, info.serial_number)},
manufacturer="Elgato", manufacturer="Elgato",
model=info.product_name, model=info.product_name,
name=info.product_name, name=info.display_name,
sw_version=f"{info.firmware_version} ({info.firmware_build_number})", 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))
}

View File

@ -16,6 +16,7 @@ from homeassistant.components.light import (
LightEntity, LightEntity,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MAC
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.entity_platform import (
AddEntitiesCallback, AddEntitiesCallback,
@ -40,7 +41,9 @@ async def async_setup_entry(
"""Set up Elgato Light based on a config entry.""" """Set up Elgato Light based on a config entry."""
data: HomeAssistantElgatoData = hass.data[DOMAIN][entry.entry_id] data: HomeAssistantElgatoData = hass.data[DOMAIN][entry.entry_id]
settings = await data.client.settings() 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_get_current_platform()
platform.async_register_entity_service( platform.async_register_entity_service(
@ -53,9 +56,11 @@ async def async_setup_entry(
class ElgatoLight(ElgatoEntity, LightEntity): class ElgatoLight(ElgatoEntity, LightEntity):
"""Defines an Elgato Light.""" """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.""" """Initialize Elgato Light."""
super().__init__(client, info) super().__init__(client, info, mac)
self._state: State | None = None self._state: State | None = None
min_mired = 143 min_mired = 143

View File

@ -6,7 +6,7 @@ from elgato import Info, Settings, State
import pytest import pytest
from homeassistant.components.elgato.const import DOMAIN 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 homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry, load_fixture
@ -19,7 +19,11 @@ def mock_config_entry() -> MockConfigEntry:
return MockConfigEntry( return MockConfigEntry(
title="CN11A1A00001", title="CN11A1A00001",
domain=DOMAIN, 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", unique_id="CN11A1A00001",
) )

View File

@ -5,9 +5,10 @@ from elgato import ElgatoError
import pytest import pytest
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS 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.const import ATTR_ENTITY_ID, ATTR_ICON, STATE_UNKNOWN
from homeassistant.core import HomeAssistant 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 homeassistant.helpers.entity import EntityCategory
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -20,6 +21,7 @@ async def test_button_identify(
mock_elgato: MagicMock, mock_elgato: MagicMock,
) -> None: ) -> None:
"""Test the Elgato identify button.""" """Test the Elgato identify button."""
device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
state = hass.states.get("button.identify") state = hass.states.get("button.identify")
@ -32,6 +34,20 @@ async def test_button_identify(
assert entry.unique_id == "CN11A1A00001_identify" assert entry.unique_id == "CN11A1A00001_identify"
assert entry.entity_category == EntityCategory.CONFIG 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( await hass.services.async_call(
BUTTON_DOMAIN, BUTTON_DOMAIN,
SERVICE_PRESS, SERVICE_PRESS,

View File

@ -6,7 +6,7 @@ from elgato import ElgatoConnectionError
from homeassistant.components import zeroconf from homeassistant.components import zeroconf
from homeassistant.components.elgato.const import DOMAIN from homeassistant.components.elgato.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF 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.core import HomeAssistant
from homeassistant.data_entry_flow import ( from homeassistant.data_entry_flow import (
RESULT_TYPE_ABORT, RESULT_TYPE_ABORT,
@ -40,6 +40,7 @@ async def test_full_user_flow_implementation(
assert result2.get("title") == "CN11A1A00001" assert result2.get("title") == "CN11A1A00001"
assert result2.get("data") == { assert result2.get("data") == {
CONF_HOST: "127.0.0.1", CONF_HOST: "127.0.0.1",
CONF_MAC: None,
CONF_PORT: 9123, CONF_PORT: 9123,
} }
assert "result" in result2 assert "result" in result2
@ -63,7 +64,7 @@ async def test_full_zeroconf_flow_implementation(
hostname="example.local.", hostname="example.local.",
name="mock_name", name="mock_name",
port=9123, port=9123,
properties={}, properties={"id": "AA:BB:CC:DD:EE:FF"},
type="mock_type", type="mock_type",
), ),
) )
@ -87,6 +88,7 @@ async def test_full_zeroconf_flow_implementation(
assert result2.get("title") == "CN11A1A00001" assert result2.get("title") == "CN11A1A00001"
assert result2.get("data") == { assert result2.get("data") == {
CONF_HOST: "127.0.0.1", CONF_HOST: "127.0.0.1",
CONF_MAC: "AA:BB:CC:DD:EE:FF",
CONF_PORT: 9123, CONF_PORT: 9123,
} }
assert "result" in result2 assert "result" in result2

View File

@ -25,7 +25,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
from homeassistant.core import HomeAssistant 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 from tests.common import MockConfigEntry
@ -36,6 +36,7 @@ async def test_light_state_temperature(
mock_elgato: MagicMock, mock_elgato: MagicMock,
) -> None: ) -> None:
"""Test the creation and values of the Elgato Lights in temperature mode.""" """Test the creation and values of the Elgato Lights in temperature mode."""
device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
# First segment of the strip # First segment of the strip
@ -54,6 +55,20 @@ async def test_light_state_temperature(
assert entry assert entry
assert entry.unique_id == "CN11A1A00001" 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( @pytest.mark.parametrize(
"mock_elgato", [{"settings": "color", "state": "color"}], indirect=True "mock_elgato", [{"settings": "color", "state": "color"}], indirect=True