From a978e3c1992d0c36031570f8b2edb01bc261fa8e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 28 Jan 2026 17:55:30 +0100 Subject: [PATCH] Remove developer tools panel, add redirects (#161789) Co-authored-by: Robert Resch --- homeassistant/components/frontend/__init__.py | 22 ++++++++++------ homeassistant/components/mqtt/strings.json | 2 +- tests/components/frontend/test_init.py | 26 ++++++++++++++----- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 77e7bdbff4e..039445132ca 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -462,6 +462,20 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # Shopping list panel was replaced by todo panel in 2023.11 hass.http.register_redirect("/shopping-list", "/todo") + # Developer tools moved to config panel in 2026.2 + for url in ( + "/developer-tools", + "/developer-tools/yaml", + "/developer-tools/state", + "/developer-tools/action", + "/developer-tools/template", + "/developer-tools/event", + "/developer-tools/statistics", + "/developer-tools/assist", + "/developer-tools/debug", + ): + hass.http.register_redirect(url, f"/config{url}") + hass.http.app.router.register_resource(IndexView(repo_path, hass)) async_register_built_in_panel( @@ -495,14 +509,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async_register_built_in_panel(hass, "profile") - async_register_built_in_panel( - hass, - "developer-tools", - require_admin=True, - sidebar_title="developer_tools", - sidebar_icon="mdi:hammer", - ) - @callback def async_change_listener( resource_type: str, diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index ebcb77222e9..9fb181d5bc6 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -1121,7 +1121,7 @@ "title": "Deprecated option object_id used" }, "invalid_platform_config": { - "description": "Home Assistant detected an invalid config for a manually configured item.\n\nPlatform domain: **{domain}**\nConfiguration file: **{config_file}**\nNear line: **{line}**\nConfiguration found:\n```yaml\n{config}\n```\nError: **{error}**.\n\nMake sure the configuration is valid and [reload](/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue.", + "description": "Home Assistant detected an invalid config for a manually configured item.\n\nPlatform domain: **{domain}**\nConfiguration file: **{config_file}**\nNear line: **{line}**\nConfiguration found:\n```yaml\n{config}\n```\nError: **{error}**.\n\nMake sure the configuration is valid and [reload](/config/developer-tools/yaml) the manually configured MQTT items or restart Home Assistant to fix this issue.", "title": "Invalid config found for MQTT {domain} item" }, "subentry_migration_discovery": { diff --git a/tests/components/frontend/test_init.py b/tests/components/frontend/test_init.py index ba6a3042279..4dda28c175f 100644 --- a/tests/components/frontend/test_init.py +++ b/tests/components/frontend/test_init.py @@ -937,13 +937,27 @@ async def test_get_version( assert msg["result"] == {"version": cur_version} -async def test_static_paths(mock_http_client: TestClient) -> None: +@pytest.mark.parametrize( + ("from_url", "to_url", "expected_status"), + [ + ("/.well-known/change-password", "/profile", 302), + ("/developer-tools", "/config/developer-tools", 301), + ("/developer-tools/yaml", "/config/developer-tools/yaml", 301), + ("/developer-tools/state", "/config/developer-tools/state", 301), + ("/developer-tools/action", "/config/developer-tools/action", 301), + ("/developer-tools/template", "/config/developer-tools/template", 301), + ("/developer-tools/event", "/config/developer-tools/event", 301), + ("/developer-tools/debug", "/config/developer-tools/debug", 301), + ("/shopping-list", "/todo", 301), + ], +) +async def test_static_paths( + mock_http_client: TestClient, from_url: str, to_url: str, expected_status: int +) -> None: """Test static paths.""" - resp = await mock_http_client.get( - "/.well-known/change-password", allow_redirects=False - ) - assert resp.status == 302 - assert resp.headers["location"] == "/profile" + resp = await mock_http_client.get(from_url, allow_redirects=False) + assert resp.status == expected_status + assert resp.headers["location"] == to_url @pytest.mark.usefixtures("frontend_themes")