From 4096687112dc0dc833f403d03f361f474270491d Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Fri, 24 Nov 2023 06:33:51 -0500 Subject: [PATCH] Allow for manual config entry of Insteon PLM path (#103705) --- .../components/insteon/config_flow.py | 22 ++++++++ homeassistant/components/insteon/schemas.py | 5 ++ tests/components/insteon/const.py | 4 ++ tests/components/insteon/test_config_flow.py | 53 ++++++++++++++++++- 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/insteon/config_flow.py b/homeassistant/components/insteon/config_flow.py index f5bafd935a0..36e977f6db0 100644 --- a/homeassistant/components/insteon/config_flow.py +++ b/homeassistant/components/insteon/config_flow.py @@ -38,6 +38,7 @@ from .schemas import ( add_x10_device, build_device_override_schema, build_hub_schema, + build_plm_manual_schema, build_plm_schema, build_remove_override_schema, build_remove_x10_schema, @@ -46,6 +47,7 @@ from .schemas import ( from .utils import async_get_usb_ports STEP_PLM = "plm" +STEP_PLM_MANUALLY = "plm_manually" STEP_HUB_V1 = "hubv1" STEP_HUB_V2 = "hubv2" STEP_CHANGE_HUB_CONFIG = "change_hub_config" @@ -55,6 +57,7 @@ STEP_ADD_OVERRIDE = "add_override" STEP_REMOVE_OVERRIDE = "remove_override" STEP_REMOVE_X10 = "remove_x10" MODEM_TYPE = "modem_type" +PLM_MANUAL = "manual" _LOGGER = logging.getLogger(__name__) @@ -129,16 +132,35 @@ class InsteonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Set up the PLM modem type.""" errors = {} if user_input is not None: + if user_input[CONF_DEVICE] == PLM_MANUAL: + return await self.async_step_plm_manually() if await _async_connect(**user_input): return self.async_create_entry(title="", data=user_input) errors["base"] = "cannot_connect" schema_defaults = user_input if user_input is not None else {} ports = await async_get_usb_ports(self.hass) + if not ports: + return await self.async_step_plm_manually() + ports[PLM_MANUAL] = "Enter manually" data_schema = build_plm_schema(ports, **schema_defaults) return self.async_show_form( step_id=STEP_PLM, data_schema=data_schema, errors=errors ) + async def async_step_plm_manually(self, user_input=None): + """Set up the PLM modem type manually.""" + errors = {} + schema_defaults = {} + if user_input is not None: + if await _async_connect(**user_input): + return self.async_create_entry(title="", data=user_input) + errors["base"] = "cannot_connect" + schema_defaults = user_input + data_schema = build_plm_manual_schema(**schema_defaults) + return self.async_show_form( + step_id=STEP_PLM_MANUALLY, data_schema=data_schema, errors=errors + ) + async def async_step_hubv1(self, user_input=None): """Set up the Hub v1 modem type.""" return await self._async_setup_hub(hub_version=1, user_input=user_input) diff --git a/homeassistant/components/insteon/schemas.py b/homeassistant/components/insteon/schemas.py index e6b22a8cbb9..497af743195 100644 --- a/homeassistant/components/insteon/schemas.py +++ b/homeassistant/components/insteon/schemas.py @@ -195,6 +195,11 @@ def build_plm_schema(ports: dict[str, str], device=vol.UNDEFINED): return vol.Schema({vol.Required(CONF_DEVICE, default=device): vol.In(ports)}) +def build_plm_manual_schema(device=vol.UNDEFINED): + """Build the manual PLM schema for config flow.""" + return vol.Schema({vol.Required(CONF_DEVICE, default=device): str}) + + def build_hub_schema( hub_version, host=vol.UNDEFINED, diff --git a/tests/components/insteon/const.py b/tests/components/insteon/const.py index e731c51d6c6..53db12acb04 100644 --- a/tests/components/insteon/const.py +++ b/tests/components/insteon/const.py @@ -38,6 +38,10 @@ MOCK_USER_INPUT_PLM = { CONF_DEVICE: MOCK_DEVICE, } +MOCK_USER_INPUT_PLM_MANUAL = { + CONF_DEVICE: "manual", +} + MOCK_USER_INPUT_HUB_V2 = { CONF_HOST: MOCK_HOSTNAME, CONF_USERNAME: MOCK_USERNAME, diff --git a/tests/components/insteon/test_config_flow.py b/tests/components/insteon/test_config_flow.py index e15b7b2a287..106c93071be 100644 --- a/tests/components/insteon/test_config_flow.py +++ b/tests/components/insteon/test_config_flow.py @@ -1,5 +1,4 @@ """Test the config flow for the Insteon integration.""" - from unittest.mock import patch import pytest @@ -15,6 +14,7 @@ from homeassistant.components.insteon.config_flow import ( STEP_HUB_V1, STEP_HUB_V2, STEP_PLM, + STEP_PLM_MANUALLY, STEP_REMOVE_OVERRIDE, STEP_REMOVE_X10, ) @@ -45,6 +45,7 @@ from .const import ( MOCK_USER_INPUT_HUB_V1, MOCK_USER_INPUT_HUB_V2, MOCK_USER_INPUT_PLM, + MOCK_USER_INPUT_PLM_MANUAL, PATCH_ASYNC_SETUP, PATCH_ASYNC_SETUP_ENTRY, PATCH_CONNECTION, @@ -155,6 +156,41 @@ async def test_form_select_plm(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 +async def test_form_select_plm_no_usb(hass: HomeAssistant) -> None: + """Test we set up the PLM when no comm ports are found.""" + + temp_usb_list = dict(USB_PORTS) + USB_PORTS.clear() + result = await _init_form(hass, STEP_PLM) + + result2, _, _ = await _device_form( + hass, result["flow_id"], mock_successful_connection, None + ) + USB_PORTS.update(temp_usb_list) + assert result2["type"] == "form" + assert result2["step_id"] == STEP_PLM_MANUALLY + + +async def test_form_select_plm_manual(hass: HomeAssistant) -> None: + """Test we set up the PLM correctly.""" + + result = await _init_form(hass, STEP_PLM) + + result2, mock_setup, mock_setup_entry = await _device_form( + hass, result["flow_id"], mock_failed_connection, MOCK_USER_INPUT_PLM_MANUAL + ) + + result3, mock_setup, mock_setup_entry = await _device_form( + hass, result2["flow_id"], mock_successful_connection, MOCK_USER_INPUT_PLM + ) + assert result2["type"] == "form" + assert result3["type"] == "create_entry" + assert result3["data"] == MOCK_USER_INPUT_PLM + + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + async def test_form_select_hub_v1(hass: HomeAssistant) -> None: """Test we set up the Hub v1 correctly.""" @@ -225,6 +261,21 @@ async def test_failed_connection_plm(hass: HomeAssistant) -> None: assert result2["errors"] == {"base": "cannot_connect"} +async def test_failed_connection_plm_manually(hass: HomeAssistant) -> None: + """Test a failed connection with the PLM.""" + + result = await _init_form(hass, STEP_PLM) + + result2, _, _ = await _device_form( + hass, result["flow_id"], mock_successful_connection, MOCK_USER_INPUT_PLM_MANUAL + ) + result3, _, _ = await _device_form( + hass, result["flow_id"], mock_failed_connection, MOCK_USER_INPUT_PLM + ) + assert result3["type"] == "form" + assert result3["errors"] == {"base": "cannot_connect"} + + async def test_failed_connection_hub(hass: HomeAssistant) -> None: """Test a failed connection with a Hub."""