From 4ec48ad6643cf156782ec26e890eccbdee38ba12 Mon Sep 17 00:00:00 2001 From: javalikescript Date: Mon, 30 Dec 2024 11:47:38 +0100 Subject: [PATCH] Reload web base when addon change --- extensions/web-base/web-base.lua | 32 +++--- extensions/web-base/www/app/app.js | 173 +++++++++++++++++------------ 2 files changed, 119 insertions(+), 86 deletions(-) diff --git a/extensions/web-base/web-base.lua b/extensions/web-base/web-base.lua index c292104..0987557 100644 --- a/extensions/web-base/web-base.lua +++ b/extensions/web-base/web-base.lua @@ -71,6 +71,16 @@ local function onWebSocketClose(webSocket) List.removeFirst(websockets, webSocket) end +local function webSocketBroadcast(data) + logger:fine('WebSocket broadcast') + local message = json.encode(data) + for _, websocket in ipairs(websockets) do + if not websocket.lhaEvent or websocket.lhaEvent == data.event then + websocket:sendTextMessage(message) + end + end +end + local batchDataChange = true local dataChangeEvent = nil @@ -82,11 +92,8 @@ local function onDataChange(value, previousValue, path) if batchDataChange then if not dataChangeEvent then event:setTimeout(function() - local message = json.encode(dataChangeEvent) + webSocketBroadcast(dataChangeEvent) dataChangeEvent = nil - for _, websocket in ipairs(websockets) do - websocket:sendTextMessage(message) - end end) dataChangeEvent = {event = 'data-change'} end @@ -94,7 +101,7 @@ local function onDataChange(value, previousValue, path) else local thingId, propertyName = string.match(path, '^data/([^/]+)/([^/]+)$') if thingId then - local message = json.encode({ + webSocketBroadcast({ event = 'data-change', data = { [thingId] = { @@ -105,9 +112,6 @@ local function onDataChange(value, previousValue, path) } } }) - for _, websocket in ipairs(websockets) do - websocket:sendTextMessage(message) - end end end end @@ -117,11 +121,13 @@ local addons = {} function extension:registerAddon(id, addon) addons[id] = addon logger:info('Web base add-on "%s" registered', id) + webSocketBroadcast({event = 'addon-change'}) end function extension:unregisterAddon(name) addons[name] = nil logger:info('Web base add-on "%s" unregistered', name) + webSocketBroadcast({event = 'addon-change'}) end function extension:registerAddonExtension(ext, script) @@ -172,11 +178,8 @@ extension:subscribeEvent('startup', function() else if not bufferMessages then event:setTimeout(function() - local content = json.encode(bufferMessages) + webSocketBroadcast(bufferMessages) bufferMessages = nil - for _, websocket in ipairs(websockets) do - websocket:sendTextMessage(content) - end end) bufferMessages = {event = 'logs', logs = {}} end @@ -241,4 +244,7 @@ extension:subscribeEvent('startup', function() logger:info('WebSocket available on /ws/') end) -extension:subscribeEvent('shutdown', cleanup) +extension:subscribeEvent('shutdown', function() + webSocketBroadcast({event = 'shutdown'}) + cleanup() +end) diff --git a/extensions/web-base/www/app/app.js b/extensions/web-base/www/app/app.js index 290c8e5..5134781 100644 --- a/extensions/web-base/www/app/app.js +++ b/extensions/web-base/www/app/app.js @@ -30,6 +30,66 @@ var unitAlias = { "watt": "W" }; +function callVueFromPage(page, name) { + var parent = page.$parent; + if (parent) { + var fn = parent[name]; + if (typeof fn === 'function') { + var args = Array.prototype.slice.call(arguments, 2); + return fn.apply(parent, args); + } + } +} + +function getPageFromVue(vue) { + if (vue && vue.$children && (vue.$children.length > 0)) { + var page = vue.$children[0]; + if (page.id && page.title) { + return page; + } + } +} + +/************************************************************ + * Toaster + ************************************************************/ +var toaster = new Vue({ + el: '#toaster', + data: { + message: '', + show: false + }, + methods: { + toast: function(message, duration) { + console.log('toast("' + message + '", ' + duration + ')'); + if (this.show) { + this.message += '\n' + message; + } else { + this.message = message; + this.show = true; + var self = this; + setTimeout(function () { + self.show = false; + }, duration || 3000) + } + } + } +}); + +function assertIsOk(response) { + if (response.ok) { + return response; + } + var message; + if (response.status === 403) { + message = 'Sorry you are not authorized'; + } else { + message = 'Failed due to ' + response.statusText; + } + toaster.toast(message); + return Promise.reject(message); +} + /************************************************************ * Main application ************************************************************/ @@ -89,25 +149,17 @@ var app = new Vue({ getPage: function(id) { return this.pages[id]; }, - emitPage: function(id) { - var page = this.pages[id]; - var emitArgs = Array.prototype.slice.call(arguments, 1); - if (page.$parent) { - page = page.$parent; - } - page.$emit.apply(page, emitArgs); - return this; + isActivePage: function(vue) { + return this.page && this.pages[this.page] === getPageFromVue(vue); }, callPage: function(id, name) { var page = this.pages[id]; - var callArgs = Array.prototype.slice.call(arguments, 2); - if (page.$parent) { - page = page.$parent; - } - var fn = page[name]; - if (typeof fn === 'function') { - fn.apply(page, callArgs); + if (page) { + return callVueFromPage(page, name); } + }, + emitPage: function(id) { + this.callPage(id, '$emit'); return this; }, showBack: function() { @@ -148,6 +200,24 @@ var app = new Vue({ this.callPage(this.page, 'onLogs', message.logs); } break; + case 'addon-change': + if (this.reloadTimeoutId) { + clearTimeout(this.reloadTimeoutId); + this.reloadTimeoutId = undefined; + } + this.reloadTimeoutId = setTimeout(function () { + toaster.toast('Reloading...'); + setTimeout(function () { + window.location.reload(); + }, 500); + }, 500); + break; + case 'shutdown': + if (this.reloadTimeoutId) { + clearTimeout(this.reloadTimeoutId); + this.reloadTimeoutId = undefined; + } + break; } }, clearCache: function() { @@ -345,11 +415,10 @@ Vue.component('app-page', { this.app.pages[this.id] = this; var page = this; app.$on('page-selected', function(id, path, previousId) { - if ((page.id === previousId) && (page.$parent) && (typeof page.$parent.onHide === 'function')) { - page.$parent.onHide(); - } - if ((page.id === id) && (page.$parent) && (typeof page.$parent.onShow === 'function')) { - page.$parent.onShow(path); + if (page.id === previousId) { + callVueFromPage(page, 'onHide'); + } else if (page.id === id) { + callVueFromPage(page, 'onShow', path); } }); } @@ -376,46 +445,6 @@ var menu = new Vue({ } }); -/************************************************************ - * Toaster - ************************************************************/ -var toaster = new Vue({ - el: '#toaster', - data: { - message: '', - show: false - }, - methods: { - toast: function(message, duration) { - console.log('toast("' + message + '", ' + duration + ')'); - if (this.show) { - this.message += '\n' + message; - } else { - this.message = message; - this.show = true; - var self = this; - setTimeout(function () { - self.show = false; - }, duration || 3000) - } - } - } -}); - -function assertIsOk(response) { - if (response.ok) { - return response; - } - var message; - if (response.status === 403) { - message = 'Sorry you are not authorized'; - } else { - message = 'Failed due to ' + response.statusText; - } - toaster.toast(message); - return Promise.reject(message); -} - /************************************************************ * Confirmation ************************************************************/ @@ -605,19 +634,17 @@ new Vue({ }); function registerPageVue(vue, icon) { - if (vue && vue.$children && (vue.$children.length > 0)) { - var page = vue.$children[0]; - if (page.id && page.title) { - menu.pages.push({ - id: page.id, - name: page.title - }); - homePage.tiles.push({ - id: page.id, - name: page.title, - icon: icon - }); - } + var page = getPageFromVue(vue); + if (page) { + menu.pages.push({ + id: page.id, + name: page.title + }); + homePage.tiles.push({ + id: page.id, + name: page.title, + icon: icon + }); } }