mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Add notify.html5_dismiss service (#19912)
* Add notify.html5_dismiss service * fix test * add can_dismiss * fix service data payload * fix hasattr -> getattr * fixes * move dismiss service to html5 * fix services.yaml * fix line to long
This commit is contained in:
parent
0ec1401be7
commit
1b79872dd6
@ -7,6 +7,7 @@ https://home-assistant.io/components/notify.html5/
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
from functools import partial
|
||||
import time
|
||||
import uuid
|
||||
|
||||
@ -20,7 +21,7 @@ from homeassistant.components.frontend import add_manifest_json_key
|
||||
from homeassistant.components.http import HomeAssistantView
|
||||
from homeassistant.components.notify import (
|
||||
ATTR_DATA, ATTR_TITLE, ATTR_TARGET, PLATFORM_SCHEMA, ATTR_TITLE_DEFAULT,
|
||||
BaseNotificationService)
|
||||
BaseNotificationService, DOMAIN)
|
||||
from homeassistant.const import (
|
||||
URL_ROOT, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED, HTTP_INTERNAL_SERVER_ERROR)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@ -34,6 +35,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REGISTRATIONS_FILE = 'html5_push_registrations.conf'
|
||||
|
||||
SERVICE_DISMISS = 'html5_dismiss'
|
||||
|
||||
ATTR_GCM_SENDER_ID = 'gcm_sender_id'
|
||||
ATTR_GCM_API_KEY = 'gcm_api_key'
|
||||
|
||||
@ -57,6 +60,7 @@ ATTR_ACTION = 'action'
|
||||
ATTR_ACTIONS = 'actions'
|
||||
ATTR_TYPE = 'type'
|
||||
ATTR_URL = 'url'
|
||||
ATTR_DISMISS = 'dismiss'
|
||||
|
||||
ATTR_JWT = 'jwt'
|
||||
|
||||
@ -80,6 +84,11 @@ SUBSCRIPTION_SCHEMA = vol.All(
|
||||
})
|
||||
)
|
||||
|
||||
DISMISS_SERVICE_SCHEMA = vol.Schema({
|
||||
vol.Optional(ATTR_TARGET): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(ATTR_DATA): dict,
|
||||
})
|
||||
|
||||
REGISTER_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_SUBSCRIPTION): SUBSCRIPTION_SCHEMA,
|
||||
vol.Required(ATTR_BROWSER): vol.In(['chrome', 'firefox']),
|
||||
@ -122,7 +131,8 @@ def get_service(hass, config, discovery_info=None):
|
||||
add_manifest_json_key(
|
||||
ATTR_GCM_SENDER_ID, config.get(ATTR_GCM_SENDER_ID))
|
||||
|
||||
return HTML5NotificationService(gcm_api_key, registrations, json_path)
|
||||
return HTML5NotificationService(
|
||||
hass, gcm_api_key, registrations, json_path)
|
||||
|
||||
|
||||
def _load_config(filename):
|
||||
@ -326,12 +336,29 @@ class HTML5PushCallbackView(HomeAssistantView):
|
||||
class HTML5NotificationService(BaseNotificationService):
|
||||
"""Implement the notification service for HTML5."""
|
||||
|
||||
def __init__(self, gcm_key, registrations, json_path):
|
||||
def __init__(self, hass, gcm_key, registrations, json_path):
|
||||
"""Initialize the service."""
|
||||
self._gcm_key = gcm_key
|
||||
self.registrations = registrations
|
||||
self.registrations_json_path = json_path
|
||||
|
||||
async def async_dismiss_message(service):
|
||||
"""Handle dismissing notification message service calls."""
|
||||
kwargs = {}
|
||||
|
||||
if self.targets is not None:
|
||||
kwargs[ATTR_TARGET] = self.targets
|
||||
elif service.data.get(ATTR_TARGET) is not None:
|
||||
kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET)
|
||||
|
||||
kwargs[ATTR_DATA] = service.data.get(ATTR_DATA)
|
||||
|
||||
await self.async_dismiss(**kwargs)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_DISMISS, async_dismiss_message,
|
||||
schema=DISMISS_SERVICE_SCHEMA)
|
||||
|
||||
@property
|
||||
def targets(self):
|
||||
"""Return a dictionary of registered targets."""
|
||||
@ -340,12 +367,28 @@ class HTML5NotificationService(BaseNotificationService):
|
||||
targets[registration] = registration
|
||||
return targets
|
||||
|
||||
def dismiss(self, **kwargs):
|
||||
"""Dismisses a notification."""
|
||||
data = kwargs.get(ATTR_DATA)
|
||||
tag = data.get(ATTR_TAG) if data else ""
|
||||
payload = {
|
||||
ATTR_TAG: tag,
|
||||
ATTR_DISMISS: True,
|
||||
ATTR_DATA: {}
|
||||
}
|
||||
|
||||
self._push_message(payload, **kwargs)
|
||||
|
||||
async def async_dismiss(self, **kwargs):
|
||||
"""Dismisses a notification.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(self.dismiss, **kwargs))
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
"""Send a message to a user."""
|
||||
import jwt
|
||||
from pywebpush import WebPusher
|
||||
|
||||
timestamp = int(time.time())
|
||||
tag = str(uuid.uuid4())
|
||||
|
||||
payload = {
|
||||
@ -354,7 +397,6 @@ class HTML5NotificationService(BaseNotificationService):
|
||||
ATTR_DATA: {},
|
||||
'icon': '/static/icons/favicon-192x192.png',
|
||||
ATTR_TAG: tag,
|
||||
'timestamp': (timestamp*1000), # Javascript ms since epoch
|
||||
ATTR_TITLE: kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
|
||||
}
|
||||
|
||||
@ -378,6 +420,17 @@ class HTML5NotificationService(BaseNotificationService):
|
||||
payload.get(ATTR_ACTIONS) is None):
|
||||
payload[ATTR_DATA][ATTR_URL] = URL_ROOT
|
||||
|
||||
self._push_message(payload, **kwargs)
|
||||
|
||||
def _push_message(self, payload, **kwargs):
|
||||
"""Send the message."""
|
||||
import jwt
|
||||
from pywebpush import WebPusher
|
||||
|
||||
timestamp = int(time.time())
|
||||
|
||||
payload['timestamp'] = (timestamp*1000) # Javascript ms since epoch
|
||||
|
||||
targets = kwargs.get(ATTR_TARGET)
|
||||
|
||||
if not targets:
|
||||
|
@ -16,6 +16,16 @@ notify:
|
||||
description: Extended information for notification. Optional depending on the platform.
|
||||
example: platform specific
|
||||
|
||||
html5_dismiss:
|
||||
description: Dismiss a html5 notification.
|
||||
fields:
|
||||
target:
|
||||
description: An array of targets. Optional.
|
||||
example: ['my_phone', 'my_tablet']
|
||||
data:
|
||||
description: Extended information of notification. Supports tag. Optional.
|
||||
example: '{ "tag": "tagname" }'
|
||||
|
||||
apns_register:
|
||||
description: Registers a device to receive push notifications.
|
||||
fields:
|
||||
|
@ -81,6 +81,40 @@ class TestHtml5Notify:
|
||||
|
||||
assert service is not None
|
||||
|
||||
@patch('pywebpush.WebPusher')
|
||||
def test_dismissing_message(self, mock_wp):
|
||||
"""Test dismissing message."""
|
||||
hass = MagicMock()
|
||||
|
||||
data = {
|
||||
'device': SUBSCRIPTION_1
|
||||
}
|
||||
|
||||
m = mock_open(read_data=json.dumps(data))
|
||||
with patch(
|
||||
'homeassistant.util.json.open',
|
||||
m, create=True
|
||||
):
|
||||
service = html5.get_service(hass, {'gcm_sender_id': '100'})
|
||||
|
||||
assert service is not None
|
||||
|
||||
service.dismiss(target=['device', 'non_existing'],
|
||||
data={'tag': 'test'})
|
||||
|
||||
assert len(mock_wp.mock_calls) == 3
|
||||
|
||||
# WebPusher constructor
|
||||
assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1['subscription']
|
||||
# Third mock_call checks the status_code of the response.
|
||||
assert mock_wp.mock_calls[2][0] == '().send().status_code.__eq__'
|
||||
|
||||
# Call to send
|
||||
payload = json.loads(mock_wp.mock_calls[1][1][0])
|
||||
|
||||
assert payload['dismiss'] is True
|
||||
assert payload['tag'] == 'test'
|
||||
|
||||
@patch('pywebpush.WebPusher')
|
||||
def test_sending_message(self, mock_wp):
|
||||
"""Test sending message."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user