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>
pull/125870/merge
StaleLoafOfBread 2025-02-28 14:59:35 -05:00 committed by GitHub
parent c21234672d
commit ed06831e9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 2 deletions

View File

@ -14,7 +14,7 @@ from homeassistant.components.notify import (
) )
from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_ON from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_ON
from homeassistant.core import Event, EventStateChangedData, HassJob, HomeAssistant 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.entity import Entity
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_point_in_time, async_track_point_in_time,
@ -195,7 +195,8 @@ class AlertEntity(Entity):
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Async Acknowledge alert.""" """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._ack = True
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -28,6 +28,7 @@ from homeassistant.const import (
STATE_ON, STATE_ON,
) )
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import ServiceValidationError
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import MockEntityPlatform, async_mock_service 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 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: async def test_reset(hass: HomeAssistant, mock_notifier: list[ServiceCall]) -> None:
"""Test resetting the alert.""" """Test resetting the alert."""
assert await async_setup_component(hass, DOMAIN, TEST_CONFIG) assert await async_setup_component(hass, DOMAIN, TEST_CONFIG)