Skip to content

Commit

Permalink
Adds a UI Command Item
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Cunningham <[email protected]>
  • Loading branch information
digitaldan committed Sep 24, 2023
1 parent 881a8b7 commit 41a7793
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
55 changes: 4 additions & 51 deletions bundles/org.openhab.ui/web/src/components/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
})
}
}
Expand Down
160 changes: 160 additions & 0 deletions bundles/org.openhab.ui/web/src/components/sse-events-mixin.js
Original file line number Diff line number Diff line change
@@ -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(/(?<!\\):/)
const combined = segments.join(':')
switch (command) {
case 'navigate':
this.$f7.views.main.router.navigate(combined)
break
case 'popup':
case 'popover':
case 'sheet':
if (combined.indexOf('page:') !== 0 && combined.indexOf('widget:') !== 0 && combined.indexOf('oh-') !== 0) {
console.error('Action target is not of the format page:uid or widget:uid')
return
}
console.debug(`Opening ${combined} in ${command} modal`)
let modalRoute = {
url: combined + '/' + command,
route: {
}
}
if (command === 'popup') modalRoute.route.popup = { component: OhPopup }
if (command === 'popover') modalRoute.route.popup = { component: OhPopover }
if (command === 'sheet') modalRoute.route.popup = { component: OhSheet }
let modalProps = {
props: {
uid: combined,
modalParams: {}
}
}
this.closePopups()
this.$f7.views.main.router.navigate(modalRoute, modalProps)
break
case 'close':
this.closePopups()
break
case 'back':
this.$f7.views.main.router.back()
break
case 'reload':
window.location.reload()
break
case 'notification':
const payload = {
text: segments[0],
closeOnClick: true,
closeTimeout: 5000
}
if (segments.length > 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)
}
}
}
}
16 changes: 16 additions & 0 deletions bundles/org.openhab.ui/web/src/components/theme-switcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,23 @@
<span v-t="'about.miscellaneous.webaudio.enable'" />
<f7-toggle :checked="webAudio === 'enabled'" @toggle:change="setWebAudio" />
</f7-list-item>
<f7-list-item>
<span v-t="'about.miscellaneous.commandItem.title'" />
<item-picker title="Item" :multiple="false" :value="commandItem" @input="setCommandItem" />
</f7-list-item>
</f7-list>
</f7-col>
</f7-row>
</f7-block>
</template>
<script>
import { loadLocaleMessages } from '@/js/i18n'
import ItemPicker from '@/components/config/controls/item-picker.vue'
export default {
components: {
ItemPicker
},
i18n: {
messages: loadLocaleMessages(require.context('@/assets/i18n/theme-switcher'))
},
Expand Down Expand Up @@ -127,6 +136,10 @@ export default {
setWebAudio (value) {
localStorage.setItem('openhab.ui:webaudio.enable', (value) ? 'enabled' : 'default')
location.reload()
},
setCommandItem (value) {
localStorage.setItem('openhab.ui:commandItem', value)
location.reload()
}
},
computed: {
Expand Down Expand Up @@ -156,6 +169,9 @@ export default {
},
webAudio () {
return localStorage.getItem('openhab.ui:webaudio.enable') || 'default'
},
commandItem () {
return localStorage.getItem('openhab.ui:commandItem') || ''
}
}
}
Expand Down

0 comments on commit 41a7793

Please sign in to comment.