diff --git a/homeassistant/components/homeworks/config_flow.py b/homeassistant/components/homeworks/config_flow.py index 68f6aa37c27..ec65cef970c 100644 --- a/homeassistant/components/homeworks/config_flow.py +++ b/homeassistant/components/homeworks/config_flow.py @@ -430,6 +430,7 @@ DATA_SCHEMA_ADD_CONTROLLER = vol.Schema( **CONTROLLER_EDIT, } ) +DATA_SCHEMA_EDIT_CONTROLLER = vol.Schema(CONTROLLER_EDIT) DATA_SCHEMA_ADD_LIGHT = vol.Schema( { vol.Optional(CONF_NAME, default=DEFAULT_LIGHT_NAME): TextSelector(), @@ -643,6 +644,66 @@ class HomeworksConfigFlowHandler(ConfigFlow, domain=DOMAIN): return self.async_show_form(step_id="import_finish", data_schema=vol.Schema({})) + async def _validate_edit_controller( + self, user_input: dict[str, Any] + ) -> dict[str, Any]: + """Validate controller setup.""" + user_input[CONF_PORT] = int(user_input[CONF_PORT]) + + our_entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + assert our_entry + other_entries = self._async_current_entries() + for entry in other_entries: + if entry.entry_id == our_entry.entry_id: + continue + if ( + user_input[CONF_HOST] == entry.options[CONF_HOST] + and user_input[CONF_PORT] == entry.options[CONF_PORT] + ): + raise SchemaFlowError("duplicated_host_port") + + await _try_connection(user_input) + return user_input + + async def async_step_reconfigure( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle a reconfigure flow.""" + entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + assert entry + + errors = {} + suggested_values = { + CONF_HOST: entry.options[CONF_HOST], + CONF_PORT: entry.options[CONF_PORT], + } + + if user_input: + suggested_values = { + CONF_HOST: user_input[CONF_HOST], + CONF_PORT: user_input[CONF_PORT], + } + try: + await self._validate_edit_controller(user_input) + except SchemaFlowError as err: + errors["base"] = str(err) + else: + new_options = entry.options | { + CONF_HOST: user_input[CONF_HOST], + CONF_PORT: user_input[CONF_PORT], + } + return self.async_update_reload_and_abort( + entry, options=new_options, reason="reconfigure_successful" + ) + + return self.async_show_form( + step_id="reconfigure", + data_schema=self.add_suggested_values_to_schema( + DATA_SCHEMA_EDIT_CONTROLLER, suggested_values + ), + errors=errors, + ) + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: diff --git a/homeassistant/components/homeworks/strings.json b/homeassistant/components/homeworks/strings.json index 2a1ddb44b44..3f1b6d3cc23 100644 --- a/homeassistant/components/homeworks/strings.json +++ b/homeassistant/components/homeworks/strings.json @@ -19,6 +19,13 @@ "name": "[%key:component::homeworks::config::step::user::data_description::name%]" } }, + "reconfigure": { + "data": { + "host": "[%key:common::config_flow::data::host%]", + "port": "[%key:common::config_flow::data::port%]" + }, + "description": "Modify a Lutron Homeworks controller connection settings" + }, "user": { "data": { "host": "[%key:common::config_flow::data::host%]", diff --git a/tests/components/homeworks/test_config_flow.py b/tests/components/homeworks/test_config_flow.py index a50e89dcc5f..00980b2f60c 100644 --- a/tests/components/homeworks/test_config_flow.py +++ b/tests/components/homeworks/test_config_flow.py @@ -18,7 +18,7 @@ from homeassistant.components.homeworks.const import ( DOMAIN, ) from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER +from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_RECONFIGURE, SOURCE_USER from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -261,6 +261,145 @@ async def test_import_flow_controller_id_exists( assert result["errors"] == {"base": "duplicated_controller_id"} +async def test_reconfigure_flow( + hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock +) -> None: + """Test reconfigure flow.""" + mock_config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_RECONFIGURE, "entry_id": mock_config_entry.entry_id}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "reconfigure" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "192.168.0.2", + CONF_PORT: 1234, + }, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert mock_config_entry.options == { + "controller_id": "main_controller", + "dimmers": [ + {"addr": "[02:08:01:01]", "name": "Foyer Sconces", "rate": 1.0}, + ], + "host": "192.168.0.2", + "keypads": [ + { + "addr": "[02:08:02:01]", + "buttons": [ + { + "name": "Morning", + "number": 1, + "release_delay": None, + }, + {"name": "Relax", "number": 2, "release_delay": None}, + {"name": "Dim up", "number": 3, "release_delay": 0.2}, + ], + "name": "Foyer Keypad", + }, + ], + "port": 1234, + } + + +async def test_reconfigure_flow_flow_duplicate( + hass: HomeAssistant, mock_homeworks: MagicMock +) -> None: + """Test reconfigure flow.""" + entry1 = MockConfigEntry( + domain=DOMAIN, + data={}, + options={ + "controller_id": "controller_1", + "host": "192.168.0.1", + "port": 1234, + }, + ) + entry1.add_to_hass(hass) + entry2 = MockConfigEntry( + domain=DOMAIN, + data={}, + options={ + "controller_id": "controller_2", + "host": "192.168.0.2", + "port": 1234, + }, + ) + entry2.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_RECONFIGURE, "entry_id": entry1.entry_id}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "reconfigure" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "192.168.0.2", + CONF_PORT: 1234, + }, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "reconfigure" + assert result["errors"] == {"base": "duplicated_host_port"} + + +async def test_reconfigure_flow_flow_no_change( + hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock +) -> None: + """Test reconfigure flow.""" + mock_config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_RECONFIGURE, "entry_id": mock_config_entry.entry_id}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "reconfigure" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "192.168.0.1", + CONF_PORT: 1234, + }, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert mock_config_entry.options == { + "controller_id": "main_controller", + "dimmers": [ + {"addr": "[02:08:01:01]", "name": "Foyer Sconces", "rate": 1.0}, + ], + "host": "192.168.0.1", + "keypads": [ + { + "addr": "[02:08:02:01]", + "buttons": [ + { + "name": "Morning", + "number": 1, + "release_delay": None, + }, + {"name": "Relax", "number": 2, "release_delay": None}, + {"name": "Dim up", "number": 3, "release_delay": 0.2}, + ], + "name": "Foyer Keypad", + } + ], + "port": 1234, + } + + async def test_options_add_light_flow( hass: HomeAssistant, mock_empty_config_entry: MockConfigEntry,