From 9fba0932f4b55f4663551f0ef3d38d28aa2ed1ad Mon Sep 17 00:00:00 2001 From: Manu Date: Tue, 14 Jul 2026 08:25:34 +0200 Subject: [PATCH] Add reconfigure flow to LED Infrared integration (#176461) --- .../components/led_infrared/config_flow.py | 45 +++++++++ .../led_infrared/quality_scale.yaml | 2 +- .../components/led_infrared/strings.json | 12 ++- .../led_infrared/test_config_flow.py | 94 +++++++++++++++++++ 4 files changed, 151 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/led_infrared/config_flow.py b/homeassistant/components/led_infrared/config_flow.py index d15c734d4ea8..dc5998297723 100644 --- a/homeassistant/components/led_infrared/config_flow.py +++ b/homeassistant/components/led_infrared/config_flow.py @@ -92,3 +92,48 @@ class LEDIrConfigFlow(ConfigFlow, domain=DOMAIN): "docs_url": "https://www.home-assistant.io/integrations/led_infrared" }, ) + + async def async_step_reconfigure( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle reconfigure flow.""" + errors: dict[str, str] = {} + + entry = self._get_reconfigure_entry() + + emitter_entity_ids = async_get_emitters(self.hass) + if not emitter_entity_ids: + return self.async_abort(reason="no_infrared_entities") + + if user_input is not None: + emitter_id = user_input.get(CONF_INFRARED_ENTITY_ID) + if emitter_id: + self._async_abort_entries_match( + { + CONF_DEVICE_TYPE: entry.data[CONF_DEVICE_TYPE], + CONF_INFRARED_ENTITY_ID: emitter_id, + } + ) + return self.async_update_reload_and_abort( + entry, data_updates=user_input + ) + + errors["base"] = "missing_infrared_entity" + + return self.async_show_form( + step_id="reconfigure", + data_schema=self.add_suggested_values_to_schema( + vol.Schema( + { + vol.Optional(CONF_INFRARED_ENTITY_ID): EntitySelector( + EntitySelectorConfig( + domain=INFRARED_DOMAIN, + include_entities=emitter_entity_ids, + ) + ) + } + ), + entry.data, + ), + errors=errors, + ) diff --git a/homeassistant/components/led_infrared/quality_scale.yaml b/homeassistant/components/led_infrared/quality_scale.yaml index b3909f093af2..a1fe453f6102 100644 --- a/homeassistant/components/led_infrared/quality_scale.yaml +++ b/homeassistant/components/led_infrared/quality_scale.yaml @@ -97,7 +97,7 @@ rules: comment: | This integration does not raise exceptions. icon-translations: done - reconfiguration-flow: todo + reconfiguration-flow: done repair-issues: status: exempt comment: | diff --git a/homeassistant/components/led_infrared/strings.json b/homeassistant/components/led_infrared/strings.json index 5b13ae6caefa..a7735543a8b6 100644 --- a/homeassistant/components/led_infrared/strings.json +++ b/homeassistant/components/led_infrared/strings.json @@ -2,12 +2,22 @@ "config": { "abort": { "already_configured": "This device has already been configured with this infrared entity.", - "no_infrared_entities": "[%key:common::config_flow::abort::no_infrared_entities%]" + "no_infrared_entities": "[%key:common::config_flow::abort::no_infrared_entities%]", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" }, "error": { "missing_infrared_entity": "Select an infrared emitter." }, "step": { + "reconfigure": { + "data": { + "infrared_entity_id": "[%key:common::config_flow::data::infrared_entity_id%]" + }, + "data_description": { + "infrared_entity_id": "[%key:common::config_flow::data_description::infrared_entity_id%]" + }, + "title": "Reconfigure LED Infrared device" + }, "user": { "data": { "device_type": "[%key:common::generic::device_type%]", diff --git a/tests/components/led_infrared/test_config_flow.py b/tests/components/led_infrared/test_config_flow.py index 760b1c2b9c38..dcbdab4cd4ea 100644 --- a/tests/components/led_infrared/test_config_flow.py +++ b/tests/components/led_infrared/test_config_flow.py @@ -97,3 +97,97 @@ async def test_user_flow_no_emitters(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.ABORT assert result["reason"] == "no_infrared_entities" + + +@pytest.mark.usefixtures("mock_infrared_emitter_entity") +async def test_flow_reconfigure(hass: HomeAssistant) -> None: + """Test reconfigure flow.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + title="LED Infrared via Test IR emitter", + entry_id="1234567890", + data={ + CONF_DEVICE_TYPE: LEDIrDeviceType.GENERIC_24_KEY, + CONF_INFRARED_ENTITY_ID: None, + }, + ) + config_entry.add_to_hass(hass) + result = await config_entry.start_reconfigure_flow(hass) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "reconfigure" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID}, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert config_entry.data[CONF_INFRARED_ENTITY_ID] == EMITTER_ENTITY_ID + + assert len(hass.config_entries.async_entries()) == 1 + + +@pytest.mark.usefixtures("mock_infrared_emitter_entity") +async def test_reconfigure_flow_requires_emitter( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test reconfigure flow requires an infrared emitter.""" + config_entry.add_to_hass(hass) + result = await config_entry.start_reconfigure_flow(hass) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "reconfigure" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={}, + ) + + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": "missing_infrared_entity"} + + +@pytest.mark.usefixtures("mock_infrared_emitter_entity") +async def test_flow_reconfigure_already_configured( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test reconfigure flow.""" + config_entry_2 = MockConfigEntry( + domain=DOMAIN, + title="LED Infrared via Test IR emitter", + entry_id="0987654321", + data={ + CONF_DEVICE_TYPE: LEDIrDeviceType.GENERIC_24_KEY, + CONF_INFRARED_ENTITY_ID: None, + }, + ) + config_entry.add_to_hass(hass) + config_entry_2.add_to_hass(hass) + result = await config_entry_2.start_reconfigure_flow(hass) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "reconfigure" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID}, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +@pytest.mark.usefixtures("init_infrared") +async def test_reconfigure_flow_no_emitters( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test reconfigure flow aborts when no infrared emitters exist.""" + config_entry.add_to_hass(hass) + result = await config_entry.start_reconfigure_flow(hass) + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "no_infrared_entities"