From 5e56bc746450c0861cdcafc9bdb000e871701a03 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 30 Jun 2017 21:07:12 -0700 Subject: [PATCH] Adding done_message to alert (#8116) * Adding done_message to alert Adding an optional entry to the config that will send a notification when an alarm goes from on to off. * Update test_alert.py * Update test_alert.py --- homeassistant/components/alert.py | 28 +++++++++++++++----- tests/components/test_alert.py | 43 ++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/alert.py b/homeassistant/components/alert.py index 09db0f84346..b4de3c4a0f5 100644 --- a/homeassistant/components/alert.py +++ b/homeassistant/components/alert.py @@ -25,6 +25,7 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'alert' ENTITY_ID_FORMAT = DOMAIN + '.{}' +CONF_DONE_MESSAGE = 'done_message' CONF_CAN_ACK = 'can_acknowledge' CONF_NOTIFIERS = 'notifiers' CONF_REPEAT = 'repeat' @@ -35,6 +36,7 @@ DEFAULT_SKIP_FIRST = False ALERT_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): cv.string, + vol.Optional(CONF_DONE_MESSAGE, default=None): cv.string, vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Required(CONF_STATE, default=STATE_ON): cv.string, vol.Required(CONF_REPEAT): vol.All(cv.ensure_list, [vol.Coerce(float)]), @@ -121,10 +123,10 @@ def async_setup(hass, config): # Setup alerts for entity_id, alert in alerts.items(): entity = Alert(hass, entity_id, - alert[CONF_NAME], alert[CONF_ENTITY_ID], - alert[CONF_STATE], alert[CONF_REPEAT], - alert[CONF_SKIP_FIRST], alert[CONF_NOTIFIERS], - alert[CONF_CAN_ACK]) + alert[CONF_NAME], alert[CONF_DONE_MESSAGE], + alert[CONF_ENTITY_ID], alert[CONF_STATE], + alert[CONF_REPEAT], alert[CONF_SKIP_FIRST], + alert[CONF_NOTIFIERS], alert[CONF_CAN_ACK]) all_alerts[entity.entity_id] = entity # Read descriptions @@ -154,8 +156,8 @@ def async_setup(hass, config): class Alert(ToggleEntity): """Representation of an alert.""" - def __init__(self, hass, entity_id, name, watched_entity_id, state, - repeat, skip_first, notifiers, can_ack): + def __init__(self, hass, entity_id, name, done_message, watched_entity_id, + state, repeat, skip_first, notifiers, can_ack): """Initialize the alert.""" self.hass = hass self._name = name @@ -163,6 +165,7 @@ class Alert(ToggleEntity): self._skip_first = skip_first self._notifiers = notifiers self._can_ack = can_ack + self._done_message = done_message self._delay = [timedelta(minutes=val) for val in repeat] self._next_delay = 0 @@ -170,6 +173,7 @@ class Alert(ToggleEntity): self._firing = False self._ack = False self._cancel = None + self._send_done_message = False self.entity_id = ENTITY_ID_FORMAT.format(entity_id) event.async_track_state_change( @@ -230,6 +234,8 @@ class Alert(ToggleEntity): self._cancel() self._ack = False self._firing = False + if self._done_message and self._send_done_message: + yield from self._notify_done_message() self.hass.async_add_job(self.async_update_ha_state) @asyncio.coroutine @@ -249,11 +255,21 @@ class Alert(ToggleEntity): if not self._ack: _LOGGER.info("Alerting: %s", self._name) + self._send_done_message = True for target in self._notifiers: yield from self.hass.services.async_call( 'notify', target, {'message': self._name}) yield from self._schedule_notify() + @asyncio.coroutine + def _notify_done_message(self, *args): + """Send notification of complete alert.""" + _LOGGER.info("Alerting: %s", self._done_message) + self._send_done_message = False + for target in self._notifiers: + yield from self.hass.services.async_call( + 'notify', target, {'message': self._done_message}) + @asyncio.coroutine def async_turn_on(self): """Async Unacknowledge alert.""" diff --git a/tests/components/test_alert.py b/tests/components/test_alert.py index 8150d08ff72..a94e5747483 100644 --- a/tests/components/test_alert.py +++ b/tests/components/test_alert.py @@ -13,19 +13,21 @@ from homeassistant.const import (CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, from tests.common import get_test_home_assistant NAME = "alert_test" +DONE_MESSAGE = "alert_gone" NOTIFIER = 'test' TEST_CONFIG = \ {alert.DOMAIN: { NAME: { CONF_NAME: NAME, + alert.CONF_DONE_MESSAGE: DONE_MESSAGE, CONF_ENTITY_ID: "sensor.test", CONF_STATE: STATE_ON, alert.CONF_REPEAT: 30, alert.CONF_SKIP_FIRST: False, alert.CONF_NOTIFIERS: [NOTIFIER]} }} -TEST_NOACK = [NAME, NAME, "sensor.test", STATE_ON, - [30], False, NOTIFIER, False] +TEST_NOACK = [NAME, NAME, DONE_MESSAGE, "sensor.test", + STATE_ON, [30], False, NOTIFIER, False] ENTITY_ID = alert.ENTITY_ID_FORMAT.format(NAME) @@ -119,6 +121,31 @@ class TestAlert(unittest.TestCase): hidden = self.hass.states.get(ENTITY_ID).attributes.get('hidden') self.assertFalse(hidden) + def test_notification_no_done_message(self): + """Test notifications.""" + events = [] + config = deepcopy(TEST_CONFIG) + del(config[alert.DOMAIN][NAME][alert.CONF_DONE_MESSAGE]) + + @callback + def record_event(event): + """Add recorded event to set.""" + events.append(event) + + self.hass.services.register( + notify.DOMAIN, NOTIFIER, record_event) + + assert setup_component(self.hass, alert.DOMAIN, config) + self.assertEqual(0, len(events)) + + self.hass.states.set("sensor.test", STATE_ON) + self.hass.block_till_done() + self.assertEqual(1, len(events)) + + self.hass.states.set("sensor.test", STATE_OFF) + self.hass.block_till_done() + self.assertEqual(1, len(events)) + def test_notification(self): """Test notifications.""" events = [] @@ -140,7 +167,7 @@ class TestAlert(unittest.TestCase): self.hass.states.set("sensor.test", STATE_OFF) self.hass.block_till_done() - self.assertEqual(1, len(events)) + self.assertEqual(2, len(events)) def test_skipfirst(self): """Test skipping first notification.""" @@ -170,3 +197,13 @@ class TestAlert(unittest.TestCase): self.hass.block_till_done() self.assertEqual(True, entity.hidden) + + def test_done_message_state_tracker_reset_on_cancel(self): + """Test that the done message is reset when cancelled.""" + entity = alert.Alert(self.hass, *TEST_NOACK) + entity._cancel = lambda *args: None + assert entity._send_done_message is False + entity._send_done_message = True + self.hass.add_job(entity.end_alerting) + self.hass.block_till_done() + assert entity._send_done_message is False