diff --git a/homeassistant/components/html5/issue.py b/homeassistant/components/html5/issue.py
index 66c11f5c7421..37cb84fbff7f 100644
--- a/homeassistant/components/html5/issue.py
+++ b/homeassistant/components/html5/issue.py
@@ -52,3 +52,33 @@ def deprecated_dismiss_action_call(hass: HomeAssistant) -> None:
"new_action": "html5.dismiss_message",
},
)
+
+
+@callback
+def deprecated_event_bus(hass: HomeAssistant, event: str) -> None:
+ """Raise a deprecation issue for listeners on the event bus."""
+
+ if listeners := hass.bus.async_listeners().get(event):
+ async_create_issue(
+ hass,
+ DOMAIN,
+ f"deprecated_event_bus_{event}",
+ breaks_in_ha_version="2027.2.0",
+ is_fixable=False,
+ severity=IssueSeverity.WARNING,
+ translation_key="deprecated_event_bus",
+ translation_placeholders={
+ "event": event,
+ "listeners": str(listeners),
+ "example_yaml": """```yaml
+triggers:
+ - trigger: event.received
+ target:
+ entity_id: event.my_device
+ options:
+ event_type:
+ - received
+```
+""",
+ },
+ )
diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py
index 00e5762b3ddc..1e6c1b743035 100644
--- a/homeassistant/components/html5/notify.py
+++ b/homeassistant/components/html5/notify.py
@@ -60,7 +60,11 @@ from .const import (
SERVICE_DISMISS,
)
from .entity import HTML5Entity, Registration
-from .issue import deprecated_dismiss_action_call, deprecated_notify_action_call
+from .issue import (
+ deprecated_dismiss_action_call,
+ deprecated_event_bus,
+ deprecated_notify_action_call,
+)
_LOGGER = logging.getLogger(__name__)
@@ -409,6 +413,9 @@ class HTML5PushCallbackView(HomeAssistantView):
event_payload[ATTR_TYPE],
event_payload,
)
+
+ deprecated_event_bus(hass, event_name)
+
return self.json({"status": "ok", "event": event_payload[ATTR_TYPE]})
diff --git a/homeassistant/components/html5/strings.json b/homeassistant/components/html5/strings.json
index 3dea89e6ead2..1bc8941c65e6 100644
--- a/homeassistant/components/html5/strings.json
+++ b/homeassistant/components/html5/strings.json
@@ -55,6 +55,10 @@
"description": "The action `{action}` is deprecated and will be removed in a future release.\n\nPlease update your automations and scripts to use the notify entities with the `{new_action}` action instead.",
"title": "[%key:component::html5::issues::deprecated_notify_action::title%]"
},
+ "deprecated_event_bus": {
+ "description": "Detected **{listeners}** listener(s) for the event `{event}`.\n\nThe HTML5 Push Notifications integration firing events on the event bus is deprecated and this functionality will be removed in a future release.\n\nPlease update your automations and scripts to use the event entities instead.\n\n## Example automation:\n\n{example_yaml}",
+ "title": "Detected use of deprecated event {event}"
+ },
"deprecated_notify_action": {
"description": "The action `{action}` is deprecated and will be removed in a future release.\n\nPlease update your automations and scripts to use the notify entities with the `{new_action_1}` or `{new_action_2}` actions instead.",
"title": "Detected use of deprecated action {action}"
diff --git a/tests/components/html5/test_event.py b/tests/components/html5/test_event.py
index cd4be641f355..25e783df5e6b 100644
--- a/tests/components/html5/test_event.py
+++ b/tests/components/html5/test_event.py
@@ -9,12 +9,13 @@ from aiohttp.hdrs import AUTHORIZATION
import pytest
from syrupy.assertion import SnapshotAssertion
+from homeassistant.components.html5.const import DOMAIN
from homeassistant.components.html5.notify import ATTR_ACTION, ATTR_TAG, ATTR_TYPE
from homeassistant.components.notify import ATTR_DATA, ATTR_TARGET
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
-from homeassistant.helpers import entity_registry as er
+from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from .test_notify import SUBSCRIPTION_1
@@ -118,3 +119,82 @@ async def test_events(
assert state.attributes.get("action") == event_payload.get(ATTR_ACTION)
assert state.attributes.get("tag") == event_payload[ATTR_TAG]
assert state.attributes.get("customKey") == event_payload[ATTR_DATA]["customKey"]
+
+
+@pytest.mark.parametrize("event_type", ["clicked", "received", "closed"])
+@pytest.mark.usefixtures("mock_wp", "mock_jwt", "mock_vapid", "mock_uuid")
+async def test_deprecation_event_bus(
+ hass: HomeAssistant,
+ config_entry: MockConfigEntry,
+ load_config: MagicMock,
+ issue_registry: ir.IssueRegistry,
+ hass_client: ClientSessionGenerator,
+ event_type: str,
+) -> None:
+ """Test deprecation of events on the event bus."""
+ load_config.return_value = {"device": SUBSCRIPTION_1}
+ await async_setup_component(hass, "http", {})
+
+ config_entry.add_to_hass(hass)
+ await hass.config_entries.async_setup(config_entry.entry_id)
+ await hass.async_block_till_done()
+
+ assert config_entry.state is ConfigEntryState.LOADED
+
+ config_entry.async_on_unload(
+ hass.bus.async_listen(f"html5_notification.{event_type}", lambda _: None)
+ )
+ client = await hass_client()
+
+ resp = await client.post(
+ "/api/notify.html5/callback",
+ json={"type": event_type, "tag": "12345", "target": "device"},
+ headers={AUTHORIZATION: "Bearer JWT"},
+ )
+
+ assert resp.status == HTTPStatus.OK
+ body = await resp.json()
+ assert body == {"event": event_type, "status": "ok"}
+
+ assert issue_registry.async_get_issue(
+ domain=DOMAIN,
+ issue_id=f"deprecated_event_bus_html5_notification.{event_type}",
+ )
+
+
+@pytest.mark.parametrize("event_type", ["clicked", "received", "closed"])
+@pytest.mark.usefixtures("mock_wp", "mock_jwt", "mock_vapid", "mock_uuid")
+async def test_deprecation_event_bus_no_listeners(
+ hass: HomeAssistant,
+ config_entry: MockConfigEntry,
+ load_config: MagicMock,
+ issue_registry: ir.IssueRegistry,
+ hass_client: ClientSessionGenerator,
+ event_type: str,
+) -> None:
+ """Test no issue is created when there are no listeners."""
+ load_config.return_value = {"device": SUBSCRIPTION_1}
+ await async_setup_component(hass, "http", {})
+
+ config_entry.add_to_hass(hass)
+ await hass.config_entries.async_setup(config_entry.entry_id)
+ await hass.async_block_till_done()
+
+ assert config_entry.state is ConfigEntryState.LOADED
+
+ client = await hass_client()
+
+ resp = await client.post(
+ "/api/notify.html5/callback",
+ json={"type": event_type, "tag": "12345", "target": "device"},
+ headers={AUTHORIZATION: "Bearer JWT"},
+ )
+
+ assert resp.status == HTTPStatus.OK
+ body = await resp.json()
+ assert body == {"event": event_type, "status": "ok"}
+
+ assert not issue_registry.async_get_issue(
+ domain=DOMAIN,
+ issue_id=f"deprecated_event_bus_html5_notification.{event_type}",
+ )