From c751a2479010d894e7ce0df1a35bd794873fe29f Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sun, 26 May 2019 23:41:12 +0200 Subject: [PATCH] Added events and websocket support in webpanel refactoring and removed old light.hue templates and scripts --- .../backend/http/static/css/light.hue.css | 127 ---- .../backend/http/static/js/application.js | 2 + platypush/backend/http/static/js/events.js | 119 +++ platypush/backend/http/static/js/light.hue.js | 712 ------------------ .../http/static/js/plugins/light.hue/index.js | 36 +- platypush/backend/http/templates/index.html | 1 + .../http/templates/plugins/light.hue.html | 33 - platypush/backend/light/hue.py | 1 - 8 files changed, 155 insertions(+), 876 deletions(-) delete mode 100644 platypush/backend/http/static/css/light.hue.css create mode 100644 platypush/backend/http/static/js/events.js delete mode 100644 platypush/backend/http/static/js/light.hue.js delete mode 100644 platypush/backend/http/templates/plugins/light.hue.html diff --git a/platypush/backend/http/static/css/light.hue.css b/platypush/backend/http/static/css/light.hue.css deleted file mode 100644 index 97ab1838..00000000 --- a/platypush/backend/http/static/css/light.hue.css +++ /dev/null @@ -1,127 +0,0 @@ -#hue-container { - background-color: #f8f8f8; - padding: 12px; - border: 1px solid #ddd; - border-radius: 10px; - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 400; - line-height: 38px; - letter-spacing: .1rem; -} - - #hue-container > .columns { - margin-left: 0; - } - - #hue-container > .three.columns { - width: 24%; - } - - #hue-container > .six.columns { - width: 52%; - } - -#rooms-list { - border-right: 1px solid #e4e4e4; -} - -#lights-list { - margin-left: 0; - border: 1px solid #e4e4e4; - border-radius: 8px; -} - -.hue-section-title { - padding: 7.5px; - background: #ececec; - border: 1px solid #e4e4e4; -} - -.room-item { - color: #333; - padding: 10px 5px; - border-bottom: 1px solid #ddd; - text-transform: uppercase; - cursor: pointer; -} - -.scene-item { - color: #333; - padding: 5px 10px; - border-bottom: 1px solid #e4e4e4; - cursor: pointer; -} - - .room-item:hover, .light-item:hover, .scene-item:hover { - background-color: #daf8e2 !important; - } - - .room-item.selected, .light-item.selected, .scene-item.selected { - background-color: #c8ffd0 !important; - } - -.room-lights-item, .room-scenes-item { - display: none; -} - -.light-item { - color: #333; - padding: 20px; - cursor: pointer; -} - - .light-item:not(:last-child) { - border-bottom: 1px solid #ddd; - } - -.light-item-name { - display: inline-block; -} - -.all-lights-item, .animation-item { - background-color: #ececec; -} - - .all-lights-item * > .light-item-name { - font-weight: bold; - } - - .animation-item * > .light-item-name { - font-style: italic; - } - -.slider-container { - padding: 1rem .5rem; -} - - .slider-container > .columns { - line-height: initial; - } - - .animation-type-container * > label { - line-height: initial; - font-weight: 100; - } - -.light-ctrl-switch-container { - float: right; - margin-top: -5px; -} - -.light-color-selector, -.animation-selector { - display: none; - background-color: white; - padding: 10px; - margin: 10px -10px -10px -10px; - border-radius: 10px; -} - -.animation-container { - display: none; -} - -.light-slider { - margin-top: 10px; -} - diff --git a/platypush/backend/http/static/js/application.js b/platypush/backend/http/static/js/application.js index da478e43..012da3d7 100644 --- a/platypush/backend/http/static/js/application.js +++ b/platypush/backend/http/static/js/application.js @@ -45,6 +45,8 @@ var app = new Vue({ setInterval(() => { self.now = new Date(); }, 1000) + + initEvents(); }, updated: function() {}, destroyed: function() {}, diff --git a/platypush/backend/http/static/js/events.js b/platypush/backend/http/static/js/events.js new file mode 100644 index 00000000..b918a664 --- /dev/null +++ b/platypush/backend/http/static/js/events.js @@ -0,0 +1,119 @@ +var websocket = { + ws: undefined, + instance: undefined, + pending: false, + opened: false, + timeout: undefined, + reconnectMsecs: 30000, + handlers: {}, +}; + +function initEvents() { + try { + url_prefix = window.config.has_ssl ? 'wss://' : 'ws://'; + websocket.ws = new WebSocket(url_prefix + window.location.hostname + ':' + window.config.websocket_port); + } catch (err) { + console.error("Websocket initialization error"); + console.log(err); + return; + } + + websocket.pending = true; + + var onWebsocketTimeout = function(self) { + return function() { + console.log('Websocket reconnection timed out, retrying'); + websocket.pending = false; + self.close(); + self.onclose(); + }; + }; + + websocket.timeout = setTimeout( + onWebsocketTimeout(websocket.ws), websocket.reconnectMsecs); + + websocket.ws.onmessage = function(event) { + console.debug(event); + handlers = []; + event = event.data; + + if (typeof event === 'string') { + event = JSON.parse(event); + } + + if (event.type !== 'event') { + // Discard non-event messages + return; + } + + if (null in websocket.handlers) { + handlers.push(websocket.handlers[null]); + } + + if (event.args.type in websocket.handlers) { + handlers.push(...websocket.handlers[event.args.type]); + } + + for (var handler of handlers) { + handler(event.args); + } + }; + + websocket.ws.onopen = function(event) { + if (websocket.instance) { + console.log("There's already an opened websocket connection, closing the newly opened one"); + this.onclose = function() {}; + this.close(); + } + + console.log('Websocket connection successful'); + websocket.instance = this; + + if (websocket.pending) { + websocket.pending = false; + } + + if (websocket.timeout) { + clearTimeout(websocket.timeout); + websocket.timeout = undefined; + } + }; + + websocket.ws.onerror = function(event) { + console.error(event); + }; + + websocket.ws.onclose = function(event) { + if (event) { + console.log('Websocket closed - code: ' + event.code + ' - reason: ' + event.reason); + } + + websocket.instance = undefined; + + if (!websocket.pending) { + websocket.pending = true; + initEvents(); + } + }; +}; + +function registerEventHandler(handler, ...events) { + if (events.length) { + // Event type filter specified + for (var event of events) { + if (!(event in websocket.handlers)) { + websocket.handlers[event] = []; + } + + websocket.handlers[event].push(handler); + } + } else { + // No event type specified, listen to all events + if (!(null in websocket.handlers)) { + websocket.handlers[null] = []; + } + + websocket.handlers[null].push(handler); + } +} + diff --git a/platypush/backend/http/static/js/light.hue.js b/platypush/backend/http/static/js/light.hue.js deleted file mode 100644 index 6a4fb711..00000000 --- a/platypush/backend/http/static/js/light.hue.js +++ /dev/null @@ -1,712 +0,0 @@ -$(document).ready(function() { - var lights, - groups, - scenes, - $roomsList = $('#rooms-list'), - $lightsList = $('#lights-list'), - $scenesList = $('#scenes-list'); - - var onEvent = function(event) { - switch (event.args.type) { - case 'platypush.message.event.light.LightStatusChangeEvent': - var $lightsList = $('#lights-list'); - var $light = $lightsList.find('.light-item').filter( - (i, light) => $(light).data('id') == event.args.light_id - ); - - var $roomLights = $light.parent('.room-lights-item').find('.light-item').filter( - (i, light) => $(light).data('type') == 'light' - ); - - var $allLightsItem = $light.parent('.room-lights-item').find('.light-item').filter( - (i, light) => $(light).data('type') == 'room' - ); - - if ('on' in event.args) { - $light.find('.light-ctrl-switch').prop('checked', event.args.on); - - if ($roomLights.find('.light-ctrl-switch:checked').length > 0) { - $allLightsItem.find('.light-ctrl-switch').prop('checked', true); - } else { - $allLightsItem.find('.light-ctrl-switch').prop('checked', false); - } - } - - if ('bri' in event.args) { - $light.find('.slider.bri').val(event.args.bri); - } - - if ('sat' in event.args) { - $light.find('.slider.sat').val(event.args.sat); - } - - if ('hue' in event.args) { - $light.find('.slider.hue').val(event.args.hue); - } - - break; - } - }; - - var createPowerToggleElement = function(data) { - var id = data['type'] + '_' + data['id']; - var $powerToggle = $('
').addClass('toggle toggle--push light-ctrl-switch-container'); - var $input = $('').attr('type', 'checkbox') - .attr('id', id).addClass('toggle--checkbox'); - - if (type === 'animation') { - $input.addClass('animation-switch'); - } else { - $input.addClass('light-ctrl-switch'); - } - - data = data || {}; - for (var attr of Object.keys(data)) { - $input.data(attr, data[attr]); - } - - var $label = $('').attr('for', id).addClass('toggle--btn'); - - $input.appendTo($powerToggle); - $label.appendTo($powerToggle); - - if ('on' in data && data['on']) { - $input.prop('checked', true); - } - - return $powerToggle; - }; - - var createColorSelector = function(data) { - var type = data.type; - var element; - - if (type === 'light') { - element = lights[data.id]; - } else if (type === 'room' || type === 'animation') { - element = groups[data.id]; - } else { - throw "Unknown type: " + type; - } - - var $colorSelector = $('
') - .addClass('light-color-selector'); - - if (type === 'animation') { - // Animation type selector - var $typeContainer = $('
') - .addClass('animation-type-container').addClass('row'); - - var $typeText = $('
').addClass('two columns').text('Type'); - - // Color transition type - var $transitionTypeContainer = $('
').addClass('five columns'); - - var $transitionType = $('').addClass('animation-type') - .addClass('one column').data('type', 'color_transition') - .attr('id', 'hue-animation-color-transition').data('name', element.name) - .attr('type', 'radio').attr('name', 'animation-type'); - - var $transitionTypeLabel = $('').attr('for', 'hue-animation-color-transition') - .addClass('four columns').text('Color transition'); - - $typeText.appendTo($typeContainer); - - $transitionType.appendTo($transitionTypeContainer); - $transitionTypeLabel.appendTo($transitionTypeContainer); - $transitionTypeContainer.appendTo($typeContainer); - - // Blink type - var $blinkTypeContainer = $('
').addClass('five columns'); - - var $blinkType = $('').addClass('animation-type') - .addClass('one column').data('type', 'blink') - .attr('id', 'hue-animation-blink').data('name', element.name) - .attr('type', 'radio').attr('name', 'animation-type'); - - var $blinkTypeLabel = $('').attr('for', 'hue-animation-blink') - .addClass('four columns').text('Blink'); - - $blinkType.appendTo($blinkTypeContainer); - $blinkTypeLabel.appendTo($blinkTypeContainer); - $blinkTypeContainer.appendTo($typeContainer); - - $typeContainer.appendTo($colorSelector); - - // Color transition container - var $animationContainer = $('
') - .addClass('animation-container row').data('animation-type', 'color_transition'); - - // Hue slider - var $hueContainer = $('
') - .addClass('slider-container').addClass('row'); - - var defaultHueRange = [0, 65535]; - var $hueText = $('
').addClass('two columns').text('Hue range'); - var $hueSlider = $('
') - .addClass('ten columns').data('animation-property', 'hue_range') - .data('id', data.id).val(defaultHueRange).slider({ - range: true, - min: 0, - max: 65535, - values: defaultHueRange, - slide: function(event, ui) { - var values = $(event.target).slider("option", "values"); - $(this).val(values); - } - }); - - $hueText.appendTo($hueContainer); - $hueSlider.appendTo($hueContainer); - $hueContainer.appendTo($animationContainer); - - // Sat slider - var $satContainer = $('
') - .addClass('slider-container').addClass('row'); - - var defaultSatRange = [155, 255]; - var $satText = $('
').addClass('two columns').text('Sat range'); - var $satSlider = $('
') - .addClass('ten columns').data('animation-property', 'sat_range') - .data('id', data.id).val(defaultSatRange).slider({ - range: true, - min: 0, - max: 255, - values: defaultSatRange, - slide: function(event, ui) { - var values = $(event.target).slider("option", "values"); - $(this).val(values); - } - }); - - $satText.appendTo($satContainer); - $satSlider.appendTo($satContainer); - $satContainer.appendTo($animationContainer); - - // Bri slider - var $briContainer = $('
') - .addClass('slider-container').addClass('row'); - - var defaultBriRange = [240, 255]; - var $briText = $('
').addClass('two columns').text('Bri range'); - var $briSlider = $('
') - .addClass('ten columns').data('animation-property', 'bri_range') - .data('id', data.id).val(defaultBriRange).slider({ - range: true, - min: 0, - max: 255, - values: defaultBriRange, - slide: function(event, ui) { - var values = $(event.target).slider("option", "values"); - $(this).val(values); - } - }); - - $briText.appendTo($briContainer); - $briSlider.appendTo($briContainer); - $briContainer.appendTo($animationContainer); - - // Hue step - var $hueStepContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $hueStepText = $('
').addClass('two columns').text('Hue step'); - var $hueStepSlider = $('').addClass('slider light-slider') - .addClass('ten columns').addClass('hue').data('animation-property', 'hue_step') - .attr('type', 'range').attr('min', 0).attr('max', 65535) - .data('id', data.id).data('name', element.name).val(1000); - - $hueStepText.appendTo($hueStepContainer); - $hueStepSlider.appendTo($hueStepContainer); - $hueStepContainer.appendTo($animationContainer); - - // Sat step - var $satStepContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $satStepText = $('
').addClass('two columns').text('Sat step'); - var $satStepSlider = $('').addClass('slider light-slider') - .addClass('ten columns').addClass('sat').data('animation-property', 'sat_step') - .attr('type', 'range').attr('min', 0).attr('max', 255) - .data('id', data.id).data('name', element.name).val(2); - - $satStepText.appendTo($satStepContainer); - $satStepSlider.appendTo($satStepContainer); - $satStepContainer.appendTo($animationContainer); - - // Bri step - var $briStepContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $briStepText = $('
').addClass('two columns').text('Bri step'); - var $briStepSlider = $('').addClass('slider light-slider') - .addClass('ten columns').addClass('bri').data('animation-property', 'bri_step') - .attr('type', 'range').attr('min', 0).attr('max', 255) - .data('id', data.id).data('name', element.name).val(1); - - $briStepText.appendTo($briStepContainer); - $briStepSlider.appendTo($briStepContainer); - $briStepContainer.appendTo($animationContainer); - - // Transition seconds - var $transitionContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $transitionText = $('
').addClass('two columns').text('Transition seconds'); - var $transitionInput = $('').data('animation-property', 'transition_seconds') - .addClass('two columns pull-right').attr('type', 'text') - .data('id', data.id).data('name', element.name).val(1); - - $transitionText.appendTo($transitionContainer); - $transitionInput.appendTo($transitionContainer); - $transitionContainer.appendTo($animationContainer); - - // Duration seconds - var $durationContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $durationText = $('
').addClass('two columns').text('Duration seconds'); - var $durationInput = $('').data('animation-property', 'duration') - .addClass('two columns pull-right').attr('type', 'text') - .data('id', data.id).data('name', element.name); - - $durationText.appendTo($durationContainer); - $durationInput.appendTo($durationContainer); - $durationContainer.appendTo($animationContainer); - - $animationContainer.appendTo($colorSelector); - - // Blink animation container - $animationContainer = $('
') - .addClass('animation-container row').data('animation-type', 'blink'); - - // Transition seconds - $transitionContainer = $('
') - .addClass('slider-container').addClass('row'); - - $transitionText = $('
').addClass('two columns').text('Transition seconds'); - $transitionInput = $('').data('animation-property', 'transition_seconds') - .addClass('two columns pull-right').attr('type', 'text') - .data('id', data.id).data('name', element.name).val(1); - - $transitionText.appendTo($transitionContainer); - $transitionInput.appendTo($transitionContainer); - $transitionContainer.appendTo($animationContainer); - - // Duration seconds - $durationContainer = $('
').data('animation-property', 'duration') - .addClass('slider-container').addClass('row'); - - $durationText = $('
').addClass('two columns').text('Duration seconds'); - $durationInput = $('').data('animation-property', 'duration') - .addClass('two columns pull-right').attr('type', 'text') - .data('id', data.id).data('name', element.name); - - $durationText.appendTo($durationContainer); - $durationInput.appendTo($durationContainer); - $durationContainer.appendTo($animationContainer); - - $animationContainer.appendTo($colorSelector); - } else { - // Hue slider - var $hueContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $hueText = $('
').addClass('two columns').text('Hue'); - var $hueSlider = $('').addClass('slider light-slider') - .addClass('ten columns').addClass('hue').data('type', type) - .attr('type', 'range').attr('min', 0).attr('max', 65535).data('property', 'hue') - .data('id', data.id).data('name', element.name).val(type === 'light' ? element.state.hue : 0); - - $hueText.appendTo($hueContainer); - $hueSlider.appendTo($hueContainer); - $hueContainer.appendTo($colorSelector); - - // Saturation slider - var $satContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $satText = $('
').addClass('two columns').text('Saturation'); - var $satSlider = $('').addClass('slider light-slider') - .addClass('ten columns').addClass('sat').data('type', type) - .attr('type', 'range').attr('min', 0).attr('max', 255).data('property', 'sat') - .data('id', data.id).data('name', element.name).val(type === 'light' ? element.state.sat : 0); - - $satText.appendTo($satContainer); - $satSlider.appendTo($satContainer); - $satContainer.appendTo($colorSelector); - - // Brightness slider - var $briContainer = $('
') - .addClass('slider-container').addClass('row'); - - var $briText = $('
').addClass('two columns').text('Brightness'); - var $briSlider = $('').addClass('slider light-slider') - .addClass('ten columns').addClass('bri').data('type', type) - .attr('type', 'range').attr('min', 0).attr('max', 255).data('property', 'bri') - .data('id', data.id).data('name', element.name).val(type === 'light' ? element.state.bri : 0); - - $briText.appendTo($briContainer); - $briSlider.appendTo($briContainer); - $briContainer.appendTo($colorSelector); - } - - return $colorSelector; - }; - - var createLightCtrlElement = function(type, id) { - var element; - - if (type === 'light') { - element = lights[id]; - } else if (type === 'room' || type === 'animation') { - element = groups[id]; - } else { - throw "Unknown type: " + type; - } - - var on; - if (type === 'light') { - on = element.state.on; - } else if (type === 'room') { - on = element.state.any_on; - } else { - on = false; - } - - var $light = $('
') - .addClass('light-item') - .data('type', type) - .data('id', id) - .data('name', element.name) - .data('on', on); - - if (type === 'room') { - $light.addClass('all-lights-item'); - } else if (type === 'animation') { - $light.addClass('animation-item'); - } - - var $row1 = $('
').addClass('row'); - var $row2 = $('
').addClass('row'); - - var lightName; - switch(type) { - case 'light': lightName = element.name; break; - case 'room': lightName = 'All Lights'; break; - case 'animation': lightName = 'Animate'; break; - } - - var $lightName = $('
') - .addClass('light-item-name') - .text(lightName); - - var $powerToggle = createPowerToggleElement({ - type: type, - id: id, - on: on, - }); - - var $colorSelector = createColorSelector({ - type: type, - id: id, - }); - - $lightName.appendTo($row1); - $powerToggle.appendTo($row1); - $colorSelector.appendTo($row2); - - $row1.appendTo($light); - $row2.appendTo($light); - - return $light; - }; - - var updateRooms = function(rooms) { - var roomByLight = {}; - var roomsByScene = {}; - - $roomsList.html(''); - $lightsList.html(''); - $scenesList.html(''); - - for (var room of Object.keys(rooms)) { - var $room = $('
') - .addClass('room-item') - .data('id', room) - .data('name', rooms[room].name) - .text(rooms[room].name); - - var $roomLights = $('
') - .addClass('room-lights-item') - .data('id', room); - - var $roomScenes = $('
') - .addClass('room-scenes-item') - .data('room-id', room) - .data('room-name', rooms[room].name); - - $room.appendTo($roomsList); - $roomLights.appendTo($lightsList); - $roomScenes.appendTo($scenesList); - - for (var light of rooms[room].lights) { - var $light = createLightCtrlElement(type='light', id=light); - $light.appendTo($roomLights); - roomByLight[light] = room; - } - - roomByLight[light] = room; - - var $animation = createLightCtrlElement(type='animation', id=room); - $animation.prependTo($roomLights); - - var $allLights = createLightCtrlElement(type='room', id=room); - $allLights.prependTo($roomLights); - } - - for (var scene of Object.keys(scenes)) { - if (scenes[scene].name.match(/(on|off) \d+$/)) { - // Old 1.0 scenes are saved as "scene_name on " but aren't visible - // not settable through the app - ignore them - continue; - } - - roomsByScene[scene] = new Set(); - for (var light of scenes[scene].lights) { - var room = roomByLight[light]; - if (roomsByScene[scene].has(room)) { - continue; - } - - roomsByScene[scene].add(room); - - var $roomScenes = $scenesList.find('.room-scenes-item') - .filter(function(i, item) { - return $(item).data('room-id') === room - }); - - var $scene = $('
') - .addClass('scene-item') - .data('id', scene) - .data('name', scenes[scene].name) - .text(scenes[scene].name); - - $scene.appendTo($roomScenes); - } - } - }; - - var initUi = function() { - $.when( - execute({ type: 'request', action: 'light.hue.get_lights' }), - execute({ type: 'request', action: 'light.hue.get_groups' }), - execute({ type: 'request', action: 'light.hue.get_scenes' }) - ).done(function(l, g, s) { - lights = l[0].response.output; - groups = g[0].response.output; - scenes = s[0].response.output; - - for (var group of Object.keys(groups)) { - if (groups[group].type.toLowerCase() !== 'room') { - delete groups[group]; - } - } - - updateRooms(groups); - }).then(function() { - initBindings(); - }); - }; - - var refreshStatus = function() { - var onResponse = function(data) { - var response = data.response.output; - for (var light of Object.keys(response)) { - var $element = $('.light-item').filter(function(i, item) { - return $(item).data('type') === 'light' && $(item).data('id') === light - }); - - $element.find('input.light-ctrl-switch').prop('checked', response[light].state.on); - $element.find('input.hue').val(response[light].state.hue); - $element.find('input.sat').val(response[light].state.sat); - $element.find('input.bri').val(response[light].state.bri); - } - }; - - execute({ type: 'request', action: 'light.hue.get_lights' }, onResponse); - }; - - var initBindings = function() { - $roomsList.on('click touch', '.room-item', function() { - $('.room-item').removeClass('selected'); - $('.room-lights-item').hide(); - $('.room-scenes-item').hide(); - - var roomId = $(this).data('id'); - var $roomLights = $('.room-lights-item').filter(function(i, item) { - return $(item).data('id') === roomId - }); - - var $roomScenes = $('.room-scenes-item').filter(function(i, item) { - return $(item).data('room-id') === roomId - }); - - $(this).addClass('selected'); - $roomLights.show(); - $roomScenes.show(); - }); - - $scenesList.on('click touch', '.scene-item', function() { - $('.scene-item').removeClass('selected'); - $(this).addClass('selected'); - - execute({ - type: 'request', - action: 'light.hue.scene', - args: { - name: $(this).data('name') - } - }, refreshStatus); - }); - - $lightsList.on('click touch', '.light-item-name', function() { - var $lightItem = $(this).parents('.light-item'); - var $colorSelector = $lightItem.find('.light-color-selector'); - - $('.light-color-selector').hide(); - $colorSelector.toggle(); - - $('.light-item').removeClass('selected'); - $lightItem.addClass('selected'); - }); - - $lightsList.on('click touch', '.light-ctrl-switch', function(e) { - e.stopPropagation(); - - var $lightItem = $($(this).parents('.light-item')); - var type = $lightItem.data('type'); - var name = $lightItem.data('name'); - var isOn = $lightItem.data('on'); - var action = 'light.hue.' + (isOn ? 'off' : 'on'); - var key = (type == 'light' ? 'lights' : 'groups'); - var args = { - type: 'request', - action: action, - args: {} - }; - - args['args'][key] = [name]; - execute(args, function() { - $lightItem.data('on', !isOn); - refreshStatus(); - }); - }); - - $lightsList.on('click touch', '.animation-switch', function(e) { - e.stopPropagation(); - - var turnedOn = $(this).prop('checked'); - var args = {}; - args['groups'] = $(this).parents('.animation-item').data('name'); - args['animation'] = $(this).parents('.animation-item') - .find('input.animation-type:checked').data('type'); - - var $animationCtrl = $(this).parents('.animation-item') - .find('.animation-container').filter( - (index, node) => $(node).data('animation-type') === args['animation'] - ); - - var params = $animationCtrl.find('*').filter( - (index, node) => $(node).data('animation-property')) - .toArray().reduce( - (map, input) => { - if ($(input).val().length) { - var val = $(input).val(); - val = Array.isArray(val) ? val.map((i) => parseFloat(i)) : parseFloat(val); - map[$(input).data('animation-property')] = val; - } - - return map - }, {} - ); - - for (var p of Object.keys(params)) { - args[p] = params[p]; - } - - if (turnedOn) { - execute( - { - type: 'request', - action: 'light.hue.animate', - args: args, - }, - - onSuccess = function() { - $(this).prop('checked', true); - } - ); - } else { - execute( - { - type: 'request', - action: 'light.hue.stop_animation', - }, - - onSuccess = function() { - $(this).prop('checked', false); - } - ); - } - }); - - $lightsList.on('mouseup touchend', '.light-slider', function() { - var property = $(this).data('property'); - var type = $(this).data('type'); - var name = $(this).data('name'); - var args = { - type: 'request', - action: 'light.hue.' + property, - args: { value: $(this).val() } - }; - - if (type === 'light') { - args.args.lights = [name]; - } else { - args.args.groups = [name]; - } - - execute(args, refreshStatus); - }); - - $lightsList.on('click touch', 'input.animation-type', function(e) { - var type = $(this).data('type'); - var $animationContainers = $(this).parents('.animation-item').find('.animation-container') - var $animationContainer = $(this).parents('.animation-item').find('.animation-container') - .filter(function() { return $(this).data('animationType') === type }) - - $animationContainers.hide(); - $animationContainer.show(); - }); - - if (window.config.light.hue.default_group) { - var $defaultRoomItem = $roomsList.find('.room-item').filter( - (i, r) => $(r).data('name') == window.config.light.hue.default_group); - - $defaultRoomItem.click(); - } - }; - - var initEvents = function() { - window.registerEventListener(onEvent); - }; - - var init = function() { - initUi(); - initEvents(); - }; - - init(); -}); - diff --git a/platypush/backend/http/static/js/plugins/light.hue/index.js b/platypush/backend/http/static/js/plugins/light.hue/index.js index 9f105131..0e431969 100644 --- a/platypush/backend/http/static/js/plugins/light.hue/index.js +++ b/platypush/backend/http/static/js/plugins/light.hue/index.js @@ -107,7 +107,7 @@ Vue.component('light-hue', { light.state.on = event.state.all_on; } - for (const attr in ['bri', 'xy', 'ct']) { + for (const attr in ['hue', 'sat', 'bri', 'xy', 'ct']) { if (attr in event.state) { light.state[attr] = event.state[attr]; } @@ -180,19 +180,49 @@ Vue.component('light-hue', { onUnitInput: function(event) { var groups = this.lights[event.id].groups; for (const [groupId, group] of Object.entries(groups)) { - if (event.on) { + 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 { + } 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; } } + + for (var attr of ['on', 'hue', 'sat', 'bri', 'xy', 'ct']) { + if (attr in event) { + this.lights[event.id].state[attr] = event[attr]; + } + } + }, + + eventHandler: function(event) { + if ('light_id' in event) { + this.onUnitInput({ + ...event, + id: event.light_id, + }); + } else if ('group_id' in event) { + var 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/templates/index.html b/platypush/backend/http/templates/index.html index 70063977..acdd962d 100644 --- a/platypush/backend/http/templates/index.html +++ b/platypush/backend/http/templates/index.html @@ -13,6 +13,7 @@ + {% for style in styles.values() %} diff --git a/platypush/backend/http/templates/plugins/light.hue.html b/platypush/backend/http/templates/plugins/light.hue.html deleted file mode 100644 index 07693d9d..00000000 --- a/platypush/backend/http/templates/plugins/light.hue.html +++ /dev/null @@ -1,33 +0,0 @@ - - - -{% set default_group = configuration['groups'][0] if 'groups' in configuration else None %} - - - -
-
-
Rooms
-
-
- -
-
Scenes
-
-
- -
-
Lights
-
-
-
- diff --git a/platypush/backend/light/hue.py b/platypush/backend/light/hue.py index c17afa25..ec7e754a 100644 --- a/platypush/backend/light/hue.py +++ b/platypush/backend/light/hue.py @@ -1,4 +1,3 @@ -import json import time from threading import Thread