- 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
This commit is contained in:
Fabio Manganiello 2018-04-17 23:09:07 +02:00
parent fcdc4d1af8
commit 02e951bd57
12 changed files with 241 additions and 129 deletions

View file

@ -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)

View file

@ -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;
}

View file

@ -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 = $('<div></div>').addClass('notification');
var $title = $('<div></div>').addClass('notification-title');
var timeout = 'timeout' in options ? options.timeout : 10000;
if ('title' in options) {
$title.text(options.title);
}
var $body = $('<div></div>').addClass('notification-body');
var $imgDiv = $('<div></div>').addClass('notification-image').addClass('three columns');
var $img = $('<i></i>').addClass('fa fa-bell').addClass('three columns').addClass('notification-image-item');
var $text = $('<div></div>').addClass('notification-text').addClass('nine columns')
if ('image' in options) {
$img = $('<img></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();
}

View file

@ -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();
});

View file

@ -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');

View file

@ -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 = $('<div></div>').addClass('toggle toggle--push light-ctrl-switch-container');

View file

@ -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': '<b>' + ('artist' in event.args.track ? event.args.track.artist : '')
+ '</b><br/>'
+ ('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;

View file

@ -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) {

View file

@ -57,6 +57,17 @@
</div>
{% endfor %}
</div>
<div id="notification-container"></div>
<div id="hidden-plugins-container">
{% for plugin in hidden_plugins.keys()|sort() %}
{% set configuration = plugins[plugin] %}
<div class="plugin" id="{% print plugin %}">
{% include 'plugins/' + plugin + '.html' %}
</div>
{% endfor %}
</div>
</main>
<body>

View file

@ -0,0 +1,3 @@
<script type="text/javascript" src="{{ url_for('static', filename='js/assistant.google.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/assistant.google.css') }}"></script>

View file

@ -6,12 +6,12 @@
<div class="eight columns">
<input type="text" name="phrase" placeholder="Text to say">
</div>
<div class="three columns">
<div class="two columns">
<input type="text" name="lang" placeholder="Lang code">
</div>
<div class="one column">
<button type="submit">
<i class="fa fa-microphone"></i>
<i class="fa fa-volume-up"></i>
</button>
</div>
</form>