diff --git a/README.md b/README.md index db8324f..5f2f02a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # ontime-timeroverlay -Simple overlay for the Ontime Timer to add specific things \ No newline at end of file +Simple overlay for the Ontime Timer to add specific things + +### Custom Fields +- **layoutimage**: display the image fullscreen on the timeroverlay view +- **ready**: turns border green or red \ No newline at end of file diff --git a/timeroverlay/app.js b/timeroverlay/app.js new file mode 100644 index 0000000..518c5a0 --- /dev/null +++ b/timeroverlay/app.js @@ -0,0 +1,143 @@ +/*eslint-env browser*/ +/** + * This is a very minimal example for a websocket client + * You could use this as a starting point to creating your own interfaces + */ + +// Data that the user needs to provide depending on the Ontime URL +const isSecure = window.location.protocol === 'https:'; +const userProvidedSocketUrl = `${isSecure ? 'wss' : 'ws'}://${window.location.host}${getStageHash()}/ws`; + +connectSocket(); + +let reconnectTimeout; +const reconnectInterval = 1000; +let reconnectAttempts = 0; + +/** + * Connects to the websocket server + * @param {string} socketUrl + */ +function connectSocket(socketUrl = userProvidedSocketUrl) { + const websocket = new WebSocket(socketUrl); + + websocket.onopen = () => { + clearTimeout(reconnectTimeout); + reconnectAttempts = 0; + console.warn('WebSocket connected'); + }; + + websocket.onclose = () => { + console.warn('WebSocket disconnected'); + reconnectTimeout = setTimeout(() => { + console.warn(`WebSocket: attempting reconnect ${reconnectAttempts}`); + if (websocket && websocket.readyState === WebSocket.CLOSED) { + reconnectAttempts += 1; + connectSocket(); + } + }, reconnectInterval); + }; + websocket.onerror = (error) => { + console.error('WebSocket error:', error); + }; + + websocket.onmessage = (event) => { + // all objects from ontime are structured with tag and payload + const { tag, payload } = JSON.parse(event.data); + + /** + * runtime-data is sent + * - on connect with the full state + * - and then on every update with a patch + */ + if (tag === 'runtime-data') { + handleOntimePayload(payload); + } + }; +} + +let localData = {}; +let localVars = {}; + +/** + * Handles the ontime payload updates + * @param {object} payload - The payload object containing the updates + */ +function handleOntimePayload(payload) { + // 1. apply the patch into your local copy of the data + localData = { ...localData, ...payload }; + + if ('eventNow' in payload) { + let imgelement = document.getElementById("image"); + let timerelement = document.getElementById("timer"); + let container = document.getElementById("container"); + + localVars.layoutimg = payload.eventNow.custom["layoutimage"] ?? null; + imgelement.src = localVars.layoutimg; + if (localVars.layoutimg) { + imgelement.classList.remove("hidden"); + timerelement.classList.add("hidden"); + }else { + imgelement.classList.add("hidden"); + timerelement.classList.remove("hidden"); + } + + let ready = payload.eventNow.custom["ready"]; + if (ready) { + container.classList.remove("redoutline"); + container.classList.add("greenoutline"); + blink("container") + }else { + container.classList.add("redoutline"); + container.classList.remove("greenoutline"); + } + + } +} + +async function blink(id) { + document.getElementById(id).classList.add("blink"); + setTimeout(() => { + document.getElementById(id).classList.remove("blink"); + }, 3000); +} + +/** + * Updates the DOM with a given payload + * @param {string} field - The runtime data field + * @param {object} payload - The patch object for the field + */ +function updateDOM(field, payload) { + const domElement = document.getElementById(field); + if (domElement) { + domElement.innerText = payload; + } +} + +/** + * Stringifies an object into a pretty string + * @param {object} data - The data object to format + * @returns {string} The formatted data string + */ +function formatObject(data) { + return JSON.stringify(data, null, 2); +} + +/** + * Utility to handle a demo deployed in an ontime stage + * You can likely ignore this in your app + * + * an url looks like + * https://cloud.getontime.no/stage-hash/external/demo/ -> /stage-hash + * @returns {string} - The stage hash if the app is running in an ontime stage + */ +function getStageHash() { + const href = window.location.href; + if (!href.includes('getontime.no')) { + return ''; + } + + const hash = href.split('/'); + const stageHash = hash.at(3); + return stageHash ? `/${stageHash}` : ''; +} \ No newline at end of file diff --git a/timeroverlay/index.html b/timeroverlay/index.html new file mode 100644 index 0000000..d4e6c75 --- /dev/null +++ b/timeroverlay/index.html @@ -0,0 +1,27 @@ + + + + + + +
+ + +