diff --git a/platypush/backend/http/__init__.py b/platypush/backend/http/__init__.py index b391af1893..c463b29448 100644 --- a/platypush/backend/http/__init__.py +++ b/platypush/backend/http/__init__.py @@ -15,7 +15,7 @@ from flask import Flask, Response, abort, jsonify, request as http_request, \ from redis import Redis from platypush.config import Config -from platypush.context import get_backend, get_or_create_event_loop +from platypush.context import get_backend, get_plugin, get_or_create_event_loop from platypush.message import Message from platypush.message.event import Event, StopEvent from platypush.message.event.web.widget import WidgetUpdateEvent @@ -279,8 +279,10 @@ class HttpBackend(Backend): else: enabled_plugins[plugin] = conf - return render_template('index.html', plugins=enabled_plugins, hidden_plugins=hidden_plugins, - token=Config.get('token'), websocket_port=self.websocket_port, + return render_template('index.html', plugins=enabled_plugins, + hidden_plugins=hidden_plugins, utils=HttpUtils, + token=Config.get('token'), + websocket_port=self.websocket_port, has_ssl=self.ssl_context is not None) @@ -548,5 +550,9 @@ class HttpUtils(object): return json.loads(data) -# vim:sw=4:ts=4:et: + @classmethod + def get_plugin(cls, plugin): + return get_plugin(plugin) + +# vim:sw=4:ts=4:et: diff --git a/platypush/backend/http/static/js/music.snapcast.js b/platypush/backend/http/static/js/music.snapcast.js new file mode 100644 index 0000000000..a59ead5677 --- /dev/null +++ b/platypush/backend/http/static/js/music.snapcast.js @@ -0,0 +1,305 @@ +$(document).ready(function() { + var statuses = [], + $container = $('#snapcast-container'); + + var createPowerToggleElement = function(data) { + data = data || {}; + + var $powerToggle = $('
').addClass('toggle toggle--push switch-container'); + var $input = $('').attr('type', 'checkbox') + .attr('id', data.id).addClass('toggle--checkbox'); + + for (var attr of Object.keys(data)) { + $input.data(attr, data[attr]); + } + + var $label = $('').attr('for', data.id).addClass('toggle--btn'); + + $input.appendTo($powerToggle); + $label.appendTo($powerToggle); + + if ('on' in data && data['on']) { + $input.prop('checked', true); + } + + return $powerToggle; + }; + + var update = function(statuses) { + $container.html(''); + + var networkNames = Object.keys(window.config.snapcast_hosts); + for (var i=0; i < networkNames.length; i++) { + var status = statuses[i]; + var networkName = networkNames[i]; + var name = status.server.host.name || status.server.host.ip; + + var $host = $('
') + .addClass('snapcast-host-container') + .data('name', name) + .data('network-name', networkName); + + var $header = $('
').addClass('row') + .addClass('snapcast-host-header'); + + var $title = $('

').text(name); + + $title.appendTo($header); + $header.appendTo($host); + + for (var group of status.groups) { + var groupName = group.name || group.stream_id; + var $group = $('
') + .addClass('snapcast-group-container') + .data('name', groupName) + .data('id', group.id); + + var $groupHeader = $('
').addClass('row') + .addClass('snapcast-group-header'); + + var $groupTitle = $('

') + .addClass('eleven columns') + .text(groupName); + + var $groupSettings = $('') + .addClass('snapcast-group-settings') + .addClass('one column') + .addClass('fa fa-cog') + .data('name', groupName) + .data('id', group.id); + + $groupTitle.appendTo($groupHeader); + $groupSettings.appendTo($groupHeader); + $groupHeader.appendTo($group); + + for (var client of group.clients) { + var clientName = client.config.name || client.host.name || client.host.ip; + var $client = $('
') + .addClass('snapcast-client-container') + .data('name', clientName) + .data('id', client.id); + + var $clientHeader = $('
').addClass('row') + .addClass('snapcast-client-header'); + + var $clientTitle = $('

') + .addClass('eleven columns') + .data('connected', client.connected) + .text(clientName); + + var $clientMuteToggle = createPowerToggleElement({ + type: 'client', + id: client.id, + on: !client.config.volume.muted, + }).addClass('one column'); + + $clientTitle.appendTo($clientHeader); + $clientMuteToggle.appendTo($clientHeader); + $clientHeader.appendTo($client); + $client.appendTo($group); + } + + $group.appendTo($host); + } + + $host.appendTo($container); + } + }; + + var initUi = function() { + var promises = []; + + for (var host of Object.keys(window.config.snapcast_hosts)) { + promises.push( + execute({ + type: 'request', + action: 'music.snapcast.status', + args: { + host: host, + port: window.config.snapcast_hosts[host], + } + }) + ); + } + + $.when.apply($, promises) + .done(function() { + statuses = []; + for (var status of arguments) { + statuses.push(status[0].response.output); + } + + update(statuses); + }).then(function() { + initBindings(); + }); + }; + + var initBindings = function() { + // $roomsList.on('click touch', '.room-item', function() { + // $('.room-item').removeClass('selected'); + // $('.room-lights-item').hide(); + // $('.room-scenes-item').hide(); + + // var roomId = $(this).data('id'); + // var $roomLights = $('.room-lights-item').filter(function(i, item) { + // return $(item).data('id') === roomId + // }); + + // var $roomScenes = $('.room-scenes-item').filter(function(i, item) { + // return $(item).data('room-id') === roomId + // }); + + // $(this).addClass('selected'); + // $roomLights.show(); + // $roomScenes.show(); + // }); + + // $scenesList.on('click touch', '.scene-item', function() { + // $('.scene-item').removeClass('selected'); + // $(this).addClass('selected'); + + // execute({ + // type: 'request', + // action: 'light.hue.scene', + // args: { + // name: $(this).data('name') + // } + // }, refreshStatus); + // }); + + // $lightsList.on('click touch', '.light-item-name', function() { + // var $lightItem = $(this).parents('.light-item'); + // var $colorSelector = $lightItem.find('.light-color-selector'); + + // $('.light-color-selector').hide(); + // $colorSelector.toggle(); + + // $('.light-item').removeClass('selected'); + // $lightItem.addClass('selected'); + // }); + + // $lightsList.on('click touch', '.light-ctrl-switch', function(e) { + // e.stopPropagation(); + + // var $lightItem = $($(this).parents('.light-item')); + // var type = $lightItem.data('type'); + // var name = $lightItem.data('name'); + // var isOn = $lightItem.data('on'); + // var action = 'light.hue.' + (isOn ? 'off' : 'on'); + // var key = (type == 'light' ? 'lights' : 'groups'); + // var args = { + // type: 'request', + // action: action, + // args: {} + // }; + + // args['args'][key] = [name]; + // execute(args, function() { + // $lightItem.data('on', !isOn); + // refreshStatus(); + // }); + // }); + + // $lightsList.on('click touch', '.animation-switch', function(e) { + // e.stopPropagation(); + + // var turnedOn = $(this).prop('checked'); + // var args = {}; + // args['groups'] = $(this).parents('.animation-item').data('name'); + // args['animation'] = $(this).parents('.animation-item') + // .find('input.animation-type:checked').data('type'); + + // var $animationCtrl = $(this).parents('.animation-item') + // .find('.animation-container').filter( + // (index, node) => $(node).data('animation-type') === args['animation'] + // ); + + // var params = $animationCtrl.find('*').filter( + // (index, node) => $(node).data('animation-property')) + // .toArray().reduce( + // (map, input) => { + // if ($(input).val().length) { + // var val = $(input).val(); + // val = Array.isArray(val) ? val.map((i) => parseFloat(i)) : parseFloat(val); + // map[$(input).data('animation-property')] = val; + // } + + // return map + // }, {} + // ); + + // for (var p of Object.keys(params)) { + // args[p] = params[p]; + // } + + // if (turnedOn) { + // execute( + // { + // type: 'request', + // action: 'light.hue.animate', + // args: args, + // }, + + // onSuccess = function() { + // $(this).prop('checked', true); + // } + // ); + // } else { + // execute( + // { + // type: 'request', + // action: 'light.hue.stop_animation', + // }, + + // onSuccess = function() { + // $(this).prop('checked', false); + // } + // ); + // } + // }); + + // $lightsList.on('mouseup touchend', '.light-slider', function() { + // var property = $(this).data('property'); + // var type = $(this).data('type'); + // var name = $(this).data('name'); + // var args = { + // type: 'request', + // action: 'light.hue.' + property, + // args: { value: $(this).val() } + // }; + + // if (type === 'light') { + // args.args.lights = [name]; + // } else { + // args.args.groups = [name]; + // } + + // execute(args, refreshStatus); + // }); + + // $lightsList.on('click touch', 'input.animation-type', function(e) { + // var type = $(this).data('type'); + // var $animationContainers = $(this).parents('.animation-item').find('.animation-container') + // var $animationContainer = $(this).parents('.animation-item').find('.animation-container') + // .filter(function() { return $(this).data('animationType') === type }) + + // $animationContainers.hide(); + // $animationContainer.show(); + // }); + + // if (window.config.light.hue.default_group) { + // var $defaultRoomItem = $roomsList.find('.room-item').filter( + // (i, r) => $(r).data('name') == window.config.light.hue.default_group); + + // $defaultRoomItem.click(); + // } + }; + + var init = function() { + initUi(); + }; + + init(); +}); + diff --git a/platypush/backend/http/templates/index.html b/platypush/backend/http/templates/index.html index 98acdb1c08..fbe5ddebae 100644 --- a/platypush/backend/http/templates/index.html +++ b/platypush/backend/http/templates/index.html @@ -55,10 +55,11 @@
{% for plugin in plugins.keys()|sort() %} - {% set configuration = plugins[plugin] %} -
- {% include 'plugins/' + plugin + '.html' %} -
+ {% with configuration=plugins[plugin], utils=utils %} +
+ {% include 'plugins/' + plugin + '.html' %} +
+ {% endwith %} {% endfor %}
diff --git a/platypush/backend/http/templates/plugins/music.snapcast.html b/platypush/backend/http/templates/plugins/music.snapcast.html new file mode 100644 index 0000000000..7b2baf6b12 --- /dev/null +++ b/platypush/backend/http/templates/plugins/music.snapcast.html @@ -0,0 +1,18 @@ + + + + + +
+
+ diff --git a/platypush/plugins/music/snapcast.py b/platypush/plugins/music/snapcast.py index eb9272e579..6b302f45dd 100644 --- a/platypush/plugins/music/snapcast.py +++ b/platypush/plugins/music/snapcast.py @@ -36,7 +36,8 @@ class MusicSnapcastPlugin(Plugin): self._latest_req_id_lock = threading.RLock() backend = get_backend('music.snapcast') - self.backend_hosts = backend.hosts if backend else [] + self.backend_hosts = backend.hosts if backend else [self.host] + self.backend_ports = backend.ports if backend else [self.port] def _get_req_id(self): with self._latest_req_id_lock: