forked from platypush/platypush
- Added Pushbullet JS logic to show mirror notifications on web FE
- Added JS and CSS files for dashboard and widgets
This commit is contained in:
parent
ae6467060b
commit
2f8c74c8e3
12 changed files with 306 additions and 27 deletions
|
@ -203,6 +203,11 @@ button[disabled] {
|
|||
font-size: 25px;
|
||||
}
|
||||
|
||||
#notification-container * > .notification-image img {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
#hidden-plugins-container > .plugin {
|
||||
display: none;
|
||||
}
|
||||
|
|
17
platypush/backend/http/static/css/dashboard.css
Normal file
17
platypush/backend/http/static/css/dashboard.css
Normal file
|
@ -0,0 +1,17 @@
|
|||
body {
|
||||
background: rgba(240,240,245,1.0);
|
||||
font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
#widgets-container {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.widget {
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
height: 18em;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
.widget.date-time-weather {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.date-time-weather-container {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
h1.temperature {
|
||||
font-size: 45px;
|
||||
margin: 4rem;
|
||||
}
|
||||
|
||||
.widget.date-time-weather * > .time {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
49
platypush/backend/http/static/css/widgets/music.css
Normal file
49
platypush/backend/http/static/css/widgets/music.css
Normal file
|
@ -0,0 +1,49 @@
|
|||
.music-container {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.track-info {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.track-info > .artist {
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.track-info > .title {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.time-bar {
|
||||
height: 7.5px;
|
||||
background: #ddd;
|
||||
margin: 7.5px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.time-bar > .elapsed {
|
||||
height: 7.5px;
|
||||
background: #98ffb0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.time-elapsed {
|
||||
text-align: left;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.time-total {
|
||||
float: right;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.time-elapsed, .time-total {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
27
platypush/backend/http/static/js/pushbullet.js
Normal file
27
platypush/backend/http/static/js/pushbullet.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
$(document).ready(function() {
|
||||
var onEvent = function(event) {
|
||||
switch (event.args.type) {
|
||||
case 'platypush.message.event.pushbullet.PushbulletEvent':
|
||||
if (event.args.push_type === 'mirror') {
|
||||
createNotification({
|
||||
'title': event.args.title,
|
||||
'text': event.args.body,
|
||||
'image': 'data:image/png;base64, ' + event.args.icon,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var initEvents = function() {
|
||||
window.registerEventListener(onEvent);
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
initEvents();
|
||||
};
|
||||
|
||||
init();
|
||||
});
|
||||
|
123
platypush/backend/http/static/js/widgets/music.js
Normal file
123
platypush/backend/http/static/js/widgets/music.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
$(document).ready(function() {
|
||||
var $widget = $('.widget.music'),
|
||||
$trackContainer = $widget.find('.track-container'),
|
||||
$timeContainer = $widget.find('.time-container'),
|
||||
$noTrackElement = $trackContainer.find('.no-track-info'),
|
||||
$trackElement = $trackContainer.find('.track-info'),
|
||||
$artistElement = $trackElement.find('[data-bind=artist]'),
|
||||
$titleElement = $trackElement.find('[data-bind=title]'),
|
||||
$timeElapsedElement = $timeContainer.find('.time-elapsed'),
|
||||
$timeTotalElement = $timeContainer.find('.time-total'),
|
||||
$elapsedTimeBar = $widget.find('.time-bar > .elapsed'),
|
||||
timeElapsed,
|
||||
timeTotal,
|
||||
refreshElapsedInterval;
|
||||
|
||||
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.MusicPlayEvent':
|
||||
case 'platypush.message.event.music.MusicPauseEvent':
|
||||
refreshTrack(event.args.track);
|
||||
|
||||
case 'platypush.message.event.music.MusicStopEvent':
|
||||
refreshStatus(event.args.status);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var initEvents = function() {
|
||||
window.registerEventListener(onEvent);
|
||||
};
|
||||
|
||||
|
||||
var setState = function(state) {
|
||||
if (state === 'play') {
|
||||
$noTrackElement.hide();
|
||||
$trackElement.show();
|
||||
$timeContainer.show();
|
||||
} else if (state === 'pause') {
|
||||
$noTrackElement.hide();
|
||||
$trackElement.show();
|
||||
$timeContainer.hide();
|
||||
} else if (state === 'stop') {
|
||||
$noTrackElement.show();
|
||||
$trackElement.hide();
|
||||
$timeContainer.hide();
|
||||
}
|
||||
};
|
||||
|
||||
var secondsToTimeString = function(seconds) {
|
||||
seconds = parseInt(seconds);
|
||||
|
||||
if (seconds) {
|
||||
return (parseInt(seconds/60) + ':' +
|
||||
(seconds%60 < 10 ? '0' : '') + seconds%60);
|
||||
} else {
|
||||
return '-:--';
|
||||
}
|
||||
};
|
||||
|
||||
var setTrackTime = function(time) {
|
||||
$timeTotalElement.text(secondsToTimeString(time));
|
||||
timeTotal = parseInt(time);
|
||||
};
|
||||
|
||||
var setTrackElapsed = function(time) {
|
||||
if (refreshElapsedInterval) {
|
||||
clearInterval(refreshElapsedInterval);
|
||||
refreshElapsedInterval = undefined;
|
||||
}
|
||||
|
||||
timeElapsed = parseInt(time);
|
||||
$timeElapsedElement.text(secondsToTimeString(timeElapsed));
|
||||
|
||||
var ratio = 100 * Math.min(timeElapsed/timeTotal, 1);
|
||||
$elapsedTimeBar.css('width', ratio + '%');
|
||||
|
||||
refreshElapsedInterval = setInterval(function() {
|
||||
timeElapsed += 1;
|
||||
ratio = 100 * Math.min(timeElapsed/timeTotal, 1);
|
||||
$elapsedTimeBar.css('width', ratio + '%');
|
||||
$timeElapsedElement.text(secondsToTimeString(timeElapsed));
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
var refreshStatus = function(status) {
|
||||
setState(state=status.state);
|
||||
if ('elapsed' in status) {
|
||||
setTrackElapsed(status.elapsed);
|
||||
}
|
||||
};
|
||||
|
||||
var refreshTrack = function(track) {
|
||||
setTrackTime(track.time);
|
||||
$artistElement.text(track.artist);
|
||||
$titleElement.text(track.title);
|
||||
};
|
||||
|
||||
var initWidget = function() {
|
||||
$.when(
|
||||
execute({ type: 'request', action: 'music.mpd.currentsong' }),
|
||||
execute({ type: 'request', action: 'music.mpd.status' })
|
||||
).done(function(t, s) {
|
||||
refreshTrack(t[0].response.output);
|
||||
refreshStatus(s[0].response.output);
|
||||
});
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
initEvents();
|
||||
initWidget();
|
||||
};
|
||||
|
||||
init();
|
||||
});
|
||||
|
|
@ -7,12 +7,14 @@
|
|||
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='font-awesome/css/font-awesome.min.css') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/application.css') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/toggles.css') }}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/dashboard.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/assistant.google.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
window.websocket_port = {% print(websocket_port) %}
|
||||
|
@ -27,6 +29,7 @@
|
|||
|
||||
<body>
|
||||
<main>
|
||||
<div id="widgets-container">
|
||||
{% for widget_name, widget in config['widgets'].items() %}
|
||||
<div class="widget {% print(utils.widget_columns_to_html_class(widget['columns'])) %}
|
||||
{% print(widget_name) %}"
|
||||
|
@ -36,6 +39,7 @@
|
|||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div id="notification-container"></div>
|
||||
</main>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
window.websocket_port = {% print(websocket_port) %}
|
||||
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
<script type="text/javascript" src="{{ url_for('static', filename='js/widgets/date-time-weather.js') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/widgets/date-time-weather.css') }}"></script>
|
||||
|
||||
<p class="date" data-bind="date"></p>
|
||||
<p class="time" data-bind="time"></p>
|
||||
<div class="date-time-weather-container">
|
||||
<div class="date" data-bind="date"></div>
|
||||
<div class="time" data-bind="time"></div>
|
||||
|
||||
<h1 class="temperature">
|
||||
<h1 class="temperature">
|
||||
<span data-bind="temperature">N/A</span>°
|
||||
</h1>
|
||||
</h1>
|
||||
|
||||
<p class="forecast" data-bind="forecast"></p>
|
||||
<div class="forecast" data-bind="forecast"></div>
|
||||
|
||||
<!-- <p class="sensor-temperature"> -->
|
||||
<!-- <span data-bind="sensor-temperature">N/A</span>° -->
|
||||
<!-- </p> -->
|
||||
<!-- <p class="sensor-temperature"> -->
|
||||
<!-- <span data-bind="sensor-temperature">N/A</span>° -->
|
||||
<!-- </p> -->
|
||||
</div>
|
||||
|
||||
|
|
31
platypush/backend/http/templates/widgets/music.html
Normal file
31
platypush/backend/http/templates/widgets/music.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
<script type="text/javascript" src="{{ url_for('static', filename='js/widgets/music.js') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/widgets/music.css') }}"></script>
|
||||
|
||||
<div class="music-container">
|
||||
<div class="track-container">
|
||||
<div class="no-track-info">No media is being played</div>
|
||||
<div class="track-info">
|
||||
<div class="artist" data-bind="artist"></div>
|
||||
<div class="title" data-bind="title"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="time-container">
|
||||
<div class="row">
|
||||
<div class="time-bar">
|
||||
<div class="elapsed"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="six columns">
|
||||
<div class="time-elapsed" data-bind="time-elapsed"></div>
|
||||
</div>
|
||||
|
||||
<div class="six columns">
|
||||
<div class="time-total" data-bind="time-total"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -60,14 +60,6 @@ class MusicMpdBackend(Backend):
|
|||
self.bus.post(PlaylistChangeEvent(changes=changes))
|
||||
last_playlist = playlist
|
||||
|
||||
if 'title' in track and ('artist' not in track
|
||||
or not track['artist']
|
||||
or re.search('^tunein:', track['file'])):
|
||||
m = re.match('^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
|
||||
if m and m.group(1) and m.group(2):
|
||||
track['artist'] = m.group(1)
|
||||
track['title'] = m.group(2)
|
||||
|
||||
if state == 'play' and track != last_track:
|
||||
self.bus.post(NewPlayingTrackEvent(status=status, track=track))
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import mpd
|
||||
import re
|
||||
|
||||
from platypush.message.response import Response
|
||||
|
||||
|
@ -105,7 +106,16 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
return Response(output=self.client.status())
|
||||
|
||||
def currentsong(self):
|
||||
return Response(output=self.client.currentsong())
|
||||
track = self.client.currentsong()
|
||||
if 'title' in track and ('artist' not in track
|
||||
or not track['artist']
|
||||
or re.search('^tunein:', track['file'])):
|
||||
m = re.match('^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
|
||||
if m and m.group(1) and m.group(2):
|
||||
track['artist'] = m.group(1)
|
||||
track['title'] = m.group(2)
|
||||
|
||||
return Response(output=track)
|
||||
|
||||
def playlistinfo(self):
|
||||
return Response(output=self.client.playlistinfo())
|
||||
|
|
Loading…
Reference in a new issue