From dce8bca103b843c6e10907df30c0790b7c716c94 Mon Sep 17 00:00:00 2001
From: StaleLoafOfBread <45444205+StaleLoafOfBread@users.noreply.github.com>
Date: Fri, 28 Feb 2025 14:59:35 -0500
Subject: [PATCH] Fix alert not respecting can_acknowledge setting (#139483)

* fix(alert): check can_ack prior to acking

* fix(alert): add test for when can_acknowledge=False

* fix(alert): warn on can_ack blocking an ack

* Raise error when trying to acknowledge alert with can_acknowledge set to False

* Rewrite can_ack check as guard

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Make can_ack service error msg human readable because it will show up in the UI

* format with ruff

* Make pytest aware of service error when acking an unackable alert

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
---
 homeassistant/components/alert/entity.py |  5 ++--
 tests/components/alert/test_init.py      | 30 ++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/alert/entity.py b/homeassistant/components/alert/entity.py
index 629047b15ba..a11b281428f 100644
--- a/homeassistant/components/alert/entity.py
+++ b/homeassistant/components/alert/entity.py
@@ -14,7 +14,7 @@ from homeassistant.components.notify import (
 )
 from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_ON
 from homeassistant.core import Event, EventStateChangedData, HassJob, HomeAssistant
-from homeassistant.exceptions import ServiceNotFound
+from homeassistant.exceptions import ServiceNotFound, ServiceValidationError
 from homeassistant.helpers.entity import Entity
 from homeassistant.helpers.event import (
     async_track_point_in_time,
@@ -195,7 +195,8 @@ class AlertEntity(Entity):
 
     async def async_turn_off(self, **kwargs: Any) -> None:
         """Async Acknowledge alert."""
-        LOGGER.debug("Acknowledged Alert: %s", self._attr_name)
+        if not self._can_ack:
+            raise ServiceValidationError("This alert cannot be acknowledged")
         self._ack = True
         self.async_write_ha_state()
 
diff --git a/tests/components/alert/test_init.py b/tests/components/alert/test_init.py
index 27997a093e5..4407775a582 100644
--- a/tests/components/alert/test_init.py
+++ b/tests/components/alert/test_init.py
@@ -28,6 +28,7 @@ from homeassistant.const import (
     STATE_ON,
 )
 from homeassistant.core import HomeAssistant, ServiceCall
+from homeassistant.exceptions import ServiceValidationError
 from homeassistant.setup import async_setup_component
 
 from tests.common import MockEntityPlatform, async_mock_service
@@ -116,6 +117,35 @@ async def test_silence(hass: HomeAssistant, mock_notifier: list[ServiceCall]) ->
     assert hass.states.get(ENTITY_ID).state == STATE_ON
 
 
+async def test_silence_can_acknowledge_false(hass: HomeAssistant) -> None:
+    """Test that attempting to silence an alert with can_acknowledge=False will not silence."""
+    # Create copy of config where can_acknowledge is False
+    config = deepcopy(TEST_CONFIG)
+    config[DOMAIN][NAME]["can_acknowledge"] = False
+
+    # Setup the alert component
+    assert await async_setup_component(hass, DOMAIN, config)
+    await hass.async_block_till_done()
+
+    # Ensure the alert is currently on
+    hass.states.async_set(ENTITY_ID, STATE_ON)
+    await hass.async_block_till_done()
+    assert hass.states.get(ENTITY_ID).state == STATE_ON
+
+    # Attempt to acknowledge
+    with pytest.raises(ServiceValidationError):
+        await hass.services.async_call(
+            DOMAIN,
+            SERVICE_TURN_OFF,
+            {ATTR_ENTITY_ID: ENTITY_ID},
+            blocking=True,
+        )
+    await hass.async_block_till_done()
+
+    # The state should still be ON because can_acknowledge=False
+    assert hass.states.get(ENTITY_ID).state == STATE_ON
+
+
 async def test_reset(hass: HomeAssistant, mock_notifier: list[ServiceCall]) -> None:
     """Test resetting the alert."""
     assert await async_setup_component(hass, DOMAIN, TEST_CONFIG)
-- 
GitLab