diff --git a/homeassistant/components/ecobee/manifest.json b/homeassistant/components/ecobee/manifest.json index b11bdf8afb0..22dfcb2a428 100644 --- a/homeassistant/components/ecobee/manifest.json +++ b/homeassistant/components/ecobee/manifest.json @@ -3,7 +3,6 @@ "name": "ecobee", "codeowners": [], "config_flow": true, - "dependencies": ["http", "repairs"], "documentation": "https://www.home-assistant.io/integrations/ecobee", "homekit": { "models": ["EB", "ecobee*"] diff --git a/homeassistant/components/ecobee/notify.py b/homeassistant/components/ecobee/notify.py index f7e2f1549d1..b9dafae0f4e 100644 --- a/homeassistant/components/ecobee/notify.py +++ b/homeassistant/components/ecobee/notify.py @@ -9,6 +9,7 @@ from homeassistant.components.notify import ( ATTR_TARGET, BaseNotificationService, NotifyEntity, + migrate_notify_issue, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -18,7 +19,6 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import Ecobee, EcobeeData from .const import DOMAIN from .entity import EcobeeBaseEntity -from .repairs import migrate_notify_issue def get_service( @@ -43,7 +43,7 @@ class EcobeeNotificationService(BaseNotificationService): async def async_send_message(self, message: str = "", **kwargs: Any) -> None: """Send a message and raise issue.""" - migrate_notify_issue(self.hass) + migrate_notify_issue(self.hass, DOMAIN, "Ecobee", "2024.11.0") await self.hass.async_add_executor_job( partial(self.send_message, message, **kwargs) ) diff --git a/homeassistant/components/ecobee/strings.json b/homeassistant/components/ecobee/strings.json index 1d64b6d6b94..b1d1df65417 100644 --- a/homeassistant/components/ecobee/strings.json +++ b/homeassistant/components/ecobee/strings.json @@ -163,18 +163,5 @@ } } } - }, - "issues": { - "migrate_notify": { - "title": "Migration of Ecobee notify service", - "fix_flow": { - "step": { - "confirm": { - "description": "The Ecobee `notify` service has been migrated. A new `notify` entity per Thermostat is available now.\n\nUpdate any automations to use the new `notify.send_message` exposed by these new entities. When this is done, fix this issue and restart Home Assistant.", - "title": "Disable legacy Ecobee notify service" - } - } - } - } } } diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index 77f3db3f9f3..af0c6b8d01c 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -4,7 +4,7 @@ "after_dependencies": ["panel_custom"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "config_flow": true, - "dependencies": ["file_upload", "repairs", "websocket_api"], + "dependencies": ["file_upload", "websocket_api"], "documentation": "https://www.home-assistant.io/integrations/knx", "integration_type": "hub", "iot_class": "local_push", diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index 9390acb2c85..1b6cd325f21 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -8,7 +8,11 @@ from xknx import XKNX from xknx.devices import Notification as XknxNotification from homeassistant import config_entries -from homeassistant.components.notify import BaseNotificationService, NotifyEntity +from homeassistant.components.notify import ( + BaseNotificationService, + NotifyEntity, + migrate_notify_issue, +) from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, CONF_TYPE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -16,7 +20,6 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS from .knx_entity import KnxEntity -from .repairs import migrate_notify_issue async def async_get_service( @@ -57,7 +60,7 @@ class KNXNotificationService(BaseNotificationService): async def async_send_message(self, message: str = "", **kwargs: Any) -> None: """Send a notification to knx bus.""" - migrate_notify_issue(self.hass) + migrate_notify_issue(self.hass, DOMAIN, "KNX", "2024.11.0") if "target" in kwargs: await self._async_send_to_device(message, kwargs["target"]) else: diff --git a/homeassistant/components/knx/repairs.py b/homeassistant/components/knx/repairs.py deleted file mode 100644 index f0a92850d36..00000000000 --- a/homeassistant/components/knx/repairs.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Repairs support for KNX.""" - -from __future__ import annotations - -from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow -from homeassistant.const import Platform -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import issue_registry as ir - -from .const import DOMAIN - - -@callback -def migrate_notify_issue(hass: HomeAssistant) -> None: - """Create issue for notify service deprecation.""" - ir.async_create_issue( - hass, - DOMAIN, - "migrate_notify", - breaks_in_ha_version="2024.11.0", - issue_domain=Platform.NOTIFY.value, - is_fixable=True, - is_persistent=True, - translation_key="migrate_notify", - severity=ir.IssueSeverity.WARNING, - ) - - -async def async_create_fix_flow( - hass: HomeAssistant, - issue_id: str, - data: dict[str, str | int | float | None] | None, -) -> RepairsFlow: - """Create flow.""" - assert issue_id == "migrate_notify" - return ConfirmRepairFlow() diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index a69ba106ffd..39b96dddf8f 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -384,18 +384,5 @@ "name": "[%key:common::action::reload%]", "description": "Reloads the KNX integration." } - }, - "issues": { - "migrate_notify": { - "title": "Migration of KNX notify service", - "fix_flow": { - "step": { - "confirm": { - "description": "The KNX `notify` service has been migrated. New `notify` entities are available now.\n\nUpdate any automations to use the new `notify.send_message` exposed by these new entities. When this is done, fix this issue and restart Home Assistant.", - "title": "Disable legacy KNX notify service" - } - } - } - } } } diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index ce4f778993c..1fc7836ecd8 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -41,6 +41,7 @@ from .legacy import ( # noqa: F401 async_setup_legacy, check_templates_warn, ) +from .repairs import migrate_notify_issue # noqa: F401 # mypy: disallow-any-generics diff --git a/homeassistant/components/notify/manifest.json b/homeassistant/components/notify/manifest.json index 1c48af7dfcc..62b69bb2df2 100644 --- a/homeassistant/components/notify/manifest.json +++ b/homeassistant/components/notify/manifest.json @@ -2,6 +2,7 @@ "domain": "notify", "name": "Notifications", "codeowners": ["@home-assistant/core"], + "dependencies": ["repairs"], "documentation": "https://www.home-assistant.io/integrations/notify", "integration_type": "entity", "quality_scale": "internal" diff --git a/homeassistant/components/ecobee/repairs.py b/homeassistant/components/notify/repairs.py similarity index 62% rename from homeassistant/components/ecobee/repairs.py rename to homeassistant/components/notify/repairs.py index 66474730b2f..5c91a9a4731 100644 --- a/homeassistant/components/ecobee/repairs.py +++ b/homeassistant/components/notify/repairs.py @@ -1,8 +1,7 @@ -"""Repairs support for Ecobee.""" +"""Repairs support for notify integration.""" from __future__ import annotations -from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN from homeassistant.components.repairs import RepairsFlow from homeassistant.components.repairs.issue_handler import ConfirmRepairFlow from homeassistant.core import HomeAssistant, callback @@ -12,17 +11,23 @@ from .const import DOMAIN @callback -def migrate_notify_issue(hass: HomeAssistant) -> None: +def migrate_notify_issue( + hass: HomeAssistant, domain: str, integration_title: str, breaks_in_ha_version: str +) -> None: """Ensure an issue is registered.""" ir.async_create_issue( hass, DOMAIN, - "migrate_notify", - breaks_in_ha_version="2024.11.0", - issue_domain=NOTIFY_DOMAIN, + f"migrate_notify_{domain}", + breaks_in_ha_version=breaks_in_ha_version, + issue_domain=domain, is_fixable=True, is_persistent=True, translation_key="migrate_notify", + translation_placeholders={ + "domain": domain, + "integration_title": integration_title, + }, severity=ir.IssueSeverity.WARNING, ) @@ -33,5 +38,5 @@ async def async_create_fix_flow( data: dict[str, str | int | float | None] | None, ) -> RepairsFlow: """Create flow.""" - assert issue_id == "migrate_notify" + assert issue_id.startswith("migrate_notify_") return ConfirmRepairFlow() diff --git a/homeassistant/components/notify/strings.json b/homeassistant/components/notify/strings.json index f6ac8c848f1..96482f5a7d5 100644 --- a/homeassistant/components/notify/strings.json +++ b/homeassistant/components/notify/strings.json @@ -60,5 +60,18 @@ } } } + }, + "issues": { + "migrate_notify": { + "title": "Migration of {integration_title} notify service", + "fix_flow": { + "step": { + "confirm": { + "description": "The {integration_title} `notify` service(s) are migrated. A new `notify` entity is available now to replace each legacy `notify` service.\n\nUpdate any automations to use the new `notify.send_message` service exposed with this new entity. When this is done, fix this issue and restart Home Assistant.", + "title": "Migrate legacy {integration_title} notify service for domain `{domain}`" + } + } + } + } } } diff --git a/tests/components/ecobee/test_repairs.py b/tests/components/ecobee/test_repairs.py index 19fdc6f7bba..897594c582f 100644 --- a/tests/components/ecobee/test_repairs.py +++ b/tests/components/ecobee/test_repairs.py @@ -48,14 +48,14 @@ async def test_ecobee_repair_flow( # Assert the issue is present assert issue_registry.async_get_issue( - domain=DOMAIN, - issue_id="migrate_notify", + domain="notify", + issue_id=f"migrate_notify_{DOMAIN}", ) assert len(issue_registry.issues) == 1 url = RepairsFlowIndexView.url resp = await http_client.post( - url, json={"handler": DOMAIN, "issue_id": "migrate_notify"} + url, json={"handler": "notify", "issue_id": f"migrate_notify_{DOMAIN}"} ) assert resp.status == HTTPStatus.OK data = await resp.json() @@ -73,7 +73,7 @@ async def test_ecobee_repair_flow( # Assert the issue is no longer present assert not issue_registry.async_get_issue( - domain=DOMAIN, + domain="notify", issue_id="migrate_notify", ) assert len(issue_registry.issues) == 0 diff --git a/tests/components/file/test_notify.py b/tests/components/file/test_notify.py index f6d30c2f166..53c8ad2d6b4 100644 --- a/tests/components/file/test_notify.py +++ b/tests/components/file/test_notify.py @@ -22,7 +22,7 @@ from tests.common import MockConfigEntry, assert_setup_component async def test_bad_config(hass: HomeAssistant) -> None: """Test set up the platform with bad/missing config.""" config = {notify.DOMAIN: {"name": "test", "platform": "file"}} - with assert_setup_component(0) as handle_config: + with assert_setup_component(0, domain="notify") as handle_config: assert await async_setup_component(hass, notify.DOMAIN, config) await hass.async_block_till_done() assert not handle_config[notify.DOMAIN] diff --git a/tests/components/homematic/test_notify.py b/tests/components/homematic/test_notify.py index 33c9b0f359e..014c0b0ae53 100644 --- a/tests/components/homematic/test_notify.py +++ b/tests/components/homematic/test_notify.py @@ -14,7 +14,7 @@ async def test_setup_full(hass: HomeAssistant) -> None: "homematic", {"homematic": {"hosts": {"ccu2": {"host": "127.0.0.1"}}}}, ) - with assert_setup_component(1) as handle_config: + with assert_setup_component(1, domain="notify") as handle_config: assert await async_setup_component( hass, "notify", @@ -40,7 +40,7 @@ async def test_setup_without_optional(hass: HomeAssistant) -> None: "homematic", {"homematic": {"hosts": {"ccu2": {"host": "127.0.0.1"}}}}, ) - with assert_setup_component(1) as handle_config: + with assert_setup_component(1, domain="notify") as handle_config: assert await async_setup_component( hass, "notify", @@ -61,6 +61,6 @@ async def test_setup_without_optional(hass: HomeAssistant) -> None: async def test_bad_config(hass: HomeAssistant) -> None: """Test invalid configuration.""" config = {notify_comp.DOMAIN: {"name": "test", "platform": "homematic"}} - with assert_setup_component(0) as handle_config: + with assert_setup_component(0, domain="notify") as handle_config: assert await async_setup_component(hass, notify_comp.DOMAIN, config) assert not handle_config[notify_comp.DOMAIN] diff --git a/tests/components/knx/test_repairs.py b/tests/components/knx/test_repairs.py index 4ad06e0addb..025f298e123 100644 --- a/tests/components/knx/test_repairs.py +++ b/tests/components/knx/test_repairs.py @@ -54,14 +54,14 @@ async def test_knx_notify_service_issue( # Assert the issue is present assert len(issue_registry.issues) == 1 assert issue_registry.async_get_issue( - domain=DOMAIN, - issue_id="migrate_notify", + domain="notify", + issue_id=f"migrate_notify_{DOMAIN}", ) # Test confirm step in repair flow resp = await http_client.post( RepairsFlowIndexView.url, - json={"handler": DOMAIN, "issue_id": "migrate_notify"}, + json={"handler": "notify", "issue_id": f"migrate_notify_{DOMAIN}"}, ) assert resp.status == HTTPStatus.OK data = await resp.json() @@ -78,7 +78,7 @@ async def test_knx_notify_service_issue( # Assert the issue is no longer present assert not issue_registry.async_get_issue( - domain=DOMAIN, - issue_id="migrate_notify", + domain="notify", + issue_id=f"migrate_notify_{DOMAIN}", ) assert len(issue_registry.issues) == 0 diff --git a/tests/components/notify/test_repairs.py b/tests/components/notify/test_repairs.py new file mode 100644 index 00000000000..f4e016418fe --- /dev/null +++ b/tests/components/notify/test_repairs.py @@ -0,0 +1,84 @@ +"""Test repairs for notify entity component.""" + +from http import HTTPStatus +from unittest.mock import AsyncMock + +from homeassistant.components.notify import ( + DOMAIN as NOTIFY_DOMAIN, + migrate_notify_issue, +) +from homeassistant.components.repairs.issue_handler import ( + async_process_repairs_platforms, +) +from homeassistant.components.repairs.websocket_api import ( + RepairsFlowIndexView, + RepairsFlowResourceView, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import issue_registry as ir +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, MockModule, mock_integration +from tests.typing import ClientSessionGenerator + +THERMOSTAT_ID = 0 + + +async def test_notify_migration_repair_flow( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + issue_registry: ir.IssueRegistry, + config_flow_fixture: None, +) -> None: + """Test the notify service repair flow is triggered.""" + await async_setup_component(hass, NOTIFY_DOMAIN, {}) + await hass.async_block_till_done() + await async_process_repairs_platforms(hass) + + http_client = await hass_client() + await hass.async_block_till_done() + config_entry = MockConfigEntry(domain="test") + config_entry.add_to_hass(hass) + mock_integration( + hass, + MockModule( + "test", + async_setup_entry=AsyncMock(return_value=True), + ), + ) + assert await hass.config_entries.async_setup(config_entry.entry_id) + + # Simulate legacy service being used and issue being registered + migrate_notify_issue(hass, "test", "Test", "2024.12.0") + await hass.async_block_till_done() + # Assert the issue is present + assert issue_registry.async_get_issue( + domain=NOTIFY_DOMAIN, + issue_id="migrate_notify_test", + ) + assert len(issue_registry.issues) == 1 + + url = RepairsFlowIndexView.url + resp = await http_client.post( + url, json={"handler": NOTIFY_DOMAIN, "issue_id": "migrate_notify_test"} + ) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data["step_id"] == "confirm" + + url = RepairsFlowResourceView.url.format(flow_id=flow_id) + resp = await http_client.post(url) + assert resp.status == HTTPStatus.OK + data = await resp.json() + assert data["type"] == "create_entry" + # Test confirm step in repair flow + await hass.async_block_till_done() + + # Assert the issue is no longer present + assert not issue_registry.async_get_issue( + domain=NOTIFY_DOMAIN, + issue_id="migrate_notify_test", + ) + assert len(issue_registry.issues) == 0