diff --git a/homeassistant/components/insteon/config_flow.py b/homeassistant/components/insteon/config_flow.py index 5bf8769f321..be68a66b70a 100644 --- a/homeassistant/components/insteon/config_flow.py +++ b/homeassistant/components/insteon/config_flow.py @@ -7,7 +7,7 @@ from pyinsteon import async_connect import voluptuous as vol from homeassistant import config_entries -from homeassistant.components import usb +from homeassistant.components import dhcp, usb from homeassistant.const import ( CONF_ADDRESS, CONF_DEVICE, @@ -19,6 +19,7 @@ from homeassistant.const import ( ) from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.dispatcher import async_dispatcher_send from .const import ( @@ -114,6 +115,7 @@ class InsteonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): _device_path: str | None = None _device_name: str | None = None + discovered_conf: dict[str, str] = {} @staticmethod @callback @@ -170,7 +172,7 @@ class InsteonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry(title="", data=user_input) user_input.pop(CONF_HUB_VERSION) errors["base"] = "cannot_connect" - schema_defaults = user_input if user_input is not None else {} + schema_defaults = user_input if user_input is not None else self.discovered_conf data_schema = build_hub_schema(hub_version=hub_version, **schema_defaults) step_id = STEP_HUB_V2 if hub_version == 2 else STEP_HUB_V1 return self.async_show_form( @@ -203,12 +205,14 @@ class InsteonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): discovery_info.pid, ) self._set_confirm_only() - self.context["title_placeholders"] = {CONF_NAME: self._device_name} + self.context["title_placeholders"] = { + CONF_NAME: f"Insteon PLM {self._device_name}" + } await self.async_set_unique_id(config_entries.DEFAULT_DISCOVERY_UNIQUE_ID) return await self.async_step_confirm_usb() async def async_step_confirm_usb(self, user_input=None): - """Confirm a discovery.""" + """Confirm a USB discovery.""" if user_input is not None: return await self.async_step_plm({CONF_DEVICE: self._device_path}) @@ -217,6 +221,15 @@ class InsteonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): description_placeholders={CONF_NAME: self._device_name}, ) + async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: + """Handle a DHCP discovery.""" + self.discovered_conf = {CONF_HOST: discovery_info.ip} + self.context["title_placeholders"] = { + CONF_NAME: f"Insteon Hub {discovery_info.ip}" + } + await self.async_set_unique_id(format_mac(discovery_info.macaddress)) + return await self.async_step_user() + class InsteonOptionsFlowHandler(config_entries.OptionsFlow): """Handle an Insteon options flow.""" diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json index 63eb24ee453..d9e1f1bfb18 100644 --- a/homeassistant/components/insteon/manifest.json +++ b/homeassistant/components/insteon/manifest.json @@ -4,6 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/insteon", "requirements": ["pyinsteon==1.0.13"], "codeowners": ["@teharris1"], + "dhcp": [{ "macaddress": "000EF3*" }, { "registered_devices": true }], "config_flow": true, "iot_class": "local_push", "loggers": ["pyinsteon", "pypubsub"], diff --git a/homeassistant/components/insteon/strings.json b/homeassistant/components/insteon/strings.json index 793a38a2694..e451e080539 100644 --- a/homeassistant/components/insteon/strings.json +++ b/homeassistant/components/insteon/strings.json @@ -43,7 +43,8 @@ }, "abort": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]" + "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]", + "not_insteon_device": "Discovered device not an Insteon device" } }, "options": { diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index d2661452a4b..3ae5b0a1376 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -49,6 +49,8 @@ DHCP: list[dict[str, str | bool]] = [ {'domain': 'hunterdouglas_powerview', 'hostname': 'hunter*', 'macaddress': '002674*'}, + {'domain': 'insteon', 'macaddress': '000EF3*'}, + {'domain': 'insteon', 'registered_devices': True}, {'domain': 'intellifire', 'hostname': 'zentrios-*'}, {'domain': 'isy994', 'registered_devices': True}, {'domain': 'isy994', 'hostname': 'isy*', 'macaddress': '0021B9*'}, diff --git a/tests/components/insteon/test_config_flow.py b/tests/components/insteon/test_config_flow.py index 878b540b721..ce49f9df816 100644 --- a/tests/components/insteon/test_config_flow.py +++ b/tests/components/insteon/test_config_flow.py @@ -2,8 +2,10 @@ from unittest.mock import patch +import voluptuous_serialize + from homeassistant import config_entries, data_entry_flow -from homeassistant.components import usb +from homeassistant.components import dhcp, usb from homeassistant.components.insteon.config_flow import ( HUB1, HUB2, @@ -37,6 +39,7 @@ from homeassistant.const import ( CONF_USERNAME, ) from homeassistant.core import HomeAssistant +import homeassistant.helpers.config_validation as cv from .const import ( MOCK_HOSTNAME, @@ -648,3 +651,48 @@ async def test_discovery_via_usb_already_setup(hass): assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "single_instance_allowed" + + +async def test_discovery_via_dhcp_hubv1(hass): + """Test usb flow.""" + await _test_dhcp(hass, HUB1) + + +async def test_discovery_via_dhcp_hubv2(hass): + """Test usb flow.""" + await _test_dhcp(hass, HUB2) + + +async def _test_dhcp(hass, modem_type): + """Test the dhcp discovery for a moddem type.""" + discovery_info = dhcp.DhcpServiceInfo( + ip="11.22.33.44", hostname="", macaddress="00:0e:f3:aa:bb:cc" + ) + result = await hass.config_entries.flow.async_init( + "insteon", + context={"source": config_entries.SOURCE_DHCP}, + data=discovery_info, + ) + await hass.async_block_till_done() + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + with patch("homeassistant.components.insteon.config_flow.async_connect"), patch( + "homeassistant.components.insteon.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"modem_type": modem_type} + ) + await hass.async_block_till_done() + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + schema = voluptuous_serialize.convert( + result2["data_schema"], + custom_serializer=cv.custom_serializer, + ) + for field in schema: + if field["name"] == "host": + assert field.get("default") == "11.22.33.44" + break