Add data/data_template/title to alert component (#17616)

* Add data/data_template/title to alert component

* Fix line length

* Fix tests

* Fix lint

* fix line length

* Fix tests, make title templatable

* Fix test

* Fix test

* Optimize data, make title templated

* Fix line length

* Add title template

* typo

* Fix tests
This commit is contained in:
Frank 2019-01-23 08:47:37 +01:00 committed by Martin Hjelmare
parent 3484e506e8
commit db277ad023
2 changed files with 76 additions and 17 deletions

View File

@ -5,19 +5,19 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alert/ https://home-assistant.io/components/alert/
""" """
import asyncio import asyncio
from datetime import datetime, timedelta
import logging import logging
from datetime import datetime, timedelta
import voluptuous as vol import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.notify import ( from homeassistant.components.notify import (
ATTR_MESSAGE, DOMAIN as DOMAIN_NOTIFY) ATTR_MESSAGE, ATTR_TITLE, ATTR_DATA, DOMAIN as DOMAIN_NOTIFY)
from homeassistant.const import ( from homeassistant.const import (
CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, CONF_STATE, STATE_ON, STATE_OFF, CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, CONF_STATE, STATE_ON, STATE_OFF,
SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, ATTR_ENTITY_ID) SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, ATTR_ENTITY_ID)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers import service, event from homeassistant.helpers import service, event
import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import ToggleEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -30,6 +30,8 @@ CONF_REPEAT = 'repeat'
CONF_SKIP_FIRST = 'skip_first' CONF_SKIP_FIRST = 'skip_first'
CONF_ALERT_MESSAGE = 'message' CONF_ALERT_MESSAGE = 'message'
CONF_DONE_MESSAGE = 'done_message' CONF_DONE_MESSAGE = 'done_message'
CONF_TITLE = 'title'
CONF_DATA = 'data'
DEFAULT_CAN_ACK = True DEFAULT_CAN_ACK = True
DEFAULT_SKIP_FIRST = False DEFAULT_SKIP_FIRST = False
@ -43,13 +45,14 @@ ALERT_SCHEMA = vol.Schema({
vol.Required(CONF_SKIP_FIRST, default=DEFAULT_SKIP_FIRST): cv.boolean, vol.Required(CONF_SKIP_FIRST, default=DEFAULT_SKIP_FIRST): cv.boolean,
vol.Optional(CONF_ALERT_MESSAGE): cv.template, vol.Optional(CONF_ALERT_MESSAGE): cv.template,
vol.Optional(CONF_DONE_MESSAGE): cv.template, vol.Optional(CONF_DONE_MESSAGE): cv.template,
vol.Optional(CONF_TITLE): cv.template,
vol.Optional(CONF_DATA): dict,
vol.Required(CONF_NOTIFIERS): cv.ensure_list}) vol.Required(CONF_NOTIFIERS): cv.ensure_list})
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
DOMAIN: cv.schema_with_slug_keys(ALERT_SCHEMA), DOMAIN: cv.schema_with_slug_keys(ALERT_SCHEMA),
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
ALERT_SERVICE_SCHEMA = vol.Schema({ ALERT_SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
}) })
@ -77,12 +80,14 @@ async def async_setup(hass, config):
done_message_template = cfg.get(CONF_DONE_MESSAGE) done_message_template = cfg.get(CONF_DONE_MESSAGE)
notifiers = cfg.get(CONF_NOTIFIERS) notifiers = cfg.get(CONF_NOTIFIERS)
can_ack = cfg.get(CONF_CAN_ACK) can_ack = cfg.get(CONF_CAN_ACK)
title_template = cfg.get(CONF_TITLE)
data = cfg.get(CONF_DATA)
entities.append(Alert(hass, object_id, name, entities.append(Alert(hass, object_id, name,
watched_entity_id, alert_state, repeat, watched_entity_id, alert_state, repeat,
skip_first, message_template, skip_first, message_template,
done_message_template, notifiers, done_message_template, notifiers,
can_ack)) can_ack, title_template, data))
if not entities: if not entities:
return False return False
@ -127,12 +132,14 @@ class Alert(ToggleEntity):
def __init__(self, hass, entity_id, name, watched_entity_id, def __init__(self, hass, entity_id, name, watched_entity_id,
state, repeat, skip_first, message_template, state, repeat, skip_first, message_template,
done_message_template, notifiers, can_ack): done_message_template, notifiers, can_ack, title_template,
data):
"""Initialize the alert.""" """Initialize the alert."""
self.hass = hass self.hass = hass
self._name = name self._name = name
self._alert_state = state self._alert_state = state
self._skip_first = skip_first self._skip_first = skip_first
self._data = data
self._message_template = message_template self._message_template = message_template
if self._message_template is not None: if self._message_template is not None:
@ -142,6 +149,10 @@ class Alert(ToggleEntity):
if self._done_message_template is not None: if self._done_message_template is not None:
self._done_message_template.hass = hass self._done_message_template.hass = hass
self._title_template = title_template
if self._title_template is not None:
self._title_template.hass = hass
self._notifiers = notifiers self._notifiers = notifiers
self._can_ack = can_ack self._can_ack = can_ack
@ -251,9 +262,20 @@ class Alert(ToggleEntity):
await self._send_notification_message(message) await self._send_notification_message(message)
async def _send_notification_message(self, message): async def _send_notification_message(self, message):
msg_payload = {ATTR_MESSAGE: message}
if self._title_template is not None:
title = self._title_template.async_render()
msg_payload.update({ATTR_TITLE: title})
if self._data:
msg_payload.update({ATTR_DATA: self._data})
_LOGGER.debug(msg_payload)
for target in self._notifiers: for target in self._notifiers:
await self.hass.services.async_call( await self.hass.services.async_call(
DOMAIN_NOTIFY, target, {ATTR_MESSAGE: message}) DOMAIN_NOTIFY, target, msg_payload)
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Async Unacknowledge alert.""" """Async Unacknowledge alert."""

View File

@ -1,17 +1,16 @@
"""The tests for the Alert component.""" """The tests for the Alert component."""
import unittest
# pylint: disable=protected-access # pylint: disable=protected-access
from copy import deepcopy from copy import deepcopy
import unittest
from homeassistant.setup import setup_component
from homeassistant.core import callback
from homeassistant.components.alert import DOMAIN
import homeassistant.components.alert as alert import homeassistant.components.alert as alert
import homeassistant.components.notify as notify import homeassistant.components.notify as notify
from homeassistant.components.alert import DOMAIN
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, CONF_STATE, ATTR_ENTITY_ID, CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, CONF_STATE,
SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON, STATE_OFF) SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON, STATE_OFF)
from homeassistant.core import callback
from homeassistant.setup import setup_component
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
NAME = "alert_test" NAME = "alert_test"
@ -19,6 +18,13 @@ DONE_MESSAGE = "alert_gone"
NOTIFIER = 'test' NOTIFIER = 'test'
TEMPLATE = "{{ states.sensor.test.entity_id }}" TEMPLATE = "{{ states.sensor.test.entity_id }}"
TEST_ENTITY = "sensor.test" TEST_ENTITY = "sensor.test"
TITLE = "{{ states.sensor.test.entity_id }}"
TEST_TITLE = "sensor.test"
TEST_DATA = {
'data': {
'inline_keyboard': ['Close garage:/close_garage']
}
}
TEST_CONFIG = \ TEST_CONFIG = \
{alert.DOMAIN: { {alert.DOMAIN: {
NAME: { NAME: {
@ -28,10 +34,13 @@ TEST_CONFIG = \
CONF_STATE: STATE_ON, CONF_STATE: STATE_ON,
alert.CONF_REPEAT: 30, alert.CONF_REPEAT: 30,
alert.CONF_SKIP_FIRST: False, alert.CONF_SKIP_FIRST: False,
alert.CONF_NOTIFIERS: [NOTIFIER]} alert.CONF_NOTIFIERS: [NOTIFIER],
alert.CONF_TITLE: TITLE,
alert.CONF_DATA: {}
}
}} }}
TEST_NOACK = [NAME, NAME, "sensor.test", TEST_NOACK = [NAME, NAME, "sensor.test",
STATE_ON, [30], False, None, None, NOTIFIER, False] STATE_ON, [30], False, None, None, NOTIFIER, False, None, None]
ENTITY_ID = alert.ENTITY_ID_FORMAT.format(NAME) ENTITY_ID = alert.ENTITY_ID_FORMAT.format(NAME)
@ -286,6 +295,34 @@ class TestAlert(unittest.TestCase):
last_event = events[-1] last_event = events[-1]
self.assertEqual(last_event.data[notify.ATTR_MESSAGE], TEST_ENTITY) self.assertEqual(last_event.data[notify.ATTR_MESSAGE], TEST_ENTITY)
def test_sending_titled_notification(self):
"""Test notifications."""
events = self._setup_notify()
config = deepcopy(TEST_CONFIG)
config[alert.DOMAIN][NAME][alert.CONF_TITLE] = TITLE
assert setup_component(self.hass, alert.DOMAIN, config)
self.hass.states.set(TEST_ENTITY, STATE_ON)
self.hass.block_till_done()
self.assertEqual(1, len(events))
last_event = events[-1]
self.assertEqual(last_event.data[notify.ATTR_TITLE], TEST_TITLE)
def test_sending_data_notification(self):
"""Test notifications."""
events = self._setup_notify()
config = deepcopy(TEST_CONFIG)
config[alert.DOMAIN][NAME][alert.CONF_DATA] = TEST_DATA
assert setup_component(self.hass, alert.DOMAIN, config)
self.hass.states.set(TEST_ENTITY, STATE_ON)
self.hass.block_till_done()
self.assertEqual(1, len(events))
last_event = events[-1]
self.assertEqual(last_event.data[notify.ATTR_DATA], TEST_DATA)
def test_skipfirst(self): def test_skipfirst(self):
"""Test skipping first notification.""" """Test skipping first notification."""
config = deepcopy(TEST_CONFIG) config = deepcopy(TEST_CONFIG)