From 02e951bd5748264b34433026e0f22db695f28361 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Tue, 17 Apr 2018 23:09:07 +0200 Subject: [PATCH] - Added hidden plugins configuration for plugins that shouldn't be shown on the web panel as tabs - Added support for popup notifications on the web panel - Added voice assistant interactive notifications to the web panel - Added new playing music notifications to the web panel --- platypush/backend/http/__init__.py | 11 +- .../backend/http/static/css/application.css | 63 ++++++++++ .../http/static/css/assistant.google.css | 0 .../backend/http/static/js/application.js | 109 ++++++++++++++++++ .../http/static/js/assistant.google.js | 36 ++++++ .../backend/http/static/js/gpio.zeroborg.js | 31 ----- platypush/backend/http/static/js/light.hue.js | 31 ----- platypush/backend/http/static/js/music.mpd.js | 40 ++----- platypush/backend/http/static/js/tts.js | 31 ----- platypush/backend/http/templates/index.html | 11 ++ .../templates/plugins/assistant.google.html | 3 + .../backend/http/templates/plugins/tts.html | 4 +- 12 files changed, 241 insertions(+), 129 deletions(-) create mode 100644 platypush/backend/http/static/css/assistant.google.css create mode 100644 platypush/backend/http/static/js/assistant.google.js create mode 100644 platypush/backend/http/templates/plugins/assistant.google.html diff --git a/platypush/backend/http/__init__.py b/platypush/backend/http/__init__.py index cb77e4704b..2d78fb7e5f 100644 --- a/platypush/backend/http/__init__.py +++ b/platypush/backend/http/__init__.py @@ -25,6 +25,9 @@ class HttpBackend(Backend): websocket_ping_tries = 3 websocket_ping_timeout = 60.0 + hidden_plugins = { + 'assistant.google' + } def __init__(self, port=8008, websocket_port=8009, disable_websocket=False, token=None, **kwargs): @@ -98,13 +101,17 @@ class HttpBackend(Backend): def index(): configured_plugins = Config.get_plugins() enabled_plugins = {} + hidden_plugins = {} for plugin, conf in configured_plugins.items(): template_file = os.path.join('plugins', plugin + '.html') if os.path.isfile(os.path.join(template_dir, template_file)): - enabled_plugins[plugin] = conf + if plugin in self.hidden_plugins: + hidden_plugins[plugin] = conf + else: + enabled_plugins[plugin] = conf - return render_template('index.html', plugins=enabled_plugins, + return render_template('index.html', plugins=enabled_plugins, hidden_plugins=hidden_plugins, token=self.token, websocket_port=self.websocket_port) diff --git a/platypush/backend/http/static/css/application.css b/platypush/backend/http/static/css/application.css index 2dab9f307c..46663d87a8 100644 --- a/platypush/backend/http/static/css/application.css +++ b/platypush/backend/http/static/css/application.css @@ -144,3 +144,66 @@ button[disabled] { text-align: right !important; } +#notification-container { + position: fixed; + bottom: 0; + right: 0; + width: 25em; +} + + #notification-container > .notification { + background: rgba(180,245,188,0.85); + border: 1px solid #bbb; + border-radius: 5px; + margin-bottom: 1rem; + margin-right: 1rem; + cursor: pointer; + display: none; + } + + #notification-container > .notification:hover { + background: rgba(160,235,168,0.9); + } + + #notification-container * > .notification-title { + padding: 4px 10px; + font-weight: bold; + text-transform: capitalize; + line-height: 30px; + letter-spacing: .1rem; + } + + #notification-container * > .notification-body { + height: 6em; + overflow: hidden; + padding-bottom: 1rem; + letter-spacing: .05rem; + } + + #notification-container * > .notification-text { + margin-left: 0 !important; + } + + #notification-container * > .notification-image { + height: 100%; + text-align: center; + padding-top: .6em; + padding-bottom: .6em; + } + + #notification-container * > .notification-image-item { + width: 100%; + height: 100%; + margin: auto; + } + + #notification-container * > .fa.notification-image-item { + margin-top: .8em; + margin-left: .2em; + font-size: 25px; + } + +#hidden-plugins-container > .plugin { + display: none; +} + diff --git a/platypush/backend/http/static/css/assistant.google.css b/platypush/backend/http/static/css/assistant.google.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/platypush/backend/http/static/js/application.js b/platypush/backend/http/static/js/application.js index 5604e13329..b841bffe14 100644 --- a/platypush/backend/http/static/js/application.js +++ b/platypush/backend/http/static/js/application.js @@ -176,3 +176,112 @@ $(document).ready(function() { init(); }); +function execute(request, onSuccess, onError, onComplete) { + request['target'] = 'localhost'; + return $.ajax({ + type: 'POST', + url: '/execute', + contentType: 'application/json', + dataType: 'json', + data: JSON.stringify(request), + complete: function() { + if (onComplete) { + onComplete(); + } + }, + error: function(xhr, status, error) { + if (onError) { + onError(xhr, status, error); + } + }, + success: function(response, status, xhr) { + if (onSuccess) { + onSuccess(response, status, xhr); + } + }, + beforeSend: function(xhr) { + if (window.token) { + xhr.setRequestHeader('X-Token', window.token); + } + }, + }); +} + +function createNotification(options) { + var $notificationContainer = $('#notification-container'); + var $notification = $('
').addClass('notification'); + var $title = $('
').addClass('notification-title'); + var timeout = 'timeout' in options ? options.timeout : 10000; + + if ('title' in options) { + $title.text(options.title); + } + + var $body = $('
').addClass('notification-body'); + var $imgDiv = $('
').addClass('notification-image').addClass('three columns'); + var $img = $('').addClass('fa fa-bell').addClass('three columns').addClass('notification-image-item'); + var $text = $('
').addClass('notification-text').addClass('nine columns') + + if ('image' in options) { + $img = $('').attr('src', options.image); + } else if ('icon' in options) { + $img.removeClass('fa-bell').addClass('fa-' + options.icon); + } + + if ('text' in options) { + $text.text(options.text); + } else if ('html' in options) { + $text.html(options.html); + } + + $img.appendTo($imgDiv); + $imgDiv.appendTo($body); + $text.appendTo($body); + + var clickHandler; + var removeTimeoutId; + + var removeNotification = function() { + $notification.fadeOut(300, function() { + $notification.remove(); + }); + }; + + var setRemoveTimeout = function() { + if (timeout) { + removeTimeoutId = setTimeout(removeNotification, timeout); + } + }; + + setRemoveTimeout(); + + if ('onclick' in options) { + clickHandler = options.onclick; + } else { + clickHandler = removeNotification; + } + + $notification.on('click', clickHandler); + $notification.hover( + // on hover start + function() { + if (removeTimeoutId) { + clearTimeout(removeTimeoutId); + removeTimeoutId = undefined; + } + }, + + // on hover end + function() { + if (timeout) { + setRemoveTimeout(); + } + } + ); + + $title.appendTo($notification); + $body.appendTo($notification); + $notification.prependTo($notificationContainer); + $notification.fadeIn(); +} + diff --git a/platypush/backend/http/static/js/assistant.google.js b/platypush/backend/http/static/js/assistant.google.js new file mode 100644 index 0000000000..7d01e25c69 --- /dev/null +++ b/platypush/backend/http/static/js/assistant.google.js @@ -0,0 +1,36 @@ +$(document).ready(function() { + var onEvent = function(event) { + switch (event.args.type) { + case 'platypush.message.event.assistant.ConversationStartEvent': + createNotification({ + 'text': 'Assistant listening', + 'icon': 'microphone', + }); + + break; + + case 'platypush.message.event.assistant.SpeechRecognizedEvent': + createNotification({ + 'title': 'Speech recognized', + 'text': event.args.phrase, + 'icon': 'microphone', + }); + + break; + + case 'platypush.message.event.assistant.ConversationEndEvent': + break; + } + }; + + var initEvents = function() { + window.registerEventListener(onEvent); + }; + + var init = function() { + initEvents(); + }; + + init(); +}); + diff --git a/platypush/backend/http/static/js/gpio.zeroborg.js b/platypush/backend/http/static/js/gpio.zeroborg.js index 9ac1822782..037e0f8110 100644 --- a/platypush/backend/http/static/js/gpio.zeroborg.js +++ b/platypush/backend/http/static/js/gpio.zeroborg.js @@ -3,37 +3,6 @@ $(document).ready(function() { $controlsContainer = $('.zb-controls-container'), curDirection = undefined; - var execute = function(request, onSuccess, onError, onComplete) { - request['target'] = 'localhost'; - return $.ajax({ - type: 'POST', - url: '/execute', - contentType: 'application/json', - dataType: 'json', - data: JSON.stringify(request), - complete: function() { - if (onComplete) { - onComplete(); - } - }, - error: function(xhr, status, error) { - if (onError) { - onError(xhr, status, error); - } - }, - success: function(response, status, xhr) { - if (onSuccess) { - onSuccess(response, status, xhr); - } - }, - beforeSend: function(xhr) { - if (window.token) { - xhr.setRequestHeader('X-Token', window.token); - } - }, - }); - }; - var initBindings = function() { $controlsContainer.on('mousedown touchstart', '.zb-ctrl-btn[data-direction]', function() { var direction = $(this).data('direction'); diff --git a/platypush/backend/http/static/js/light.hue.js b/platypush/backend/http/static/js/light.hue.js index d1d8a96bb3..e1d009f400 100644 --- a/platypush/backend/http/static/js/light.hue.js +++ b/platypush/backend/http/static/js/light.hue.js @@ -6,37 +6,6 @@ $(document).ready(function() { $lightsList = $('#lights-list'), $scenesList = $('#scenes-list'); - var execute = function(request, onSuccess, onError, onComplete) { - request['target'] = 'localhost'; - return $.ajax({ - type: 'POST', - url: '/execute', - contentType: 'application/json', - dataType: 'json', - data: JSON.stringify(request), - complete: function() { - if (onComplete) { - onComplete(); - } - }, - error: function(xhr, status, error) { - if (onError) { - onError(xhr, status, error); - } - }, - success: function(response, status, xhr) { - if (onSuccess) { - onSuccess(response, status, xhr); - } - }, - beforeSend: function(xhr) { - if (window.token) { - xhr.setRequestHeader('X-Token', window.token); - } - }, - }); - }; - var createPowerToggleElement = function(data) { var id = data['type'] + '_' + data['id']; var $powerToggle = $('
').addClass('toggle toggle--push light-ctrl-switch-container'); diff --git a/platypush/backend/http/static/js/music.mpd.js b/platypush/backend/http/static/js/music.mpd.js index 3608c6a47f..7613e7169d 100644 --- a/platypush/backend/http/static/js/music.mpd.js +++ b/platypush/backend/http/static/js/music.mpd.js @@ -16,37 +16,6 @@ $(document).ready(function() { $resetSearchBtn = $('#music-search-reset'); $doSearchBtns = $('.do-search-btns'); - var execute = function(request, onSuccess, onError, onComplete) { - request['target'] = 'localhost'; - $.ajax({ - type: 'POST', - url: '/execute', - contentType: 'application/json', - dataType: 'json', - data: JSON.stringify(request), - complete: function() { - if (onComplete) { - onComplete(); - } - }, - error: function(xhr, status, error) { - if (onError) { - onError(xhr, status, error); - } - }, - success: function(response, status, xhr) { - if (onSuccess) { - onSuccess(response, status, xhr); - } - }, - beforeSend: function(xhr) { - if (window.token) { - xhr.setRequestHeader('X-Token', window.token); - } - }, - }); - }; - var formatMinutes = function(time) { if (typeof time === 'string') { time = parseInt(time); @@ -198,10 +167,17 @@ $(document).ready(function() { var onEvent = function(event) { switch (event.args.type) { + case 'platypush.message.event.music.NewPlayingTrackEvent': + createNotification({ + 'icon': 'play', + 'html': '' + ('artist' in event.args.track ? event.args.track.artist : '') + + '
' + + ('title' in event.args.track ? event.args.track.title : '[No name]'), + }); + case 'platypush.message.event.music.MusicStopEvent': case 'platypush.message.event.music.MusicPlayEvent': case 'platypush.message.event.music.MusicPauseEvent': - case 'platypush.message.event.music.NewPlayingTrackEvent': updateControls(status=event.args.status, track=event.args.track); break; diff --git a/platypush/backend/http/static/js/tts.js b/platypush/backend/http/static/js/tts.js index 2b46af89c2..bac32f326f 100644 --- a/platypush/backend/http/static/js/tts.js +++ b/platypush/backend/http/static/js/tts.js @@ -2,37 +2,6 @@ $(document).ready(function() { var $container = $('#tts-container'), $ttsForm = $('#tts-form'); - var execute = function(request, onSuccess, onError, onComplete) { - request['target'] = 'localhost'; - return $.ajax({ - type: 'POST', - url: '/execute', - contentType: 'application/json', - dataType: 'json', - data: JSON.stringify(request), - complete: function() { - if (onComplete) { - onComplete(); - } - }, - error: function(xhr, status, error) { - if (onError) { - onError(xhr, status, error); - } - }, - success: function(response, status, xhr) { - if (onSuccess) { - onSuccess(response, status, xhr); - } - }, - beforeSend: function(xhr) { - if (window.token) { - xhr.setRequestHeader('X-Token', window.token); - } - }, - }); - }; - var initBindings = function() { $ttsForm.on('submit', function(event) { var formData = $(this).serializeArray().reduce(function(obj, item) { diff --git a/platypush/backend/http/templates/index.html b/platypush/backend/http/templates/index.html index 281f84e85b..0eeaca6ac0 100644 --- a/platypush/backend/http/templates/index.html +++ b/platypush/backend/http/templates/index.html @@ -57,6 +57,17 @@ {% endfor %} + +
+ +
+ {% for plugin in hidden_plugins.keys()|sort() %} + {% set configuration = plugins[plugin] %} +
+ {% include 'plugins/' + plugin + '.html' %} +
+ {% endfor %} +
diff --git a/platypush/backend/http/templates/plugins/assistant.google.html b/platypush/backend/http/templates/plugins/assistant.google.html new file mode 100644 index 0000000000..a6e62c1d3e --- /dev/null +++ b/platypush/backend/http/templates/plugins/assistant.google.html @@ -0,0 +1,3 @@ + + + diff --git a/platypush/backend/http/templates/plugins/tts.html b/platypush/backend/http/templates/plugins/tts.html index e33391fbd5..9d00b9fba0 100644 --- a/platypush/backend/http/templates/plugins/tts.html +++ b/platypush/backend/http/templates/plugins/tts.html @@ -6,12 +6,12 @@
-
+