>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];if(n&&1===n.type){var r=Pa(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+Ga(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ka(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ka))}function Ja(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Ua(e,t,Ja,"null");if(e.for&&!e.forProcessed)return za(e,t,Ja);var r=e.slotScope===ca?"":String(e.slotScope),i="function("+r+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(qa(e,t)||"undefined")+":undefined":qa(e,t)||"undefined":Ra(e,t))+"}",o=r?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function qa(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(r||Ra)(a,t)+s}var c=n?function(e,t){for(var n=0,r=0;r':'',ts.innerHTML.indexOf("
")>0}var os=!!z&&is(!1),as=!!z&&is(!0),ss=g(function(e){var t=Yn(e);return t&&t.innerHTML}),cs=wn.prototype.$mount;return wn.prototype.$mount=function(e,t){if((e=e&&Yn(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=ss(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){var i=rs(r,{outputSourceRange:!1,shouldDecodeNewlines:os,shouldDecodeNewlinesForHref:as,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return cs.call(this,e,t)},wn.compile=rs,wn});
\ No newline at end of file
diff --git a/platypush/backend/http/static/js/notifications.js b/platypush/backend/http/static/js/notifications.js
deleted file mode 100644
index 8b8b1767..00000000
--- a/platypush/backend/http/static/js/notifications.js
+++ /dev/null
@@ -1,63 +0,0 @@
-Vue.component('notifications', {
- template: '#tmpl-notifications',
- props: {
- duration: {
- // Default notification duration in milliseconds
- type: Number,
- default: 10000,
- }
- },
-
- data: function() {
- return {
- index: 0,
- notifications: {},
- timeouts: {},
- };
- },
-
- methods: {
- create: function(args) {
- var id = this.index++;
- Vue.set(this.notifications, id, args);
-
- if (args.duration == null) {
- args.duration = this.duration;
- }
-
- if (args.duration != 0) {
- this.timeouts[id] = setTimeout(this.destroy.bind(null, id), args.duration);
- }
- },
-
- destroy: function(id) {
- Vue.delete(this.notifications, id);
- delete this.timeouts[id];
- },
- },
-});
-
-Vue.component('notification', {
- template: '#tmpl-notification',
- props: ['id','text','html','title','image','link','error','warning'],
-
- methods: {
- mousein: function(event) {
- },
-
- mouseout: function(event) {
- },
- clicked: function(event) {
- if (this.link) {
- window.open(this.link, '_blank');
- }
-
- this.$emit('clicked', this.id);
- },
- },
-});
-
-function createNotification(args) {
- window.vm.$refs.notifications.create(args);
-}
-
diff --git a/platypush/backend/http/static/js/plugins/assistant.google/index.js b/platypush/backend/http/static/js/plugins/assistant.google/index.js
deleted file mode 100644
index 24e39d9d..00000000
--- a/platypush/backend/http/static/js/plugins/assistant.google/index.js
+++ /dev/null
@@ -1,124 +0,0 @@
-const Assistant = Vue.extend({
- template: `
-
-
-
-
-
-
-
-
-
-
- Assistant listening
-
-
-
-
-
-
-
-
-
- `,
-
- data: function() {
- return {
- responseText: '',
- phrase: '',
- visible: false,
- hideTimeout: undefined,
-
- state: {
- listening: false,
- speechRecognized: false,
- responding: false,
- alerting: false,
- },
- };
- },
-
- methods: {
- reset: function() {
- this.state.listening = false;
- this.state.speechRecognized = false;
- this.state.responding = false;
- this.state.alerting = false;
- this.phrase = '';
- this.responseText = '';
- },
-
- conversationStart: function() {
- this.reset();
- this.state.listening = true;
- this.visible = true;
-
- if (this.hideTimeout) {
- clearTimeout(this.hideTimeout);
- this.hideTimeout = undefined;
- }
- },
-
- conversationEnd: function() {
- const self = this;
-
- this.hideTimeout = setTimeout(() => {
- this.reset();
- self.visible = false;
- self.hideTimeout = undefined;
- }, 4000);
- },
-
- speechRecognized: function(event) {
- this.reset();
- this.state.speechRecognized = true;
- this.phrase = event.phrase;
- this.visible = true;
- },
-
- response: function(event) {
- this.reset();
- this.state.responding = true;
- this.responseText = event.response_text;
- this.visible = true;
- },
-
- alertOn: function() {
- this.reset();
- this.state.alerting = true;
- this.visible = true;
- },
-
- alertOff: function() {
- this.reset();
- this.state.alerting = false;
- this.visible = false;
- },
-
- registerHandlers: function() {
- registerEventHandler(this.conversationStart, 'platypush.message.event.assistant.ConversationStartEvent');
- registerEventHandler(this.alertOn, 'platypush.message.event.assistant.AlertStartedEvent');
- registerEventHandler(this.alertOff, 'platypush.message.event.assistant.AlertEndEvent');
- registerEventHandler(this.speechRecognized, 'platypush.message.event.assistant.SpeechRecognizedEvent');
- registerEventHandler(this.response, 'platypush.message.event.assistant.ResponseEvent');
- registerEventHandler(this.conversationEnd, 'platypush.message.event.assistant.ConversationEndEvent');
- registerEventHandler(this.conversationEnd, 'platypush.message.event.assistant.NoResponseEvent');
- registerEventHandler(this.conversationEnd, 'platypush.message.event.assistant.ConversationTimeoutEvent');
- },
- },
-
- mounted: function() {
- this.registerHandlers();
- },
-});
-
-onReady(() => {
- const container = document.createElement('div');
- const containerId = 'assistant-google-container';
-
- container.setAttribute('id', containerId);
- document.querySelector('#app').appendChild(container);
-
- new Assistant().$mount('#' + containerId);
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera.android.ipcam/index.js b/platypush/backend/http/static/js/plugins/camera.android.ipcam/index.js
deleted file mode 100644
index 565fd506..00000000
--- a/platypush/backend/http/static/js/plugins/camera.android.ipcam/index.js
+++ /dev/null
@@ -1,113 +0,0 @@
-Vue.component('camera-android-ipcam', {
- template: '#tmpl-camera-android-ipcam',
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- loading: false,
- streaming: false,
- capturing: false,
- recording: false,
- cameras: {},
- selectedCamera: undefined,
- };
- },
-
- computed: {
- hasMultipleCameras: function () {
- return Object.keys(this.cameras).length > 1;
- },
- },
-
- methods: {
- startStreaming: function() {
- if (this.streaming)
- return;
-
- const cam = this.cameras[this.selectedCamera];
- this.streaming = true;
- this.capturing = false;
- this.$refs.frame.setAttribute('src', cam.stream_url);
- },
-
- stopStreaming: function() {
- if (!this.streaming)
- return;
-
- this.streaming = false;
- this.capturing = false;
- this.$refs.frame.removeAttribute('src');
- },
-
- capture: function() {
- if (this.capturing)
- return;
-
- const cam = this.cameras[this.selectedCamera];
- this.streaming = false;
- this.capturing = true;
- this.$refs.frame.setAttribute('src', cam.image_url + '?t=' + (new Date()).getTime());
- },
-
- onFrameLoaded: function(event) {
- if (this.capturing)
- this.capturing = false;
- },
-
- onCameraSelected: function(event) {
- this.selectedCamera = event.target.value;
- },
-
- flipCamera: async function() {
- const cam = this.cameras[this.selectedCamera];
- this.loading = true;
-
- try {
- const value = !cam.ffc;
- await request('camera.android.ipcam.set_front_facing_camera', {
- activate: value, camera: cam.name
- });
-
- this.cameras[this.selectedCamera].ffc = value;
- } finally {
- this.loading = false;
- }
- },
-
- updateCameraStatus: async function() {
- this.loading = true;
-
- try {
- const cameras = await request('camera.android.ipcam.status');
- this.cameras = cameras.reduce((cameras, cam) => {
- for (const attr of ['stream_url', 'image_url', 'audio_url']) {
- if (cam[attr].startsWith('https://')) {
- cam[attr] = cam[attr].replace('https://', 'http://');
- }
-
- if (cam.name in this.config.cameras && this.config.cameras[cam.name].username) {
- cam[attr] = 'http://' + this.config.cameras[cam.name].username + ':' +
- this.config.cameras[cam.name].password + '@' + cam[attr].substr(7);
- }
- }
-
- cameras[cam.name] = cam;
- return cameras;
- }, {});
-
- if (cameras.length)
- this.selectedCamera = cameras[0].name;
-
- } finally {
- this.loading = false;
- }
- },
- },
-
- mounted: function() {
- this.$refs.frame.addEventListener('load', this.onFrameLoaded);
- this.updateCameraStatus();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera.cv/index.js b/platypush/backend/http/static/js/plugins/camera.cv/index.js
deleted file mode 100644
index 4674c92b..00000000
--- a/platypush/backend/http/static/js/plugins/camera.cv/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-Vue.component('camera-cv', {
- template: '#tmpl-camera-cv',
- mixins: [cameraMixin],
-
- methods: {
- startStreaming: function() {
- this._startStreaming('cv');
- },
-
- capture: function() {
- this._capture('cv');
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera.ffmpeg/index.js b/platypush/backend/http/static/js/plugins/camera.ffmpeg/index.js
deleted file mode 100644
index 5e8ee321..00000000
--- a/platypush/backend/http/static/js/plugins/camera.ffmpeg/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-Vue.component('camera-ffmpeg', {
- template: '#tmpl-camera-ffmpeg',
- mixins: [cameraMixin],
-
- methods: {
- startStreaming: function() {
- this._startStreaming('ffmpeg');
- },
-
- capture: function() {
- this._capture('ffmpeg');
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera.gstreamer/index.js b/platypush/backend/http/static/js/plugins/camera.gstreamer/index.js
deleted file mode 100644
index 59afd23b..00000000
--- a/platypush/backend/http/static/js/plugins/camera.gstreamer/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-Vue.component('camera-gstreamer', {
- template: '#tmpl-camera-gstreamer',
- mixins: [cameraMixin],
-
- methods: {
- startStreaming: function() {
- this._startStreaming('gstreamer');
- },
-
- capture: function() {
- this._capture('gstreamer');
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera.ir.mlx90640/index.js b/platypush/backend/http/static/js/plugins/camera.ir.mlx90640/index.js
deleted file mode 100644
index 2bfc460a..00000000
--- a/platypush/backend/http/static/js/plugins/camera.ir.mlx90640/index.js
+++ /dev/null
@@ -1,19 +0,0 @@
-Vue.component('camera-ir-mlx90640', {
- template: '#tmpl-camera-ir-mlx90640',
- mixins: [cameraMixin],
-
- methods: {
- startStreaming: function() {
- this._startStreaming('ir.mlx90640');
- },
-
- capture: function() {
- this._capture('ir.mlx90640');
- },
- },
-
- mounted: function() {
- this.attrs.resolution = [32, 24];
- }
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera.pi/index.js b/platypush/backend/http/static/js/plugins/camera.pi/index.js
deleted file mode 100644
index 7341c589..00000000
--- a/platypush/backend/http/static/js/plugins/camera.pi/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-Vue.component('camera-pi', {
- template: '#tmpl-camera-pi',
- mixins: [cameraMixin],
-
- methods: {
- startStreaming: function() {
- this._startStreaming('pi');
- },
-
- capture: function() {
- this._capture('pi');
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/camera/index.js b/platypush/backend/http/static/js/plugins/camera/index.js
deleted file mode 100644
index b8d871e5..00000000
--- a/platypush/backend/http/static/js/plugins/camera/index.js
+++ /dev/null
@@ -1,103 +0,0 @@
-var cameraMixin = {
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- streaming: false,
- capturing: false,
- showParams: false,
- url: null,
- attrs: {
- resolution: this.config.resolution || [640, 480],
- device: this.config.device,
- horizontal_flip: this.config.horizontal_flip || 0,
- vertical_flip: this.config.vertical_flip || 0,
- rotate: this.config.rotate || 0,
- scale_x: this.config.scale_x || 1.0,
- scale_y: this.config.scale_y || 1.0,
- fps: this.config.fps || 16.0,
- grayscale: this.config.grayscale || 0,
- stream_format: this.config.stream_format || 'mjpeg',
- },
- };
- },
-
- computed: {
- params: function() {
- return {
- resolution: this.attrs.resolution,
- device: this.attrs.device != null && ('' + this.attrs.device).length > 0 ? this.attrs.device : null,
- horizontal_flip: parseInt(0 + this.attrs.horizontal_flip),
- vertical_flip: parseInt(0 + this.attrs.vertical_flip),
- rotate: parseFloat(this.attrs.rotate),
- scale_x: parseFloat(this.attrs.scale_x),
- scale_y: parseFloat(this.attrs.scale_y),
- fps: parseFloat(this.attrs.fps),
- grayscale: parseInt(0 + this.attrs.grayscale),
- };
- },
-
- window: function() {
- return window;
- },
- },
-
- methods: {
- getUrl: function(plugin, action) {
- return '/camera/' + plugin + '/' + action + '?' +
- Object.entries(this.params).filter(([k, v]) => v != null && ('' + v).length > 0)
- .map(([k, v]) => k + '=' + v).join('&');
- },
-
- _startStreaming: function(plugin) {
- if (this.streaming)
- return;
-
- this.streaming = true;
- this.capturing = false;
- this.url = this.getUrl(plugin, 'video.' + this.attrs.stream_format);
- },
-
- stopStreaming: function() {
- if (!this.streaming)
- return;
-
- this.streaming = false;
- this.capturing = false;
- this.url = null;
- },
-
- _capture: function(plugin) {
- if (this.capturing)
- return;
-
- this.streaming = false;
- this.capturing = true;
- this.url = this.getUrl(plugin, 'photo.jpg') + '&t=' + (new Date()).getTime();
- },
-
- onFrameLoaded: function(event) {
- if (this.capturing) {
- this.capturing = false;
- }
- },
-
- onDeviceChanged: function(event) {},
- onFlipChanged: function(event) {},
- onSizeChanged: function(event) {
- const degToRad = (deg) => (deg * Math.PI)/180;
- const rot = degToRad(this.params.rotate);
- this.$refs.frameContainer.style.width = Math.round(this.params.scale_x * Math.abs(this.params.resolution[0] * Math.cos(rot) + this.params.resolution[1] * Math.sin(rot))) + 'px';
- this.$refs.frameContainer.style.height = Math.round(this.params.scale_y * Math.abs(this.params.resolution[0] * Math.sin(rot) + this.params.resolution[1] * Math.cos(rot))) + 'px';
- },
-
- onFpsChanged: function(event) {},
- onGrayscaleChanged: function(event) {},
- },
-
- mounted: function() {
- this.$refs.frame.addEventListener('load', this.onFrameLoaded);
- this.onSizeChanged();
- },
-};
diff --git a/platypush/backend/http/static/js/plugins/execute/index.js b/platypush/backend/http/static/js/plugins/execute/index.js
deleted file mode 100644
index ffa8b574..00000000
--- a/platypush/backend/http/static/js/plugins/execute/index.js
+++ /dev/null
@@ -1,215 +0,0 @@
-Vue.component('execute', {
- template: '#tmpl-execute',
- props: ['config'],
- data: function() {
- return {
- loading: false,
- running: false,
- structuredInput: true,
- actionChanged: false,
- selectedDoc: undefined,
- selectedProcedure: {
- name: undefined,
- args: {},
- },
-
- response: undefined,
- error: undefined,
- htmlDoc: false,
- rawRequest: undefined,
- actions: {},
- plugins: {},
- procedures: {},
- action: {
- name: undefined,
- args: {},
- extraArgs: [],
- supportsExtraArgs: false,
- },
- };
- },
-
- methods: {
- refresh: async function() {
- this.loading = true;
- this.procedures = window.config.procedures;
- this.plugins = await request('inspect.get_all_plugins', {html_doc: true});
-
- for (const plugin of Object.values(this.plugins)) {
- if (plugin.html_doc)
- this.htmlDoc = true;
-
- for (const action of Object.values(plugin.actions)) {
- action.name = plugin.name + '.' + action.name;
- action.supportsExtraArgs = !!action.has_kwargs;
- delete action.has_kwargs;
- this.actions[action.name] = action;
- }
- }
-
- const self = this;
- autocomplete(this.$refs.actionName, Object.keys(this.actions).sort(), (evt, value) => {
- this.action.name = value;
- self.updateAction();
- });
-
- this.loading = false;
- },
-
- updateAction: function() {
- if (!this.actionChanged || !(this.action.name in this.actions))
- return;
-
- this.loading = true;
- this.action = {
- ...this.actions[this.action.name],
- args: Object.entries(this.actions[this.action.name].args).reduce((args, entry) => {
- args[entry[0]] = {
- ...entry[1],
- value: entry[1].default,
- };
-
- return args;
- }, {}),
- extraArgs: [],
- };
-
- this.selectedDoc = this.action.doc;
- this.actionChanged = false;
- this.response = undefined;
- this.error = undefined;
- this.loading = false;
- },
-
- updateProcedure: function(name) {
- if (event.target.getAttribute('type') === 'submit') {
- return;
- }
-
- if (this.selectedProcedure.name === name) {
- this.selectedProcedure = {
- name: undefined,
- args: {},
- };
-
- return;
- }
-
- if (!(name in this.procedures)) {
- console.warn('Procedure not found: ' + name);
- return;
- }
-
- this.selectedProcedure = {
- name: name,
- args: this.procedures[name].args.reduce((args, arg) => {
- args[arg] = undefined;
- return args;
- }, {}),
- };
- },
-
- addParameter: function() {
- this.action.extraArgs.push({
- name: undefined,
- value: undefined,
- })
- },
-
- removeParameter: function(i) {
- this.action.extraArgs.pop(i);
- },
-
- selectAttrDoc: function(name) {
- this.response = undefined;
- this.error = undefined;
- this.selectedDoc = this.action.args[name].doc;
- },
-
- resetDoc: function() {
- this.response = undefined;
- this.error = undefined;
- this.selectedDoc = this.action.doc;
- },
-
- onInputTypeChange: function(structuredInput) {
- this.structuredInput = structuredInput;
- this.response = undefined;
- this.error = undefined;
- },
-
- onResponse: function(response) {
- this.response = '' + JSON.stringify(response, null, 2) + '
';
- this.error = undefined;
- },
-
- onError: function(error) {
- this.response = undefined;
- this.error = '' + error + '
';
- },
-
- onDone: function() {
- this.running = false;
- },
-
- executeAction: function() {
- if (!this.action.name && !this.rawRequest || this.running)
- return;
-
- this.running = true;
- if (this.structuredInput) {
- const args = {
- ...Object.entries(this.action.args).reduce((args, param) => {
- if (param[1].value != null) {
- let value = param[1].value;
- try {value = JSON.parse(value);}
- catch (e) {}
- args[param[0]] = value;
- }
- return args;
- }, {}),
-
- ...this.action.extraArgs.reduce((args, param) => {
- let value = args[param.value];
- try {value = JSON.parse(value);}
- catch (e) {}
-
- args[param.name] = value;
- return args;
- }, {})
- };
-
- request(this.action.name, args).then(this.onResponse).catch(this.onError).finally(this.onDone);
- } else {
- execute(JSON.parse(this.rawRequest)).then(this.onResponse).catch(this.onError).finally(this.onDone);
- }
- },
-
- executeProcedure: function(event) {
- if (!this.selectedProcedure.name || this.running)
- return;
-
- event.stopPropagation();
- this.running = true;
- const args = {
- ...Object.entries(this.selectedProcedure.args).reduce((args, param) => {
- if (param[1] != null) {
- let value = param[1];
- try {value = JSON.parse(value);}
- catch (e) {}
- args[param[0]] = value;
- }
- return args;
- }, {}),
- };
-
- request('procedure.' + this.selectedProcedure.name, args)
- .then(this.onResponse).catch(this.onError).finally(this.onDone);
- },
- },
-
- created: function() {
- this.refresh();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/gpio/index.js b/platypush/backend/http/static/js/plugins/gpio/index.js
deleted file mode 100644
index 72d5da84..00000000
--- a/platypush/backend/http/static/js/plugins/gpio/index.js
+++ /dev/null
@@ -1,35 +0,0 @@
-Vue.component('gpio', {
- template: '#tmpl-gpio',
- props: ['config'],
-
- data: function() {
- return {
- pins: {},
- };
- },
-
- methods: {
- refresh: async function() {
- const pins = await request('gpio.read_all');
- this.pins = pins.reduce((pins, pin) => {
- pins[pin.pin] = {
- name: pin.name,
- number: pin.pin,
- on: !!pin.value,
- };
-
- return pins;
- }, {});
- },
-
- toggle: async function(pin) {
- await request('gpio.write', {pin: pin, value: +(!this.pins[pin].on)});
- this.refresh();
- },
- },
-
- mounted: function() {
- this.refresh();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/animations.js b/platypush/backend/http/static/js/plugins/light.hue/animations.js
deleted file mode 100644
index ed345b34..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/animations.js
+++ /dev/null
@@ -1,39 +0,0 @@
-Vue.component('light-hue-animations-container', {
- template: '#tmpl-light-hue-animations-container',
- props: ['groupId','animation','collapsed'],
- data: function() {
- return {
- selectedAnimation: 'color_transition',
- };
- },
-
- methods: {
- animationsCollapsedToggled: function() {
- this.$emit('animations-collapsed-toggled', {
- type: 'animation',
- id: this.groupId,
- });
- },
- toggled: async function(event) {
- if (event.value) {
- var args = {
- ...this.$refs[this.selectedAnimation].value,
- animation: this.selectedAnimation,
- groups: [this.groupId],
- }
-
- await request('light.hue.on', {groups: [this.groupId]});
- await request('light.hue.animate', args);
-
- this.$emit('animation-started', {
- ...this.$refs[this.selectedAnimation].value,
- type: this.selectedAnimation,
- });
- } else {
- await request('light.hue.stop_animation');
- this.$emit('animation-stopped', {});
- }
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/animations/blink.js b/platypush/backend/http/static/js/plugins/light.hue/animations/blink.js
deleted file mode 100644
index 51e09f5f..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/animations/blink.js
+++ /dev/null
@@ -1,28 +0,0 @@
-Vue.component('light-hue-animation-blink', {
- template: '#tmpl-light-hue-animation-blink',
- data: function() {
- return {
- value: {
- transition_seconds: 1,
- duration: undefined,
- },
- transitionSecondsRange: [0.1, 60],
- durationRange: [0, 600],
- };
- },
-
- methods: {
- onTransitionSecondsChange: function(event) {
- this.value.transition_seconds = event.target.value;
- },
- onDurationChanged: function(event) {
- var value = event.target.value;
- if (value == null || value.length === 0 || parseFloat(value) == 0) {
- value = undefined;
- }
-
- this.value.duration = value;
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/animations/color_transition.js b/platypush/backend/http/static/js/plugins/light.hue/animations/color_transition.js
deleted file mode 100644
index f36c7413..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/animations/color_transition.js
+++ /dev/null
@@ -1,59 +0,0 @@
-Vue.component('light-hue-animation-color_transition', {
- template: '#tmpl-light-hue-animation-color_transition',
- data: function() {
- return {
- value: {
- hue_range: [0,65535],
- sat_range: [150,255],
- bri_range: [190,255],
- hue_step: 150,
- sat_step: 5,
- bri_step: 2,
- transition_seconds: 1,
- duration: undefined,
- },
- };
- },
-
- computed: {
- hueStepRange: function() {
- return [1, parseInt((this.value.hue_range[1]-this.value.hue_range[0])/2)-1];
- },
- satStepRange: function() {
- return [1, parseInt((this.value.sat_range[1]-this.value.sat_range[0])/2)-1];
- },
- briStepRange: function() {
- return [1, parseInt((this.value.bri_range[1]-this.value.bri_range[0])/2)-1];
- },
- transitionSecondsRange: function() {
- return [0.1, 60];
- },
- durationRange: function() {
- return [0, 600];
- },
- },
-
- methods: {
- hueRangeChanged: function(value) {
- this.value.hue_range = value;
- },
- satRangeChanged: function(value) {
- this.value.sat_range = value;
- },
- briRangeChanged: function(value) {
- this.value.bri_range = value;
- },
- onTransitionSecondsChange: function(event) {
- this.value.transition_seconds = event.target.value;
- },
- onDurationChanged: function(event) {
- var value = event.target.value;
- if (value == null || value.length === 0 || parseFloat(value) == 0) {
- value = undefined;
- }
-
- this.value.duration = value;
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/converter.js b/platypush/backend/http/static/js/plugins/light.hue/converter.js
deleted file mode 100644
index a2d531ef..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/converter.js
+++ /dev/null
@@ -1,106 +0,0 @@
-// Source: https://gist.github.com/uredkar/bd305f2dda9abf5b393d417424777c87#file-cie_rgb_converter-js
-
-/**
- * Converts CIE color space to RGB color space
- * @param {Number} x
- * @param {Number} y
- * @param {Number} brightness - Ranges from 1 to 254
- * @return {Array} Array that contains the color values for red, green and blue
- */
-function toRGB(x, y, brightness) {
- //Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons)
- if (brightness === undefined) {
- brightness = 254;
- }
-
- var z = 1.0 - x - y;
- var Y = (brightness / 254).toFixed(2);
- var X = (Y / y) * x;
- var Z = (Y / y) * z;
-
- //Convert to RGB using Wide RGB D65 conversion
- var red = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
- var green = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
- var blue = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
-
- //If red, green or blue is larger than 1.0 set it back to the maximum of 1.0
- if (red > blue && red > green && red > 1.0) {
-
- green = green / red;
- blue = blue / red;
- red = 1.0;
- }
- else if (green > blue && green > red && green > 1.0) {
-
- red = red / green;
- blue = blue / green;
- green = 1.0;
- }
- else if (blue > red && blue > green && blue > 1.0) {
-
- red = red / blue;
- green = green / blue;
- blue = 1.0;
- }
-
- //Reverse gamma correction
- red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, (1.0 / 2.4)) - 0.055;
- green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, (1.0 / 2.4)) - 0.055;
- blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, (1.0 / 2.4)) - 0.055;
-
-
- //Convert normalized decimal to decimal
- red = Math.round(red * 255);
- green = Math.round(green * 255);
- blue = Math.round(blue * 255);
-
- if (isNaN(red))
- red = 0;
-
- if (isNaN(green))
- green = 0;
-
- if (isNaN(blue))
- blue = 0;
-
-
- return [red, green, blue];
-}
-
-
-/**
- * Converts RGB color space to CIE color space
- * @param {Number} red
- * @param {Number} green
- * @param {Number} blue
- * @return {Array} Array that contains the CIE color values for x and y
- */
-function toXY(red, green, blue) {
- if (red > 1) { red /= 255; }
- if (green > 1) { green /= 255; }
- if (blue > 1) { blue /= 255; }
-
- //Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device
- var red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
- var green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
- var blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
-
- //RGB values to XYZ using the Wide RGB D65 conversion formula
- var X = red * 0.664511 + green * 0.154324 + blue * 0.162028;
- var Y = red * 0.283881 + green * 0.668433 + blue * 0.047685;
- var Z = red * 0.000088 + green * 0.072310 + blue * 0.986039;
-
- //Calculate the xy values from the XYZ values
- var x = (X / (X + Y + Z)).toFixed(4);
- var y = (Y / (X + Y + Z)).toFixed(4);
-
- if (isNaN(x))
- x = 0;
-
- if (isNaN(y))
- y = 0;
-
-
- return [x, y];
-}
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/elements.js b/platypush/backend/http/static/js/plugins/light.hue/elements.js
deleted file mode 100644
index 0dd084df..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/elements.js
+++ /dev/null
@@ -1,41 +0,0 @@
-Vue.component('light-hue-property-selector', {
- template: '#tmpl-light-hue-property-selector',
- props: ['id','value'],
- computed: {
- rgb: function() {
- if (!(this.value && 'xy' in this.value)) {
- return;
- }
-
- return toRGB(this.value.xy[0], this.value.xy[1], this.value.bri);
- },
- },
-
- methods: {
- changed: function(event) {
- var value = parseInt(event.target.value);
- var xy;
-
- if (event.target.getAttribute('class').split(' ').indexOf('bri') > -1) {
- this.$emit('bri-changed', {bri: value});
- return;
- } else if (event.target.getAttribute('class').split(' ').indexOf('ct') > -1) {
- this.$emit('ct-changed', {ct: value});
- return;
- }
-
- if (event.target.getAttribute('class').split(' ').indexOf('red') > -1) {
- xy = toXY(value, this.rgb[1], this.rgb[2]);
- } else if (event.target.getAttribute('class').split(' ').indexOf('green') > -1) {
- xy = toXY(this.rgb[0], value, this.rgb[2]);
- } else if (event.target.getAttribute('class').split(' ').indexOf('blue') > -1) {
- xy = toXY(this.rgb[0], this.rgb[1], value);
- } else {
- return;
- }
-
- this.$emit('color-changed', {xy: xy});
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/groups.js b/platypush/backend/http/static/js/plugins/light.hue/groups.js
deleted file mode 100644
index b9b6b19c..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/groups.js
+++ /dev/null
@@ -1,118 +0,0 @@
-Vue.component('light-hue-group', {
- template: '#tmpl-light-hue-group',
- props: ['id','name'],
-});
-
-Vue.component('light-hue-group-controller', {
- template: '#tmpl-light-hue-group-controller',
- props: ['id','groups','value','collapsed'],
- computed: {
- lights: function() {
- return this.groups[this.id].lights;
- },
-
- name: function() {
- return this.groups[this.id].name;
- },
-
- properties: function() {
- var self = this;
- var avg = function(values) {
- if (values.length) {
- return values.reduce((sum,value) => sum+value) / values.length;
- } else {
- return 0;
- }
- };
-
- var getLightValues = function(attribute) {
- return Object.values(self.lights).map(
- light => attribute in light.state && light.state.on ? light.state[attribute] : undefined
- ).filter(value => value !== undefined);
- };
-
- return {
- xy: [
- avg(getLightValues('xy').map(_ => parseFloat(_[0]))),
- avg(getLightValues('xy').map(_ => parseFloat(_[1])))
- ],
- ct: avg(getLightValues('ct')),
- bri: avg(getLightValues('bri')),
- };
- },
- },
-
- methods: {
- toggled: async function(event) {
- await request(
- 'light.hue.' + (event.value ? 'on' : 'off'),
- { groups: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- lights: this._updateLights('on', event.value),
- });
-
- this.$emit('input', {...this.value, state: {...this.value.state, any_on: event.value, all_on: event.value}});
- },
-
- propertiesCollapsedToggled: function() {
- this.$emit('properties-collapsed-toggled', {
- type: 'group',
- id: this.id,
- });
- },
-
- colorChanged: async function(event) {
- await request(
- 'light.hue.xy',
- { value: event.xy, groups: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- lights: this._updateLights('xy', event.xy),
- });
- },
-
- briChanged: async function(event) {
- await request(
- 'light.hue.bri',
- { value: event.bri, groups: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- lights: this._updateLights('bri', event.bri),
- });
- },
-
- ctChanged: async function(event) {
- await request(
- 'light.hue.ct',
- { value: event.ct, groups: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- lights: this._updateLights('ct', event.ct),
- });
- },
-
- _updateLights: function(attr, value) {
- var lights = [];
- for (const light of Object.values(this.value.lights)) {
- var state = light.state;
- state[attr] = value;
- lights.push({
- ...light,
- state: state,
- });
- }
-
- return lights;
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/index.js b/platypush/backend/http/static/js/plugins/light.hue/index.js
deleted file mode 100644
index d903a07f..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/index.js
+++ /dev/null
@@ -1,229 +0,0 @@
-Vue.component('light-hue', {
- template: '#tmpl-light-hue',
- props: ['config'],
- data: function() {
- return {
- groups: {},
- lights: {},
- scenes: {},
- animations: {},
- selectedGroup: undefined,
- selectedScene: undefined,
- selectedProperties: {
- type: undefined,
- id: undefined,
- },
- };
- },
-
- methods: {
- _prepareGroups: function() {
- for (const [groupId, group] of Object.entries(this.groups)) {
- if (group.type !== 'Room' || group.recycle) {
- delete this.groups[groupId];
- continue;
- }
-
- this.groups[groupId].scenes = {};
- let lights = {};
-
- for (const lightId of this.groups[groupId].lights) {
- lights[lightId] = this.lights[lightId];
- }
-
- this.groups[groupId].lights = lights;
- }
- },
-
- _prepareScenes: function() {
- for (const [sceneId, scene] of Object.entries(this.scenes)) {
- if (scene.recycle) {
- delete this.scenes[sceneId];
- continue;
- }
-
- this.scenes[sceneId].groups = {};
- }
- },
-
- _linkLights: function() {
- // Special group for lights with no group
- this.groups[-1] = {
- type: undefined,
- lights: {},
- scenes: {},
- name: "[No Group]",
- recycle: false,
- };
-
- for (const [lightId, light] of Object.entries(this.lights)) {
- this.lights[lightId].groups = {};
-
- for (const [groupId, group] of Object.entries(this.groups)) {
- if (lightId in group.lights) {
- this.lights[lightId].groups[groupId] = group;
- }
- }
-
- if (!light.groups.length) {
- this.groups[-1].lights[lightId] = light;
- }
- }
-
- if (!this.groups[-1].lights.length) {
- delete this.groups[-1];
- }
- },
-
- _linkScenes: function() {
- for (const [sceneId, scene] of Object.entries(this.scenes)) {
- for (const lightId of scene.lights) {
- for (const [groupId, group] of Object.entries(this.lights[lightId].groups)) {
- this.scenes[sceneId].groups[groupId] = group;
- this.groups[groupId].scenes[sceneId] = scene;
- }
- }
- }
- },
-
- refresh: async function() {
- const getLights = request('light.hue.get_lights');
- const getGroups = request('light.hue.get_groups');
- const getScenes = request('light.hue.get_scenes');
- const getAnimations = request('light.hue.get_animations');
-
- [this.lights, this.groups, this.scenes, this.animations] = await Promise.all(
- [getLights, getGroups, getScenes, getAnimations]);
-
- this._prepareGroups();
- this._prepareScenes();
- this._linkLights();
- this._linkScenes();
- },
-
- updatedGroup: function(event) {
- for (const light of Object.values(this.groups[this.selectedGroup].lights)) {
- if (event.state.any_on === event.state.all_on) {
- light.state.on = event.state.all_on;
- }
-
- for (const attr in ['hue', 'sat', 'bri', 'xy', 'ct']) {
- if (attr in event.state) {
- light.state[attr] = event.state[attr];
- }
- }
- }
- },
-
- startedAnimation: function(value) {
- this.animations.groups[this.selectedGroup] = value;
- },
-
- stoppedAnimation: function() {
- this.animations.groups[this.selectedGroup] = undefined;
- },
-
- selectScene: async function(event) {
- await request(
- 'light.hue.scene', {
- name: event.name,
- groups: [this.groups[this.selectedGroup].name],
- },
- );
-
- this.selectedScene = event.id;
- const groups = {};
-
- for (const lightId of Object.values(this.scenes[this.selectedScene].lights)) {
- this.lights[lightId].state.on = true;
-
- for (const [groupId, group] of Object.entries(this.lights[lightId].groups)) {
- if (!(group.id in groups)) {
- groups[groupId] = {
- any_on: true,
- all_on: group.state.all_on,
- lights: [],
- }
- }
-
- groups[groupId].lights.push(lightId);
-
- if (groups[groupId].lights.length === Object.values(group.lights).length) {
- groups[groupId].all_on = true;
- }
- }
- }
-
- for (const [id, group] of Object.entries(groups)) {
- this.groups[id].state = {
- ...group.state,
- any_on: group.any_on,
- any_off: group.any_off,
- }
- }
- },
-
- collapsedToggled: function(event) {
- if (event.type === this.selectedProperties.type
- && event.id === this.selectedProperties.id) {
- this.selectedProperties = {
- type: undefined,
- id: undefined,
- };
- } else {
- this.selectedProperties = {
- type: event.type,
- id: event.id,
- };
- }
- },
-
- onUnitInput: function(event) {
- for (let attr of ['on', 'hue', 'sat', 'bri', 'xy', 'ct']) {
- if (attr in event) {
- this.lights[event.id].state[attr] = event[attr];
- }
- }
-
- let groups = this.lights[event.id].groups;
- for (const [groupId, group] of Object.entries(groups)) {
- if (event.on === true) {
- this.groups[groupId].state.any_on = true;
- this.groups[groupId].state.all_on = Object.values(group.lights).filter((l) => l.state.on).length === Object.values(group.lights).length;
- } else if (event.on === false) {
- this.groups[groupId].state.all_on = false;
- this.groups[groupId].state.any_on = Object.values(group.lights).filter((l) => l.state.on).length > 0;
- }
- }
- },
-
- eventHandler: function(event) {
- if ('light_id' in event) {
- this.onUnitInput({
- ...event,
- id: event.light_id,
- });
- } else if ('group_id' in event) {
- let args = {
- id: event.group_id,
- state: {
- ...event,
- },
- };
-
- if ('on' in event) {
- args.state.any_on = event.on;
- args.state.all_on = event.on;
- }
-
- this.updatedGroup(args);
- }
- },
- },
-
- created: function() {
- this.refresh();
- registerEventHandler(this.eventHandler, 'platypush.message.event.light.LightStatusChangeEvent');
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/scenes.js b/platypush/backend/http/static/js/plugins/light.hue/scenes.js
deleted file mode 100644
index a72f67b0..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/scenes.js
+++ /dev/null
@@ -1,11 +0,0 @@
-Vue.component('light-hue-scene', {
- template: '#tmpl-light-hue-scene',
- props: ['id','name'],
-
- methods: {
- clicked: function(event) {
- this.$emit('input', {id: this.id, name: this.name});
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/light.hue/units.js b/platypush/backend/http/static/js/plugins/light.hue/units.js
deleted file mode 100644
index a32739cd..00000000
--- a/platypush/backend/http/static/js/plugins/light.hue/units.js
+++ /dev/null
@@ -1,69 +0,0 @@
-Vue.component('light-hue-unit', {
- template: '#tmpl-light-hue-unit',
- props: ['id','capabilities','config','name',
- 'uniqueid','type','productname','modelid',
- 'manufacturername', 'swupdate','swversion','value',
- 'collapsed'],
-
- methods: {
- toggled: async function(event) {
- await request(
- 'light.hue.' + (event.value ? 'on' : 'off'),
- { lights: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- id: this.id,
- on: event.value
- });
- },
-
- colorChanged: async function(event) {
- await request(
- 'light.hue.xy',
- { value: event.xy, lights: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- id: this.id,
- xy: event.xy
- });
- },
-
- briChanged: async function(event) {
- await request(
- 'light.hue.bri',
- { value: event.bri, lights: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- id: this.id,
- bri: event.bri
- });
- },
-
- ctChanged: async function(event) {
- await request(
- 'light.hue.ct',
- { value: event.ct, lights: [this.id] },
- );
-
- this.$emit('input', {
- ...this.value,
- id: this.id,
- ct: event.ct
- });
- },
-
- propertiesCollapsedToggled: function() {
- this.$emit('properties-collapsed-toggled', {
- type: 'unit',
- id: this.id
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media.gstreamer/index.js b/platypush/backend/http/static/js/plugins/media.gstreamer/index.js
deleted file mode 100644
index 67682dc3..00000000
--- a/platypush/backend/http/static/js/plugins/media.gstreamer/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Vue.component('media-gstreamer', {
- template: '#tmpl-media-gstreamer',
- props: ['config'],
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media.mplayer/index.js b/platypush/backend/http/static/js/plugins/media.mplayer/index.js
deleted file mode 100644
index ab8f2b5c..00000000
--- a/platypush/backend/http/static/js/plugins/media.mplayer/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Vue.component('media-mplayer', {
- template: '#tmpl-media-mplayer',
- props: ['config'],
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media.mpv/index.js b/platypush/backend/http/static/js/plugins/media.mpv/index.js
deleted file mode 100644
index 4bdc97b8..00000000
--- a/platypush/backend/http/static/js/plugins/media.mpv/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Vue.component('media-mpv', {
- template: '#tmpl-media-mpv',
- props: ['config'],
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media.omxplayer/index.js b/platypush/backend/http/static/js/plugins/media.omxplayer/index.js
deleted file mode 100644
index 7bf182f1..00000000
--- a/platypush/backend/http/static/js/plugins/media.omxplayer/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Vue.component('media-omxplayer', {
- template: '#tmpl-media-omxplayer',
- props: ['config'],
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media.vlc/index.js b/platypush/backend/http/static/js/plugins/media.vlc/index.js
deleted file mode 100644
index 679f02f5..00000000
--- a/platypush/backend/http/static/js/plugins/media.vlc/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Vue.component('media-vlc', {
- template: '#tmpl-media-vlc',
- props: ['config'],
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/controls.js b/platypush/backend/http/static/js/plugins/media/controls.js
deleted file mode 100644
index 81670b9a..00000000
--- a/platypush/backend/http/static/js/plugins/media/controls.js
+++ /dev/null
@@ -1,12 +0,0 @@
-Vue.component('media-controls', {
- template: '#tmpl-media-controls',
- mixins: [mediaUtils],
- props: {
- bus: { type: Object },
- status: {
- type: Object,
- default: () => {},
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/devices.js b/platypush/backend/http/static/js/plugins/media/devices.js
deleted file mode 100644
index 07093236..00000000
--- a/platypush/backend/http/static/js/plugins/media/devices.js
+++ /dev/null
@@ -1,147 +0,0 @@
-// Will be filled by dynamically loading device scripts
-const MediaPlayers = {};
-
-Vue.component('media-devices', {
- template: '#tmpl-media-devices',
- props: {
- bus: { type: Object },
- localPlayer: { type: String },
- },
-
- data: function() {
- return {
- showDevicesMenu: false,
- selectedDevice: {},
- loading: false,
- devices: [],
- };
- },
-
- computed: {
- staticItems: function() {
- return [
- {
- text: 'Refresh',
- type: 'refresh',
- icon: 'sync-alt',
- preventClose: true,
- },
- ];
- },
-
- dropdownItems: function() {
- const self = this;
- const onClick = (menuItem) => {
- return () => {
- if (self.loading) {
- return;
- }
-
- self.selectDevice(menuItem.device);
- };
- };
-
- return self.staticItems.concat(
- self.devices.map($dev => {
- return {
- name: $dev.name,
- text: $dev.text || $dev.name,
- icon: $dev.icon,
- iconClass: $dev.iconClass,
- device: $dev,
- };
- })
- ).map(item => {
- item.click = item.type === 'refresh' ? self.refreshDevices : onClick(item);
- item.disabled = self.loading;
- return item;
- });
- },
- },
-
- methods: {
- refreshDevices: async function() {
- if (this.loading) {
- return;
- }
-
- this.loading = true;
- const self = this;
-
- try {
- const promises = Object.entries(MediaPlayers).map((p) => {
- const playerType = p[0];
- const Player = p[1];
-
- return new Promise((resolve, reject) => {
- const player = new Player();
-
- if (player.scan) {
- player.scan().then(devs => {
- resolve(devs.map(device => {
- const handler = new Player();
- handler.device = device;
- return handler;
- }));
- });
-
- return;
- }
-
- if (player.type === 'local') {
- player.device = {
- plugin: self.localPlayer,
- };
- } else {
- player.device = {};
- }
-
- resolve([player]);
- });
- });
-
- this.devices = (await Promise.all(promises)).reduce((list, devs) => {
- return [...list, ...devs];
- }, []).sort((a,b) => {
- if (a.type === 'local')
- return -1;
- if (b.type === 'local')
- return 1;
- if (a.type === 'browser')
- return -1;
- if (b.type === 'browser')
- return 1;
- if (a.type !== b.type)
- return b.type.localeCompare(a);
- return b.name.localeCompare(a);
- });
-
- this.devices.forEach(dev => {
- dev.status().then(status => {
- self.bus.$emit('status-update', {
- device: dev,
- status: status,
- });
- });
- });
- } finally {
- this.loading = false;
- this.selectDevice(this.devices.filter(_ => _.type === 'local')[0]);
- }
- },
-
- selectDevice: function(device) {
- this.selectedDevice = device;
- this.bus.$emit('selected-device', device);
- },
-
- openDevicesMenu: function() {
- openDropdown(this.$refs.menu);
- },
- },
-
- created: function() {
- this.refreshDevices();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/handlers/base.js b/platypush/backend/http/static/js/plugins/media/handlers/base.js
deleted file mode 100644
index b6e50544..00000000
--- a/platypush/backend/http/static/js/plugins/media/handlers/base.js
+++ /dev/null
@@ -1,64 +0,0 @@
-MediaHandlers.base = Vue.extend({
- props: {
- bus: { type: Object },
- iconClass: {
- type: String,
- },
- },
-
- computed: {
- dropdownItems: function() {
- return [
- {
- text: 'Play',
- icon: 'play',
- action: this.play,
- },
-
- {
- text: 'View info',
- icon: 'info',
- action: this.info,
- },
- ];
- },
- },
-
- methods: {
- matchesUrl: function(url) {
- return false;
- },
-
- getMetadata: async function(item, onlyBase=false) {
- return {};
- },
-
- play: function(item) {
- this.bus.$emit('play', item);
- },
-
- info: async function(item) {
- this.bus.$emit('info-loading');
- this.bus.$emit('info', {...item, ...(await this.getMetadata(item))});
- },
-
- infoLoad: function(url) {
- if (!this.matchesUrl(url))
- return;
-
- this.info(url);
- },
-
- searchSubtitles: function(item) {
- this.bus.$emit('search-subs', item);
- },
- },
-
- created: function() {
- const self = this;
- setTimeout(() => {
- self.infoLoadWatch = self.bus.$on('info-load', this.infoLoad);
- }, 1000);
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/handlers/file.js b/platypush/backend/http/static/js/plugins/media/handlers/file.js
deleted file mode 100644
index 5cc272eb..00000000
--- a/platypush/backend/http/static/js/plugins/media/handlers/file.js
+++ /dev/null
@@ -1,114 +0,0 @@
-MediaHandlers.file = MediaHandlers.base.extend({
- props: {
- iconClass: {
- type: String,
- default: 'fa fa-hdd',
- },
- },
-
- computed: {
- dropdownItems: function() {
- return [
- {
- text: 'Play',
- icon: 'play',
- action: this.play,
- },
-
- {
- text: 'Play with subtitles',
- iconClass: 'fas fa-closed-captioning',
- action: this.searchSubtitles,
- },
-
- {
- text: 'Download (on client)',
- icon: 'download',
- action: this.download,
- },
-
- {
- text: 'View info',
- icon: 'info',
- action: this.info,
- },
- ];
- },
- },
-
- methods: {
- matchesUrl: function(url) {
- return !!url.match('^(file://)?/');
- },
-
- getMetadata: async function(item, onlyBase=false) {
- if (typeof item === 'string') {
- item = {
- url: item,
- };
- }
-
- if (!item.path)
- item.path = item.url.startsWith('file://') ? item.url.substr(7) : item.url;
-
- if (!item.title)
- item.title = item.path.split('/').pop();
-
- if (!item.size && !onlyBase)
- item.size = await request('file.getsize', {filename: item.path});
-
- if (!item.duration && !onlyBase)
- item.duration = await request('media.get_media_file_duration', {filename: item.path});
-
- return item;
- },
-
- download: async function(item) {
- this.bus.$on('streaming-started', (media) => {
- if (media.resource === item.url) {
- this.bus.$off('streaming-started');
- window.open(media.url + '?download', '_blank');
- }
- });
-
- this.bus.$emit('start-streaming', item.url);
- },
- },
-});
-
-MediaHandlers.generic = MediaHandlers.file.extend({
- props: {
- iconClass: {
- type: String,
- default: 'fa fa-globe',
- },
- },
-
- computed: {
- dropdownItems: function() {
- return [
- {
- text: 'Play',
- icon: 'play',
- action: this.play,
- },
-
- {
- text: 'View info',
- icon: 'info',
- action: this.info,
- },
- ];
- },
- },
-
- methods: {
- getMetadata: async function(item) {
- return {
- url: item.url,
- title: item.url,
- };
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/handlers/torrent.js b/platypush/backend/http/static/js/plugins/media/handlers/torrent.js
deleted file mode 100644
index 9df370d1..00000000
--- a/platypush/backend/http/static/js/plugins/media/handlers/torrent.js
+++ /dev/null
@@ -1,304 +0,0 @@
-MediaHandlers.torrent = MediaHandlers.base.extend({
- props: {
- bus: { type: Object },
- iconClass: {
- type: String,
- default: 'fa fa-magnet',
- },
- },
-
- computed: {
- dropdownItems: function() {
- return [
- {
- text: 'Play',
- icon: 'play',
- action: this.play,
- },
-
- {
- text: 'Download (on server)',
- icon: 'download',
- action: this.download,
- },
-
- {
- text: 'View info',
- icon: 'info',
- action: this.info,
- },
- ];
- },
- },
-
- data: function() {
- return {
- torrentStatus: {},
- };
- },
-
- methods: {
- matchesUrl: function(url) {
- return !!(
- url.match('^magnet:?') ||
- url.match('^https?://.*\.torrent$') ||
- url.match('^(file://)?/.*\.torrent$')
- );
- },
-
- getTorrentPlugin: async function() {
- if (this.config && this.config.torrent_plugin) {
- return this.config.torrent_plugin;
- }
-
- const config = await request('inspect.get_config');
- if ('rtorrent' in config)
- return 'rtorrent';
- if ('webtorrent' in config)
- return 'webtorrent';
- return 'torrent'
- },
-
- getMetadata: async function(item, onlyBase=false) {
- let status = {};
-
- if (!onlyBase)
- status = await this.status({url: item.url});
-
- let transferInfo = {};
- if (item.url in this.torrentStatus)
- transferInfo = this.torrentStatus[item.url];
-
- return {...status, ...transferInfo};
- },
-
- play: async function(item) {
- let status = await this.download(item);
- status.waitingPlay = true;
- const torrentId = this.getTorrentUrlOrHash(item.url);
-
- if (torrentId in this.torrentStatus) {
- this.firePlay(event);
- }
- },
-
- pause: async function(item) {
- const torrentPlugin = await this.getTorrentPlugin();
- const torrentId = this.getTorrentUrlOrHash(item.url);
- let status = {};
-
- if (item.paused) {
- status = await request(torrentPlugin + '.resume', {torrent: torrentId});
- } else {
- status = await request(torrentPlugin + '.pause', {torrent: torrentId});
- }
-
- this.mergeStatus(status);
- },
-
- remove: async function(item) {
- const torrentPlugin = await this.getTorrentPlugin();
- const torrentId = this.getTorrentUrlOrHash(item.url);
- let status = await request(torrentPlugin + '.remove', {torrent: torrentId});
- if (torrentId in this.torrentStatus)
- delete this.torrentStatus[torrentId];
- },
-
- status: async function(item) {
- const torrentPlugin = await this.getTorrentPlugin();
- if (item) {
- const torrentId = this.getTorrentUrlOrHash(typeof item === 'string' ? item : item.url);
- return await request(torrentPlugin + '.status', {
- torrent: torrentId,
- });
- }
-
- return await request(torrentPlugin + '.status');
- },
-
- getTorrentUrlOrHash: function(torrent) {
- if (torrent.startsWith('magnet:?')) {
- m = torrent.match(/xt=urn:btih:([^&/]+)/, torrent)
- if (m) {
- return m[1]; // Torrent hash
- }
- }
-
- return torrent;
- },
-
- download: async function(item) {
- const torrentPlugin = await this.getTorrentPlugin();
- let status = await this.status(item.url);
-
- if (status && Object.keys(status).length > 1) {
- createNotification({
- text: 'This torrent is already being downloaded, please play the downloading local media file instead',
- image: {
- icon: 'download',
- },
- });
-
- return status;
- }
-
- status = await request(
- torrentPlugin + '.download',
- {
- torrent: item.url,
- _async: true,
- is_media: true,
- },
- timeout=120000 // Wait up to two minutes while downloading enough torrent chunks
- );
-
- const torrentId = this.getTorrentUrlOrHash(item.url);
- this.torrentStatus[torrentId] = {
- ...item, ...status,
- scheduledPlay: false,
- torrentState: status.state,
- state: 'idle',
- };
-
- return this.torrentStatus[torrentId];
- },
-
- onTorrentEvent: function(event) {
- this.mergeStatus(event);
- },
-
- onTorrentQueued: function(event) {
- if (!this.mergeStatus(event))
- this.torrentStatus[event.url] = event;
-
- createNotification({
- text: 'Torrent download queued. Will start playing when enough chunks have been downloaded',
- image: {
- icon: 'clock',
- },
- });
- },
-
- onTorrentStart: function(event) {
- if (!this.mergeStatus(event))
- return;
-
- createNotification({
- text: 'Download of '.concat(event.name, ' started'),
- image: {
- icon: 'download',
- },
- });
- },
-
- onTorrentStop: function(event) {
- if (!this.mergeStatus(event))
- return;
-
- const torrentId = this.getTorrentUrlOrHash(event.url);
- if (torrentId in this.torrentStatus)
- delete this.torrentStatus[torrentId];
-
- this.bus.$emit('torrent-status-update', this.torrentStatus);
- },
-
- onTorrentProgress: function(event) {
- if (!this.mergeStatus(event))
- return;
-
- const torrentId = this.getTorrentUrlOrHash(event.url);
- if (this.torrentStatus[torrentId].waitingPlay)
- this.firePlay(event);
- },
-
- onTorrentCompleted: function(event) {
- if (!this.mergeStatus(event))
- return;
-
- const torrentId = this.getTorrentUrlOrHash(event.url);
- if (torrentId in this.torrentStatus)
- delete this.torrentStatus[event.url];
-
- this.bus.$emit('torrent-status-update', this.torrentStatus);
-
- createNotification({
- text: 'Download of '.concat(event.name, ' completed'),
- image: {
- icon: 'check',
- },
- });
- },
-
- firePlay: function(item) {
- if (!item.files || !item.files.length) {
- console.warn('Torrent ' + item.url + ' has no media files available yet');
- return;
- }
-
- if (event.progress < 5) {
- console.warn('Please wait for enough chunks to be downloaded before playing');
- return;
- }
-
- const url = 'file://' + item.files[0];
- this.bus.$emit('play', {...item, type: 'file', url: url});
-
- if (this.torrentStatus[item.url].waitingPlay)
- this.torrentStatus[item.url].waitingPlay = false;
-
- createNotification({
- text: 'Playback of '.concat(item.name, ' started'),
- image: {
- icon: 'play',
- },
- });
- },
-
- mergeStatus: function(event) {
- const torrentId = this.getTorrentUrlOrHash(event.url);
- const torrentState = event.state;
- delete event.state;
-
- this.torrentStatus[torrentId] = {
- ...this.torrentStatus[torrentId],
- ...event,
- torrentState: torrentState,
- };
-
- this.bus.$emit('torrent-status-update', this.torrentStatus);
- return this.torrentStatus[torrentId];
- },
- },
-
- created: function() {
- registerEventHandler(this.onTorrentStart, 'platypush.message.event.torrent.TorrentDownloadStartEvent');
- registerEventHandler(this.onTorrentProgress, 'platypush.message.event.torrent.TorrentDownloadProgressEvent');
- registerEventHandler(this.onTorrentCompleted, 'platypush.message.event.torrent.TorrentDownloadCompletedEvent');
- registerEventHandler(this.onTorrentQueued, 'platypush.message.event.torrent.TorrentQueuedEvent');
- registerEventHandler(this.onTorrentStop, 'platypush.message.event.torrent.TorrentDownloadStopEvent',
- 'platypush.message.event.torrent.TorrentRemovedEvent');
- registerEventHandler(this.onTorrentEvent, 'platypush.message.event.torrent.TorrentPausedEvent',
- 'platypush.message.event.torrent.TorrentResumedEvent',
- 'platypush.message.event.torrent.TorrentDownloadStartEvent',
- 'platypush.message.event.torrent.TorrentStateChangeEvent');
-
- const self = this;
- this.status().then((status) => {
- if (!status)
- return;
-
- for (const [url, torrent] of Object.entries(status)) {
- if (!torrent.url)
- continue;
- self.mergeStatus(torrent);
- }
- });
-
- setTimeout(() => {
- self.bus.$on('torrent-play', self.firePlay);
- self.bus.$on('torrent-pause', self.pause);
- self.bus.$on('torrent-remove', self.remove);
- }, 100);
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/handlers/youtube.js b/platypush/backend/http/static/js/plugins/media/handlers/youtube.js
deleted file mode 100644
index 07cd35a2..00000000
--- a/platypush/backend/http/static/js/plugins/media/handlers/youtube.js
+++ /dev/null
@@ -1,97 +0,0 @@
-MediaHandlers.youtube = MediaHandlers.base.extend({
- props: {
- iconClass: {
- type: String,
- default: 'fab fa-youtube',
- },
- },
-
- computed: {
- dropdownItems: function() {
- return [
- {
- text: 'Play',
- icon: 'play',
- action: this.play,
- },
-
- {
- text: 'Download (on server)',
- icon: 'download',
- action: this.downloadServer,
- },
-
- {
- text: 'Download (on client)',
- icon: 'download',
- action: this.downloadClient,
- },
-
- {
- text: 'View info',
- icon: 'info',
- action: this.info,
- },
- ];
- },
- },
-
- methods: {
- matchesUrl: function(url) {
- return !!(url.match('^https?://(www\.)?youtube.com/') || url.match('^https?://youtu.be/') || url.match('^https?://.*googlevideo.com/'));
- },
-
- getMetadata: function(item) {
- return {};
- },
-
- _getRawUrl: async function(url) {
- if (url.indexOf('.googlevideo.com') < 0) {
- url = await request('media.get_youtube_url', {url: url});
- }
-
- return url;
- },
-
- play: async function(item) {
- if (typeof item === 'string')
- item = {url: item};
-
- let url = await this._getRawUrl(item.url);
- this.bus.$emit('play', {...item, url:url});
- },
-
- downloadServer: async function(item) {
- createNotification({
- text: 'Downloading video',
- image: {
- icon: 'download',
- },
- });
-
- let url = await this._getRawUrl(item.url);
- let args = {
- url: url,
- }
-
- if (item.title) {
- args.filename = item.title + '.webm';
- }
-
- let path = await request('media.download', args);
-
- createNotification({
- text: 'Video downloaded to ' + path,
- image: {
- icon: 'check',
- },
- });
- },
-
- downloadClient: async function(item) {
- let url = await this._getRawUrl(item.url);
- window.open(url, '_blank');
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/index.js b/platypush/backend/http/static/js/plugins/media/index.js
deleted file mode 100644
index 6cf186c4..00000000
--- a/platypush/backend/http/static/js/plugins/media/index.js
+++ /dev/null
@@ -1,355 +0,0 @@
-// Will be filled by dynamically loading media type handler scripts
-const MediaHandlers = {};
-
-const mediaUtils = {
- methods: {
- convertTime: function(time) {
- time = parseFloat(time); // Normalize strings
- var t = {};
- t.h = '' + parseInt(time/3600);
- t.m = '' + parseInt(time/60 - t.h*60);
- t.s = '' + parseInt(time - (t.h*3600 + t.m*60));
-
- for (var attr of ['m','s']) {
- if (parseInt(t[attr]) < 10) {
- t[attr] = '0' + t[attr];
- }
- }
-
- var ret = [];
- if (parseInt(t.h)) {
- ret.push(t.h);
- }
-
- ret.push(t.m, t.s);
- return ret.join(':');
- },
-
- convertSize: function(size) {
- size = parseInt(size); // Normalize strings
-
- const units = ['B', 'KB', 'MB', 'GB'];
- let s=size, i=0;
-
- for (; s > 1024 && i < units.length; i++, s = parseInt(s/1024));
- return (size / Math.pow(2, 10*i)).toFixed(2) + ' ' + units[i];
- },
- },
-};
-
-Vue.component('media', {
- template: '#tmpl-media',
- props: ['config','player'],
- mixins: [mediaUtils],
-
- data: function() {
- return {
- bus: new Vue({}),
- results: [],
- status: {},
- selectedDevice: {},
- deviceHandlers: {},
-
- loading: {
- results: false,
- media: false,
- },
-
- infoModal: {
- visible: false,
- loading: false,
- item: {},
- },
-
- torrentModal: {
- visible: false,
- items: {},
- },
-
- subsModal: {
- visible: false,
- },
- };
- },
-
- computed: {
- types: function() {
- return MediaHandlers;
- },
-
- torrentsDownloading: function() {
- return Object.entries(this.torrentModal.items).length > 0;
- },
- },
-
- methods: {
- onResultsLoading: function() {
- this.loading.results = true;
- },
-
- onResultsReady: async function(results) {
- for (const result of results) {
- if (result.type && MediaHandlers[result.type]) {
- result.handler = MediaHandlers[result.type];
- } else {
- result.type = 'generic';
- result.handler = MediaHandlers.generic;
-
- for (const [handlerType, handler] of Object.entries(MediaHandlers)) {
- if (handler.matchesUrl && handler.matchesUrl(result.url)) {
- result.type = handlerType;
- result.handler = handler;
- break;
- }
- }
- }
-
- Object.entries(await result.handler.getMetadata(result, onlyBase=true)).forEach(entry => {
- Vue.set(result, entry[0], entry[1]);
- });
- }
-
- this.results = results;
- this.loading.results = false;
- },
-
- play: async function(item) {
- if (!this.selectedDevice.accepts[item.type]) {
- item = await this.startStreaming(item);
- }
-
- let status = await this.selectedDevice.play(item, item.subtitles);
-
- if (item.title)
- status.title = item.title;
-
- this.subsModal.visible = false;
- this.onStatusUpdate({
- device: this.selectedDevice,
- status: status,
- });
- },
-
- pause: async function() {
- let status = await this.selectedDevice.pause();
- this.onStatusUpdate({
- device: this.selectedDevice,
- status: status,
- });
- },
-
- stop: async function() {
- let status = await this.selectedDevice.stop();
- this.onStatusUpdate({
- device: this.selectedDevice,
- status: status,
- });
- },
-
- seek: async function(position) {
- let status = await this.selectedDevice.seek(position);
- this.onStatusUpdate({
- device: this.selectedDevice,
- status: status,
- });
- },
-
- setVolume: async function(volume) {
- let status = await this.selectedDevice.setVolume(volume);
- this.onStatusUpdate({
- device: this.selectedDevice,
- status: status,
- });
- },
-
- info: function(item) {
- Vue.set(this.infoModal, 'item', item);
- this.infoModal.loading = false;
- this.infoModal.visible = true;
- },
-
- infoLoading: function() {
- this.infoModal.loading = true;
- this.infoModal.visible = true;
- },
-
- startStreaming: async function(item) {
- if (typeof item === 'string')
- item = {url: item};
-
- const ret = await request('media.start_streaming', {
- media: item.url,
- subtitles: item.subtitles,
- });
-
- this.bus.$emit('streaming-started', {
- url: ret.url,
- resource: item.url,
- subtitles_url: ret.subtiles_url,
- });
-
- return {...item, ...ret};
- },
-
- searchSubs: function(item) {
- if (typeof item === 'string')
- item = {url: item};
-
- this.subsModal.visible = true;
- this.$refs.subs.search(item);
- },
-
- selectDevice: async function(device) {
- this.selectedDevice = device;
- let status = await this.selectedDevice.status();
-
- this.onStatusUpdate({
- device: this.selectedDevice,
- status: status,
- });
- },
-
- syncPosition: function(status) {
- if (!status)
- return;
-
- status._syncTime = {
- timestamp: new Date(),
- position: status.position,
- };
- },
-
- getTorrentPlugin: async function() {
- if (this.config && this.config.torrent_plugin) {
- return this.config.torrent_plugin;
- }
-
- const config = await request('inspect.get_config');
- if ('rtorrent' in config)
- return 'rtorrent';
- if ('webtorrent' in config)
- return 'webtorrent';
- return 'torrent'
- },
-
- torrentStatusUpdate: function(torrents) {
- Vue.set(this.torrentModal, 'items', {});
-
- for (const [url, torrent] of Object.entries(torrents)) {
- Vue.set(this.torrentModal.items, url, torrent);
- }
- },
-
- refreshTorrents: async function() {
- const torrentPlugin = await this.getTorrentPlugin();
- const torrents = await request(torrentPlugin + '.status');
- this.torrentStatusUpdate(torrents);
- },
-
- onStatusUpdate: function(event) {
- const dev = event.device;
- const status = event.status;
- this.syncPosition(status);
-
- if (status.state !== 'stop' && this.status[dev.type] && this.status[dev.type][dev.name]) {
- status.title = status.title || this.status[dev.type][dev.name].title;
- }
-
- if (!this.status[dev.type])
- Vue.set(this.status, dev.type, {});
- Vue.set(this.status[dev.type], dev.name, status);
-
- if (!this.deviceHandlers[dev.type])
- Vue.set(this.deviceHandlers, dev.type, {});
- Vue.set(this.deviceHandlers[dev.type], dev.name, dev);
- },
-
- onMediaEvent: async function(event) {
- var type, player;
- const plugin = event.plugin.replace(/^media\./, '');
-
- if (this.status[event.player] && this.status[event.player][plugin]) {
- type = event.player;
- player = plugin;
- } else if (this.status[plugin] && this.status[plugin][event.player]) {
- type = plugin;
- player = event.player;
- }
-
- var handler;
- if (this.deviceHandlers[event.player] && this.deviceHandlers[event.player][plugin]) {
- handler = this.deviceHandlers[event.player][plugin];
- } else if (this.deviceHandlers[plugin] && this.deviceHandlers[plugin][event.player]) {
- handler = this.deviceHandlers[plugin][event.player];
- } else {
- // No handlers
- console.warn('No handlers found for device type '.concat(event.plugin, ' and player ', event.player));
- return;
- }
-
- let status = await handler.status(event.player);
- this.syncPosition(status);
-
- if (event.resource) {
- event.url = event.resource;
- delete event.resource;
- }
-
- if (status.state !== 'stop' || event.type.endsWith('.MediaPlayEvent')) {
- status.title = status.title || this.status[type][player].title;
- }
-
- if (event.type.endsWith('.MediaPlayEvent')) {
- status.state = 'play';
- } else if (event.type.endsWith('.MediaPauseEvent')) {
- status.state = 'pause';
- }
-
- Vue.set(this.status[type], player, status);
- },
-
- timerFunc: function() {
- for (const [playerType, players] of Object.entries(this.status)) {
- for (const [playerName, status] of Object.entries(players)) {
- if (status.state === 'play' && !isNaN(status.position) && status._syncTime) {
- status.position = status._syncTime.position +
- ((new Date()).getTime()/1000) - (status._syncTime.timestamp.getTime()/1000);
- }
- }
- }
- },
- },
-
- created: function() {
- for (const [type, Handler] of Object.entries(MediaHandlers)) {
- MediaHandlers[type] = new Handler();
- MediaHandlers[type].bus = this.bus;
- }
-
- this.refreshTorrents();
- registerEventHandler(this.onMediaEvent,
- 'platypush.message.event.media.NewPlayingMediaEvent',
- 'platypush.message.event.media.MediaPlayEvent',
- 'platypush.message.event.media.MediaPauseEvent',
- 'platypush.message.event.media.MediaStopEvent',
- 'platypush.message.event.media.MediaSeekEvent');
-
- this.bus.$on('play', this.play);
- this.bus.$on('pause', this.pause);
- this.bus.$on('stop', this.stop);
- this.bus.$on('seek', this.seek);
- this.bus.$on('volume', this.setVolume);
- this.bus.$on('info', this.info);
- this.bus.$on('info-loading', this.infoLoading);
- this.bus.$on('selected-device', this.selectDevice);
- this.bus.$on('results-loading', this.onResultsLoading);
- this.bus.$on('results-ready', this.onResultsReady);
- this.bus.$on('status-update', this.onStatusUpdate);
- this.bus.$on('start-streaming', this.startStreaming);
- this.bus.$on('search-subs', this.searchSubs);
- this.bus.$on('torrent-status-update', this.torrentStatusUpdate);
-
- setInterval(this.timerFunc, 1000);
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/info.js b/platypush/backend/http/static/js/plugins/media/info.js
deleted file mode 100644
index 8360d53d..00000000
--- a/platypush/backend/http/static/js/plugins/media/info.js
+++ /dev/null
@@ -1,13 +0,0 @@
-Vue.component('media-info', {
- template: '#tmpl-media-info',
- mixins: [mediaUtils],
- props: {
- bus: { type: Object },
-
- item: {
- type: Object,
- default: () => {},
- }
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/item.js b/platypush/backend/http/static/js/plugins/media/item.js
deleted file mode 100644
index c4ab9c15..00000000
--- a/platypush/backend/http/static/js/plugins/media/item.js
+++ /dev/null
@@ -1,28 +0,0 @@
-Vue.component('media-item', {
- template: '#tmpl-media-item',
- props: {
- bus: { type: Object },
-
- selected: {
- type: Boolean,
- default: false,
- },
-
- active: {
- type: Boolean,
- default: false,
- },
-
- item: {
- type: Object,
- default: () => {},
- }
- },
-
- methods: {
- onClick: function(event) {
- this.bus.$emit('result-clicked', this.item);
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/players/browser.js b/platypush/backend/http/static/js/plugins/media/players/browser.js
deleted file mode 100644
index 140e1ea9..00000000
--- a/platypush/backend/http/static/js/plugins/media/players/browser.js
+++ /dev/null
@@ -1,56 +0,0 @@
-MediaPlayers.browser = Vue.extend({
- props: {
- type: {
- type: String,
- default: 'browser',
- },
-
- accepts: {
- type: Object,
- default: () => {
- return {
- youtube: true,
- generic: true,
- };
- },
- },
-
- subFormats: {
- type: Array,
- default: () => {
- return ['vtt'];
- },
- },
-
- name: {
- type: String,
- default: 'Browser',
- },
-
- iconClass: {
- type: String,
- default: 'fa fa-laptop',
- },
- },
-
- methods: {
- status: async function() {
- return {};
- },
-
- play: async function(item, subtitles) {
- let url = item.url;
-
- if (item.source && !item.source.match('https?://')) {
- // Non-HTTP resource streamed over HTTP
- const hostRegex = /^(https?:\/\/[^:/]+(:[0-9]+)?\/?)/;
- const baseURL = window.location.href.match(hostRegex)[1];
- url = url.replace(hostRegex, baseURL) + '?webplayer';
- }
-
- window.open(url, '_blank');
- return {};
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/players/chromecast.js b/platypush/backend/http/static/js/plugins/media/players/chromecast.js
deleted file mode 100644
index c53d3807..00000000
--- a/platypush/backend/http/static/js/plugins/media/players/chromecast.js
+++ /dev/null
@@ -1,88 +0,0 @@
-MediaPlayers.chromecast = Vue.extend({
- props: {
- type: {
- type: String,
- default: 'chromecast',
- },
-
- accepts: {
- type: Object,
- default: () => {
- return {
- youtube: true,
- generic: true,
- };
- },
- },
-
- subFormats: {
- type: Array,
- default: () => {
- return ['vtt'];
- },
- },
-
- device: {
- type: null,
- address: null,
- port: null,
- uuid: null,
- status: {},
- name: '',
- model_name: null,
- },
- },
-
- computed: {
- name: function() {
- return this.device.name;
- },
-
- iconClass: function() {
- return this.device.type === 'audio' ? 'fa fa-volume-up' : 'fab fa-chromecast';
- },
- },
-
- methods: {
- scan: async function() {
- return await request('media.chromecast.get_chromecasts');
- },
-
- status: async function(device) {
- return await request('media.chromecast.status', {chromecast: device || this.device.name});
- },
-
- play: async function(item) {
- return await request('media.chromecast.play', {
- resource: item.url,
- chromecast: this.device.name,
- title: item.title || item.url,
- subtitles: item.subtitles_url,
- content_type: item.mime_type,
- });
- },
-
- pause: async function() {
- return await request('media.chromecast.pause', {chromecast: this.device.name});
- },
-
- stop: async function() {
- return await request('media.chromecast.stop', {chromecast: this.device.name});
- },
-
- seek: async function(position) {
- return await request('media.chromecast.set_position', {
- position: position,
- chromecast: this.device.name,
- });
- },
-
- setVolume: async function(volume) {
- return await request('media.chromecast.set_volume', {
- volume: volume,
- chromecast: this.device.name,
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/players/kodi.js b/platypush/backend/http/static/js/plugins/media/players/kodi.js
deleted file mode 100644
index 52896060..00000000
--- a/platypush/backend/http/static/js/plugins/media/players/kodi.js
+++ /dev/null
@@ -1,86 +0,0 @@
-MediaPlayers.kodi = Vue.extend({
- props: {
- type: {
- type: String,
- default: 'kodi',
- },
-
- device: {
- type: Object,
- default: () => {
- return {
- host: undefined,
- };
- },
- },
-
- accepts: {
- type: Object,
- default: () => {
- return {
- file: true,
- generic: true,
- youtube: true,
- };
- },
- },
-
- iconClass: {
- type: String,
- default: 'fa fa-film',
- },
- },
-
- computed: {
- name: function() {
- return this.device.host;
- },
-
- text: function() {
- return 'Kodi '.concat('[', this.device.host, ']');
- },
- },
-
- methods: {
- scan: async function() {
- const plugin = await request('inspect.get_config', {entry: 'media.kodi'});
- if (!(plugin && plugin.host)) {
- return [];
- }
-
- return [{ host: plugin.host }];
- },
-
- status: async function() {
- return await request('media.kodi.status');
- },
-
- play: async function(item) {
- return await request('media.kodi.play', {
- resource: item.url,
- subtitles: item.subtitles_url,
- });
- },
-
- pause: async function() {
- return await request('media.kodi.pause');
- },
-
- stop: async function() {
- return await request('media.kodi.stop');
- },
-
- seek: async function(position) {
- return await request('media.kodi.set_position', {
- position: position,
- });
- },
-
- setVolume: async function(volume) {
- return await request('media.kodi.set_volume', {
- volume: volume,
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/players/local.js b/platypush/backend/http/static/js/plugins/media/players/local.js
deleted file mode 100644
index 1260130c..00000000
--- a/platypush/backend/http/static/js/plugins/media/players/local.js
+++ /dev/null
@@ -1,86 +0,0 @@
-MediaPlayers.local = Vue.extend({
- props: {
- type: {
- type: String,
- default: 'local',
- },
-
- accepts: {
- type: Object,
- default: () => {
- return {
- file: true,
- youtube: true,
- generic: true,
- };
- },
- },
-
- subFormats: {
- type: Array,
- default: () => {
- return ['srt'];
- },
- },
-
- device: {
- type: Object,
- default: () => {
- return {
- plugin: undefined,
- };
- },
- },
-
- iconClass: {
- type: String,
- default: 'fa fa-desktop',
- },
- },
-
- computed: {
- name: function() {
- return this.device.plugin;
- },
-
- pluginPrefix: function() {
- return 'media.' + this.device.plugin;
- },
- },
-
- methods: {
- status: async function() {
- return await request(this.pluginPrefix.concat('.status'));
- },
-
- play: async function(item, subtitles=undefined) {
- return await request(
- this.pluginPrefix.concat('.play'),
- {resource: item.url, subtitles: subtitles}
- );
- },
-
- pause: async function() {
- return await request(this.pluginPrefix.concat('.pause'));
- },
-
- stop: async function() {
- return await request(this.pluginPrefix.concat('.stop'));
- },
-
- seek: async function(position) {
- return await request(
- this.pluginPrefix.concat('.set_position'),
- {position: position},
- );
- },
-
- setVolume: async function(volume) {
- return await request(
- this.pluginPrefix.concat('.set_volume'),
- {volume: volume}
- );
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/results.js b/platypush/backend/http/static/js/plugins/media/results.js
deleted file mode 100644
index 2fb1128d..00000000
--- a/platypush/backend/http/static/js/plugins/media/results.js
+++ /dev/null
@@ -1,69 +0,0 @@
-Vue.component('media-results', {
- template: '#tmpl-media-results',
- props: {
- bus: { type: Object },
- searching: {
- type: Boolean,
- default: false,
- },
- loading: {
- type: Boolean,
- default: false,
- },
- results: {
- type: Array,
- default: () => [],
- },
- status: {
- type: Object,
- default: () => {},
- },
- resize: {
- type: Boolean,
- default: false,
- },
- },
-
- data: function() {
- return {
- selectedItem: {},
- };
- },
-
- computed: {
- mediaItemDropdownItems: function() {
- if (!Object.keys(this.selectedItem).length) {
- return [];
- }
-
- const self = this;
-
- return this.selectedItem.handler.dropdownItems.map(item => {
- return {
- text: item.text,
- icon: item.icon,
- iconClass: item.iconClass,
- click: function() {
- item.action(self.selectedItem);
- },
- };
- });
- },
- },
-
- methods: {
- itemClicked: function(item) {
- if (this.selectedItem.length && this.selectedItem.url === item.url) {
- return;
- }
-
- this.selectedItem = item;
- openDropdown(this.$refs.mediaItemDropdown);
- },
- },
-
- created: function() {
- this.bus.$on('result-clicked', this.itemClicked);
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/search.js b/platypush/backend/http/static/js/plugins/media/search.js
deleted file mode 100644
index 8b7f5a16..00000000
--- a/platypush/backend/http/static/js/plugins/media/search.js
+++ /dev/null
@@ -1,71 +0,0 @@
-Vue.component('media-search', {
- template: '#tmpl-media-search',
- props: {
- bus: { type: Object },
- supportedTypes: { type: Object },
- },
-
- data: function() {
- return {
- searching: false,
- showFilter: false,
- query: '',
-
- types: Object.keys(this.supportedTypes).reduce((obj, type) => {
- obj[type] = true;
- return obj;
- }, {}),
-
- searchTypes: Object.keys(this.supportedTypes).reduce((obj, type) => {
- if (type !== 'generic' && type !== 'base')
- obj[type] = true;
- return obj;
- }, {}),
- };
- },
-
- methods: {
- isUrl: function(query) {
- const match = query.match('^([^:]+)://');
- if (match) {
- let protocol = match[1];
- if (protocol === 'https')
- protocol = 'http';
-
- return protocol;
- }
- },
-
- search: async function(event) {
- const types = Object.entries(this.searchTypes).filter(t => t[1]).map(t => t[0]);
- const protocol = this.isUrl(this.query);
-
- if (protocol) {
- this.bus.$emit('results-ready', [{
- type: protocol,
- url: this.query,
- }]);
-
- return;
- }
-
- var results = [];
- this.searching = true;
- this.bus.$emit('results-loading');
-
- try {
- results = await request('media.search', {
- query: this.query,
- types: types,
- });
- } finally {
- this.searching = false;
- this.bus.$emit('results-ready', results);
- }
- },
- },
-
- created: function() {
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/subs.js b/platypush/backend/http/static/js/plugins/media/subs.js
deleted file mode 100644
index 68c1e075..00000000
--- a/platypush/backend/http/static/js/plugins/media/subs.js
+++ /dev/null
@@ -1,45 +0,0 @@
-Vue.component('media-subs', {
- template: '#tmpl-media-subs',
- props: {
- bus: { type: Object },
- subFormats: {
- type: Array,
- default: () => [],
- },
- },
-
- data: function() {
- return {
- loading: false,
- media: {},
- items: [],
- selectedItem: undefined,
- };
- },
-
- methods: {
- search: async function(media) {
- this.loading = true;
-
- this.media = media;
- this.selectedItem = undefined;
- this.items = await request('media.subtitles.get_subtitles', {resource: this.media.url});
-
- this.loading = false;
- },
-
- play: async function() {
- let args = {link: this.selectedItem.SubDownloadLink};
-
- if (this.media.url && this.media.url.startsWith('file://'))
- args.media_resource = this.media.url;
-
- if (this.subFormats.indexOf('srt') < 0)
- args.convert_to_vtt = true;
-
- this.media.subtitles = (await request('media.subtitles.download', args)).filename;
- this.bus.$emit('play', this.media);
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/media/torrents.js b/platypush/backend/http/static/js/plugins/media/torrents.js
deleted file mode 100644
index eddb2db2..00000000
--- a/platypush/backend/http/static/js/plugins/media/torrents.js
+++ /dev/null
@@ -1,93 +0,0 @@
-Vue.component('media-torrents', {
- template: '#tmpl-media-torrents',
- mixins: [mediaUtils],
- props: {
- bus: { type: Object },
- torrents: {
- type: Object,
- default: () => {},
- },
- },
-
- data: function() {
- return {
- selectedItem: undefined,
- };
- },
-
- computed: {
- dropdownItems: function() {
- const self = this;
- return [
- {
- name: 'play',
- text: 'Play',
- iconClass: 'fa fa-play',
- click: function() {
- self.bus.$emit('torrent-play', self.selectedItem);
- },
- },
-
- {
- name: 'pause',
- text: 'Pause/unpause transfer',
- iconClass: 'fa fa-pause',
- click: function() {
- self.bus.$emit('torrent-pause', self.selectedItem);
- },
- },
-
- {
- name: 'cancel',
- text: 'Cancel transfer',
- iconClass: 'fa fa-trash',
- click: function() {
- self.bus.$emit('torrent-remove', self.selectedItem);
- },
- },
-
- {
- name: 'info',
- text: 'View details',
- iconClass: 'fa fa-info',
- click: function() {
- self.bus.$emit('info', self.selectedItem);
- },
- },
- ];
- },
- },
-
- methods: {
- getTorrentPlugin: async function() {
- if (this.config && this.config.torrent_plugin) {
- return this.config.torrent_plugin;
- }
-
- const config = await request('inspect.get_config');
- if ('rtorrent' in config)
- return 'rtorrent';
- if ('webtorrent' in config)
- return 'webtorrent';
- return 'torrent'
- },
-
- openDropdown: function(item) {
- this.selectedItem = item;
- openDropdown(this.$refs.menu);
- },
-
- onMagnetDownload: async function() {
- const magnet = this.$refs.magnetLink.value.trim();
- if (!magnet.length)
- return;
-
- const torrentPlugin = await this.getTorrentPlugin();
- await request(torrentPlugin + '.download', {
- torrent: magnet,
- _async: true,
- });
- }
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/browser.js b/platypush/backend/http/static/js/plugins/music.mpd/browser.js
deleted file mode 100644
index e748fd13..00000000
--- a/platypush/backend/http/static/js/plugins/music.mpd/browser.js
+++ /dev/null
@@ -1,26 +0,0 @@
-Vue.component('music-mpd-browser-item', {
- template: '#tmpl-music-mpd-browser-item',
- props: {
- id: { type: String, },
- type: { type: String, },
- name: { type: String, },
- file: { type: String, },
- time: { type: String, },
- artist: { type: String, },
- title: { type: String, },
- date: { type: String, },
- track: { type: String, },
- genre: { type: String, },
- lastModified: { type: String, },
- albumUri: { type: String, },
-
- selected: {
- type: Boolean,
- default: false,
- },
- },
-
- methods: {
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/index.js b/platypush/backend/http/static/js/plugins/music.mpd/index.js
deleted file mode 100644
index 0b4e0665..00000000
--- a/platypush/backend/http/static/js/plugins/music.mpd/index.js
+++ /dev/null
@@ -1,1265 +0,0 @@
-Vue.component('music-mpd', {
- template: '#tmpl-music-mpd',
- props: ['config'],
- mixins: [utils],
- data: function() {
- return {
- track: {},
- status: {},
- timer: null,
- playlist: [],
- playlists: [],
- playlistFilter: '',
- browserFilter: '',
- playlistAddFilter: '',
- editorFilter: '',
- browserPath: [],
- browserItems: [],
-
- selectionMode: {
- playlist: false,
- browser: false,
- editor: false,
- },
-
- moveMode: {
- playlist: false,
- editor: false,
- },
-
- infoItem: {},
- modalVisible: {
- info: false,
- editor: false,
- playlistAdd: false,
- },
-
- addToPlaylistItems: [],
- selectedPlaylist: {},
- selectedPlaylistItems: {},
- selectedPlaylistAddItems: {},
- selectedEditorItems: {},
- selectedBrowserItems: {},
-
- syncTime: {
- timestamp: null,
- elapsed: null,
- },
- };
- },
-
- computed: {
- playlistDropdownItems: function() {
- var self = this;
- var items = [];
-
- if (Object.keys(this.selectedPlaylistItems).length === 1) {
- const track = Object.values(this.selectedPlaylistItems)[0];
-
- items.push({
- text: 'Play',
- icon: 'play',
- click: async function() {
- await self.playpos();
- self.selectedPlaylistItems = {};
- },
- }
- );
-
- if (track.artist && track.artist.length) {
- items.push({
- text: 'View artist',
- icon: 'user',
- click: async function() {
- await self.searchArtist(track);
- self.selectedPlaylistItems = {};
- }
- });
- }
-
- if (track.album && track.album.length) {
- items.push({
- text: 'View album',
- icon: 'compact-disc',
- click: async function() {
- await self.searchAlbum(track);
- self.selectedPlaylistItems = {};
- },
- });
- }
- }
-
- items.push({
- text: 'Add to playlist',
- icon: 'list',
- click: async function() {
- self.addToPlaylistItems = Object.values(self.selectedPlaylistItems).map(_ => _.file);
- self.selectedPlaylistItems = {};
- self.modalVisible.playlistAdd = true;
- await self.listplaylists();
- },
- });
-
- if (Object.keys(this.selectedPlaylistItems).length < this.playlist.length) {
- items.push({
- text: 'Move',
- icon: 'retweet',
- click: function() {
- self.moveMode.playlist = true;
- },
- });
- }
-
- items.push({
- text: 'Remove from queue',
- icon: 'trash',
- click: async function() {
- await self.del();
- self.selectedPlaylistItems = {};
- },
- });
-
- if (Object.keys(this.selectedPlaylistItems).length === 1) {
- items.push({
- text: 'View track info',
- icon: 'info',
- click: async function() {
- await self.info(Object.values(self.selectedPlaylistItems)[0]);
- },
- });
- }
-
- return items;
- },
-
- browserDropdownItems: function() {
- var self = this;
- var items = [];
-
- if (Object.keys(this.selectedBrowserItems).length === 1 &&
- Object.values(this.selectedBrowserItems)[0].type === 'directory') {
- items.push({
- text: 'Open',
- icon: 'folder',
- click: async function() {
- await self.cd();
- self.selectedBrowserItems = {};
- },
- });
- }
-
- if (Object.keys(this.selectedBrowserItems).length === 1) {
- const item = Object.values(this.selectedBrowserItems)[0];
-
- items.push(
- {
- text: 'Play',
- icon: 'play',
- click: async function() {
- const item = Object.values(self.selectedBrowserItems)[0];
- var promise;
-
- switch (item.type) {
- case 'playlist':
- promise = self.load(item.name);
- break;
- case 'file':
- promise = self.add(item.name, position=0);
- break;
- case 'directory':
- promise = self.add(item.name);
- break;
- default:
- console.warning('Unable to handle type: ' + item.type);
- break;
- }
-
- await promise;
- await self.playpos(0);
- self.selectedBrowserItems = {};
- },
- },
- {
- text: 'Replace and play',
- icon: 'play',
- click: async function() {
- await self.clear();
-
- const item = Object.values(self.selectedBrowserItems)[0];
- var promise;
-
- switch (item.type) {
- case 'playlist':
- promise = self.load(item.name);
- break;
- case 'file':
- promise = self.add(item.name, position=0);
- break;
- case 'directory':
- promise = self.add(item.name);
- break;
- default:
- console.warning('Unable to handle type: ' + item.type);
- break;
- }
-
- await promise;
- await self.playpos(0);
- self.selectedBrowserItems = {};
- },
- }
- );
-
- if (item.artist && item.artist.length) {
- items.push({
- text: 'View artist',
- icon: 'user',
- click: async function() {
- await self.searchArtist(item);
- self.selectedBrowserItems = {};
- }
- });
- }
-
- if (item.album && item.album.length) {
- items.push({
- text: 'View album',
- icon: 'compact-disc',
- click: async function() {
- await self.searchAlbum(item);
- self.selectedBrowserItems = {};
- },
- });
- }
- }
-
- items.push(
- {
- text: 'Add to queue',
- icon: 'plus',
- click: async function() {
- const items = Object.values(self.selectedBrowserItems);
- const promises = items.map(item => item.type === 'playlist' ? self.load(item.name) : self.add(item.name));
-
- await Promise.all(promises);
- self.selectedBrowserItems = {};
- },
- },
- );
-
- if (Object.values(this.selectedBrowserItems).filter(_ => _.type === 'file').length === Object.values(this.selectedBrowserItems).length) {
- items.push(
- {
- text: 'Add to playlist',
- icon: 'list',
- click: async function() {
- self.addToPlaylistItems = Object.keys(self.selectedBrowserItems);
- self.modalVisible.playlistAdd = true;
- await self.listplaylists();
- self.selectedBrowserItems = {};
- },
- },
- );
- }
-
- if (Object.keys(this.selectedBrowserItems).length === 1
- && Object.values(this.selectedBrowserItems)[0].type === 'playlist') {
- items.push({
- text: 'Edit',
- icon: 'pen',
- click: async function() {
- const item = Object.values(self.selectedBrowserItems)[0];
- self.selectedPlaylist.name = item.name;
- await self.refreshSelectedPlaylist();
- self.modalVisible.editor = true;
- self.selectedBrowserItems = {};
- },
- });
- }
-
- if (Object.values(this.selectedBrowserItems).filter(item => item.type === 'playlist').length === Object.values(this.selectedBrowserItems).length) {
- items.push({
- text: 'Remove',
- icon: 'trash',
- click: async function() {
- if (!confirm('Are you sure you want to remove the selected playlist' +
- (Object.values(self.selectedBrowserItems).length > 1 ? 's' : '') + '?')) {
- return;
- }
-
- const items = Object.values(self.selectedBrowserItems);
- await self.rm(items);
- self.selectedBrowserItems = {};
- },
- });
- }
-
- if (Object.keys(this.selectedBrowserItems).length === 1
- && Object.values(this.selectedBrowserItems)[0].type === 'file') {
- items.push({
- text: 'View info',
- icon: 'info',
- click: async function() {
- await self.info(Object.values(self.selectedBrowserItems)[0].name);
- },
- });
- }
-
- return items;
- },
-
- editorDropdownItems: function() {
- var self = this;
- var items = [];
-
- if (Object.keys(this.selectedEditorItems).length === 1) {
- const item = Object.values(this.selectedEditorItems)[0];
-
- items.push(
- {
- text: 'Play',
- icon: 'play',
- click: async function() {
- await self.add(item.file, position=0);
- await self.playpos(0);
- self.selectedEditorItems = {};
- },
- },
- {
- text: 'Replace and play',
- icon: 'play',
- click: async function() {
- await self.clear();
- await self.add(item.file, position=0);
- await self.playpos(0);
- self.selectedEditorItems = {};
- },
- }
- );
-
- if (item.artist && item.artist.length) {
- items.push({
- text: 'View artist',
- icon: 'user',
- click: async function() {
- await self.searchArtist(item);
- self.selectedEditorItems = {};
- }
- });
- }
-
- if (item.album && item.album.length) {
- items.push({
- text: 'View album',
- icon: 'compact-disc',
- click: async function() {
- await self.searchAlbum(item);
- self.selectedEditorItems = {};
- },
- });
- }
- }
-
- items.push(
- {
- text: 'Add to queue',
- icon: 'plus',
- click: async function() {
- const items = Object.values(self.selectedEditorItems);
- const promises = items.map(item => self.add(item.file));
-
- await Promise.all(promises);
- self.selectedEditorItems = {};
- },
- },
- {
- text: 'Add to playlist',
- icon: 'list',
- click: async function() {
- self.addToPlaylistItems = Object.keys(self.selectedEditorItems);
- self.modalVisible.playlistAdd = true;
- await self.listplaylists();
- self.selectedEditorItems = {};
- },
- }
- );
-
- if (Object.keys(this.selectedEditorItems).length < this.selectedPlaylist.items.length) {
- items.push({
- text: 'Move',
- icon: 'retweet',
- click: function() {
- self.moveMode.editor = true;
- },
- });
- }
-
- items.push(
- {
- text: 'Remove',
- icon: 'trash',
- click: async function() {
- if (!confirm('Are you sure you want to remove the selected track' +
- (Object.values(self.selectedEditorItems).length > 1 ? 's' : '') + ' from the playlist?')) {
- return;
- }
-
- const items = Object.values(self.selectedEditorItems);
- await self.playlistdelete(items.map(_ => _.pos));
- self.selectedEditorItems = {};
- },
- }
- );
-
- if (Object.keys(this.selectedEditorItems).length === 1) {
- const item = Object.values(self.selectedEditorItems)[0];
-
- items.push({
- text: 'View info',
- icon: 'info',
- click: async function() {
- await self.info(item.file);
- },
- });
- }
-
- return items;
- },
- },
-
- methods: {
- refresh: async function() {
- const getStatus = request('music.mpd.status');
- const getTrack = request('music.mpd.currentsong');
- const getPlaylist = request('music.mpd.playlistinfo');
- const getBrowserItems = request('music.mpd.lsinfo');
-
- let [status, track, playlist, browserItems] = await Promise.all([getStatus, getTrack, getPlaylist, getBrowserItems]);
-
- this._parseStatus(status);
- this._parseTrack(track);
- this._parsePlaylist(playlist);
- this._parseBrowserItems(browserItems);
-
- if (this.status.state === 'play') {
- this.startTimer();
- }
- },
-
- // Hack-ish workaround to get the browser and playlist panels to keep their height
- // in sync with the nav and control bars, as both those elements are fixed.
- adjustLayout: function() {
- const adjust = (self) => {
- const nav = document.querySelector('nav');
- const panels = document.querySelectorAll('.music-mpd-container .panels .panel');
- const controls = document.querySelector('.music-mpd-container .controls');
-
- return () => {
- const panelHeight = window.innerHeight - nav.clientHeight - controls.clientHeight - 5;
- if (panelHeight >= 0) {
- for (const panel of panels) {
- if (panelHeight != parseFloat(panel.style.height)) {
- panel.style.height = panelHeight + 'px';
- }
- }
- }
- }
- };
- },
-
- _parseStatus: async function(status) {
- if (!status || status.length === 0) {
- status = await request('music.mpd.status');
- }
-
- for (const [attr, value] of Object.entries(status)) {
- if (['consume','random','repeat','single','bitrate'].indexOf(attr) >= 0) {
- Vue.set(this.status, attr, !!parseInt(value));
- } else if (['nextsong','nextsongid','playlist','playlistlength',
- 'volume','xfade','song','songid'].indexOf(attr) >= 0) {
- Vue.set(this.status, attr, parseInt(value));
- } else if (['elapsed'].indexOf(attr) >= 0) {
- Vue.set(this.status, attr, parseFloat(value));
- } else {
- Vue.set(this.status, attr, value);
- }
- }
- },
-
- _parseTrack: async function(track) {
- if (!track || track.length === 0) {
- track = await request('music.mpd.currentsong');
- }
-
- for (const [attr, value] of Object.entries(track)) {
- if (['id','pos','time','track','disc'].indexOf(attr) >= 0) {
- Vue.set(this.track, attr, parseInt(value));
- } else {
- Vue.set(this.track, attr, value);
- }
- }
- },
-
- _parsePlaylist: function(playlist) {
- if (!playlist || playlist.length === 0) {
- return;
- }
-
- this.playlist = [];
-
- for (var track of playlist) {
- for (const [attr, value] of Object.entries(track)) {
- if (['time','pos','id','track','disc'].indexOf(attr) >= 0) {
- track[attr] = parseInt(value);
- } else {
- track[attr] = value;
- }
- }
-
- this.playlist.push(track);
- }
- },
-
- _parseBrowserItems: function(browserItems) {
- if (!browserItems || browserItems.length === 0) {
- return;
- }
-
- this.browserItems = [];
-
- for (var item of browserItems) {
- if (item.directory) {
- this.browserItems.push({
- id: 'directory:' + item.directory,
- type: 'directory',
- name: item.directory,
- });
- } else if (item.playlist) {
- this.browserItems.push({
- id: 'playlist:' + item.playlist,
- type: 'playlist',
- name: item.playlist,
- 'last-modified': item['last-modified'],
- });
- } else if (item.file) {
- this.browserItems.push({
- id: item.file,
- type: 'file',
- name: item.file,
- ...item,
- });
- }
- }
- },
-
- previous: async function() {
- await request('music.mpd.previous');
- let track = await request('music.mpd.currentsong');
- this.onNewPlayingTrack({track: track});
- },
-
- repeat: async function() {
- await request('music.mpd.repeat');
- let status = await request('music.mpd.status');
- this._parseStatus(status);
- },
-
- consume: async function() {
- await request('music.mpd.consume');
- let status = await request('music.mpd.status');
- this._parseStatus(status);
- },
-
- single: async function() {
- await request('music.mpd.single');
- let status = await request('music.mpd.status');
- this._parseStatus(status);
- },
-
- playPause: async function() {
- await request('music.mpd.pause');
- let status = await request('music.mpd.status');
- const method = status.state === 'play' ? this.onMusicPlay : this.onMusicPause;
- method({ status: status });
- },
-
- playpos: async function(pos) {
- if (pos == null) {
- if (!Object.keys(this.selectedPlaylistItems).length) {
- return;
- }
-
- pos = Object.keys(this.selectedPlaylistItems)[0];
- }
-
- let status = await request('music.mpd.play_pos', {pos: pos});
- this._parseStatus(status);
-
- let track = await request('music.mpd.currentsong');
- this._parseTrack(track);
- },
-
- stop: async function() {
- await request('music.mpd.stop');
- this.onMusicStop({});
- },
-
- random: async function() {
- await request('music.mpd.random');
- let status = await request('music.mpd.status');
- this._parseStatus(status);
- },
-
- next: async function() {
- await request('music.mpd.next');
- let track = await request('music.mpd.currentsong');
- this.onNewPlayingTrack({track: track});
- },
-
- seek: async function(event) {
- var value;
-
- if (event.target) {
- value = parseFloat(event.target.value);
- } else if (event.value) {
- value = parseFloat(event.value);
- } else {
- value = parseFloat(event);
- }
-
- const status = await request('music.mpd.seekcur', {value: value});
- this.onSeekChange({status: status});
- },
-
- volume: async function(event) {
- var value;
-
- if (event.target) {
- value = parseFloat(event.target.value);
- } else if (event.value) {
- value = parseFloat(event.value);
- } else {
- value = parseFloat(event);
- }
-
- const status = await request('music.mpd.setvol', {vol: value});
- this.onVolumeChange({status: status});
- },
-
- clear: async function() {
- if (!confirm('Are you sure that you want to clear the playlist?')) {
- return;
- }
-
- await request('music.mpd.clear');
- this.stopTimer();
- this.track = {};
- this.playlist = [];
-
- let status = await request('music.mpd.status');
- this._parseStatus(status);
- },
-
- add: async function(resource, position=null) {
- var args = {resource: resource};
- if (position != null) {
- args.position = position;
- }
-
- let status = await request('music.mpd.add', args);
- this._parseStatus(status);
-
- let playlist = await request('music.mpd.playlistinfo');
- this._parsePlaylist(playlist);
- },
-
- load: async function(item, play=false) {
- await request('music.mpd.load', {playlist:item, play:play});
- let playlist = await request('music.mpd.playlistinfo');
- this._parsePlaylist(playlist);
- },
-
- del: async function() {
- const positions = Object.keys(this.selectedPlaylistItems);
- if (!positions.length) {
- return;
- }
-
- let status = await request('music.mpd.delete', {'positions': positions});
- this._parseStatus(status);
-
- for (const pos in positions) {
- Vue.delete(this.selectedPlaylistItems, pos);
- }
- },
-
- rm: async function(items) {
- if (!items) {
- items = Object.values(this.selectedBrowserItems);
- }
-
- if (!items.length) {
- return;
- }
-
- let status = await request('music.mpd.rm', {playlist: items.map(_ => _.name)});
- this._parseStatus(status);
-
- items = await request('music.mpd.lsinfo', {uri: this.browserPath.join('/')});
- this._parseBrowserItems(items);
- },
-
- move: async function(fromPos, toPos, updateChanges=true) {
- let status = await request('music.mpd.move', {from_pos: fromPos, to_pos: toPos});
-
- if (updateChanges) {
- this._parseStatus(status);
- const playlist = await request('music.mpd.playlistinfo');
- this._parsePlaylist(playlist);
- }
- },
-
- playlistmove: async function(fromPos, toPos) {
- if (!this.selectedPlaylist.name) {
- return;
- }
-
- await request('music.mpd.playlistmove', {name: this.selectedPlaylist.name, from_pos: fromPos, to_pos: toPos});
- },
-
- swap: async function() {
- if (Object.keys(this.selectedPlaylistItems).length !== 2) {
- return;
- }
-
- const positions = Object.keys(this.selectedPlaylistItems).sort();
- await request('music.mpd.move', {from_pos: positions[1], to_pos: positions[0]});
-
- let status = await request('music.mpd.move', {from_pos: positions[0]+1, to_pos: positions[1]});
- this._parseStatus(status);
-
- const playlist = await request('music.mpd.playlistinfo');
- this._parsePlaylist(playlist);
- },
-
- cd: async function() {
- const item = Object.values(this.selectedBrowserItems)[0];
-
- if (item.name === '..') {
- if (this.browserPath.length) {
- this.browserPath.pop();
- }
- } else {
- this.browserPath = item.name.split('/');
- }
-
- const items = await request('music.mpd.lsinfo', {uri: this.browserPath.join('/')});
- this._parseBrowserItems(items);
- },
-
- info: async function(item) {
- var info = item;
-
- if (typeof(item) === 'string') {
- var items = await request('music.mpd.search', {filter: {file: info}});
- item = items.length ? items[0] : {file: info};
- }
-
- this.infoItem = item;
- this.modalVisible.info = true;
- },
-
- searchArtist: async function(item) {
- await this.search({artist: item.artist});
- },
-
- searchAlbum: async function(item) {
- var query = {};
-
- if (item['x-albumuri']) {
- query.file = item['x-albumuri'];
- } else {
- query.artist = item.albumartist || item.artist;
- query.album = item.album;
- }
-
- await this.search(query);
- },
-
- search: async function(query) {
- this.$refs.search.resetQuery();
- this.$refs.search.query = query;
- this.$refs.search.visible = true;
- await this.$refs.search.search();
- },
-
- listplaylists: async function() {
- this.playlists = [];
- let playlists = await request('music.mpd.listplaylists');
-
- for (const p of playlists) {
- this.playlists.push(p);
- }
- },
-
- listplaylist: async function(name) {
- return await request('music.mpd.listplaylist', {name: name});
- },
-
- listplaylistinfo: async function(name) {
- return await request('music.mpd.listplaylistinfo', {name: name});
- },
-
- playlistadd: async function(items=[], playlists=[]) {
- if (!playlists.length) {
- if (this.modalVisible.playlistAdd) {
- playlists = Object.keys(this.selectedPlaylistAddItems);
- }
- }
-
- if (!items.length) {
- items = this.addToPlaylistItems;
- }
-
- if (!items.length || !playlists.length) {
- return;
- }
-
- var promises = [];
- for (const playlist of playlists) {
- promises.push(request('music.mpd.playlistadd', {
- name: playlist, uri: items.map(_ => typeof(_) === 'object' ? _.file : _)
- }));
- }
-
- await Promise.all(promises);
- this.modalVisible.playlistAdd = false;
- this.addToPlaylistItems = [];
- },
-
- playlistdelete: async function(items=[]) {
- if (!items.length) {
- items = Object.keys(this.selectedEditorItems);
- }
-
- if (!items.length || !this.selectedPlaylist.name) {
- return;
- }
-
- await request('music.mpd.playlistdelete', {name: this.selectedPlaylist.name, pos: items});
- await this.refreshSelectedPlaylist();
- },
-
- playlistclear: async function() {
- if (!confirm('Are you sure that you want to clear this playlist? This operation is NOT REVERSIBLE')) {
- return;
- }
-
- await request('music.mpd.playlistclear', {name: this.selectedPlaylist.name});
- await this.refreshSelectedPlaylist();
- },
-
- rename: async function() {
- if (!this.selectedPlaylist.name) {
- return;
- }
-
- var newName = prompt('New name for the playlist', this.selectedPlaylist.name);
- if (!newName.length) {
- return;
- }
-
- await request('music.mpd.rename', {name: this.selectedPlaylist.name, new_name: newName});
- await this.listplaylists();
-
- for (var item of this.browserItems) {
- if (item.type === 'playlist' && item.name === this.selectedPlaylist.name) {
- item.name = newName;
- }
- }
-
- this.selectedPlaylist.name = newName;
- },
-
- onNewPlayingTrack: async function(event) {
- var previousTrack = {
- file: this.track.file,
- artist: this.track.artist,
- title: this.track.title,
- };
-
- this.status.state = 'play';
- Vue.set(this.status, 'elapsed', 0);
- this.track = {};
- this._parseTrack(event.track);
-
- let status = event.status ? event.status : await request('music.mpd.status');
- this._parseStatus(status);
- this.startTimer();
-
- if (this.track.file != previousTrack.file
- || this.track.artist != previousTrack.artist
- || this.track.title != previousTrack.title) {
- this.showNewTrackNotification();
-
- const self = this;
- setTimeout(function() {
- self.scrollToActiveTrack();
- }, 100);
- }
- },
-
- showNewTrackNotification: function() {
- createNotification({
- html: '' + (this.track.artist || '[No Artist]') + '
' +
- (this.track.title || '[No Title]'),
- image: {
- icon: 'play',
- }
- });
- },
-
- onMusicStop: function(event) {
- this.status.state = 'stop';
- Vue.set(this.status, 'elapsed', 0);
- this._parseStatus(event.status);
- this._parseTrack(event.track);
- this.stopTimer();
- },
-
- onMusicPlay: function(event) {
- this.status.state = 'play';
- this._parseStatus(event.status);
- this._parseTrack(event.track);
- this.startTimer();
- },
-
- onMusicPause: function(event) {
- this.status.state = 'pause';
- this._parseStatus(event.status);
- this._parseTrack(event.track);
-
- Vue.set(this.syncTime, 'timestamp', new Date());
- Vue.set(this.syncTime, 'elapsed', this.status.elapsed);
- },
-
- onSeekChange: function(event) {
- if (event.position != null)
- Vue.set(this.status, 'elapsed', parseFloat(event.position));
- if (event.status)
- this._parseStatus(event.status);
- if (event.track)
- this._parseTrack(event.track);
-
- Vue.set(this.syncTime, 'timestamp', new Date());
- Vue.set(this.syncTime, 'elapsed', this.status.elapsed);
- },
-
- onPlaylistChange: async function(event) {
- if (event.changes) {
- this.playlist = event.changes;
- } else {
- const playlist = await request('music.mpd.playlistinfo');
- this._parsePlaylist(playlist);
- }
- },
-
- onVolumeChange: function(event) {
- if (event.volume != null)
- this.status.volume = parseFloat(event.volume);
- if (event.status)
- this._parseStatus(event.status);
- if (event.track)
- this._parseTrack(event.track);
- },
-
- onRepeatChange: function(event) {
- this.status.repeat = event.state;
- },
-
- onRandomChange: function(event) {
- this.status.random = event.state;
- },
-
- onConsumeChange: function(event) {
- this.status.consume = event.state;
- },
-
- onSingleChange: function(event) {
- this.status.single = event.state;
- },
-
- startTimer: function() {
- if (this.timer != null) {
- this.stopTimer();
- }
-
- Vue.set(this.syncTime, 'timestamp', new Date());
- Vue.set(this.syncTime, 'elapsed', this.status.elapsed);
- this.timer = setInterval(this.timerFunc, 1000);
- },
-
- stopTimer: function() {
- if (this.timer == null) {
- clearInterval(this.timer);
- this.timer = null;
- }
- },
-
- timerFunc: function() {
- if (this.status.state !== 'play' || this.status.elapsed == null) {
- return;
- }
-
- Vue.set(this.status, 'elapsed', this.syncTime.elapsed +
- ((new Date()).getTime()/1000) - (this.syncTime.timestamp.getTime()/1000));
- },
-
- matchesPlaylistFilter: function(track) {
- if (this.playlistFilter.length === 0)
- return true;
-
- const filter = this.playlistFilter.split(' ').filter(_ => _.length > 0).map(_ => _.toLocaleLowerCase()).join(' ');
- return [track.artist || '', track.title || '', track.album || ''].join(' ').toLocaleLowerCase().indexOf(filter) >= 0;
- },
-
- matchesEditorFilter: function(track) {
- if (this.editorFilter.length === 0)
- return true;
-
- const filter = this.editorFilter.split(' ').filter(_ => _.length > 0).map(_ => _.toLocaleLowerCase()).join(' ');
- return [track.artist || '', track.title || '', track.album || ''].join(' ').toLocaleLowerCase().indexOf(filter) >= 0;
- },
-
- matchesBrowserFilter: function(item) {
- if (this.browserFilter.length === 0)
- return true;
-
- const filter = this.browserFilter.toLocaleLowerCase().split(' ').filter(_ => _.length > 0).join(' ');
- return (item.artist || '').concat(item.name).toLocaleLowerCase().indexOf(filter) >= 0;
- },
-
- matchesPlaylistAddFilter: function(item) {
- if (this.playlistAddFilter.length === 0)
- return true;
-
- const filter = this.playlistAddFilter.toLocaleLowerCase().split(' ').filter(_ => _.length > 0).join(' ');
- return (item.playlist || '').toLocaleLowerCase().indexOf(filter) >= 0;
- },
-
- onPlaylistItemClick: async function(track) {
- if (this.selectionMode.playlist) {
- if (track.pos in this.selectedPlaylistItems) {
- Vue.delete(this.selectedPlaylistItems, track.pos);
- } else {
- Vue.set(this.selectedPlaylistItems, track.pos, track);
- }
- } else if (this.moveMode.playlist) {
- var fromPos = Object.values(this.selectedPlaylistItems).map(_ => _.pos);
- var toPos = track.pos;
- this.moveMode.playlist = false;
-
- const promises = fromPos.map((pos,i) => this.move(pos, toPos+i, false));
- await Promise.all(promises);
- this.selectedPlaylistItems = {};
-
- const playlist = await request('music.mpd.playlistinfo');
- this._parsePlaylist(playlist);
- } else if (track.pos in this.selectedPlaylistItems) {
- Vue.delete(this.selectedPlaylistItems, track.pos);
- } else {
- this.selectedPlaylistItems = {};
- Vue.set(this.selectedPlaylistItems, track.pos, track);
- openDropdown(this.$refs.playlistDropdown.$el);
- }
- },
-
- onEditorItemClick: async function(track) {
- if (this.selectionMode.editor) {
- if (track.pos in this.selectedEditorItems) {
- Vue.delete(this.selectedEditorItems, track.pos);
- } else {
- Vue.set(this.selectedEditorItems, track.pos, track);
- }
- } else if (this.moveMode.editor) {
- var fromPos = Object.values(this.selectedEditorItems).map(_ => _.pos);
- var toPos = track.pos;
- this.moveMode.editor = false;
-
- const promises = fromPos.map((pos,i) => this.playlistmove(pos, toPos+i));
- await Promise.all(promises);
- await this.refreshSelectedPlaylist();
- } else if (track.pos in this.selectedEditorItems) {
- Vue.delete(this.selectedEditorItems, track.pos);
- } else {
- this.selectedEditorItems = {};
- Vue.set(this.selectedEditorItems, track.pos, track);
- openDropdown(this.$refs.editorDropdown.$el);
- }
- },
-
- onBrowserItemClick: function(item) {
- if (item.type === 'directory' && item.name === '..') {
- this.selectedBrowserItems = {};
- this.selectedBrowserItems[item.id] = item;
- this.cd();
- this.selectedBrowserItems = {};
- return;
- }
-
- if (this.selectionMode.browser) {
- if (item.id in this.selectedBrowserItems) {
- Vue.delete(this.selectedBrowserItems, item.id);
- } else {
- Vue.set(this.selectedBrowserItems, item.id, item);
- }
- } else if (item.id in this.selectedBrowserItems) {
- Vue.delete(this.selectedBrowserItems, item.id);
- } else {
- this.selectedBrowserItems = {};
- Vue.set(this.selectedBrowserItems, item.id, item);
- openDropdown(this.$refs.browserDropdown.$el);
- }
- },
-
- onPlaylistAddItemClick: function(playlist) {
- if (playlist.playlist in this.selectedPlaylistAddItems) {
- Vue.delete(this.selectedPlaylistAddItems, playlist.playlist);
- } else {
- Vue.set(this.selectedPlaylistAddItems, playlist.playlist, playlist);
- }
- },
-
- togglePlaylistSelectionMode: function() {
- if (this.selectionMode.playlist && Object.keys(this.selectedPlaylistItems).length) {
- openDropdown(this.$refs.playlistDropdown.$el);
- }
-
- this.selectionMode.playlist = !this.selectionMode.playlist;
- },
-
- playlistSelectAll: function() {
- this.selectedPlaylistItems = {};
- this.selectionMode.playlist = true;
-
- for (var track of this.playlist) {
- this.selectedPlaylistItems[track.pos] = track;
- }
-
- openDropdown(this.$refs.playlistDropdown.$el);
- },
-
- toggleBrowserSelectionMode: function() {
- if (this.selectionMode.browser && Object.keys(this.selectedBrowserItems).length) {
- openDropdown(this.$refs.browserDropdown.$el);
- }
-
- this.selectionMode.browser = !this.selectionMode.browser;
- },
-
- toggleEditorSelectionMode: function() {
- if (this.selectionMode.editor && Object.keys(this.selectedEditorItems).length) {
- openDropdown(this.$refs.editorDropdown.$el);
- }
-
- this.selectionMode.editor = !this.selectionMode.editor;
- },
-
- browserSelectAll: function() {
- this.selectedBrowserItems = {};
- this.selectionMode.browser = true;
-
- for (var item of this.browserItems) {
- if (item.type !== 'directory' && item.name !== '..') {
- this.selectedBrowserItems[item.id] = item;
- }
- }
-
- openDropdown(this.$refs.browserDropdown.$el);
- },
-
- editorSelectAll: function() {
- this.selectedEditorItems = {};
- this.selectionMode.editor = true;
-
- for (var item of this.selectedPlaylist.items) {
- Vue.set(this.selectedEditorItems, item.pos, item);
- }
-
- openDropdown(this.$refs.editorDropdown.$el);
- },
-
- scrollToActiveTrack: function() {
- if (this.$refs.activePlaylistTrack && this.$refs.activePlaylistTrack.length) {
- this.$refs.activePlaylistTrack[0].$el.scrollIntoView({behavior: 'smooth'});
- } else {
- return;
- }
- },
-
- addToPlaylistPrompt: async function() {
- var resource = prompt('Path or URI of the resource to add');
- if (!resource.length) {
- return;
- }
-
- await this.add(resource);
- },
-
- addToPlaylistEditorPrompt: async function() {
- var resource = prompt('Path or URI of the resource to add');
- if (!resource.length) {
- return;
- }
-
- await request('music.mpd.playlistadd', {name: this.selectedPlaylist.name, uri: resource});
- await this.refreshSelectedPlaylist();
- },
-
- savePlaylistPrompt: async function() {
- var name = prompt('Playlist name');
- if (!name.length) {
- return;
- }
-
- let status = await request('music.mpd.save', {name: name});
- this._parseStatus(status);
-
- let items = await request('music.mpd.lsinfo', {uri: this.browserPath.join('/')});
- this._parseBrowserItems(items);
- },
-
- refreshSelectedPlaylist: async function() {
- if (!this.selectedPlaylist.name) {
- return;
- }
-
- let items = (await this.listplaylistinfo(this.selectedPlaylist.name)).map((_, i) => {
- return { ..._, pos: i }
- });
-
- Vue.set(this.selectedPlaylist, 'items', items);
- },
- },
-
- created: function() {
- this.refresh();
- registerEventHandler(this.onNewPlayingTrack, 'platypush.message.event.music.NewPlayingTrackEvent');
- registerEventHandler(this.onMusicStop, 'platypush.message.event.music.MusicStopEvent');
- registerEventHandler(this.onMusicPlay, 'platypush.message.event.music.MusicPlayEvent');
- registerEventHandler(this.onMusicPause, 'platypush.message.event.music.MusicPauseEvent');
- registerEventHandler(this.onSeekChange, 'platypush.message.event.music.SeekChangeEvent');
- registerEventHandler(this.onPlaylistChange, 'platypush.message.event.music.PlaylistChangeEvent');
- registerEventHandler(this.onVolumeChange, 'platypush.message.event.music.VolumeChangeEvent');
- registerEventHandler(this.onRepeatChange, 'platypush.message.event.music.PlaybackRepeatModeChangeEvent');
- registerEventHandler(this.onRandomChange, 'platypush.message.event.music.PlaybackRandomModeChangeEvent');
- registerEventHandler(this.onConsumeChange, 'platypush.message.event.music.PlaybackConsumeModeChangeEvent');
- registerEventHandler(this.onSingleChange, 'platypush.message.event.music.PlaybackSingleModeChangeEvent');
- },
-
- mounted: function() {
- this.adjustLayout();
- this.scrollToActiveTrack();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/playlist.js b/platypush/backend/http/static/js/plugins/music.mpd/playlist.js
deleted file mode 100644
index 99a0eca7..00000000
--- a/platypush/backend/http/static/js/plugins/music.mpd/playlist.js
+++ /dev/null
@@ -1,26 +0,0 @@
-Vue.component('music-mpd-playlist-item', {
- template: '#tmpl-music-mpd-playlist-item',
- mixins: [utils],
- props: {
- track: {
- type: Object,
- default: {},
- },
-
- selected: {
- type: Boolean,
- default: false,
- },
-
- active: {
- type: Boolean,
- default: false,
- },
-
- move: {
- type: Boolean,
- default: false,
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/search.js b/platypush/backend/http/static/js/plugins/music.mpd/search.js
deleted file mode 100644
index dcf8a789..00000000
--- a/platypush/backend/http/static/js/plugins/music.mpd/search.js
+++ /dev/null
@@ -1,229 +0,0 @@
-Vue.component('music-mpd-search', {
- template: '#tmpl-music-mpd-search',
- props: ['mpd'],
- data: function() {
- return {
- visible: false,
- showResults: false,
- results: [],
- filter: '',
- selectionMode: false,
- selectedItems: {},
-
- query: {
- any: '',
- file: '',
- artist: '',
- title: '',
- album: '',
- },
- };
- },
-
- computed: {
- dropdownItems: function() {
- var self = this;
- var items = [];
-
- if (Object.keys(this.selectedItems).length === 1) {
- items.push(
- {
- text: 'Play',
- icon: 'play',
- click: async function() {
- const item = Object.values(self.selectedItems)[0];
- await self.mpd.add(item.file, position=0);
- await self.mpd.playpos(0);
- self.selectedItems = {};
- },
- },
- {
- text: 'Replace and play',
- icon: 'play',
- click: async function() {
- await self.mpd.clear();
-
- const item = Object.values(self.selectedItems)[0];
- await self.mpd.add(item.file, position=0);
- await self.mpd.playpos(0);
- self.selectedItems = {};
- },
- }
- );
- }
-
- items.push(
- {
- text: 'Add to queue',
- icon: 'plus',
- click: async function() {
- const items = Object.values(self.selectedItems);
- const promises = items.map(item => self.mpd.add(item.file));
-
- await Promise.all(promises);
- self.selectedItems = {};
- },
- },
- );
-
- if (Object.values(this.selectedItems).filter(_ => _.time != null).length === Object.values(this.selectedItems).length) {
- items.push(
- {
- text: 'Add to playlist',
- icon: 'list',
- click: async function() {
- self.mpd.addToPlaylistItems = Object.values(self.selectedItems).map(_ => _.file);
- self.mpd.modalVisible.playlistAdd = true;
- await self.mpd.listplaylists();
- self.selectedItems = {};
- },
- },
- );
- }
-
- if (Object.keys(this.selectedItems).length === 1) {
- const item = Object.values(this.selectedItems)[0];
-
- if (item.artist && item.artist.length) {
- items.push({
- text: 'View artist',
- icon: 'user',
- click: async function() {
- await self.mpd.searchArtist(item);
- self.selectedItems = {};
- }
- });
- }
-
- if (item.album && item.album.length) {
- items.push({
- text: 'View album',
- icon: 'compact-disc',
- click: async function() {
- await self.mpd.searchAlbum(item);
- self.selectedItems = {};
- },
- });
- }
-
- items.push({
- text: 'View info',
- icon: 'info',
- click: function() {
- self.$emit('info', item);
- },
- });
- }
-
- return items;
- },
- },
-
- methods: {
- search: async function() {
- const filter = Object.keys(this.query).reduce((query, key) => {
- if (this.query[key].length) {
- query[key] = this.query[key];
- }
-
- return query;
- }, {});
-
- this.results = [];
- var results = await request('music.mpd.search', {filter: filter});
-
- this.results = results.sort((a,b) => {
- if (a.artist != b.artist)
- return (a.artist || '').localeCompare(b.artist || '');
- if (a.album != b.album)
- return (a.album || '').localeCompare(b.album || '');
- if (a.track != b.track)
- return parseInt(a.track || 0) > parseInt(b.track || 0);
- if (a.title != b.title)
- return (a.title || '').localeCompare(b.title || '');
- if (a.file != b.file)
- return (a.file || '').localeCompare(b.file || '');
- return 0;
- });
-
- this.showResults = true;
- },
-
- matchesFilter: function(item) {
- if (this.filter.length === 0)
- return true;
-
- const filter = this.filter.split(' ').filter(_ => _.length > 0).map(_ => _.toLocaleLowerCase()).join(' ');
- return [item.file || '', item.artist || '', item.title || '', item.album || ''].join(' ').toLocaleLowerCase().indexOf(filter) >= 0;
- },
-
- onItemClick: function(item) {
- if (this.selectionMode) {
- if (item.file in this.selectedItems) {
- Vue.delete(this.selectedItems, item.file);
- } else {
- Vue.set(this.selectedItems, item.file, item);
- }
- } else if (item.file in this.selectedItems) {
- Vue.delete(this.selectedItems, item.file);
- } else {
- this.selectedItems = {};
- Vue.set(this.selectedItems, item.file, item);
- openDropdown(this.$refs.dropdown.$el);
- }
- },
-
- toggleSelectionMode: function() {
- if (this.selectionMode && Object.keys(this.selectedItems).length) {
- openDropdown(this.$refs.dropdown.$el);
- }
-
- this.selectionMode = !this.selectionMode;
- },
-
- selectAll: function() {
- this.selectedItems = {};
- this.selectionMode = true;
-
- for (var item of this.results) {
- this.selectedItems[item.file] = item;
- }
-
- openDropdown(this.$refs.dropdown.$el);
- },
-
- resetQuery: function() {
- this.filter = '';
- for (const attr of Object.keys(this.query)) {
- this.query[attr] = '';
- }
- },
-
- resetForm: function() {
- this.resetQuery();
- this.showResults = false;
- var self = this;
-
- setTimeout(() => {
- self.$refs.form.querySelector('input[type=text]:first-child').focus()
- }, 100)
- },
- },
-});
-
-Vue.component('music-mpd-search-item', {
- template: '#tmpl-music-mpd-search-item',
- mixins: [utils],
- props: {
- item: {
- type: Object,
- default: {},
- },
-
- selected: {
- type: Boolean,
- default: false,
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/utils.js b/platypush/backend/http/static/js/plugins/music.mpd/utils.js
deleted file mode 100644
index 5fe84b74..00000000
--- a/platypush/backend/http/static/js/plugins/music.mpd/utils.js
+++ /dev/null
@@ -1,26 +0,0 @@
-var utils = {
- methods: {
- convertTime: function(time) {
- time = parseFloat(time); // Normalize strings
- var t = {};
- t.h = '' + parseInt(time/3600);
- t.m = '' + parseInt(time/60 - t.h*60);
- t.s = '' + parseInt(time - (t.h*3600 + t.m*60));
-
- for (var attr of ['m','s']) {
- if (parseInt(t[attr]) < 10) {
- t[attr] = '0' + t[attr];
- }
- }
-
- var ret = [];
- if (parseInt(t.h)) {
- ret.push(t.h);
- }
-
- ret.push(t.m, t.s);
- return ret.join(':');
- },
- },
-};
-
diff --git a/platypush/backend/http/static/js/plugins/music.snapcast/client.js b/platypush/backend/http/static/js/plugins/music.snapcast/client.js
deleted file mode 100644
index 105e118e..00000000
--- a/platypush/backend/http/static/js/plugins/music.snapcast/client.js
+++ /dev/null
@@ -1,66 +0,0 @@
-Vue.component('music-snapcast-client', {
- template: '#tmpl-music-snapcast-client',
- props: {
- config: { type: Object },
- connected: { type: Boolean },
- host: { type: Object },
- id: { type: String },
- groupId: { type: String },
- lastSeen: { type: Object },
- snapclient: { type: Object },
- server: { type: Object },
- bus: { type: Object },
- },
-
- methods: {
- muteToggled: function(event) {
- this.bus.$emit('client-mute-changed', {
- id: this.id,
- server: this.server,
- value: !event.value,
- });
- },
-
- volumeChanged: function(event) {
- this.bus.$emit('client-volume-changed', {
- id: this.id,
- server: this.server,
- value: parseInt(event.target.value),
- });
- },
- },
-});
-
-Vue.component('music-snapcast-client-info', {
- props: {
- info: { type: Object }
- },
-
- data: function() {
- return {
- loading: false,
- };
- },
-
- methods: {
- deleteClient: async function(event) {
- if (!confirm('Are you SURE that you want to remove this client?')) {
- return;
- }
-
- this.loading = true;
- await request('music.snapcast.delete_client', {
- client: this.info.id,
- host: this.info.server.host.name,
- port: this.info.server.host.port,
- });
-
- this.loading = false;
- createNotification({
- text: 'Snapcast client successfully removed',
- image: { icon: 'check' }
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.snapcast/group.js b/platypush/backend/http/static/js/plugins/music.snapcast/group.js
deleted file mode 100644
index 626bd5fc..00000000
--- a/platypush/backend/http/static/js/plugins/music.snapcast/group.js
+++ /dev/null
@@ -1,80 +0,0 @@
-Vue.component('music-snapcast-group', {
- template: '#tmpl-music-snapcast-group',
- props: {
- id: { type: String },
- clients: { type: Object },
- muted: { type: Boolean },
- name: { type: String },
- stream: { type: Object },
- server: { type: Object },
- bus: { type: Object },
- },
-
- methods: {
- muteToggled: function(event) {
- this.bus.$emit('group-mute-changed', {
- id: this.id,
- server: this.server,
- value: !event.value,
- });
- },
- },
-});
-
-Vue.component('music-snapcast-group-info', {
- props: {
- info: { type: Object }
- },
-
- data: function() {
- return {
- loading: false,
- };
- },
-
- methods: {
- onClientUpdate: async function(event) {
- var clients = this.$refs.groupClients
- .map(row => row.querySelector('input[type=checkbox]:checked'))
- .filter(_ => _ != null)
- .map(input => input.value);
-
- this.loading = true;
- await request('music.snapcast.group_set_clients', {
- clients: clients,
- group: this.info.group.id,
- host: this.info.server.host.name,
- port: this.info.server.host.port,
- });
-
- this.loading = false;
- createNotification({
- text: 'Snapcast group successfully updated',
- image: {
- icon: 'check',
- }
- });
- },
-
- onStreamUpdate: async function(event) {
- this.loading = true;
- await request('music.snapcast.group_set_stream', {
- stream_id: event.target.value,
- group: this.info.group.id,
- host: this.info.server.host.name,
- port: this.info.server.host.port,
- });
-
- this.loading = false;
- this.info.group.stream_id = event.target.value;
-
- createNotification({
- text: 'Snapcast stream successfully updated',
- image: {
- icon: 'check',
- }
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.snapcast/host.js b/platypush/backend/http/static/js/plugins/music.snapcast/host.js
deleted file mode 100644
index e27ab147..00000000
--- a/platypush/backend/http/static/js/plugins/music.snapcast/host.js
+++ /dev/null
@@ -1,22 +0,0 @@
-Vue.component('music-snapcast-host', {
- template: '#tmpl-music-snapcast-host',
- props: {
- groups: { type: Object },
- server: { type: Object },
- streams: { type: Object },
- bus: { type: Object },
- },
-
- data: function() {
- return {
- collapsed: false,
- };
- },
-});
-
-Vue.component('music-snapcast-host-info', {
- props: {
- info: { type: Object }
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/music.snapcast/index.js b/platypush/backend/http/static/js/plugins/music.snapcast/index.js
deleted file mode 100644
index 812e3d9b..00000000
--- a/platypush/backend/http/static/js/plugins/music.snapcast/index.js
+++ /dev/null
@@ -1,195 +0,0 @@
-Vue.component('music-snapcast', {
- template: '#tmpl-music-snapcast',
- props: ['config'],
- data: function() {
- return {
- hosts: {},
- ports: {},
- modal: {
- host: {
- visible: false,
- info: {},
- },
- group: {
- visible: false,
- info: {},
- },
- client: {
- visible: false,
- info: {},
- },
- },
-
- bus: new Vue({}),
- };
- },
-
- methods: {
- _parseServerStatus: function(status) {
- status.server.host.port = this.ports[status.server.host.name];
- var groups = {};
-
- for (const group of status.groups) {
- var clients = {};
- for (const client of group.clients) {
- clients[client.id] = client;
- }
-
- group.clients = clients;
- groups[group.id] = group;
- }
-
- status.groups = groups;
- var streams = {};
-
- for (const stream of status.streams) {
- streams[stream.id] = stream;
- }
-
- status.streams = streams;
- Vue.set(this.hosts, status.server.host.name, status);
- },
-
- refresh: async function() {
- let hosts = await request('music.snapcast.get_backend_hosts');
- let promises = Object.keys(hosts).map(
- (host) => request('music.snapcast.status', {host: host, port: hosts[host]})
- );
-
- let statuses = await Promise.all(promises);
- this.hosts = {};
-
- for (const status of statuses) {
- this.ports[status.server.host.name] = hosts[status.server.host.name];
- this._parseServerStatus(status);
- }
- },
-
- onClientUpdate: function(event) {
- for (const groupId of Object.keys(this.hosts[event.host].groups)) {
- if (event.client.id in this.hosts[event.host].groups[groupId].clients) {
- this.hosts[event.host].groups[groupId].clients[event.client.id] = event.client;
- }
- }
- },
-
- onGroupStreamChange: function(event) {
- this.hosts[event.host].groups[event.group].stream_id = event.stream;
- },
-
- onServerUpdate: function(event) {
- this._parseServerStatus(event.server);
- },
-
- onStreamUpdate: function(event) {
- this.hosts[event.host].streams[event.stream.id] = event.stream;
- },
-
- onClientVolumeChange: function(event) {
- for (const groupId of Object.keys(this.hosts[event.host].groups)) {
- if (event.client in this.hosts[event.host].groups[groupId].clients) {
- if (event.volume != null) {
- this.hosts[event.host].groups[groupId].clients[event.client].config.volume.percent = event.volume;
- }
-
- if (event.muted != null) {
- this.hosts[event.host].groups[groupId].clients[event.client].config.volume.muted = event.muted;
- }
- }
- }
- },
-
- onGroupMuteChange: function(event) {
- this.hosts[event.host].groups[event.group].muted = event.muted;
- },
-
- modalShow: function(event) {
- switch(event.type) {
- case 'host':
- this.modal[event.type].info = this.hosts[event.host];
- break;
- case 'group':
- this.modal[event.type].info.server = this.hosts[event.host].server;
- this.modal[event.type].info.group = this.hosts[event.host].groups[event.group];
- this.modal[event.type].info.streams = this.hosts[event.host].streams;
- this.modal[event.type].info.clients = {};
-
- for (const group of Object.values(this.hosts[event.host].groups)) {
- for (const client of Object.values(group.clients)) {
- this.modal[event.type].info.clients[client.id] = client;
- }
- }
-
- break;
- case 'client':
- this.modal[event.type].info = this.hosts[event.host].groups[event.group].clients[event.client];
- this.modal[event.type].info.server = this.hosts[event.host].server;
- break;
- }
-
- this.modal[event.type].visible = true;
- },
-
- groupMute: async function(event) {
- await request('music.snapcast.mute', {
- group: event.id,
- host: event.server.ip || event.server.name,
- port: event.server.port,
- mute: event.value,
- });
-
- this.hosts[event.server.name].groups[event.id].muted = event.value;
- },
-
- clientMute: async function(event) {
- await request('music.snapcast.mute', {
- client: event.id,
- host: event.server.ip || event.server.name,
- port: event.server.port,
- mute: event.value,
- });
-
- for (const groupId of Object.keys(this.hosts[event.server.name].groups)) {
- if (event.id in this.hosts[event.server.name].groups[groupId].clients) {
- this.hosts[event.server.name].groups[groupId].clients[event.id].config.volume.muted = event.value;
- }
- }
- },
-
- clientSetVolume: async function(event) {
- await request('music.snapcast.volume', {
- client: event.id,
- host: event.server.ip || event.server.name,
- port: event.server.port,
- volume: event.value,
- });
-
- for (const groupId of Object.keys(this.hosts[event.server.name].groups)) {
- if (event.id in this.hosts[event.server.name].groups[groupId].clients) {
- this.hosts[event.server.name].groups[groupId].clients[event.id].config.volume.percent = event.value;
- }
- }
- },
- },
-
- created: function() {
- this.refresh();
-
- registerEventHandler(this.onClientUpdate,
- 'platypush.message.event.music.snapcast.ClientConnectedEvent',
- 'platypush.message.event.music.snapcast.ClientDisconnectedEvent',
- 'platypush.message.event.music.snapcast.ClientNameChangeEvent');
-
- registerEventHandler(this.onGroupStreamChange, 'platypush.message.event.music.snapcast.GroupStreamChangeEvent');
- registerEventHandler(this.onServerUpdate, 'platypush.message.event.music.snapcast.ServerUpdateEvent');
- registerEventHandler(this.onStreamUpdate, 'platypush.message.event.music.snapcast.StreamUpdateEvent');
- registerEventHandler(this.onClientVolumeChange, 'platypush.message.event.music.snapcast.ClientVolumeChangeEvent');
- registerEventHandler(this.onGroupMuteChange, 'platypush.message.event.music.snapcast.GroupMuteChangeEvent');
-
- this.bus.$on('group-mute-changed', this.groupMute);
- this.bus.$on('client-mute-changed', this.clientMute);
- this.bus.$on('client-volume-changed', this.clientSetVolume);
- this.bus.$on('modal-show', this.modalShow);
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/pushbullet/index.js b/platypush/backend/http/static/js/plugins/pushbullet/index.js
deleted file mode 100644
index 323a686a..00000000
--- a/platypush/backend/http/static/js/plugins/pushbullet/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const Pushbullet = {
- onMsg: function(event) {
- if (event.push_type === 'mirror') {
- createNotification({
- title: event.title,
- text: event.body,
- image: {
- src: event.icon ? 'data:image/png;base64, ' + event.icon : undefined,
- icon: event.icon ? undefined : 'bell',
- },
- });
- }
- },
-
- registerHandlers: function() {
- registerEventHandler(this.onMsg, 'platypush.message.event.pushbullet.PushbulletEvent');
- },
-};
-
-onReady(() => {
- Pushbullet.registerHandlers();
-});
-
diff --git a/platypush/backend/http/static/js/plugins/sensors/index.js b/platypush/backend/http/static/js/plugins/sensors/index.js
deleted file mode 100644
index 2cfbb263..00000000
--- a/platypush/backend/http/static/js/plugins/sensors/index.js
+++ /dev/null
@@ -1,76 +0,0 @@
-Vue.component('sensor-metric', {
- template: '#tmpl-sensor-metric',
- props: {
- bus: {
- type: Object,
- },
-
- name: {
- type: String,
- },
-
- value: {
- type: [String, Number, Object, Boolean, Array],
- },
- },
-});
-
-Vue.component('sensors', {
- template: '#tmpl-sensors',
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- metrics: {},
- };
- },
-
- methods: {
- refresh: async function() {
- if (!this.config.plugins) {
- console.warn('Please specify a list of sensor plugins in your sensors section configuration');
- return;
- }
-
- const promises = this.config.plugins.map(plugin => {
- return new Promise((resolve, reject) => {
- if (plugin === 'serial') {
- // Don't refresh reads over the serial port,
- // as it might mess up any data transfer already in progress
- resolve();
- return;
- }
-
- request(plugin + '.get_measurement').then(metrics => {
- resolve(metrics);
- });
- });
- });
-
- Vue.set(this, 'metrics', (await Promise.all(promises)).reduce((obj, metrics) => {
- if (!metrics)
- return obj;
-
- for (const [name, value] of Object.entries(metrics)) {
- obj[name] = value;
- }
-
- return obj;
- }, {}));
- },
-
- onSensorEvent: function(event) {
- const data = event.data;
- for (const [name, value] of Object.entries(data)) {
- Vue.set(this.metrics, name, value);
- }
- },
- },
-
- mounted: function() {
- registerEventHandler(this.onSensorEvent, 'platypush.message.event.sensor.SensorDataChangeEvent');
- this.refresh();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/sound/index.js b/platypush/backend/http/static/js/plugins/sound/index.js
deleted file mode 100644
index 5e2230b4..00000000
--- a/platypush/backend/http/static/js/plugins/sound/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-Vue.component('sound', {
- template: '#tmpl-sound',
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- recording: false,
- };
- },
-
- methods: {
- startRecording: function() {
- this.recording = true;
- },
-
- stopRecording: async function() {
- this.recording = false;
- await request('sound.stop_recording')
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/switches/index.js b/platypush/backend/http/static/js/plugins/switches/index.js
deleted file mode 100644
index f2a68429..00000000
--- a/platypush/backend/http/static/js/plugins/switches/index.js
+++ /dev/null
@@ -1,99 +0,0 @@
-const SwitchDevice = Vue.component('switch-device', {
- template: '#tmpl-switch-device',
- props: {
- bus: {
- type: Object,
- },
-
- device: {
- type: Object,
- default: () => {},
- },
- },
-});
-
-const SwitchType = Vue.component('switch-type', {
- template: '#tmpl-switch-type',
- props: {
- bus: {
- type: Object,
- },
-
- name: {
- type: String,
- },
-
- devices: {
- type: Object,
- default: () => {},
- },
- },
-});
-
-Vue.component('switches', {
- template: '#tmpl-switches',
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- plugins: {},
- };
- },
-
- methods: {
- refresh: async function() {
- if (!this.config.plugins) {
- console.warn('Please specify a list of switch plugins in your switch section configuration');
- return;
- }
-
- const promises = this.config.plugins.map(plugin => {
- return new Promise((resolve, reject) => {
- request(plugin + '.status').then(status => {
- const ret = {};
- ret[plugin] = status;
- resolve(ret);
- });
- });
- });
-
- const statuses = (await Promise.all(promises)).reduce((obj, status) => {
- obj[Object.keys(status)[0]] = Object.values(status)[0].reduce((obj2, device) => {
- device.type = Object.keys(status)[0];
- obj2[device.id] = device;
- return obj2;
- }, {});
-
- return obj;
- }, {});
-
- for (const [name, status] of Object.entries(statuses)) {
- this.plugins[name] = status;
-
- const switchType = new SwitchType();
- switchType.bus = this.bus;
- switchType.name = name;
- switchType.devices = this.plugins[name];
-
- switchType.$mount();
- this.$refs.root.appendChild(switchType.$el);
- }
- },
-
- toggle: async function(type, device) {
- let status = await request(type + '.toggle', {device: device});
- this.plugins[type][status.id].on = status.on;
- },
- },
-
- mounted: function() {
- const self = this;
- this.refresh();
-
- this.bus.$on('switch-toggled', (evt) => {
- self.toggle(evt.type, evt.device);
- });
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/tts.google/index.js b/platypush/backend/http/static/js/plugins/tts.google/index.js
deleted file mode 100644
index b9a16866..00000000
--- a/platypush/backend/http/static/js/plugins/tts.google/index.js
+++ /dev/null
@@ -1,26 +0,0 @@
-Vue.component('tts-google', {
- template: '#tmpl-tts-google',
- data: function() {
- return {
- talking: false,
- };
- },
-
- methods: {
- talk: async function(event) {
- event.preventDefault();
-
- const args = [...event.target.querySelectorAll('input')].reduce((obj, el) => {
- if (el.value.length)
- obj[el.name] = el.value;
- return obj;
- }, {});
-
- this.talking = true;
- await request('tts.google.say', args);
- this.talking = false;
- },
- },
-});
-
-
diff --git a/platypush/backend/http/static/js/plugins/tts/index.js b/platypush/backend/http/static/js/plugins/tts/index.js
deleted file mode 100644
index 6a8ee794..00000000
--- a/platypush/backend/http/static/js/plugins/tts/index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-Vue.component('tts', {
- template: '#tmpl-tts',
- data: function() {
- return {
- talking: false,
- };
- },
-
- methods: {
- talk: async function(event) {
- event.preventDefault();
-
- const args = [...event.target.querySelectorAll('input')].reduce((obj, el) => {
- if (el.value.length)
- obj[el.name] = el.value;
- return obj;
- }, {});
-
- this.talking = true;
- await request('tts.say', args);
- this.talking = false;
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/tv.samsung.ws/index.js b/platypush/backend/http/static/js/plugins/tv.samsung.ws/index.js
deleted file mode 100644
index b4718d01..00000000
--- a/platypush/backend/http/static/js/plugins/tv.samsung.ws/index.js
+++ /dev/null
@@ -1,115 +0,0 @@
-Vue.component('tv-samsung-ws', {
- template: '#tmpl-tv-samsung-ws',
- data: function() {
- return {};
- },
-
- methods: {
- up: async function() {
- await request('tv.samsung.ws.up');
- },
-
- down: async function() {
- await request('tv.samsung.ws.down');
- },
-
- left: async function() {
- await request('tv.samsung.ws.left');
- },
-
- right: async function() {
- await request('tv.samsung.ws.right');
- },
-
- enter: async function() {
- await request('tv.samsung.ws.enter');
- },
-
- power: async function() {
- await request('tv.samsung.ws.power');
- },
-
- red: async function() {
- await request('tv.samsung.ws.red');
- },
-
- yellow: async function() {
- await request('tv.samsung.ws.yellow');
- },
-
- green: async function() {
- await request('tv.samsung.ws.green');
- },
-
- blue: async function() {
- await request('tv.samsung.ws.blue');
- },
-
- volumeUp: async function() {
- await request('tv.samsung.ws.volume_up');
- },
-
- volumeDown: async function() {
- await request('tv.samsung.ws.volume_down');
- },
-
- channelUp: async function() {
- await request('tv.samsung.ws.channel_up');
- },
-
- channelDown: async function() {
- await request('tv.samsung.ws.channel_down');
- },
-
- mute: async function() {
- await request('tv.samsung.ws.mute');
- },
-
- home: async function() {
- await request('tv.samsung.ws.home');
- },
-
- back: async function() {
- await request('tv.samsung.ws.back');
- },
-
- menu: async function() {
- await request('tv.samsung.ws.menu');
- },
-
- info: async function() {
- await request('tv.samsung.ws.info');
- },
-
- source: async function() {
- await request('tv.samsung.ws.source');
- },
-
- tools: async function() {
- await request('tv.samsung.ws.tools');
- },
-
- browser: async function() {
- const url = prompt('URL to open').trim();
- if (!url.length) {
- return;
- }
-
- await request('tv.samsung.ws.open_browser', {'url': url});
- },
-
- channel: async function() {
- const ch = prompt('Channel number').trim();
- if (!ch.length) {
- return;
- }
-
- await request('tv.samsung.ws.channel', {channel: parseInt(ch)});
- },
-
- color: async function(event) {
- await request('tv.samsung.ws.' + event.target.value);
- }
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zigbee.mqtt/device.js b/platypush/backend/http/static/js/plugins/zigbee.mqtt/device.js
deleted file mode 100644
index 102bfae8..00000000
--- a/platypush/backend/http/static/js/plugins/zigbee.mqtt/device.js
+++ /dev/null
@@ -1,127 +0,0 @@
-Vue.component('zigbee-device', {
- template: '#tmpl-zigbee-device',
- props: ['device','bus','selected'],
- data: function() {
- return {
- newPropertyName: '',
- editMode: {
- name: false,
- },
- };
- },
-
- methods: {
- onDeviceClicked: function() {
- this.bus.$emit('deviceClicked', {
- deviceId: this.device.friendly_name,
- });
- },
-
- setValue: async function(event) {
- let name = undefined;
- if (this.newPropertyName && this.newPropertyName.length) {
- name = this.newPropertyName;
- } else {
- name = event.event
- ? event.event.target.parentElement.dataset.name
- : event.target.dataset.name;
- }
-
- if (!name || !name.length) {
- return;
- }
-
- const target = event.event
- ? event.event.target.parentElement.querySelector('input')
- : event.target;
-
- const value = target.getAttribute('type') === 'checkbox'
- ? (target.checked ? 'OFF' : 'ON')
- : target.value;
-
- await request('zigbee.mqtt.device_set', {
- device: this.device.friendly_name,
- property: name,
- value: value,
- });
-
- if (this.newPropertyName && this.newPropertyName.length) {
- this.newPropertyName = '';
- }
-
- this.bus.$emit('refreshDevices');
- },
-
- removeDevice: async function(force=false) {
- if (!confirm('Are you sure that you want to remove this device?')) {
- return;
- }
-
- await request('zigbee.mqtt.device_remove', {
- device: this.device.friendly_name,
- force: force,
- });
-
- this.bus.$emit('refreshDevices');
- },
-
- banDevice: async function() {
- if (!confirm('Are you sure that you want to ban this device?')) {
- return;
- }
-
- await request('zigbee.mqtt.device_ban', {
- device: this.device.friendly_name,
- });
-
- this.bus.$emit('refreshDevices');
- },
-
- whitelistDevice: async function() {
- if (!confirm('Are you sure that you want to whitelist this device? Note: ALL the other non-whitelisted ' +
- 'devices will be removed from the network')) {
- return;
- }
-
- await request('zigbee.mqtt.device_whitelist', {
- device: this.device.friendly_name,
- });
-
- this.bus.$emit('refreshDevices');
- },
-
- disableForm: function(form) {
- form.querySelector('input,button').readOnly = true;
- },
-
- enableForm: function(form) {
- form.querySelector('input,button').readOnly = false;
- },
-
- onEditMode: function(mode) {
- Vue.set(this.editMode, mode, true);
- const form = this.$refs[mode + 'Form'];
- const input = form.querySelector('input[type=text]');
-
- setTimeout(() => {
- input.focus();
- input.select();
- }, 10);
- },
-
- editName: async function(event) {
- this.disableForm(event.target);
- const name = event.target.querySelector('input[name=name]').value;
-
- await request('zigbee.mqtt.device_rename', {
- device: this.device.friendly_name,
- name: name,
- });
-
- this.editMode.name = false;
- this.enableForm(event.target);
- this.bus.$emit('refreshDevices');
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zigbee.mqtt/group.js b/platypush/backend/http/static/js/plugins/zigbee.mqtt/group.js
deleted file mode 100644
index 26f90257..00000000
--- a/platypush/backend/http/static/js/plugins/zigbee.mqtt/group.js
+++ /dev/null
@@ -1,149 +0,0 @@
-Vue.component('zigbee-group', {
- template: '#tmpl-zigbee-group',
- props: ['group','bus','selected'],
- data: function() {
- return {
- properties: {},
- };
- },
-
- methods: {
- anyOn: async function() {
- for (const dev of this.group.devices) {
- const params = await request('zigbee.mqtt.device_get', {device: dev.friendly_name});
- if (params.state === 'ON') {
- return true;
- }
- }
-
- return false;
- },
-
- allOn: async function() {
- for (const dev of this.group.devices) {
- const params = await request('zigbee.mqtt.device_get', {device: dev.friendly_name});
- if (params.state === 'OFF') {
- return false;
- }
- }
-
- return true;
- },
-
- refreshProperties: async function() {
- const props = {};
-
- for (const dev of this.group.devices) {
- const params = await request('zigbee.mqtt.device_get', {device: dev.friendly_name});
- for (const [name, value] of Object.entries(params)) {
- if (name === 'linkquality') {
- continue;
- }
-
- if (name in props) {
- props[name].push(value);
- } else {
- props[name] = [value];
- }
- }
- }
-
- for (const [name, values] of Object.entries(props)) {
- if (name === 'state') {
- props[name] = values.filter((value) => value === 'ON').length > 0;
- } else if (!isNaN(values[0])) {
- props[name] = values.reduce((sum, value) => sum + value, 0) / values.length;
- } else {
- props[name] = values[0];
- }
- }
-
- this.properties = props;
- },
-
- onGroupClicked: function() {
- this.bus.$emit('groupClicked', {
- groupId: this.group.id,
- });
- },
-
- setValue: async function(event) {
- const name = event.target.dataset.name;
- if (!name || !name.length) {
- return;
- }
-
- await request('zigbee.mqtt.group_set', {
- group: this.group.friendly_name,
- property: name,
- value: event.target.value,
- });
-
- this.bus.$emit('refreshDevices');
- },
-
- toggleState: async function() {
- const state = (await this.anyOn()) ? 'OFF' : 'ON';
- await request('zigbee.mqtt.group_set', {
- group: this.group.friendly_name,
- property: 'state',
- value: state,
- });
-
- this.bus.$emit('refreshDevices');
- },
-
- renameGroup: async function() {
- const name = prompt('New name', this.group.friendly_name);
- if (!name || !name.length || name === this.group.friendly_name) {
- return;
- }
-
- this.commandRunning = true;
- await request('zigbee.mqtt.group_rename', {
- name: name,
- group: this.group.friendly_name,
- });
-
- this.commandRunning = false;
- const self = this;
-
- setTimeout(() => {
- self.bus.$emit('refreshGroups');
- }, 100);
- },
-
- removeGroup: async function() {
- if (!confirm('Are you sure that you want to delete this group?')) {
- return;
- }
-
- this.commandRunning = true;
- await request('zigbee.mqtt.group_remove', {name: this.group.friendly_name});
- this.commandRunning = false;
- this.bus.$emit('refreshGroups');
- },
-
- removeFromGroup: async function(device) {
- if (!confirm('Are you sure that you want to remove this node from ' + this.group.label + '?')) {
- return;
- }
-
- await request('zigbee.mqtt.group_remove_device', {
- device: device,
- group: this.group.friendly_name,
- });
-
- this.bus.$emit('refreshGroups');
- },
- },
-
- created: function() {
- this.refreshProperties();
- this.bus.$on('refresh', this.refreshProperties);
- this.bus.$on('refreshDevices', this.refreshProperties);
- this.bus.$on('refreshGroups', this.refreshProperties);
- this.bus.$on('refreshProperties', this.refreshProperties);
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zigbee.mqtt/index.js b/platypush/backend/http/static/js/plugins/zigbee.mqtt/index.js
deleted file mode 100644
index df4c04c0..00000000
--- a/platypush/backend/http/static/js/plugins/zigbee.mqtt/index.js
+++ /dev/null
@@ -1,325 +0,0 @@
-Vue.component('zigbee-mqtt', {
- template: '#tmpl-zigbee-mqtt',
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- status: {},
- devices: {},
- groups: {},
- commandRunning: false,
- selected: {
- view: 'devices',
- deviceId: undefined,
- groupId: undefined,
- },
- loading: {
- status: false,
- devices: false,
- groups: false,
- },
- views: {
- devices: true,
- groups: true,
- },
- modal: {
- group: {
- visible: false,
- },
- },
- };
- },
-
- computed: {
- networkDropdownItems: function() {
- const self = this;
- return [
- {
- text: 'Start Network',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zigbee.mqtt.start_network');
- self.commandRunning = false;
- },
- },
-
- {
- text: 'Stop Network',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zigbee.mqtt.stop_network');
- self.commandRunning = false;
- },
- },
-
- {
- text: 'Permit Join',
- disabled: this.commandRunning,
- click: async function() {
- let seconds = prompt('Join allow period in seconds (type 0 for no time limits)', '60');
- if (!seconds) {
- return;
- }
-
- seconds = parseInt(seconds);
- self.commandRunning = true;
- await request('zigbee.mqtt.permit_join', {permit: true, timeout: seconds || null});
- self.commandRunning = false;
- },
- },
-
- {
- text: 'Reset',
- disabled: this.commandRunning,
- click: async function() {
- if (!confirm('Are you sure that you want to reset the device?')) {
- return;
- }
-
- await request('zigbee.mqtt.reset');
- },
- },
-
- {
- text: 'Factory Reset',
- disabled: this.commandRunning,
- classes: ['error'],
- click: async function() {
- if (!confirm('Are you sure that you want to do a device soft reset? ALL network information and custom firmware will be lost!!')) {
- return;
- }
-
- await request('zigbee.mqtt.factory_reset');
- },
- },
- ]
- },
-
- addToGroupDropdownItems: function() {
- const self = this;
- return Object.values(this.groups).filter((group) => {
- return !group.values || !group.values.length || !(this.selected.valueId in this.scene.values);
- }).map((group) => {
- return {
- text: group.name,
- disabled: this.commandRunning,
- click: async function () {
- if (!self.selected.valueId) {
- return;
- }
-
- self.commandRunning = true;
- await request('zwave.scene_add_value', {
- id_on_network: self.selected.valueId,
- scene_id: group.scene_id,
- });
-
- self.commandRunning = false;
- self.refresh();
- },
- };
- });
- },
- },
-
- methods: {
- refreshDevices: async function () {
- const self = this;
- this.loading.devices = true;
- this.devices = (await request('zigbee.mqtt.devices')).reduce((devices, device) => {
- if (device.friendly_name in self.devices) {
- device = {
- values: self.devices[device.friendly_name].values || {},
- ...self.devices[device.friendly_name],
- }
- }
-
- devices[device.friendly_name] = device;
- return devices;
- }, {});
-
- Object.values(this.devices).forEach((device) => {
- if (device.type === 'Coordinator') {
- return;
- }
-
- request('zigbee.mqtt.device_get', {device: device.friendly_name}).then((response) => {
- Vue.set(self.devices[device.friendly_name], 'values', response || {});
- });
- });
-
- this.loading.devices = false;
- },
-
- refreshGroups: async function () {
- this.loading.groups = true;
- this.groups = (await request('zigbee.mqtt.groups')).reduce((groups, group) => {
- groups[group.id] = group;
- return groups;
- }, {});
-
- this.loading.groups = false;
- },
-
- refresh: function () {
- this.refreshDevices();
- this.refreshGroups();
- this.bus.$emit('refreshProperties');
- },
-
- updateProperties: function(device, props) {
- Vue.set(this.devices[device], 'values', props);
- },
-
- addGroup: async function() {
- const name = prompt('Group name');
- if (!name) {
- return;
- }
-
- this.commandRunning = true;
- await request('zigbee.mqtt.group_add', {name: name});
- this.commandRunning = false;
- this.refreshGroups();
- },
-
- onViewChange: function(event) {
- Vue.set(this.selected, 'view', event.target.value);
- },
-
- onDeviceClicked: function(event) {
- Vue.set(this.selected, 'deviceId', event.deviceId === this.selected.deviceId ? undefined : event.deviceId);
- },
-
- onGroupClicked: function(event) {
- Vue.set(this.selected, 'groupId', event.groupId === this.selected.groupId ? undefined : event.groupId);
- },
-
- openNetworkCommandsDropdown: function() {
- openDropdown(this.$refs.networkCommandsDropdown);
- },
-
- openAddToGroupDropdown: function(event) {
- this.selected.valueId = event.valueId;
- openDropdown(this.$refs.addToGroupDropdown);
- },
-
- addToGroup: async function(device, group) {
- this.commandRunning = true;
- await request('zigbee.mqtt.group_add_device', {
- device: device,
- group: group,
- });
-
- this.commandRunning = false;
- const self = this;
-
- setTimeout(() => {
- self.refresh();
- self.bus.$emit('refreshProperties');
- }, 100)
- },
-
- removeNodeFromGroup: async function(event) {
- if (!confirm('Are you sure that you want to remove this value from the group?')) {
- return;
- }
-
- this.commandRunning = true;
- await request('zigbee.mqtt.group_remove_device', {
- group: event.group,
- device: event.device,
- });
-
- this.commandRunning = false;
- },
- },
-
- created: function() {
- const self = this;
- this.bus.$on('refresh', this.refresh);
- this.bus.$on('refreshDevices', this.refreshDevices);
- this.bus.$on('refreshGroups', this.refreshGroups);
- this.bus.$on('deviceClicked', this.onDeviceClicked);
- this.bus.$on('groupClicked', this.onGroupClicked);
- this.bus.$on('openAddToGroupModal', () => {self.modal.group.visible = true});
- this.bus.$on('openAddToGroupDropdown', this.openAddToGroupDropdown);
- this.bus.$on('removeFromGroup', this.removeNodeFromGroup);
-
- registerEventHandler(() => {
- createNotification({
- text: 'WARNING: The controller is now offline',
- error: true,
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttOfflineEvent');
-
- registerEventHandler(() => {
- createNotification({
- text: 'The controller is now online',
- iconClass: 'fas fa-check',
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttOfflineEvent');
-
- registerEventHandler(() => {
- createNotification({
- text: 'Failed to remove the device',
- error: true,
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceRemovedFailedEvent');
-
- registerEventHandler(() => {
- createNotification({
- text: 'Failed to add the group',
- error: true,
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttGroupAddedFailedEvent');
-
- registerEventHandler(() => {
- createNotification({
- text: 'Failed to remove the group',
- error: true,
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttGroupRemovedFailedEvent');
-
- registerEventHandler(() => {
- createNotification({
- text: 'Failed to remove the devices from the group',
- error: true,
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttGroupRemoveAllFailedEvent');
-
- registerEventHandler((event) => {
- createNotification({
- text: 'Unhandled Zigbee error: ' + (event.error || '[Unknown error]'),
- error: true,
- });
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttErrorEvent');
-
- registerEventHandler((event) => {
- self.updateProperties(event.device, event.properties);
- }, 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDevicePropertySetEvent');
-
- registerEventHandler(this.refresh,
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttOnlineEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDevicePairingEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceConnectedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceBannedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceRemovedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceWhitelistedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceRenamedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceBindEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttDeviceUnbindEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttGroupAddedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttGroupRemovedEvent',
- 'platypush.message.event.zigbee.mqtt.ZigbeeMqttGroupRemoveAllEvent',
- );
- },
-
- mounted: function() {
- this.refresh();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zwave/group.js b/platypush/backend/http/static/js/plugins/zwave/group.js
deleted file mode 100644
index 0410c1af..00000000
--- a/platypush/backend/http/static/js/plugins/zwave/group.js
+++ /dev/null
@@ -1,24 +0,0 @@
-Vue.component('zwave-group', {
- template: '#tmpl-zwave-group',
- props: ['group','nodes','bus','selected'],
-
- methods: {
- onGroupClicked: function() {
- this.bus.$emit('groupClicked', {
- groupId: this.group.index,
- });
- },
-
- removeFromGroup: async function(nodeId) {
- if (!confirm('Are you sure that you want to remove this node from ' + this.group.label + '?')) {
- return;
- }
-
- await request('zwave.remove_node_from_group', {
- node_id: nodeId,
- group_index: this.group.index,
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zwave/index.js b/platypush/backend/http/static/js/plugins/zwave/index.js
deleted file mode 100644
index 90c2b074..00000000
--- a/platypush/backend/http/static/js/plugins/zwave/index.js
+++ /dev/null
@@ -1,473 +0,0 @@
-Vue.component('zwave', {
- template: '#tmpl-zwave',
- props: ['config'],
-
- data: function() {
- return {
- bus: new Vue({}),
- status: {},
- views: {},
- nodes: {},
- groups: {},
- scenes: {},
- commandRunning: false,
- values: {
- switches: {},
- dimmers: {},
- sensors: {},
- battery_levels: {},
- power_levels: {},
- bulbs: {},
- doorlocks: {},
- usercodes: {},
- thermostats: {},
- protections: {},
- },
- selected: {
- view: 'nodes',
- nodeId: undefined,
- groupId: undefined,
- sceneId: undefined,
- valueId: undefined,
- },
- loading: {
- status: false,
- nodes: false,
- groups: false,
- scenes: false,
- },
- modal: {
- networkInfo: {
- visible: false,
- },
- group: {
- visible: false,
- },
- },
- };
- },
-
- computed: {
- valuesMap: function() {
- const values = {};
- for (const node of Object.values(this.nodes)) {
- for (const value of Object.values(node.values)) {
- values[value.id_on_network] = value;
- }
- }
-
- return values;
- },
-
- networkDropdownItems: function() {
- const self = this;
- return [
- {
- text: 'Start Network',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.start_network');
- self.commandRunning = false;
- },
- },
-
- {
- text: 'Stop Network',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.stop_network');
- self.commandRunning = false;
- },
- },
-
- {
- text: 'Switch All On',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.switch_all', {state: true});
- self.commandRunning = false;
- self.refresh();
- },
- },
-
- {
- text: 'Switch All Off',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.switch_all', {state: false});
- self.commandRunning = false;
- self.refresh();
- },
- },
-
- {
- text: 'Cancel Command',
- click: async function() {
- await request('zwave.cancel_command');
- },
- },
-
- {
- text: 'Kill Command',
- click: async function() {
- await request('zwave.kill_command');
- },
- },
-
- {
- text: 'Set Controller Name',
- disabled: this.commandRunning,
- click: async function() {
- const name = prompt('Controller name');
- if (!name) {
- return;
- }
-
- self.commandRunning = true;
- await request('zwave.set_controller_name', {name: name});
- self.commandRunning = false;
- self.refresh();
- },
- },
-
-
- {
- text: 'Receive Configuration From Primary',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.receive_configuration');
- self.commandRunning = false;
- self.refresh();
- },
- },
-
- {
- text: 'Create New Primary',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.create_new_primary');
- self.commandRunning = false;
- self.refresh();
- },
- },
-
- {
- text: 'Transfer Primary Role',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.transfer_primary_role');
- self.commandRunning = false;
- self.refresh();
- },
- },
-
- {
- text: 'Heal Network',
- disabled: this.commandRunning,
- click: async function() {
- self.commandRunning = true;
- await request('zwave.heal');
- self.commandRunning = false;
- self.refresh();
- },
- },
-
- {
- text: 'Soft Reset',
- disabled: this.commandRunning,
- click: async function() {
- if (!confirm('Are you sure that you want to do a device soft reset? Network information will not be lost')) {
- return;
- }
-
- await request('zwave.soft_reset');
- },
- },
-
- {
- text: 'Hard Reset',
- disabled: this.commandRunning,
- click: async function() {
- if (!confirm('Are you sure that you want to do a device soft reset? ALL network information will be lost!')) {
- return;
- }
-
- await request('zwave.hard_reset');
- },
- },
- ]
- },
-
- addToSceneDropdownItems: function() {
- const self = this;
- return Object.values(this.scenes).filter((scene) => {
- return !scene.values || !scene.values.length || !(this.selected.valueId in this.scene.values);
- }).map((scene) => {
- return {
- text: scene.label,
- disabled: this.commandRunning,
- click: async function () {
- if (!self.selected.valueId) {
- return;
- }
-
- self.commandRunning = true;
- await request('zwave.scene_add_value', {
- id_on_network: self.selected.valueId,
- scene_id: scene.scene_id,
- });
-
- self.commandRunning = false;
- self.refresh();
- },
- };
- });
- },
- },
-
- methods: {
- refreshNodes: async function () {
- this.loading.nodes = true;
- this.nodes = await request('zwave.get_nodes');
-
- if (Object.keys(this.nodes).length) {
- Vue.set(this.views, 'values', true);
- }
- },
-
- refreshGroups: async function () {
- this.loading.groups = true;
- this.groups = Object.values(await request('zwave.get_groups'))
- .filter((group) => group.index)
- .reduce((groups, group) => {
- groups[group.index] = group;
- return groups;
- }, {});
-
- if (Object.keys(this.groups).length) {
- Vue.set(this.views, 'groups', true);
- }
-
- this.loading.groups = false;
- },
-
- refreshScenes: async function () {
- this.loading.scenes = true;
- this.scenes = Object.values(await request('zwave.get_scenes'))
- .filter((scene) => scene.scene_id)
- .reduce((scenes, scene) => {
- scenes[scene.scene_id] = scene;
- return scenes;
- }, {});
-
- this.loading.scenes = false;
- },
-
- refreshValues: async function(type) {
- Vue.set(this.values, type, Object.values(await request('zwave.get_' + type))
- .filter((item) => item.id_on_network)
- .reduce((values, value) => {
- values[value.id_on_network] = true;
- return values;
- }, {}));
-
- if (Object.keys(this.values[type]).length) {
- Vue.set(this.views, type, true);
- }
- },
-
- refreshStatus: async function() {
- this.loading.status = true;
- this.status = await request('zwave.status');
- this.loading.status = false;
- },
-
- refresh: function () {
- this.views = {
- nodes: true,
- scenes: true,
- };
-
- this.refreshNodes();
- this.refreshGroups();
- this.refreshScenes();
- this.refreshValues('switches');
- this.refreshValues('dimmers');
- this.refreshValues('sensors');
- this.refreshValues('bulbs');
- this.refreshValues('doorlocks');
- this.refreshValues('usercodes');
- this.refreshValues('thermostats');
- this.refreshValues('protections');
- this.refreshValues('battery_levels');
- this.refreshValues('power_levels');
- this.refreshValues('node_config');
- this.refreshStatus();
- },
-
- addScene: async function() {
- const name = prompt('Scene name');
- if (!name) {
- return;
- }
-
- this.commandRunning = true;
- await request('zwave.create_scene', {label: name});
- this.commandRunning = false;
- this.refreshScenes();
- },
-
- removeScene: async function(sceneId) {
- if (!confirm('Are you sure that you want to delete this scene?')) {
- return;
- }
-
- this.commandRunning = true;
- await request('zwave.remove_scene', {scene_id: sceneId});
- this.commandRunning = false;
- this.refreshScenes();
- },
-
- onNodeUpdate: function(event) {
- Vue.set(this.nodes, event.node.node_id, event.node);
- },
-
- onViewChange: function(event) {
- Vue.set(this.selected, 'view', event.target.value);
- },
-
- onNodeClicked: function(event) {
- Vue.set(this.selected, 'nodeId', event.nodeId === this.selected.nodeId ? undefined : event.nodeId);
- },
-
- onGroupClicked: function(event) {
- Vue.set(this.selected, 'groupId', event.groupId === this.selected.groupId ? undefined : event.groupId);
- },
-
- onSceneClicked: function(event) {
- Vue.set(this.selected, 'sceneId', event.sceneId === this.selected.sceneId ? undefined : event.sceneId);
- },
-
- onNetworkInfoModalOpen: function() {
- this.refreshStatus();
- this.modal.networkInfo.visible = true;
- },
-
- onCommandEvent: function(event) {
- if (event.error && event.error.length) {
- createNotification({
- text: event.state_description + ': ' + event.error_description,
- error: true,
- });
- }
- },
-
- openNetworkCommandsDropdown: function() {
- openDropdown(this.$refs.networkCommandsDropdown);
- },
-
- openAddToSceneDropdown: function(event) {
- this.selected.valueId = event.valueId;
- openDropdown(this.$refs.addToSceneDropdown);
- },
-
- addNode: async function() {
- this.commandRunning = true;
- await request('zwave.add_node');
- this.commandRunning = false;
- },
-
- addToGroup: async function(nodeId, groupId) {
- this.commandRunning = true;
- await request('zwave.add_node_to_group', {
- node_id: nodeId,
- group_index: groupId,
- });
-
- this.commandRunning = false;
- this.refreshGroups();
- },
-
- removeNode: async function() {
- this.commandRunning = true;
- await request('zwave.remove_node');
- this.commandRunning = false;
- },
-
- removeNodeFromScene: async function(event) {
- if (!confirm('Are you sure that you want to remove this value from the scene?')) {
- return;
- }
-
- this.commandRunning = true;
- await request('zwave.scene_remove_value', {
- id_on_network: event.valueId,
- scene_id: event.sceneId,
- });
-
- this.commandRunning = false;
- },
-
- renameScene: async function(sceneId) {
- const scene = this.scenes[sceneId];
- const name = prompt('New name', scene.label);
-
- if (!name || !name.length || name === scene.label) {
- return;
- }
-
- this.commandRunning = true;
- await request('zwave.set_scene_label', {
- new_label: name,
- scene_id: sceneId,
- });
-
- this.commandRunning = false;
- this.refreshScenes();
- },
- },
-
- created: function() {
- const self = this;
- this.bus.$on('refresh', this.refresh);
- this.bus.$on('refreshNodes', this.refreshNodes);
- this.bus.$on('nodeClicked', this.onNodeClicked);
- this.bus.$on('groupClicked', this.onGroupClicked);
- this.bus.$on('openAddToGroupModal', () => {self.modal.group.visible = true});
- this.bus.$on('openAddToSceneDropdown', this.openAddToSceneDropdown);
- this.bus.$on('removeFromScene', this.removeNodeFromScene);
-
- registerEventHandler(this.refreshGroups, 'platypush.message.event.zwave.ZwaveNodeGroupEvent');
- registerEventHandler(this.refreshScenes, 'platypush.message.event.zwave.ZwaveNodeSceneEvent');
- registerEventHandler(this.refreshNodes, 'platypush.message.event.zwave.ZwaveNodeRemovedEvent');
- registerEventHandler(this.onCommandEvent, 'platypush.message.event.zwave.ZwaveCommandEvent');
-
- registerEventHandler(this.refreshStatus,
- 'platypush.message.event.zwave.ZwaveNetworkReadyEvent',
- 'platypush.message.event.zwave.ZwaveNetworkStoppedEvent',
- 'platypush.message.event.zwave.ZwaveNetworkErrorEvent',
- 'platypush.message.event.zwave.ZwaveNetworkResetEvent');
-
- registerEventHandler(this.onNodeUpdate,
- 'platypush.message.event.zwave.ZwaveNodeEvent',
- 'platypush.message.event.zwave.ZwaveNodeAddedEvent',
- 'platypush.message.event.zwave.ZwaveNodeRenamedEvent',
- 'platypush.message.event.zwave.ZwaveNodeReadyEvent',
- 'platypush.message.event.zwave.ZwaveValueAddedEvent',
- 'platypush.message.event.zwave.ZwaveValueChangedEvent',
- 'platypush.message.event.zwave.ZwaveValueRemovedEvent',
- 'platypush.message.event.zwave.ZwaveValueRefreshedEvent');
- },
-
- mounted: function() {
- this.refresh();
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zwave/node.js b/platypush/backend/http/static/js/plugins/zwave/node.js
deleted file mode 100644
index 9dc88a44..00000000
--- a/platypush/backend/http/static/js/plugins/zwave/node.js
+++ /dev/null
@@ -1,96 +0,0 @@
-Vue.component('zwave-node', {
- template: '#tmpl-zwave-node',
- props: ['node','bus','selected'],
- data: function() {
- return {
- editMode: {
- name: false,
- },
- };
- },
-
- methods: {
- onNodeClicked: function() {
- this.bus.$emit('nodeClicked', {
- nodeId: this.node.node_id,
- });
- },
-
- removeFailedNode: async function() {
- if (!confirm('Are you sure that you want to remove this node?')) {
- return;
- }
-
- await request('zwave.remove_node', {
- node_id: this.node.node_id,
- });
- },
-
- replaceFailedNode: async function() {
- if (!confirm('Are you sure that you want to replace this node?')) {
- return;
- }
-
- await request('zwave.replace_node', {
- node_id: this.node.node_id,
- });
- },
-
- replicationSend: async function() {
- await request('zwave.replication_send', {
- node_id: this.node.node_id,
- });
- },
-
- requestNetworkUpdate: async function() {
- await request('zwave.request_network_update', {
- node_id: this.node.node_id,
- });
- },
-
- requestNeighbourUpdate: async function() {
- await request('zwave.request_node_neighbour_update', {
- node_id: this.node.node_id,
- });
- },
-
- disableForm: function(form) {
- form.querySelector('input,button').readOnly = true;
- },
-
- enableForm: function(form) {
- form.querySelector('input,button').readOnly = false;
- },
-
- onEditMode: function(mode) {
- Vue.set(this.editMode, mode, true);
- const form = this.$refs[mode + 'Form'];
- const input = form.querySelector('input[type=text]');
-
- setTimeout(() => {
- input.focus();
- input.select();
- }, 10);
- },
-
- editName: async function(event) {
- this.disableForm(event.target);
- const name = event.target.querySelector('input[name=name]').value;
-
- await request('zwave.set_node_name', {
- node_id: this.node.node_id,
- new_name: name,
- });
-
- this.editMode.name = false;
- this.enableForm(event.target);
- },
-
- heal: async function(event) {
- await request('zwave.node_heal', {
- node_id: this.node.node_id,
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/plugins/zwave/value.js b/platypush/backend/http/static/js/plugins/zwave/value.js
deleted file mode 100644
index a1f85081..00000000
--- a/platypush/backend/http/static/js/plugins/zwave/value.js
+++ /dev/null
@@ -1,55 +0,0 @@
-Vue.component('zwave-value', {
- template: '#tmpl-zwave-value',
- props: ['value','node','bus','selected','sceneId'],
- data: function() {
- return {};
- },
-
- methods: {
- disableForm: function(form) {
- form.querySelector('input,button').readOnly = true;
- },
-
- enableForm: function(form) {
- form.querySelector('input,button').readOnly = false;
- },
-
- editName: function(event) {
- const value = this.node.values[event.target.parentElement.dataset.idOnNetwork];
- const name = prompt('New name', value.label);
-
- if (!name || !name.length || name === value.label) {
- return;
- }
-
- request('zwave.set_value_label', {
- id_on_network: value.id_on_network,
- new_label: name,
- }).then(() => {
- this.bus.$emit('refreshNodes');
- createNotification({
- text: 'Value successfully renamed',
- image: { icon: 'check' }
- });
- });
- },
-
- onValueChanged: function(event) {
- const target = event.target ? event.target : event.event.target.parentElement;
- const value = this.node.values[target.dataset.idOnNetwork];
- const data = value.type === 'List' ? value.data_items[event.target.value] : (target.value || event.value);
-
- request('zwave.set_value', {
- id_on_network: value.id_on_network,
- data: data,
- }).then(() => {
- this.bus.$emit('refreshNodes');
- createNotification({
- text: 'Value successfully modified',
- image: { icon: 'check' }
- });
- });
- },
- },
-});
-
diff --git a/platypush/backend/http/static/js/settings/index.js b/platypush/backend/http/static/js/settings/index.js
deleted file mode 100644
index b6f906d6..00000000
--- a/platypush/backend/http/static/js/settings/index.js
+++ /dev/null
@@ -1,186 +0,0 @@
-window.vm = new Vue({
- el: '#app',
- data: function() {
- return {
- config: window.config,
- document: undefined,
- sessionToken: undefined,
- selectedTab: 'users',
- selectedUser: undefined,
-
- modalVisible: {
- addUser: false,
- changePassword: false,
- },
-
- formDisabled: {
- addUser: false,
- changePassword: false,
- },
- };
- },
-
- computed: {
- userDropdownItems: function() {
- const self = this;
- return [
- {
- text: 'Change password',
- iconClass: 'fas fa-key',
- click: function() {
- self.modalVisible.changePassword = true;
- },
- },
- {
- text: 'Delete user',
- iconClass: 'fa fa-trash',
- click: async function() {
- if (!confirm('Are you sure that you want to remove the user ' + self.selectedUser + '?'))
- return;
-
- await request('user.delete_user', {
- username: self.selectedUser,
- session_token: self.sessionToken,
- });
-
- createNotification({
- text: 'User ' + self.selectedUser + ' removed',
- image: {
- iconClass: 'fas fa-check',
- },
- });
-
- window.location.reload();
- },
- },
- ];
- },
- },
-
- methods: {
- createUser: async function(event) {
- event.preventDefault();
-
- let form = [...this.$refs.createUserForm.querySelectorAll('input[name]')].reduce((map, input) => {
- map[input.name] = input.value;
- return map;
- }, {});
-
- if (form.password != form.confirm_password) {
- createNotification({
- text: 'Please check that the passwords match',
- image: {
- iconClass: 'fas fa-times',
- },
- error: true,
- });
-
- return;
- }
-
- this.formDisabled.addUser = true;
- await request('user.create_user', {
- username: form.username,
- password: form.password,
- session_token: this.sessionToken,
- });
-
- this.formDisabled.addUser = false;
- createNotification({
- text: 'User ' + form.username + ' created',
- image: {
- iconClass: 'fas fa-check',
- },
- });
-
- this.modalVisible.addUser = false;
- window.location.reload();
- },
-
- onUserClick: function(username) {
- this.selectedUser = username;
- openDropdown(this.$refs.userDropdown.$el);
- },
-
- onTokenFocus: function(event) {
- event.target.select();
- this.document.execCommand('copy');
- event.target.setAttribute('disabled', true);
-
- createNotification({
- text: 'Token copied to clipboard',
- image: {
- iconClass: 'fas fa-copy',
- },
- });
- },
-
- onTokenBlur: function(event) {
- event.target.select();
- this.document.execCommand('copy');
- event.target.removeAttribute('disabled');
-
- createNotification({
- text: 'Token copied to clipboard',
- image: {
- iconClass: 'fas fa-copy',
- },
- });
- },
-
- changePassword: async function(event) {
- event.preventDefault();
-
- let form = [...this.$refs.changePasswordForm.querySelectorAll('input[name]')].reduce((map, input) => {
- map[input.name] = input.value;
- return map;
- }, {});
-
- if (form.new_password !== form.confirm_new_password) {
- createNotification({
- text: 'Please check that the passwords match',
- image: {
- iconClass: 'fas fa-times',
- },
- error: true,
- });
-
- return;
- }
-
- this.formDisabled.changePassword = true;
- let success = await request('user.update_password', {
- username: form.username,
- old_password: form.password,
- new_password: form.new_password,
- });
-
- this.formDisabled.changePassword = false;
-
- if (success) {
- this.modalVisible.changePassword = false;
- createNotification({
- text: 'Password successfully updated',
- image: {
- iconClass: 'fas fa-check',
- },
- });
- } else {
- createNotification({
- text: 'Unable to update password: the current password is incorrect',
- image: {
- iconClass: 'fas fa-times',
- },
- error: true,
- });
- }
- },
- },
-
- created: function() {
- this.document = document;
- let cookies = Object.fromEntries(this.document.cookie.split('; ').map(x => x.split('=')));
- this.sessionToken = cookies.session_token;
- },
-});
-
diff --git a/platypush/backend/http/static/js/utils.js b/platypush/backend/http/static/js/utils.js
deleted file mode 100644
index 0f6c9c46..00000000
--- a/platypush/backend/http/static/js/utils.js
+++ /dev/null
@@ -1,47 +0,0 @@
-function enterFullScreen() {
- const app = document.documentElement;
- if (app.requestFullscreen()) {
- return app.requestFullscreen();
- } else if (app.msRequestFullscreen()) {
- return app.msRequestFullscreen();
- } else if (app.mozRequestFullScreen()) {
- return app.mozRequestFullScreen();
- } else if (app.webkitRequestFullscreen()) {
- return app.webkitRequestFullscreen();
- } else {
- console.warn('This browser does not support fullscreen mode');
- }
-
- // The day will come when browser developers will have to pay web developers
- // back for all the time wasted to write such crappy portable code. A two-lines
- // function becomes a 10-lines function just because they can't talk to
- // one another before implementing sh*t.
-}
-
-function exitFullScreen() {
- if (document.exitFullscreen()) {
- return document.exitFullscreen();
- } else if (document.msExitFullscreen()) {
- return document.msExitFullscreen();
- } else if (document.mozCancelFullScreen()) {
- return document.mozCancelFullScreen();
- } else if (document.webkitExitFullscreen()) {
- return document.webkitExitFullscreen();
- } else {
- console.warn('This browser does not support fullscreen mode');
- }
-}
-
-function toggleFullScreen() {
- const elem = document.fullscreenElement
- || document.webkitFullscreenElement
- || document.msFullscreenElement
- || document.mozFullScreenElement;
-
- if (elem) {
- exitFullScreen();
- } else {
- enterFullScreen();
- }
-}
-
diff --git a/platypush/backend/http/static/js/widgets/calendar/index.js b/platypush/backend/http/static/js/widgets/calendar/index.js
deleted file mode 100644
index c5a299b0..00000000
--- a/platypush/backend/http/static/js/widgets/calendar/index.js
+++ /dev/null
@@ -1,37 +0,0 @@
-Vue.component('calendar', {
- template: '#tmpl-widget-calendar',
- props: ['config'],
-
- data: function() {
- return {
- events: [],
- };
- },
-
- methods: {
- refresh: async function() {
- this.events = (await request('calendar.get_upcoming_events')).map(event => {
- if (event.start)
- event.start = new Date(event.start.dateTime || event.start.date);
- if (event.end)
- event.end = new Date(event.end.dateTime || event.end.date);
-
- return event;
- });
- },
-
- formatDate: function(date) {
- return date.toDateString().substring(0, 10);
- },
-
- formatTime: function(date) {
- return date.toTimeString().substring(0, 5);
- },
- },
-
- mounted: function() {
- this.refresh();
- setInterval(this.refresh, 600000);
- },
-});
-
diff --git a/platypush/backend/http/static/js/widgets/date-time-weather/index.js b/platypush/backend/http/static/js/widgets/date-time-weather/index.js
deleted file mode 100644
index 0a1417a9..00000000
--- a/platypush/backend/http/static/js/widgets/date-time-weather/index.js
+++ /dev/null
@@ -1,68 +0,0 @@
-Vue.component('date-time-weather', {
- template: '#tmpl-widget-date-time-weather',
- props: ['config'],
-
- data: function() {
- return {
- weather: undefined,
- sensors: {},
- now: new Date(),
- weatherIcon: undefined,
- };
- },
-
- methods: {
- refresh: async function() {
- let weather = (await request('weather.darksky.get_hourly_forecast')).data[0];
- this.onWeatherChange(weather);
- },
-
- refreshTime: function() {
- this.now = new Date();
- },
-
- formatDate: function(date) {
- return date.toDateString().substring(0, 10);
- },
-
- formatTime: function(date) {
- return date.toTimeString().substring(0, 8);
- },
-
- onWeatherChange: function(event) {
- if (!this.weather)
- this.weather = {};
-
- Vue.set(this, 'weather', {...this.weather, ...event});
-
- var skycons = new Skycons({
- 'color':'#333', 'resizeClear':'true'
- });
-
- if (this.weatherIcon) {
- skycons.remove('weather-icon');
- }
-
- skycons.add('weather-icon', this.weather.icon);
- this.weatherIcon = this.weather.icon;
- },
-
- onSensorData: function(event) {
- if ('temperature' in event.data)
- this.sensors.temperature = event.data.temperature;
-
- if ('humidity' in event.data)
- this.sensors.temperature = event.data.humidity;
- },
- },
-
- mounted: function() {
- this.refresh();
- setInterval(this.refresh, 900000);
- setInterval(this.refreshTime, 1000);
-
- registerEventHandler(this.onWeatherChange, 'platypush.message.event.weather.NewWeatherConditionEvent');
- registerEventHandler(this.onSensorData, 'platypush.message.event.sensor.SensorDataChangeEvent');
- },
-});
-
diff --git a/platypush/backend/http/static/js/widgets/date-time-weather/skycons.js b/platypush/backend/http/static/js/widgets/date-time-weather/skycons.js
deleted file mode 100644
index d8a1583e..00000000
--- a/platypush/backend/http/static/js/widgets/date-time-weather/skycons.js
+++ /dev/null
@@ -1,726 +0,0 @@
-(function(global) {
- "use strict";
-
- /* Set up a RequestAnimationFrame shim so we can animate efficiently FOR
- * GREAT JUSTICE. */
- var requestInterval, cancelInterval;
-
- (function() {
- var raf = global.requestAnimationFrame ||
- global.webkitRequestAnimationFrame ||
- global.mozRequestAnimationFrame ||
- global.oRequestAnimationFrame ||
- global.msRequestAnimationFrame ,
- caf = global.cancelAnimationFrame ||
- global.webkitCancelAnimationFrame ||
- global.mozCancelAnimationFrame ||
- global.oCancelAnimationFrame ||
- global.msCancelAnimationFrame ;
-
- if(raf && caf) {
- requestInterval = function(fn) {
- var handle = {value: null};
-
- function loop() {
- handle.value = raf(loop);
- fn();
- }
-
- loop();
- return handle;
- };
-
- cancelInterval = function(handle) {
- caf(handle.value);
- };
- }
-
- else {
- requestInterval = setInterval;
- cancelInterval = clearInterval;
- }
- }());
-
- /* Catmull-rom spline stuffs. */
- /*
- function upsample(n, spline) {
- var polyline = [],
- len = spline.length,
- bx = spline[0],
- by = spline[1],
- cx = spline[2],
- cy = spline[3],
- dx = spline[4],
- dy = spline[5],
- i, j, ax, ay, px, qx, rx, sx, py, qy, ry, sy, t;
-
- for(i = 6; i !== spline.length; i += 2) {
- ax = bx;
- bx = cx;
- cx = dx;
- dx = spline[i ];
- px = -0.5 * ax + 1.5 * bx - 1.5 * cx + 0.5 * dx;
- qx = ax - 2.5 * bx + 2.0 * cx - 0.5 * dx;
- rx = -0.5 * ax + 0.5 * cx ;
- sx = bx ;
-
- ay = by;
- by = cy;
- cy = dy;
- dy = spline[i + 1];
- py = -0.5 * ay + 1.5 * by - 1.5 * cy + 0.5 * dy;
- qy = ay - 2.5 * by + 2.0 * cy - 0.5 * dy;
- ry = -0.5 * ay + 0.5 * cy ;
- sy = by ;
-
- for(j = 0; j !== n; ++j) {
- t = j / n;
-
- polyline.push(
- ((px * t + qx) * t + rx) * t + sx,
- ((py * t + qy) * t + ry) * t + sy
- );
- }
- }
-
- polyline.push(
- px + qx + rx + sx,
- py + qy + ry + sy
- );
-
- return polyline;
- }
-
- function downsample(n, polyline) {
- var len = 0,
- i, dx, dy;
-
- for(i = 2; i !== polyline.length; i += 2) {
- dx = polyline[i ] - polyline[i - 2];
- dy = polyline[i + 1] - polyline[i - 1];
- len += Math.sqrt(dx * dx + dy * dy);
- }
-
- len /= n;
-
- var small = [],
- target = len,
- min = 0,
- max, t;
-
- small.push(polyline[0], polyline[1]);
-
- for(i = 2; i !== polyline.length; i += 2) {
- dx = polyline[i ] - polyline[i - 2];
- dy = polyline[i + 1] - polyline[i - 1];
- max = min + Math.sqrt(dx * dx + dy * dy);
-
- if(max > target) {
- t = (target - min) / (max - min);
-
- small.push(
- polyline[i - 2] + dx * t,
- polyline[i - 1] + dy * t
- );
-
- target += len;
- }
-
- min = max;
- }
-
- small.push(polyline[polyline.length - 2], polyline[polyline.length - 1]);
-
- return small;
- }
- */
-
- /* Define skycon things. */
- /* FIXME: I'm *really really* sorry that this code is so gross. Really, I am.
- * I'll try to clean it up eventually! Promise! */
- var KEYFRAME = 500,
- STROKE = 0.08,
- TAU = 2.0 * Math.PI,
- TWO_OVER_SQRT_2 = 2.0 / Math.sqrt(2);
-
- function circle(ctx, x, y, r) {
- ctx.beginPath();
- ctx.arc(x, y, r, 0, TAU, false);
- ctx.fill();
- }
-
- function line(ctx, ax, ay, bx, by) {
- ctx.beginPath();
- ctx.moveTo(ax, ay);
- ctx.lineTo(bx, by);
- ctx.stroke();
- }
-
- function puff(ctx, t, cx, cy, rx, ry, rmin, rmax) {
- var c = Math.cos(t * TAU),
- s = Math.sin(t * TAU);
-
- rmax -= rmin;
-
- circle(
- ctx,
- cx - s * rx,
- cy + c * ry + rmax * 0.5,
- rmin + (1 - c * 0.5) * rmax
- );
- }
-
- function puffs(ctx, t, cx, cy, rx, ry, rmin, rmax) {
- var i;
-
- for(i = 5; i--; )
- puff(ctx, t + i / 5, cx, cy, rx, ry, rmin, rmax);
- }
-
- function cloud(ctx, t, cx, cy, cw, s, color) {
- t /= 30000;
-
- var a = cw * 0.21,
- b = cw * 0.12,
- c = cw * 0.24,
- d = cw * 0.28;
-
- ctx.fillStyle = color;
- puffs(ctx, t, cx, cy, a, b, c, d);
-
- ctx.globalCompositeOperation = 'destination-out';
- puffs(ctx, t, cx, cy, a, b, c - s, d - s);
- ctx.globalCompositeOperation = 'source-over';
- }
-
- function sun(ctx, t, cx, cy, cw, s, color) {
- t /= 120000;
-
- var a = cw * 0.25 - s * 0.5,
- b = cw * 0.32 + s * 0.5,
- c = cw * 0.50 - s * 0.5,
- i, p, cos, sin;
-
- ctx.strokeStyle = color;
- ctx.lineWidth = s;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- ctx.beginPath();
- ctx.arc(cx, cy, a, 0, TAU, false);
- ctx.stroke();
-
- for(i = 8; i--; ) {
- p = (t + i / 8) * TAU;
- cos = Math.cos(p);
- sin = Math.sin(p);
- line(ctx, cx + cos * b, cy + sin * b, cx + cos * c, cy + sin * c);
- }
- }
-
- function moon(ctx, t, cx, cy, cw, s, color) {
- t /= 15000;
-
- var a = cw * 0.29 - s * 0.5,
- b = cw * 0.05,
- c = Math.cos(t * TAU),
- p = c * TAU / -16;
-
- ctx.strokeStyle = color;
- ctx.lineWidth = s;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- cx += c * b;
-
- ctx.beginPath();
- ctx.arc(cx, cy, a, p + TAU / 8, p + TAU * 7 / 8, false);
- ctx.arc(cx + Math.cos(p) * a * TWO_OVER_SQRT_2, cy + Math.sin(p) * a * TWO_OVER_SQRT_2, a, p + TAU * 5 / 8, p + TAU * 3 / 8, true);
- ctx.closePath();
- ctx.stroke();
- }
-
- function rain(ctx, t, cx, cy, cw, s, color) {
- t /= 1350;
-
- var a = cw * 0.16,
- b = TAU * 11 / 12,
- c = TAU * 7 / 12,
- i, p, x, y;
-
- ctx.fillStyle = color;
-
- for(i = 4; i--; ) {
- p = (t + i / 4) % 1;
- x = cx + ((i - 1.5) / 1.5) * (i === 1 || i === 2 ? -1 : 1) * a;
- y = cy + p * p * cw;
- ctx.beginPath();
- ctx.moveTo(x, y - s * 1.5);
- ctx.arc(x, y, s * 0.75, b, c, false);
- ctx.fill();
- }
- }
-
- function sleet(ctx, t, cx, cy, cw, s, color) {
- t /= 750;
-
- var a = cw * 0.1875,
- i, p, x, y;
-
- ctx.strokeStyle = color;
- ctx.lineWidth = s * 0.5;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- for(i = 4; i--; ) {
- p = (t + i / 4) % 1;
- x = Math.floor(cx + ((i - 1.5) / 1.5) * (i === 1 || i === 2 ? -1 : 1) * a) + 0.5;
- y = cy + p * cw;
- line(ctx, x, y - s * 1.5, x, y + s * 1.5);
- }
- }
-
- function snow(ctx, t, cx, cy, cw, s, color) {
- t /= 3000;
-
- var a = cw * 0.16,
- b = s * 0.75,
- u = t * TAU * 0.7,
- ux = Math.cos(u) * b,
- uy = Math.sin(u) * b,
- v = u + TAU / 3,
- vx = Math.cos(v) * b,
- vy = Math.sin(v) * b,
- w = u + TAU * 2 / 3,
- wx = Math.cos(w) * b,
- wy = Math.sin(w) * b,
- i, p, x, y;
-
- ctx.strokeStyle = color;
- ctx.lineWidth = s * 0.5;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- for(i = 4; i--; ) {
- p = (t + i / 4) % 1;
- x = cx + Math.sin((p + i / 4) * TAU) * a;
- y = cy + p * cw;
-
- line(ctx, x - ux, y - uy, x + ux, y + uy);
- line(ctx, x - vx, y - vy, x + vx, y + vy);
- line(ctx, x - wx, y - wy, x + wx, y + wy);
- }
- }
-
- function fogbank(ctx, t, cx, cy, cw, s, color) {
- t /= 30000;
-
- var a = cw * 0.21,
- b = cw * 0.06,
- c = cw * 0.21,
- d = cw * 0.28;
-
- ctx.fillStyle = color;
- puffs(ctx, t, cx, cy, a, b, c, d);
-
- ctx.globalCompositeOperation = 'destination-out';
- puffs(ctx, t, cx, cy, a, b, c - s, d - s);
- ctx.globalCompositeOperation = 'source-over';
- }
-
- /*
- var WIND_PATHS = [
- downsample(63, upsample(8, [
- -1.00, -0.28,
- -0.75, -0.18,
- -0.50, 0.12,
- -0.20, 0.12,
- -0.04, -0.04,
- -0.07, -0.18,
- -0.19, -0.18,
- -0.23, -0.05,
- -0.12, 0.11,
- 0.02, 0.16,
- 0.20, 0.15,
- 0.50, 0.07,
- 0.75, 0.18,
- 1.00, 0.28
- ])),
- downsample(31, upsample(16, [
- -1.00, -0.10,
- -0.75, 0.00,
- -0.50, 0.10,
- -0.25, 0.14,
- 0.00, 0.10,
- 0.25, 0.00,
- 0.50, -0.10,
- 0.75, -0.14,
- 1.00, -0.10
- ]))
- ];
- */
-
- var WIND_PATHS = [
- [
- -0.7500, -0.1800, -0.7219, -0.1527, -0.6971, -0.1225,
- -0.6739, -0.0910, -0.6516, -0.0588, -0.6298, -0.0262,
- -0.6083, 0.0065, -0.5868, 0.0396, -0.5643, 0.0731,
- -0.5372, 0.1041, -0.5033, 0.1259, -0.4662, 0.1406,
- -0.4275, 0.1493, -0.3881, 0.1530, -0.3487, 0.1526,
- -0.3095, 0.1488, -0.2708, 0.1421, -0.2319, 0.1342,
- -0.1943, 0.1217, -0.1600, 0.1025, -0.1290, 0.0785,
- -0.1012, 0.0509, -0.0764, 0.0206, -0.0547, -0.0120,
- -0.0378, -0.0472, -0.0324, -0.0857, -0.0389, -0.1241,
- -0.0546, -0.1599, -0.0814, -0.1876, -0.1193, -0.1964,
- -0.1582, -0.1935, -0.1931, -0.1769, -0.2157, -0.1453,
- -0.2290, -0.1085, -0.2327, -0.0697, -0.2240, -0.0317,
- -0.2064, 0.0033, -0.1853, 0.0362, -0.1613, 0.0672,
- -0.1350, 0.0961, -0.1051, 0.1213, -0.0706, 0.1397,
- -0.0332, 0.1512, 0.0053, 0.1580, 0.0442, 0.1624,
- 0.0833, 0.1636, 0.1224, 0.1615, 0.1613, 0.1565,
- 0.1999, 0.1500, 0.2378, 0.1402, 0.2749, 0.1279,
- 0.3118, 0.1147, 0.3487, 0.1015, 0.3858, 0.0892,
- 0.4236, 0.0787, 0.4621, 0.0715, 0.5012, 0.0702,
- 0.5398, 0.0766, 0.5768, 0.0890, 0.6123, 0.1055,
- 0.6466, 0.1244, 0.6805, 0.1440, 0.7147, 0.1630,
- 0.7500, 0.1800
- ],
- [
- -0.7500, 0.0000, -0.7033, 0.0195, -0.6569, 0.0399,
- -0.6104, 0.0600, -0.5634, 0.0789, -0.5155, 0.0954,
- -0.4667, 0.1089, -0.4174, 0.1206, -0.3676, 0.1299,
- -0.3174, 0.1365, -0.2669, 0.1398, -0.2162, 0.1391,
- -0.1658, 0.1347, -0.1157, 0.1271, -0.0661, 0.1169,
- -0.0170, 0.1046, 0.0316, 0.0903, 0.0791, 0.0728,
- 0.1259, 0.0534, 0.1723, 0.0331, 0.2188, 0.0129,
- 0.2656, -0.0064, 0.3122, -0.0263, 0.3586, -0.0466,
- 0.4052, -0.0665, 0.4525, -0.0847, 0.5007, -0.1002,
- 0.5497, -0.1130, 0.5991, -0.1240, 0.6491, -0.1325,
- 0.6994, -0.1380, 0.7500, -0.1400
- ]
- ],
- WIND_OFFSETS = [
- {start: 0.36, end: 0.11},
- {start: 0.56, end: 0.16}
- ];
-
- function leaf(ctx, t, x, y, cw, s, color) {
- var a = cw / 8,
- b = a / 3,
- c = 2 * b,
- d = (t % 1) * TAU,
- e = Math.cos(d),
- f = Math.sin(d);
-
- ctx.fillStyle = color;
- ctx.strokeStyle = color;
- ctx.lineWidth = s;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- ctx.beginPath();
- ctx.arc(x , y , a, d , d + Math.PI, false);
- ctx.arc(x - b * e, y - b * f, c, d + Math.PI, d , false);
- ctx.arc(x + c * e, y + c * f, b, d + Math.PI, d , true );
- ctx.globalCompositeOperation = 'destination-out';
- ctx.fill();
- ctx.globalCompositeOperation = 'source-over';
- ctx.stroke();
- }
-
- function swoosh(ctx, t, cx, cy, cw, s, index, total, color) {
- t /= 2500;
-
- var path = WIND_PATHS[index],
- a = (t + index - WIND_OFFSETS[index].start) % total,
- c = (t + index - WIND_OFFSETS[index].end ) % total,
- e = (t + index ) % total,
- b, d, f, i;
-
- ctx.strokeStyle = color;
- ctx.lineWidth = s;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- if(a < 1) {
- ctx.beginPath();
-
- a *= path.length / 2 - 1;
- b = Math.floor(a);
- a -= b;
- b *= 2;
- b += 2;
-
- ctx.moveTo(
- cx + (path[b - 2] * (1 - a) + path[b ] * a) * cw,
- cy + (path[b - 1] * (1 - a) + path[b + 1] * a) * cw
- );
-
- if(c < 1) {
- c *= path.length / 2 - 1;
- d = Math.floor(c);
- c -= d;
- d *= 2;
- d += 2;
-
- for(i = b; i !== d; i += 2)
- ctx.lineTo(cx + path[i] * cw, cy + path[i + 1] * cw);
-
- ctx.lineTo(
- cx + (path[d - 2] * (1 - c) + path[d ] * c) * cw,
- cy + (path[d - 1] * (1 - c) + path[d + 1] * c) * cw
- );
- }
-
- else
- for(i = b; i !== path.length; i += 2)
- ctx.lineTo(cx + path[i] * cw, cy + path[i + 1] * cw);
-
- ctx.stroke();
- }
-
- else if(c < 1) {
- ctx.beginPath();
-
- c *= path.length / 2 - 1;
- d = Math.floor(c);
- c -= d;
- d *= 2;
- d += 2;
-
- ctx.moveTo(cx + path[0] * cw, cy + path[1] * cw);
-
- for(i = 2; i !== d; i += 2)
- ctx.lineTo(cx + path[i] * cw, cy + path[i + 1] * cw);
-
- ctx.lineTo(
- cx + (path[d - 2] * (1 - c) + path[d ] * c) * cw,
- cy + (path[d - 1] * (1 - c) + path[d + 1] * c) * cw
- );
-
- ctx.stroke();
- }
-
- if(e < 1) {
- e *= path.length / 2 - 1;
- f = Math.floor(e);
- e -= f;
- f *= 2;
- f += 2;
-
- leaf(
- ctx,
- t,
- cx + (path[f - 2] * (1 - e) + path[f ] * e) * cw,
- cy + (path[f - 1] * (1 - e) + path[f + 1] * e) * cw,
- cw,
- s,
- color
- );
- }
- }
-
- var Skycons = function(opts) {
- this.list = [];
- this.interval = null;
- this.color = opts && opts.color ? opts.color : "black";
- this.resizeClear = !!(opts && opts.resizeClear);
- };
-
- Skycons.CLEAR_DAY = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- sun(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, color);
- };
-
- Skycons.CLEAR_NIGHT = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- moon(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, color);
- };
-
- Skycons.PARTLY_CLOUDY_DAY = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- sun(ctx, t, w * 0.625, h * 0.375, s * 0.75, s * STROKE, color);
- cloud(ctx, t, w * 0.375, h * 0.625, s * 0.75, s * STROKE, color);
- };
-
- Skycons.PARTLY_CLOUDY_NIGHT = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- moon(ctx, t, w * 0.667, h * 0.375, s * 0.75, s * STROKE, color);
- cloud(ctx, t, w * 0.375, h * 0.625, s * 0.75, s * STROKE, color);
- };
-
- Skycons.CLOUDY = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- cloud(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, color);
- };
-
- Skycons.RAIN = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- rain(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
- cloud(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
- };
-
- Skycons.SLEET = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- sleet(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
- cloud(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
- };
-
- Skycons.SNOW = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- snow(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
- cloud(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
- };
-
- Skycons.WIND = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h);
-
- swoosh(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, 0, 2, color);
- swoosh(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, 1, 2, color);
- };
-
- Skycons.FOG = function(ctx, t, color) {
- var w = ctx.canvas.width,
- h = ctx.canvas.height,
- s = Math.min(w, h),
- k = s * STROKE;
-
- fogbank(ctx, t, w * 0.5, h * 0.32, s * 0.75, k, color);
-
- t /= 5000;
-
- var a = Math.cos((t ) * TAU) * s * 0.02,
- b = Math.cos((t + 0.25) * TAU) * s * 0.02,
- c = Math.cos((t + 0.50) * TAU) * s * 0.02,
- d = Math.cos((t + 0.75) * TAU) * s * 0.02,
- n = h * 0.936,
- e = Math.floor(n - k * 0.5) + 0.5,
- f = Math.floor(n - k * 2.5) + 0.5;
-
- ctx.strokeStyle = color;
- ctx.lineWidth = k;
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
-
- line(ctx, a + w * 0.2 + k * 0.5, e, b + w * 0.8 - k * 0.5, e);
- line(ctx, c + w * 0.2 + k * 0.5, f, d + w * 0.8 - k * 0.5, f);
- };
-
- Skycons.prototype = {
- _determineDrawingFunction: function(draw) {
- if(typeof draw === "string")
- draw = Skycons[draw.toUpperCase().replace(/-/g, "_")] || null;
-
- return draw;
- },
- add: function(el, draw) {
- var obj;
-
- if(typeof el === "string")
- el = document.getElementById(el);
-
- // Does nothing if canvas name doesn't exists
- if(el === null)
- return;
-
- draw = this._determineDrawingFunction(draw);
-
- // Does nothing if the draw function isn't actually a function
- if(typeof draw !== "function")
- return;
-
- obj = {
- element: el,
- context: el.getContext("2d"),
- drawing: draw
- };
-
- this.list.push(obj);
- this.draw(obj, KEYFRAME);
- },
- set: function(el, draw) {
- var i;
-
- if(typeof el === "string")
- el = document.getElementById(el);
-
- for(i = this.list.length; i--; )
- if(this.list[i].element === el) {
- this.list[i].drawing = this._determineDrawingFunction(draw);
- this.draw(this.list[i], KEYFRAME);
- return;
- }
-
- this.add(el, draw);
- },
- remove: function(el) {
- var i;
-
- if(typeof el === "string")
- el = document.getElementById(el);
-
- for(i = this.list.length; i--; )
- if(this.list[i].element === el) {
- this.list.splice(i, 1);
- return;
- }
- },
- draw: function(obj, time) {
- var canvas = obj.context.canvas;
-
- if(this.resizeClear)
- canvas.width = canvas.width;
-
- else
- obj.context.clearRect(0, 0, canvas.width, canvas.height);
-
- obj.drawing(obj.context, time, this.color);
- },
- play: function() {
- var self = this;
-
- this.pause();
- this.interval = requestInterval(function() {
- var now = Date.now(),
- i;
-
- for(i = self.list.length; i--; )
- self.draw(self.list[i], now);
- }, 1000 / 60);
- },
- pause: function() {
- if(this.interval) {
- cancelInterval(this.interval);
- this.interval = null;
- }
- }
- };
-
- global.Skycons = Skycons;
-}(this));
diff --git a/platypush/backend/http/static/js/widgets/image-carousel/index.js b/platypush/backend/http/static/js/widgets/image-carousel/index.js
deleted file mode 100644
index f7e4e991..00000000
--- a/platypush/backend/http/static/js/widgets/image-carousel/index.js
+++ /dev/null
@@ -1,59 +0,0 @@
-Vue.component('image-carousel', {
- template: '#tmpl-widget-image-carousel',
- props: ['config'],
-
- data: function() {
- return {
- images: [],
- currentImage: undefined,
- };
- },
-
- methods: {
- refresh: async function() {
- if (!this.images.length) {
- this.images = await request('utils.search_web_directory', {
- directory: this.config.images_path,
- extensions: ['.jpg', '.jpeg', '.png'],
- });
-
- this.shuffleImages();
- }
-
- this.currentImage = this.images.pop();
- },
-
- onNewImage: function() {
- this.$refs.background.style['background-image'] = 'url(' + this.currentImage + ')';
- this.$refs.img.style.width = 'auto';
-
- if (this.$refs.img.width > this.$refs.img.height) {
- if ((this.$refs.img.width / this.$refs.img.height) >= 1.25) {
- this.$refs.img.style.width = '100%';
- }
-
- if ((this.$refs.img.width / this.$refs.img.height) <= 16/9) {
- this.$refs.img.style.height = '100%';
- }
- }
- },
-
- shuffleImages: function() {
- for (var i=this.images.length-1; i > 0; i--) {
- let j = Math.floor(Math.random() * (i+1));
- let x = this.images[i];
- Vue.set(this.images, i, this.images[j]);
- Vue.set(this.images, j, x);
- }
- },
- },
-
- mounted: function() {
- this.$refs.img.addEventListener('load', this.onNewImage);
- this.$refs.img.addEventListener('error', this.refresh);
-
- this.refresh();
- setInterval(this.refresh, 'refresh_seconds' in this.config ? this.config.refresh_seconds*1000 : 15000);
- },
-});
-
diff --git a/platypush/backend/http/static/js/widgets/music/index.js b/platypush/backend/http/static/js/widgets/music/index.js
deleted file mode 100644
index 0626f101..00000000
--- a/platypush/backend/http/static/js/widgets/music/index.js
+++ /dev/null
@@ -1,233 +0,0 @@
-Vue.component('music', {
- template: '#tmpl-widget-music',
- props: ['config'],
-
- data: function() {
- return {
- track: undefined,
- status: undefined,
- timer: undefined,
-
- syncTime: {
- timestamp: null,
- elapsed: null,
- },
- };
- },
-
- methods: {
- refresh: async function() {
- let status = await request('music.mpd.status');
- let track = await request('music.mpd.currentsong');
-
- this._parseStatus(status);
- this._parseTrack(track);
-
- if (status.state === 'play' && !this.timer)
- this.startTimer();
- else if (status.state !== 'play' && this.timer)
- this.stopTimer();
- },
-
- convertTime: function(time) {
- time = parseFloat(time); // Normalize strings
- var t = {};
- t.h = '' + parseInt(time/3600);
- t.m = '' + parseInt(time/60 - t.h*60);
- t.s = '' + parseInt(time - (t.h*3600 + t.m*60));
-
- for (var attr of ['m','s']) {
- if (parseInt(t[attr]) < 10) {
- t[attr] = '0' + t[attr];
- }
- }
-
- var ret = [];
- if (parseInt(t.h)) {
- ret.push(t.h);
- }
-
- ret.push(t.m, t.s);
- return ret.join(':');
- },
-
- _parseStatus: async function(status) {
- if (!status || status.length === 0)
- status = await request('music.mpd.status');
-
- if (!this.status)
- this.status = {};
-
- for (const [attr, value] of Object.entries(status)) {
- if (['consume','random','repeat','single','bitrate'].indexOf(attr) >= 0) {
- Vue.set(this.status, attr, !!parseInt(value));
- } else if (['nextsong','nextsongid','playlist','playlistlength',
- 'volume','xfade','song','songid'].indexOf(attr) >= 0) {
- Vue.set(this.status, attr, parseInt(value));
- } else if (['elapsed'].indexOf(attr) >= 0) {
- Vue.set(this.status, attr, parseFloat(value));
- } else {
- Vue.set(this.status, attr, value);
- }
- }
- },
-
- _parseTrack: async function(track) {
- if (!track || track.length === 0) {
- track = await request('music.mpd.currentsong');
- }
-
- if (!this.track)
- this.track = {};
-
- for (const [attr, value] of Object.entries(track)) {
- if (['id','pos','time','track','disc'].indexOf(attr) >= 0) {
- Vue.set(this.track, attr, parseInt(value));
- } else {
- Vue.set(this.track, attr, value);
- }
- }
- },
-
- showNewTrackNotification: function() {
- createNotification({
- html: '' + (this.track.artist || '[No Artist]') + '
' +
- (this.track.title || '[No Title]'),
- image: {
- icon: 'play',
- }
- });
- },
-
- onNewPlayingTrack: async function(event) {
- let previousTrack = undefined;
-
- if (this.track) {
- previousTrack = {
- file: this.track.file,
- artist: this.track.artist,
- title: this.track.title,
- };
- }
-
- this.status.state = 'play';
- Vue.set(this.status, 'elapsed', 0);
- this.track = {};
- this._parseTrack(event.track);
-
- let status = event.status ? event.status : await request('music.mpd.status');
- this._parseStatus(status);
- this.startTimer();
-
- if (previousTrack && this.track.file != previousTrack.file
- || this.track.artist != previousTrack.artist
- || this.track.title != previousTrack.title) {
- this.showNewTrackNotification();
- }
- },
-
- onMusicStop: function(event) {
- this.status.state = 'stop';
- Vue.set(this.status, 'elapsed', 0);
- this._parseStatus(event.status);
- this._parseTrack(event.track);
- this.stopTimer();
- },
-
- onMusicPlay: function(event) {
- this.status.state = 'play';
- this._parseStatus(event.status);
- this._parseTrack(event.track);
- this.startTimer();
- },
-
- onMusicPause: function(event) {
- this.status.state = 'pause';
- this._parseStatus(event.status);
- this._parseTrack(event.track);
-
- Vue.set(this.syncTime, 'timestamp', new Date());
- Vue.set(this.syncTime, 'elapsed', this.status.elapsed);
- },
-
- onSeekChange: function(event) {
- if (event.position != null)
- Vue.set(this.status, 'elapsed', parseFloat(event.position));
- if (event.status)
- this._parseStatus(event.status);
- if (event.track)
- this._parseTrack(event.track);
-
- Vue.set(this.syncTime, 'timestamp', new Date());
- Vue.set(this.syncTime, 'elapsed', this.status.elapsed);
- },
-
- onVolumeChange: function(event) {
- if (event.volume != null)
- this.status.volume = parseFloat(event.volume);
- if (event.status)
- this._parseStatus(event.status);
- if (event.track)
- this._parseTrack(event.track);
- },
-
- onRepeatChange: function(event) {
- this.status.repeat = event.state;
- },
-
- onRandomChange: function(event) {
- this.status.random = event.state;
- },
-
- onConsumeChange: function(event) {
- this.status.consume = event.state;
- },
-
- onSingleChange: function(event) {
- this.status.single = event.state;
- },
-
- startTimer: function() {
- if (this.timer != null) {
- this.stopTimer();
- }
-
- Vue.set(this.syncTime, 'timestamp', new Date());
- Vue.set(this.syncTime, 'elapsed', this.status.elapsed);
- this.timer = setInterval(this.timerFunc, 1000);
- },
-
- stopTimer: function() {
- if (this.timer == null) {
- clearInterval(this.timer);
- this.timer = null;
- }
- },
-
- timerFunc: function() {
- if (this.status.state !== 'play' || this.status.elapsed == null) {
- return;
- }
-
- Vue.set(this.status, 'elapsed', this.syncTime.elapsed +
- ((new Date()).getTime()/1000) - (this.syncTime.timestamp.getTime()/1000));
- },
- },
-
- mounted: function() {
- this.refresh();
- setInterval(this.refresh, 60000);
-
- registerEventHandler(this.onNewPlayingTrack, 'platypush.message.event.music.NewPlayingTrackEvent');
- registerEventHandler(this.onMusicStop, 'platypush.message.event.music.MusicStopEvent');
- registerEventHandler(this.onMusicPlay, 'platypush.message.event.music.MusicPlayEvent');
- registerEventHandler(this.onMusicPause, 'platypush.message.event.music.MusicPauseEvent');
- registerEventHandler(this.onSeekChange, 'platypush.message.event.music.SeekChangeEvent');
- registerEventHandler(this.onVolumeChange, 'platypush.message.event.music.VolumeChangeEvent');
- registerEventHandler(this.onRepeatChange, 'platypush.message.event.music.PlaybackRepeatModeChangeEvent');
- registerEventHandler(this.onRandomChange, 'platypush.message.event.music.PlaybackRandomModeChangeEvent');
- registerEventHandler(this.onConsumeChange, 'platypush.message.event.music.PlaybackConsumeModeChangeEvent');
- registerEventHandler(this.onSingleChange, 'platypush.message.event.music.PlaybackSingleModeChangeEvent');
- },
-});
-
diff --git a/platypush/backend/http/static/js/widgets/rss-news/index.js b/platypush/backend/http/static/js/widgets/rss-news/index.js
deleted file mode 100644
index 1e16c6b0..00000000
--- a/platypush/backend/http/static/js/widgets/rss-news/index.js
+++ /dev/null
@@ -1,40 +0,0 @@
-Vue.component('rss-news', {
- template: '#tmpl-widget-rss-news',
- props: ['config'],
-
- data: function() {
- return {
- articles: [],
- queue: [],
- currentArticle: undefined,
- };
- },
-
- methods: {
- refresh: async function() {
- if (!this.queue.length) {
- this.articles = await request('db.select', {
- engine: this.config.db,
- query: "select s.title as source, e.title, e.summary, " +
- "strftime('%Y-%m-%dT%H:%M:%fZ', e.published) as published " +
- "from FeedEntry e join FeedSource s " +
- "on e.source_id = s.id order by e.published desc limit " +
- ('limit' in this.config ? this.config.limit : 10)
- });
-
- this.queue = [...this.articles];
- }
-
- if (!this.queue.length)
- return;
-
- this.currentArticle = this.queue.pop();
- },
- },
-
- mounted: function() {
- this.refresh();
- setInterval(this.refresh, 'refresh_seconds' in this.config ? this.config.refresh_seconds*1000 : 15000);
- },
-});
-
diff --git a/platypush/backend/http/templates/css-common.html b/platypush/backend/http/templates/css-common.html
deleted file mode 100644
index cd882722..00000000
--- a/platypush/backend/http/templates/css-common.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/dashboard.html b/platypush/backend/http/templates/dashboard.html
deleted file mode 100644
index 7ccc4d74..00000000
--- a/platypush/backend/http/templates/dashboard.html
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
- Platypush Dashboard
-
-
- {% include 'css-common.html' %}
-
-
-
- {% include 'js-common.html' %}
-
-
-
-
-
-
- {% include 'elements.html' %}
-
- {% for widget in config['widgets'] %}
- {% with name = widget['widget'] %}
- {% include 'widgets/' + name + '/index.html' %}
-
- {% with js_file = static_folder + '/js/widgets/' + name + '/index.js' %}
- {% if utils.isfile(js_file) %}
-
- {% endif %}
- {% endwith %}
-
- {% with css_file = static_folder + '/css/dist/dashboard/widgets/' + name + '.css' %}
- {% if utils.isfile(css_file) %}
-
- {% endif %}
- {% endwith %}
- {% endwith %}
- {% endfor %}
-
-
-
-
-
-
-
-
-
-
-
-
- {% include 'notifications.html' %}
-
-
-
- {% include 'widgets/template.html' %}
-
-
-
-
diff --git a/platypush/backend/http/templates/elements.html b/platypush/backend/http/templates/elements.html
deleted file mode 100644
index a9a6dc3d..00000000
--- a/platypush/backend/http/templates/elements.html
+++ /dev/null
@@ -1,6 +0,0 @@
-{% include 'elements/common.html' %}
-{% include 'elements/switch.html' %}
-{% include 'elements/range-slider.html' %}
-{% include 'elements/dropdown.html' %}
-{% include 'elements/modal.html' %}
-
diff --git a/platypush/backend/http/templates/elements/common.html b/platypush/backend/http/templates/elements/common.html
deleted file mode 100644
index 23ee1261..00000000
--- a/platypush/backend/http/templates/elements/common.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/elements/dropdown.html b/platypush/backend/http/templates/elements/dropdown.html
deleted file mode 100644
index 82e4e9b4..00000000
--- a/platypush/backend/http/templates/elements/dropdown.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/elements/modal.html b/platypush/backend/http/templates/elements/modal.html
deleted file mode 100644
index 299d2851..00000000
--- a/platypush/backend/http/templates/elements/modal.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/elements/range-slider.html b/platypush/backend/http/templates/elements/range-slider.html
deleted file mode 100644
index e394f0d3..00000000
--- a/platypush/backend/http/templates/elements/range-slider.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/elements/switch.html b/platypush/backend/http/templates/elements/switch.html
deleted file mode 100644
index a8e8ed10..00000000
--- a/platypush/backend/http/templates/elements/switch.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/index.html b/platypush/backend/http/templates/index.html
deleted file mode 100644
index 4c7a31d1..00000000
--- a/platypush/backend/http/templates/index.html
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
- Platypush Web Panel
-
-
-
- {% include 'css-common.html' %}
-
-
-
-
- {% include 'js-common.html' %}
-
-
- {% for style in styles.values() %}
-
- {% endfor %}
-
- {% include 'elements.html' %}
-
- {% for plugin, conf in templates.items() %}
- {% with configuration=templates[plugin] %}
- {% include conf['_template_file'] %}
- {% endwith %}
- {% endfor %}
-
- {% for script in scripts.values() %}
-
- {% endfor %}
-
-
-
-
- {% with plugins=templates.keys() %}
- {% include 'nav.html' %}
- {% endwith %}
-
-
-
-
-
- {% include 'notifications.html' %}
-
-
- {% include 'plugins/template.html' %}
-
-
-
-
diff --git a/platypush/backend/http/templates/js-common.html b/platypush/backend/http/templates/js-common.html
deleted file mode 100644
index b99394d1..00000000
--- a/platypush/backend/http/templates/js-common.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% if utils.isfile(static_folder, 'js', 'lib', 'vue.js') %}
-
-{% else %}
-
-{% endif %}
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/login.html b/platypush/backend/http/templates/login.html
deleted file mode 100644
index 42e177a9..00000000
--- a/platypush/backend/http/templates/login.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
- Platypush login page
-
- {% include 'css-common.html' %}
-
-
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/nav.html b/platypush/backend/http/templates/nav.html
deleted file mode 100644
index 6023c120..00000000
--- a/platypush/backend/http/templates/nav.html
+++ /dev/null
@@ -1,61 +0,0 @@
-{%
- with pluginIcons = {
- 'camera': 'fas fa-camera',
- 'camera.android.ipcam': 'fab fa-android',
- 'camera.cv': 'fas fa-camera',
- 'camera.ffmpeg': 'fas fa-camera',
- 'camera.gstreamer': 'fas fa-camera',
- 'camera.pi': 'fab fa-raspberry-pi',
- 'camera.ir.mlx90640': 'fas fa-sun',
- 'execute': 'fas fa-play',
- 'gpio': 'fas fa-plug',
- 'gpio.zeroborg': 'fas fa-robot',
- 'light.hue': 'fa fa-lightbulb',
- 'media.gstreamer': 'fa fa-film',
- 'media.mplayer': 'fa fa-film',
- 'media.mpv': 'fa fa-film',
- 'media.omxplayer': 'fa fa-film',
- 'media.vlc': 'fa fa-film',
- 'music.mpd': 'fa fa-music',
- 'music.snapcast': 'fa fa-volume-up',
- 'sensors': 'fas fa-thermometer-half',
- 'sound': 'fas fa-microphone',
- 'switches': 'fa fa-toggle-on',
- 'tts': 'fa fa-comment',
- 'tts.google': 'fa fa-comment',
- 'tv.samsung.ws': 'fas fa-tv',
- 'zigbee.mqtt': 'fa fa-zigbee',
- 'zwave': 'fa fa-zwave',
- }
-%}
-
-
-
-{% endwith %}
-
diff --git a/platypush/backend/http/templates/notifications.html b/platypush/backend/http/templates/notifications.html
deleted file mode 100644
index 0825ff5a..00000000
--- a/platypush/backend/http/templates/notifications.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugin.html b/platypush/backend/http/templates/plugin.html
deleted file mode 100644
index 72c4256f..00000000
--- a/platypush/backend/http/templates/plugin.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
- {{ plugin }}
-
-
-
- {% include 'css-common.html' %}
-
-
-
-
- {% include 'js-common.html' %}
-
-
-
-
-
- {% include 'elements.html' %}
-
- {% with configuration=conf %}
- {% include template %}
- {% endwith %}
-
-
-
-
-
-
-
-
-
-
- {% include 'notifications.html' %}
-
-
- {% include 'plugins/template.html' %}
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/camera.android.ipcam/index.html b/platypush/backend/http/templates/plugins/camera.android.ipcam/index.html
deleted file mode 100644
index f588f39b..00000000
--- a/platypush/backend/http/templates/plugins/camera.android.ipcam/index.html
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/camera.cv/index.html b/platypush/backend/http/templates/plugins/camera.cv/index.html
deleted file mode 100644
index 988bcd66..00000000
--- a/platypush/backend/http/templates/plugins/camera.cv/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/camera.ffmpeg/index.html b/platypush/backend/http/templates/plugins/camera.ffmpeg/index.html
deleted file mode 100644
index bb22481c..00000000
--- a/platypush/backend/http/templates/plugins/camera.ffmpeg/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/camera.gstreamer/index.html b/platypush/backend/http/templates/plugins/camera.gstreamer/index.html
deleted file mode 100644
index 60510169..00000000
--- a/platypush/backend/http/templates/plugins/camera.gstreamer/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/camera.ir.mlx90640/index.html b/platypush/backend/http/templates/plugins/camera.ir.mlx90640/index.html
deleted file mode 100644
index 943cfb02..00000000
--- a/platypush/backend/http/templates/plugins/camera.ir.mlx90640/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/camera.pi/index.html b/platypush/backend/http/templates/plugins/camera.pi/index.html
deleted file mode 100644
index a6f31603..00000000
--- a/platypush/backend/http/templates/plugins/camera.pi/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/camera/index.html b/platypush/backend/http/templates/plugins/camera/index.html
deleted file mode 100644
index 1a05b3d8..00000000
--- a/platypush/backend/http/templates/plugins/camera/index.html
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
The camera is not active
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/execute/index.html b/platypush/backend/http/templates/plugins/execute/index.html
deleted file mode 100644
index 6ea560bb..00000000
--- a/platypush/backend/http/templates/plugins/execute/index.html
+++ /dev/null
@@ -1,112 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/gpio/index.html b/platypush/backend/http/templates/plugins/gpio/index.html
deleted file mode 100644
index 84a5d915..00000000
--- a/platypush/backend/http/templates/plugins/gpio/index.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/animations.html b/platypush/backend/http/templates/plugins/light.hue/animations.html
deleted file mode 100644
index 168003bb..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/animations.html
+++ /dev/null
@@ -1,41 +0,0 @@
-{% with templates = utils.find_templates_in_dir('plugins/light.hue/animations') %}
- {% for template in templates %}
- {% include template %}
- {% endfor %}
-{% endwith %}
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/animations/blink.html b/platypush/backend/http/templates/plugins/light.hue/animations/blink.html
deleted file mode 100644
index bd72d9c7..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/animations/blink.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/animations/color_transition.html b/platypush/backend/http/templates/plugins/light.hue/animations/color_transition.html
deleted file mode 100644
index 5aa3fdd8..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/animations/color_transition.html
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/elements.html b/platypush/backend/http/templates/plugins/light.hue/elements.html
deleted file mode 100644
index 357dccfd..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/elements.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/groups.html b/platypush/backend/http/templates/plugins/light.hue/groups.html
deleted file mode 100644
index 1b4450d8..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/groups.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/index.html b/platypush/backend/http/templates/plugins/light.hue/index.html
deleted file mode 100644
index aaf7a34b..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/index.html
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-{% include 'plugins/light.hue/elements.html' %}
-{% include 'plugins/light.hue/groups.html' %}
-{% include 'plugins/light.hue/scenes.html' %}
-{% include 'plugins/light.hue/units.html' %}
-{% include 'plugins/light.hue/animations.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/scenes.html b/platypush/backend/http/templates/plugins/light.hue/scenes.html
deleted file mode 100644
index f522ac4b..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/scenes.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/light.hue/units.html b/platypush/backend/http/templates/plugins/light.hue/units.html
deleted file mode 100644
index 3fe70759..00000000
--- a/platypush/backend/http/templates/plugins/light.hue/units.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media.gstreamer/index.html b/platypush/backend/http/templates/plugins/media.gstreamer/index.html
deleted file mode 100644
index dc35992f..00000000
--- a/platypush/backend/http/templates/plugins/media.gstreamer/index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-{% include 'plugins/media/index.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media.mplayer/index.html b/platypush/backend/http/templates/plugins/media.mplayer/index.html
deleted file mode 100644
index f1e52f66..00000000
--- a/platypush/backend/http/templates/plugins/media.mplayer/index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-{% include 'plugins/media/index.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media.mpv/index.html b/platypush/backend/http/templates/plugins/media.mpv/index.html
deleted file mode 100644
index dba35b8b..00000000
--- a/platypush/backend/http/templates/plugins/media.mpv/index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-{% include 'plugins/media/index.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media.omxplayer/index.html b/platypush/backend/http/templates/plugins/media.omxplayer/index.html
deleted file mode 100644
index 6547fee4..00000000
--- a/platypush/backend/http/templates/plugins/media.omxplayer/index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-{% include 'plugins/media/index.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media.vlc/index.html b/platypush/backend/http/templates/plugins/media.vlc/index.html
deleted file mode 100644
index 9d6fb93a..00000000
--- a/platypush/backend/http/templates/plugins/media.vlc/index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-{% include 'plugins/media/index.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/controls.html b/platypush/backend/http/templates/plugins/media/controls.html
deleted file mode 100644
index 90205177..00000000
--- a/platypush/backend/http/templates/plugins/media/controls.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/devices.html b/platypush/backend/http/templates/plugins/media/devices.html
deleted file mode 100644
index 37064cdc..00000000
--- a/platypush/backend/http/templates/plugins/media/devices.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-{% for script in utils.search_directory(static_folder + '/js/plugins/media/players', 'js', recursive=True) %}
-
-{% endfor %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/index.html b/platypush/backend/http/templates/plugins/media/index.html
deleted file mode 100644
index 61889906..00000000
--- a/platypush/backend/http/templates/plugins/media/index.html
+++ /dev/null
@@ -1,75 +0,0 @@
-{% include 'plugins/media/search.html' %}
-{% include 'plugins/media/controls.html' %}
-{% include 'plugins/media/results.html' %}
-{% include 'plugins/media/item.html' %}
-{% include 'plugins/media/info.html' %}
-{% include 'plugins/media/subs.html' %}
-{% include 'plugins/media/torrents.html' %}
-
-
-
-{% for script in utils.search_directory(static_folder + '/js/plugins/media/handlers', 'js', recursive=True) %}
- {% if script != 'base.js' %}
-
- {% endif %}
-{% endfor %}
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/info.html b/platypush/backend/http/templates/plugins/media/info.html
deleted file mode 100644
index d59b5ef3..00000000
--- a/platypush/backend/http/templates/plugins/media/info.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/item.html b/platypush/backend/http/templates/plugins/media/item.html
deleted file mode 100644
index 45b63005..00000000
--- a/platypush/backend/http/templates/plugins/media/item.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/results.html b/platypush/backend/http/templates/plugins/media/results.html
deleted file mode 100644
index 77abbbaa..00000000
--- a/platypush/backend/http/templates/plugins/media/results.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/search.html b/platypush/backend/http/templates/plugins/media/search.html
deleted file mode 100644
index c759b889..00000000
--- a/platypush/backend/http/templates/plugins/media/search.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% include 'plugins/media/devices.html' %}
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/subs.html b/platypush/backend/http/templates/plugins/media/subs.html
deleted file mode 100644
index c9e75f89..00000000
--- a/platypush/backend/http/templates/plugins/media/subs.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/media/torrents.html b/platypush/backend/http/templates/plugins/media/torrents.html
deleted file mode 100644
index 703f6d49..00000000
--- a/platypush/backend/http/templates/plugins/media/torrents.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.mpd/browser.html b/platypush/backend/http/templates/plugins/music.mpd/browser.html
deleted file mode 100644
index 2720db41..00000000
--- a/platypush/backend/http/templates/plugins/music.mpd/browser.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.mpd/index.html b/platypush/backend/http/templates/plugins/music.mpd/index.html
deleted file mode 100644
index 1de680c9..00000000
--- a/platypush/backend/http/templates/plugins/music.mpd/index.html
+++ /dev/null
@@ -1,336 +0,0 @@
-
-
-{% include 'plugins/music.mpd/browser.html' %}
-{% include 'plugins/music.mpd/playlist.html' %}
-{% include 'plugins/music.mpd/search.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.mpd/playlist.html b/platypush/backend/http/templates/plugins/music.mpd/playlist.html
deleted file mode 100644
index b24cb7c9..00000000
--- a/platypush/backend/http/templates/plugins/music.mpd/playlist.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.mpd/search.html b/platypush/backend/http/templates/plugins/music.mpd/search.html
deleted file mode 100644
index 3e153980..00000000
--- a/platypush/backend/http/templates/plugins/music.mpd/search.html
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/client.html b/platypush/backend/http/templates/plugins/music.snapcast/client.html
deleted file mode 100644
index 00392b62..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/client.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/group.html b/platypush/backend/http/templates/plugins/music.snapcast/group.html
deleted file mode 100644
index 7f7e80ea..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/group.html
+++ /dev/null
@@ -1,33 +0,0 @@
-{% include 'plugins/music.snapcast/client.html' %}
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/host.html b/platypush/backend/http/templates/plugins/music.snapcast/host.html
deleted file mode 100644
index b79cd4d4..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/host.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% include 'plugins/music.snapcast/group.html' %}
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/index.html b/platypush/backend/http/templates/plugins/music.snapcast/index.html
deleted file mode 100644
index d65ff5e2..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/index.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% include 'plugins/music.snapcast/host.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/modals/client.html b/platypush/backend/http/templates/plugins/music.snapcast/modals/client.html
deleted file mode 100644
index d22cb36e..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/modals/client.html
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
-
-
-
-
-
Volume
-
{% raw %}{{ info.config.volume.percent }}%{% endraw %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/modals/group.html b/platypush/backend/http/templates/plugins/music.snapcast/modals/group.html
deleted file mode 100644
index a0c90bb4..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/modals/group.html
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
Stream
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/music.snapcast/modals/host.html b/platypush/backend/http/templates/plugins/music.snapcast/modals/host.html
deleted file mode 100644
index 24954688..00000000
--- a/platypush/backend/http/templates/plugins/music.snapcast/modals/host.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Control protocol version
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/sensors/index.html b/platypush/backend/http/templates/plugins/sensors/index.html
deleted file mode 100644
index 5583e3e3..00000000
--- a/platypush/backend/http/templates/plugins/sensors/index.html
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
diff --git a/platypush/backend/http/templates/plugins/sound/index.html b/platypush/backend/http/templates/plugins/sound/index.html
deleted file mode 100644
index e80d86ab..00000000
--- a/platypush/backend/http/templates/plugins/sound/index.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/switches/index.html b/platypush/backend/http/templates/plugins/switches/index.html
deleted file mode 100644
index f8241a4e..00000000
--- a/platypush/backend/http/templates/plugins/switches/index.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/template.html b/platypush/backend/http/templates/plugins/template.html
deleted file mode 100644
index cca5bd02..00000000
--- a/platypush/backend/http/templates/plugins/template.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/tts.google/index.html b/platypush/backend/http/templates/plugins/tts.google/index.html
deleted file mode 100644
index e08192d4..00000000
--- a/platypush/backend/http/templates/plugins/tts.google/index.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/tts/common.html b/platypush/backend/http/templates/plugins/tts/common.html
deleted file mode 100644
index 992ba8e4..00000000
--- a/platypush/backend/http/templates/plugins/tts/common.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/platypush/backend/http/templates/plugins/tts/index.html b/platypush/backend/http/templates/plugins/tts/index.html
deleted file mode 100644
index a8de0e8c..00000000
--- a/platypush/backend/http/templates/plugins/tts/index.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/tv.samsung.ws/index.html b/platypush/backend/http/templates/plugins/tv.samsung.ws/index.html
deleted file mode 100644
index 0824208e..00000000
--- a/platypush/backend/http/templates/plugins/tv.samsung.ws/index.html
+++ /dev/null
@@ -1,158 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/plugins/zigbee.mqtt/device.html b/platypush/backend/http/templates/plugins/zigbee.mqtt/device.html
deleted file mode 100644
index a481399a..00000000
--- a/platypush/backend/http/templates/plugins/zigbee.mqtt/device.html
+++ /dev/null
@@ -1,163 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zigbee.mqtt/group.html b/platypush/backend/http/templates/plugins/zigbee.mqtt/group.html
deleted file mode 100644
index 9ba2e44e..00000000
--- a/platypush/backend/http/templates/plugins/zigbee.mqtt/group.html
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zigbee.mqtt/index.html b/platypush/backend/http/templates/plugins/zigbee.mqtt/index.html
deleted file mode 100644
index e059679f..00000000
--- a/platypush/backend/http/templates/plugins/zigbee.mqtt/index.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{% include 'plugins/zigbee.mqtt/device.html' %}
-{% include 'plugins/zigbee.mqtt/group.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zigbee.mqtt/modals/group.html b/platypush/backend/http/templates/plugins/zigbee.mqtt/modals/group.html
deleted file mode 100644
index 0f7f39e5..00000000
--- a/platypush/backend/http/templates/plugins/zigbee.mqtt/modals/group.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zwave/group.html b/platypush/backend/http/templates/plugins/zwave/group.html
deleted file mode 100644
index f2ae849d..00000000
--- a/platypush/backend/http/templates/plugins/zwave/group.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zwave/index.html b/platypush/backend/http/templates/plugins/zwave/index.html
deleted file mode 100644
index 709c60d9..00000000
--- a/platypush/backend/http/templates/plugins/zwave/index.html
+++ /dev/null
@@ -1,172 +0,0 @@
-{% include 'plugins/zwave/node.html' %}
-{% include 'plugins/zwave/group.html' %}
-{% include 'plugins/zwave/value.html' %}
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zwave/modals/group.html b/platypush/backend/http/templates/plugins/zwave/modals/group.html
deleted file mode 100644
index 668bb309..00000000
--- a/platypush/backend/http/templates/plugins/zwave/modals/group.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zwave/modals/network.html b/platypush/backend/http/templates/plugins/zwave/modals/network.html
deleted file mode 100644
index 1cc7fce9..00000000
--- a/platypush/backend/http/templates/plugins/zwave/modals/network.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
- Loading status...
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zwave/node.html b/platypush/backend/http/templates/plugins/zwave/node.html
deleted file mode 100644
index 27cce6d9..00000000
--- a/platypush/backend/http/templates/plugins/zwave/node.html
+++ /dev/null
@@ -1,193 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/plugins/zwave/value.html b/platypush/backend/http/templates/plugins/zwave/value.html
deleted file mode 100644
index c5183b2e..00000000
--- a/platypush/backend/http/templates/plugins/zwave/value.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/register.html b/platypush/backend/http/templates/register.html
deleted file mode 100644
index 45f025be..00000000
--- a/platypush/backend/http/templates/register.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- Platypush registration page
-
- {% include 'css-common.html' %}
-
-
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/settings/index.html b/platypush/backend/http/templates/settings/index.html
deleted file mode 100644
index 2f022f17..00000000
--- a/platypush/backend/http/templates/settings/index.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
- Platypush Settings
-
-
-
- {% include 'css-common.html' %}
-
-
-
-
- {% include 'js-common.html' %}
- {% include 'elements.html' %}
-
-
-
-
- {% include 'settings/nav.html' %}
-
-
- {% include 'settings/sections/users.html' %}
- {% include 'settings/sections/token.html' %}
-
-
- {% include 'notifications.html' %}
-
-
-
-
-
diff --git a/platypush/backend/http/templates/settings/nav.html b/platypush/backend/http/templates/settings/nav.html
deleted file mode 100644
index aaa4f767..00000000
--- a/platypush/backend/http/templates/settings/nav.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/settings/sections/token.html b/platypush/backend/http/templates/settings/sections/token.html
deleted file mode 100644
index 03b94a6e..00000000
--- a/platypush/backend/http/templates/settings/sections/token.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
- No token configured for requests. It is strongly advised to generate a random token (e.g.
- dd if=/dev/urandom bs=18 count=1 | base64) and copy it in the token:
- section of your configuration file.
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/settings/sections/users.html b/platypush/backend/http/templates/settings/sections/users.html
deleted file mode 100644
index df55ce67..00000000
--- a/platypush/backend/http/templates/settings/sections/users.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% for user in users %}
- - {{ user.username }}
- {% endfor %}
-
-
-
-
-
-
-
diff --git a/platypush/backend/http/templates/webplayer.html b/platypush/backend/http/templates/webplayer.html
deleted file mode 100644
index af7216c4..00000000
--- a/platypush/backend/http/templates/webplayer.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
diff --git a/platypush/backend/http/templates/widgets/calendar/index.html b/platypush/backend/http/templates/widgets/calendar/index.html
deleted file mode 100644
index 2be6e8d4..00000000
--- a/platypush/backend/http/templates/widgets/calendar/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/platypush/backend/http/templates/widgets/date-time-weather/index.html b/platypush/backend/http/templates/widgets/date-time-weather/index.html
deleted file mode 100644
index 866e119a..00000000
--- a/platypush/backend/http/templates/widgets/date-time-weather/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
diff --git a/platypush/backend/http/templates/widgets/image-carousel/index.html b/platypush/backend/http/templates/widgets/image-carousel/index.html
deleted file mode 100644
index 4768849c..00000000
--- a/platypush/backend/http/templates/widgets/image-carousel/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/platypush/backend/http/templates/widgets/music/index.html b/platypush/backend/http/templates/widgets/music/index.html
deleted file mode 100644
index 08f59925..00000000
--- a/platypush/backend/http/templates/widgets/music/index.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
diff --git a/platypush/backend/http/templates/widgets/rss-news/index.html b/platypush/backend/http/templates/widgets/rss-news/index.html
deleted file mode 100644
index aad0a9fb..00000000
--- a/platypush/backend/http/templates/widgets/rss-news/index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/platypush/backend/http/templates/widgets/template.html b/platypush/backend/http/templates/widgets/template.html
deleted file mode 100644
index 22852a26..00000000
--- a/platypush/backend/http/templates/widgets/template.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-