From 4a0594e5435fd210f8971f8f5f3598a9e3eada25 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Mon, 31 Aug 2026 15:54:16 +0200 Subject: [PATCH] Deprecate Configurator integration (#180912) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Josef Zweck --- .../components/configurator/__init__.py | 50 +++++++++++++++++- tests/components/configurator/test_init.py | 52 +++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/configurator/__init__.py b/homeassistant/components/configurator/__init__.py index 149fccc0a84e..e891601c0dc5 100644 --- a/homeassistant/components/configurator/__init__.py +++ b/homeassistant/components/configurator/__init__.py @@ -24,12 +24,16 @@ from homeassistant.core import ( from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.event import async_call_later +from homeassistant.helpers.frame import ReportBehavior, report_usage from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType from homeassistant.util.async_ import run_callback_threadsafe _KEY_INSTANCE = "configurator" +# The configurator integration is deprecated and can be removed in HA Core 2027.10 +_BREAKS_IN_HA_VERSION = "2027.10" + DATA_REQUESTS = "configurator_requests" ATTR_CONFIGURE_ID = "configure_id" @@ -54,6 +58,17 @@ type ConfiguratorCallback = Callable[[list[dict[str, str]]], None] CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN) +def _report_deprecation() -> None: + """Report use of the deprecated configurator integration.""" + report_usage( + "uses the deprecated configurator integration, which should be replaced " + "by a config flow", + breaks_in_ha_version=_BREAKS_IN_HA_VERSION, + core_behavior=ReportBehavior.LOG, + exclude_integrations={DOMAIN}, + ) + + @async_callback def async_request_config( hass: HomeAssistant, @@ -69,6 +84,38 @@ def async_request_config( ) -> str: """Create a new request for configuration. + Will return an ID to be used for subsequent calls. + """ + _report_deprecation() + return _async_request_config( + hass, + name, + callback=callback, + description=description, + description_image=description_image, + submit_caption=submit_caption, + fields=fields, + link_name=link_name, + link_url=link_url, + entity_picture=entity_picture, + ) + + +@async_callback +def _async_request_config( + hass: HomeAssistant, + name: str, + callback: ConfiguratorCallback | None = None, + description: str | None = None, + description_image: str | None = None, + submit_caption: str | None = None, + fields: list[dict[str, str]] | None = None, + link_name: str | None = None, + link_url: str | None = None, + entity_picture: str | None = None, +) -> str: + """Create a new request for configuration. + Will return an ID to be used for sequent calls. """ if description and link_name is not None and link_url is not None: @@ -97,8 +144,9 @@ def request_config(hass: HomeAssistant, *args: Any, **kwargs: Any) -> str: Will return an ID to be used for sequent calls. """ + _report_deprecation() return run_callback_threadsafe( - hass.loop, ft.partial(async_request_config, hass, *args, **kwargs) + hass.loop, ft.partial(_async_request_config, hass, *args, **kwargs) ).result() diff --git a/tests/components/configurator/test_init.py b/tests/components/configurator/test_init.py index 8796370cf782..98db650f7b20 100644 --- a/tests/components/configurator/test_init.py +++ b/tests/components/configurator/test_init.py @@ -1,6 +1,8 @@ """The tests for the Configurator component.""" +from collections.abc import Callable, Coroutine from datetime import timedelta +from typing import Any import pytest @@ -150,3 +152,53 @@ async def test_configure_service_requires_admin( context=Context(user_id=hass_read_only_user.id), blocking=True, ) + + +async def _async_request_config( + hass: HomeAssistant, name: str, callback: configurator.ConfiguratorCallback +) -> str: + """Request config using the async API.""" + return configurator.async_request_config(hass, name, callback) + + +async def _sync_request_config( + hass: HomeAssistant, name: str, callback: configurator.ConfiguratorCallback +) -> str: + """Request config using the sync API from an executor thread.""" + return await hass.async_add_executor_job( + configurator.request_config, hass, name, callback + ) + + +@pytest.mark.parametrize( + "ignore_missing_translations", ["component.configurator.services.configure."] +) +@pytest.mark.parametrize("integration_frame_path", ["custom_components/my_integration"]) +@pytest.mark.usefixtures("mock_integration_frame") +@pytest.mark.parametrize( + "request_config", + [_async_request_config, _sync_request_config], + ids=["async", "sync"], +) +async def test_request_config_deprecated( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + request_config: Callable[ + [HomeAssistant, str, configurator.ConfiguratorCallback], + Coroutine[Any, Any, str], + ], +) -> None: + """Test that requesting a config warns about the deprecated integration.""" + request_id = await request_config(hass, "Test Request", lambda _: None) + + states = hass.states.async_all() + assert len(states) == 1 + state = states[0] + assert state.state == configurator.STATE_CONFIGURE + assert state.attributes.get(configurator.ATTR_CONFIGURE_ID) == request_id + + assert ( + "Detected that custom integration 'my_integration' uses the deprecated " + "configurator integration" in caplog.text + ) + assert "This will stop working in Home Assistant 2027.10" in caplog.text