From f3e2f99c8f7ff2cf3f5ca8ab8dc0a6d122ffad0e Mon Sep 17 00:00:00 2001 From: G Johansson Date: Tue, 26 Aug 2025 19:47:27 +0000 Subject: [PATCH] Add reload support to schema options flow handler --- .../helpers/schema_config_entry_flow.py | 15 ++++ .../helpers/test_schema_config_entry_flow.py | 73 ++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 0ee406a7a19..2ad8a09b721 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -16,6 +16,7 @@ from homeassistant.config_entries import ( ConfigFlow, ConfigFlowResult, OptionsFlow, + OptionsFlowWithReload, ) from homeassistant.core import HomeAssistant, callback, split_entity_id from homeassistant.data_entry_flow import UnknownHandler @@ -330,6 +331,7 @@ class SchemaConfigFlowHandler(ConfigFlow, ABC): config_flow: Mapping[str, SchemaFlowStep] options_flow: Mapping[str, SchemaFlowStep] | None = None + options_flow_reloads: bool = False VERSION = 1 @@ -345,6 +347,13 @@ class SchemaConfigFlowHandler(ConfigFlow, ABC): if cls.options_flow is None: raise UnknownHandler + if cls.options_flow_reloads is True: + return SchemaOptionsFlowHandlerWithReload( + config_entry, + cls.options_flow, + cls.async_options_flow_finished, + cls.async_setup_preview, + ) return SchemaOptionsFlowHandler( config_entry, cls.options_flow, @@ -498,6 +507,12 @@ class SchemaOptionsFlowHandler(OptionsFlow): return super().async_create_entry(data=data, **kwargs) +class SchemaOptionsFlowHandlerWithReload( + SchemaOptionsFlowHandler, OptionsFlowWithReload +): + """Handle a schema based options flow which automatically reloads.""" + + @callback def wrapped_entity_config_entry_title( hass: HomeAssistant, entity_id_or_uuid: str diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index 0ad21a1950a..6a5107700ed 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Mapping from typing import Any -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest import voluptuous as vol @@ -789,3 +789,74 @@ async def test_options_flow_omit_optional_keys( "advanced_default": "a very reasonable default", "optional_default": "a very reasonable default", } + + +@pytest.mark.parametrize( + ( + "new_options", + "expected_loads", + "expected_unloads", + ), + [ + ({}, 1, 0), + ({"some_string": "some_value"}, 2, 1), + ], + ids=["should_not_reload", "should_reload"], +) +async def test_options_flow_with_automatic_reload( + hass: HomeAssistant, + manager: data_entry_flow.FlowManager, + new_options: dict[str, str], + expected_loads: int, + expected_unloads: int, +) -> None: + """Test using options flow with automatic reloading.""" + manager.hass = hass + + OPTIONS_SCHEMA = vol.Schema({vol.Optional("some_string"): str}) + + OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA) + } + + class TestFlow(MockSchemaConfigFlowHandler, domain="test"): + config_flow = {} + options_flow = OPTIONS_FLOW + options_flow_reloads = True + + load_entry_mock = AsyncMock(return_value=True) + unload_entry_mock = AsyncMock(return_value=True) + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=load_entry_mock, + async_unload_entry=unload_entry_mock, + ), + ) + mock_platform(hass, "test.config_flow", None) + config_entry = MockConfigEntry( + data={}, + domain="test", + options={ + "optional_no_default": "abc123", + "optional_default": "not default", + "advanced_no_default": "abc123", + "advanced_default": "not default", + }, + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + assert len(load_entry_mock.mock_calls) == 1 + + # Start flow in basic mode + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + + result = await hass.config_entries.options.async_configure( + result["flow_id"], new_options + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + + assert len(load_entry_mock.mock_calls) == expected_loads + assert len(unload_entry_mock.mock_calls) == expected_unloads