From 41a77931e5756d8dbb9d3b6fe00a9486770ab052 Mon Sep 17 00:00:00 2001 From: Dan Cunningham Date: Sun, 24 Sep 2023 13:10:29 -0700 Subject: [PATCH] Adds a UI Command Item Signed-off-by: Dan Cunningham --- .../src/assets/i18n/theme-switcher/en.json | 4 +- .../org.openhab.ui/web/src/components/app.vue | 55 +----- .../web/src/components/sse-events-mixin.js | 160 ++++++++++++++++++ .../web/src/components/theme-switcher.vue | 16 ++ 4 files changed, 183 insertions(+), 52 deletions(-) create mode 100644 bundles/org.openhab.ui/web/src/components/sse-events-mixin.js diff --git a/bundles/org.openhab.ui/web/src/assets/i18n/theme-switcher/en.json b/bundles/org.openhab.ui/web/src/assets/i18n/theme-switcher/en.json index a13161dea4..ccf6e6eb4d 100644 --- a/bundles/org.openhab.ui/web/src/assets/i18n/theme-switcher/en.json +++ b/bundles/org.openhab.ui/web/src/assets/i18n/theme-switcher/en.json @@ -12,5 +12,7 @@ "about.miscellaneous.home.hideChatInput": "Hide chat input box on home page", "about.miscellaneous.home.disableCardExpansionAnimation": "Disable card expansion animations", "about.miscellaneous.theme.disablePageTransition": "Disable page transition animations", - "about.miscellaneous.webaudio.enable": "Enable Web Audio sink support" + "about.miscellaneous.webaudio.enable": "Enable Web Audio sink support", + "about.miscellaneous.commandItem.title": "Listen for UI commands", + "about.miscellaneous.commandItem.selectItem": "Item" } diff --git a/bundles/org.openhab.ui/web/src/components/app.vue b/bundles/org.openhab.ui/web/src/components/app.vue index bc7d551abf..db1030e8ba 100644 --- a/bundles/org.openhab.ui/web/src/components/app.vue +++ b/bundles/org.openhab.ui/web/src/components/app.vue @@ -272,12 +272,13 @@ import DeveloperSidebar from './developer/developer-sidebar.vue' import auth from './auth-mixin.js' import i18n from './i18n-mixin.js' +import sseEvents from './sse-events-mixin.js' import dayjs from 'dayjs' import dayjsLocales from 'dayjs/locale.json' export default { - mixins: [auth, i18n], + mixins: [auth, i18n, sseEvents], components: { PanelRight, DeveloperSidebar @@ -622,52 +623,6 @@ export default { ev.preventDefault() } }, - startEventSource () { - this.eventSource = this.$oh.sse.connect('/rest/events?topics=openhab/webaudio/playurl', null, (event) => { - const topicParts = event.topic.split('/') - switch (topicParts[2]) { - case 'playurl': - this.playAudioUrl(JSON.parse(event.payload)) - break - } - }) - }, - stopEventSource () { - this.$oh.sse.close(this.eventSource) - this.eventSource = null - }, - playAudioUrl (audioUrl) { - try { - if (!this.audioContext) { - window.AudioContext = window.AudioContext || window.webkitAudioContext - if (typeof (window.AudioContext) !== 'undefined') { - this.audioContext = new AudioContext() - unlockAudioContext(this.audioContext) - } - } - console.log('Playing audio URL: ' + audioUrl) - this.$oh.api.getPlain(audioUrl, '', '*/*', 'arraybuffer').then((data) => { - this.audioContext.decodeAudioData(data, (buffer) => { - let source = this.audioContext.createBufferSource() - source.buffer = buffer - source.connect(this.audioContext.destination) - source.start(0) - }) - }) - } catch (e) { - console.warn('Error while playing audio URL: ' + e.toString()) - } - // Safari requires a touch event after the stream has started, hence this workaround - // Credit: https://www.mattmontag.com/web/unlock-web-audio-in-safari-for-ios-and-macos - function unlockAudioContext (audioContext) { - if (audioContext.state !== 'suspended') return - const b = document.body - const events = ['touchstart', 'touchend', 'mousedown', 'keydown'] - events.forEach(e => b.addEventListener(e, unlock, false)) - function unlock () { audioContext.resume().then(clean) } - function clean () { events.forEach(e => b.removeEventListener(e, unlock)) } - } - }, /** * Creates and opens a toast message that indicates a failure, e.g. of SSE connection * @param {string} message message to show @@ -835,10 +790,8 @@ export default { if (window) { window.addEventListener('keydown', this.keyDown) } - - if (localStorage.getItem('openhab.ui:webaudio.enable') === 'enabled') { - this.startEventSource() - } + // sse-event mixin + this.startEventSource() }) } } diff --git a/bundles/org.openhab.ui/web/src/components/sse-events-mixin.js b/bundles/org.openhab.ui/web/src/components/sse-events-mixin.js new file mode 100644 index 0000000000..66516003dd --- /dev/null +++ b/bundles/org.openhab.ui/web/src/components/sse-events-mixin.js @@ -0,0 +1,160 @@ +import OhPopup from './widgets/modals/oh-popup.vue' +import OhSheet from './widgets/modals/oh-sheet.vue' +import OhPopover from './widgets/modals/oh-popover.vue' + +export default { + data () { + return { + eventSource: null, + audioContext: null + } + }, + methods: { + startEventSource () { + const topicAudio = 'openhab/webaudio/playurl' + const commandItem = localStorage.getItem('openhab.ui:commandItem') + const topicCommand = `openhab/items/${commandItem || ''}/command` + let topics = null + if (localStorage.getItem('openhab.ui:webaudio.enable') === 'enabled') { + topics = topicAudio + } + if (commandItem) { + topics = topics ? `${topics},${topicCommand}` : topicCommand + } + if (!topics) return + this.eventSource = this.$oh.sse.connect(`/rest/events?topics=${topics}`, null, (event) => { + console.debug('Received SSE event: ' + JSON.stringify(event)) + function closePopup () { + const popupEl = this.$el.querySelector('.popup') + if (popupEl) { + this.$f7.popup.close(popupEl) + } + } + switch (event.topic) { + case topicAudio: + const topicParts = event.topic.split('/') + switch (topicParts[2]) { + case 'playurl': + this.playAudioUrl(JSON.parse(event.payload)) + break + } + break + case topicCommand: + const payload = JSON.parse(event.payload) + const [command, ...segments] = payload.value.trim().split(/(? 1) { + payload.title = segments[1] + } + if (segments.length > 2) { + payload.subtitle = segments[2] + } + if (segments.length > 3) { + payload.titleRightText = segments[3] + } + if (segments.length > 4) { + payload.closeTimeout = parseInt(segments[4]) + } + this.$f7.notification.create(payload).open() + break + } + break + } + }) + }, + stopEventSource () { + this.$oh.sse.close(this.eventSource) + this.eventSource = null + }, + playAudioUrl (audioUrl) { + try { + if (!this.audioContext) { + window.AudioContext = window.AudioContext || window.webkitAudioContext + if (typeof (window.AudioContext) !== 'undefined') { + this.audioContext = new AudioContext() + unlockAudioContext(this.audioContext) + } + } + console.log('Playing audio URL: ' + audioUrl) + this.$oh.api.getPlain(audioUrl, '', '*/*', 'arraybuffer').then((data) => { + this.audioContext.decodeAudioData(data, (buffer) => { + let source = this.audioContext.createBufferSource() + source.buffer = buffer + source.connect(this.audioContext.destination) + source.start(0) + }) + }) + } catch (e) { + console.warn('Error while playing audio URL: ' + e.toString()) + } + // Safari requires a touch event after the stream has started, hence this workaround + // Credit: https://www.mattmontag.com/web/unlock-web-audio-in-safari-for-ios-and-macos + function unlockAudioContext (audioContext) { + if (audioContext.state !== 'suspended') return + const b = document.body + const events = ['touchstart', 'touchend', 'mousedown', 'keydown'] + events.forEach(e => b.addEventListener(e, unlock, false)) + function unlock () { audioContext.resume().then(clean) } + function clean () { events.forEach(e => b.removeEventListener(e, unlock)) } + } + }, + closePopups () { + const popupEl = this.$el.querySelector('.popup') + if (popupEl) { + this.$f7.popup.close(popupEl) + } + const popoverEl = this.$el.querySelector('.popover') + if (popoverEl) { + this.$f7.popover.close(popoverEl) + } + const sheetEl = this.$el.querySelector('.sheet-modal') + if (sheetEl) { + this.$f7.sheet.close(sheetEl) + } + } + } +} diff --git a/bundles/org.openhab.ui/web/src/components/theme-switcher.vue b/bundles/org.openhab.ui/web/src/components/theme-switcher.vue index 1233edd493..e36c3ae557 100644 --- a/bundles/org.openhab.ui/web/src/components/theme-switcher.vue +++ b/bundles/org.openhab.ui/web/src/components/theme-switcher.vue @@ -74,6 +74,10 @@ + + + + @@ -81,7 +85,12 @@