From 98ca9754d78856aa359179e5bf1f3c3122c05fa2 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 29 Mar 2022 01:01:17 +0200 Subject: [PATCH] Motion Blinds dhcp discovery (#68809) Co-authored-by: J. Nick Koston --- .../components/motion_blinds/config_flow.py | 21 +++++++++++-- .../components/motion_blinds/manifest.json | 6 ++++ .../components/motion_blinds/strings.json | 1 + .../motion_blinds/translations/en.json | 2 +- homeassistant/generated/dhcp.py | 2 ++ .../motion_blinds/test_config_flow.py | 31 +++++++++++++++++++ 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/motion_blinds/config_flow.py b/homeassistant/components/motion_blinds/config_flow.py index d93a1abd865..a956289d72e 100644 --- a/homeassistant/components/motion_blinds/config_flow.py +++ b/homeassistant/components/motion_blinds/config_flow.py @@ -5,9 +5,11 @@ from motionblinds import AsyncMotionMulticast, MotionDiscovery import voluptuous as vol from homeassistant import config_entries -from homeassistant.components import network +from homeassistant.components import dhcp, network from homeassistant.const import CONF_API_KEY, CONF_HOST from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.device_registry import format_mac from .const import ( CONF_INTERFACE, @@ -72,6 +74,21 @@ class MotionBlindsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Get the options flow.""" return OptionsFlowHandler(config_entry) + async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: + """Handle discovery via dhcp.""" + mac_address = format_mac(discovery_info.macaddress).replace(":", "") + await self.async_set_unique_id(mac_address) + self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip}) + + short_mac = mac_address[-6:].upper() + self.context["title_placeholders"] = { + "short_mac": short_mac, + "ip_address": discovery_info.ip, + } + + self._host = discovery_info.ip + return await self.async_step_connect() + async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" errors = {} @@ -137,7 +154,7 @@ class MotionBlindsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): mac_address = motion_gateway.mac - await self.async_set_unique_id(mac_address) + await self.async_set_unique_id(mac_address, raise_on_progress=False) self._abort_if_unique_id_configured( updates={ CONF_HOST: self._host, diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index adfe7722ca4..c218c4cdd1f 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -5,6 +5,12 @@ "documentation": "https://www.home-assistant.io/integrations/motion_blinds", "requirements": ["motionblinds==0.6.2"], "dependencies": ["network"], + "dhcp": [ + {"registered_devices": true}, + { + "hostname": "motion_*" + } + ], "codeowners": ["@starkillerOG"], "iot_class": "local_push", "loggers": ["motionblinds"] diff --git a/homeassistant/components/motion_blinds/strings.json b/homeassistant/components/motion_blinds/strings.json index 9b64da7add4..c62c6dc2873 100644 --- a/homeassistant/components/motion_blinds/strings.json +++ b/homeassistant/components/motion_blinds/strings.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{short_mac} ({ip_address})", "step": { "user": { "description": "Connect to your Motion Gateway, if the IP address is not set, auto-discovery is used", diff --git a/homeassistant/components/motion_blinds/translations/en.json b/homeassistant/components/motion_blinds/translations/en.json index 5e111278a8d..92931ee27ab 100644 --- a/homeassistant/components/motion_blinds/translations/en.json +++ b/homeassistant/components/motion_blinds/translations/en.json @@ -9,7 +9,7 @@ "discovery_error": "Failed to discover a Motion Gateway", "invalid_interface": "Invalid network interface" }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 48760218f3c..3780b7914c4 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -55,6 +55,8 @@ DHCP: list[dict[str, str | bool]] = [ {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': '48A2E6*'}, {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': 'B82CA0*'}, {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': '00D02D*'}, + {'domain': 'motion_blinds', 'registered_devices': True}, + {'domain': 'motion_blinds', 'hostname': 'motion_*'}, {'domain': 'myq', 'macaddress': '645299*'}, {'domain': 'nest', 'macaddress': '18B430*'}, {'domain': 'nest', 'macaddress': '641666*'}, diff --git a/tests/components/motion_blinds/test_config_flow.py b/tests/components/motion_blinds/test_config_flow.py index 77f5e242974..fce86f5f343 100644 --- a/tests/components/motion_blinds/test_config_flow.py +++ b/tests/components/motion_blinds/test_config_flow.py @@ -5,6 +5,7 @@ from unittest.mock import Mock, patch import pytest from homeassistant import config_entries, data_entry_flow +from homeassistant.components import dhcp from homeassistant.components.motion_blinds import const from homeassistant.components.motion_blinds.config_flow import DEFAULT_GATEWAY_NAME from homeassistant.const import CONF_API_KEY, CONF_HOST @@ -337,6 +338,36 @@ async def test_config_flow_invalid_interface(hass): assert result["errors"] == {const.CONF_INTERFACE: "invalid_interface"} +async def test_dhcp_flow(hass): + """Successful flow from DHCP discovery.""" + dhcp_data = dhcp.DhcpServiceInfo( + ip=TEST_HOST, + hostname="MOTION_abcdef", + macaddress=TEST_MAC, + ) + + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=dhcp_data + ) + + assert result["type"] == "form" + assert result["step_id"] == "connect" + assert result["errors"] == {} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: TEST_API_KEY}, + ) + + assert result["type"] == "create_entry" + assert result["title"] == DEFAULT_GATEWAY_NAME + assert result["data"] == { + CONF_HOST: TEST_HOST, + CONF_API_KEY: TEST_API_KEY, + const.CONF_INTERFACE: TEST_HOST_HA, + } + + async def test_options_flow(hass): """Test specifying non default settings using options flow.""" config_entry = MockConfigEntry(