From 564150169bfc69efdfeda25a99d803441f3a4b10 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 16 Sep 2022 08:35:12 +0200 Subject: [PATCH] Update LaMetric config entry using DHCP discovery data (#78527) * Update LaMetric config entry using DHCP discovery data * Update translations --- .../components/lametric/config_flow.py | 18 +++++++ .../components/lametric/manifest.json | 3 +- .../components/lametric/strings.json | 3 +- .../components/lametric/translations/en.json | 3 +- homeassistant/generated/dhcp.py | 1 + tests/components/lametric/test_config_flow.py | 50 ++++++++++++++++++- 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/lametric/config_flow.py b/homeassistant/components/lametric/config_flow.py index 4bb293b0a4d..b91fff6cf37 100644 --- a/homeassistant/components/lametric/config_flow.py +++ b/homeassistant/components/lametric/config_flow.py @@ -20,6 +20,7 @@ from demetriek import ( import voluptuous as vol from yarl import URL +from homeassistant.components.dhcp import DhcpServiceInfo from homeassistant.components.ssdp import ( ATTR_UPNP_FRIENDLY_NAME, ATTR_UPNP_SERIAL, @@ -29,6 +30,7 @@ from homeassistant.const import CONF_API_KEY, CONF_DEVICE, CONF_HOST, CONF_MAC from homeassistant.data_entry_flow import AbortFlow, FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler +from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.selector import ( SelectOptionDict, SelectSelector, @@ -245,6 +247,22 @@ class LaMetricFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN): }, ) + async def async_step_dhcp(self, discovery_info: DhcpServiceInfo) -> FlowResult: + """Handle dhcp discovery to update existing entries.""" + mac = format_mac(discovery_info.macaddress) + for entry in self._async_current_entries(): + if format_mac(entry.data[CONF_MAC]) == mac: + self.hass.config_entries.async_update_entry( + entry, + data=entry.data | {CONF_HOST: discovery_info.ip}, + ) + self.hass.async_create_task( + self.hass.config_entries.async_reload(entry.entry_id) + ) + return self.async_abort(reason="already_configured") + + return self.async_abort(reason="unknown") + # Replace OAuth create entry with a fetch devices step # LaMetric only use OAuth to get device information, but doesn't # use it later on. diff --git a/homeassistant/components/lametric/manifest.json b/homeassistant/components/lametric/manifest.json index 735c05e659c..251378860be 100644 --- a/homeassistant/components/lametric/manifest.json +++ b/homeassistant/components/lametric/manifest.json @@ -12,5 +12,6 @@ { "deviceType": "urn:schemas-upnp-org:device:LaMetric:1" } - ] + ], + "dhcp": [{ "registered_devices": true }] } diff --git a/homeassistant/components/lametric/strings.json b/homeassistant/components/lametric/strings.json index 53271a8d0d8..433f70df18d 100644 --- a/homeassistant/components/lametric/strings.json +++ b/homeassistant/components/lametric/strings.json @@ -38,7 +38,8 @@ "link_local_address": "Link local addresses are not supported", "missing_configuration": "The LaMetric integration is not configured. Please follow the documentation.", "no_devices": "The authorized user has no LaMetric devices", - "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]" + "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]", + "unknown": "[%key:common::config_flow::error::unknown%]" } }, "issues": { diff --git a/homeassistant/components/lametric/translations/en.json b/homeassistant/components/lametric/translations/en.json index c02b7d6d05f..52e483ec1f0 100644 --- a/homeassistant/components/lametric/translations/en.json +++ b/homeassistant/components/lametric/translations/en.json @@ -7,7 +7,8 @@ "link_local_address": "Link local addresses are not supported", "missing_configuration": "The LaMetric integration is not configured. Please follow the documentation.", "no_devices": "The authorized user has no LaMetric devices", - "no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})" + "no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})", + "unknown": "Unexpected error" }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 8ced5265136..11e0bbb0405 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -60,6 +60,7 @@ DHCP: list[dict[str, str | bool]] = [ {'domain': 'isy994', 'registered_devices': True}, {'domain': 'isy994', 'hostname': 'isy*', 'macaddress': '0021B9*'}, {'domain': 'isy994', 'hostname': 'polisy*', 'macaddress': '000DB9*'}, + {'domain': 'lametric', 'registered_devices': True}, {'domain': 'lifx', 'macaddress': 'D073D5*'}, {'domain': 'lifx', 'registered_devices': True}, {'domain': 'litterrobot', 'hostname': 'litter-robot4'}, diff --git a/tests/components/lametric/test_config_flow.py b/tests/components/lametric/test_config_flow.py index 2134ee135f6..338fe5052d1 100644 --- a/tests/components/lametric/test_config_flow.py +++ b/tests/components/lametric/test_config_flow.py @@ -11,13 +11,14 @@ from demetriek import ( ) import pytest +from homeassistant.components.dhcp import DhcpServiceInfo from homeassistant.components.lametric.const import DOMAIN from homeassistant.components.ssdp import ( ATTR_UPNP_FRIENDLY_NAME, ATTR_UPNP_SERIAL, SsdpServiceInfo, ) -from homeassistant.config_entries import SOURCE_SSDP, SOURCE_USER +from homeassistant.config_entries import SOURCE_DHCP, SOURCE_SSDP, SOURCE_USER from homeassistant.const import CONF_API_KEY, CONF_DEVICE, CONF_HOST, CONF_MAC from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -695,3 +696,50 @@ async def test_cloud_errors( assert len(mock_lametric_config_flow.device.mock_calls) == 2 assert len(mock_lametric_config_flow.notify.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_dhcp_discovery_updates_entry( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, +) -> None: + """Test DHCP discovery updates config entries.""" + mock_config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=DhcpServiceInfo( + hostname="lametric", + ip="127.0.0.42", + macaddress="aa:bb:cc:dd:ee:ff", + ), + ) + + assert result.get("type") == FlowResultType.ABORT + assert result.get("reason") == "already_configured" + assert mock_config_entry.data == { + CONF_API_KEY: "mock-from-fixture", + CONF_HOST: "127.0.0.42", + CONF_MAC: "AA:BB:CC:DD:EE:FF", + } + + +async def test_dhcp_unknown_device( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, +) -> None: + """Test unknown DHCP discovery aborts flow.""" + mock_config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=DhcpServiceInfo( + hostname="lametric", + ip="127.0.0.42", + macaddress="aa:bb:cc:dd:ee:00", + ), + ) + + assert result.get("type") == FlowResultType.ABORT + assert result.get("reason") == "unknown"