diff --git a/edge-apps/peripheral-integration-demo/bun.lock b/edge-apps/peripheral-integration-demo/bun.lock index 88f957150..3ca4acfc5 100644 --- a/edge-apps/peripheral-integration-demo/bun.lock +++ b/edge-apps/peripheral-integration-demo/bun.lock @@ -4,6 +4,9 @@ "workspaces": { "": { "name": "peripheral-integration-demo", + "dependencies": { + "highlight.js": "^11.11.1", + }, "devDependencies": { "@playwright/test": "^1.58.0", "@screenly/edge-apps": "workspace:../edge-apps-library", @@ -725,6 +728,8 @@ "heap": ["heap@0.2.7", "", {}, "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="], + "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], + "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], diff --git a/edge-apps/peripheral-integration-demo/index.html b/edge-apps/peripheral-integration-demo/index.html index 16a1c3b40..a64ed6d06 100644 --- a/edge-apps/peripheral-integration-demo/index.html +++ b/edge-apps/peripheral-integration-demo/index.html @@ -251,6 +251,48 @@ + + +
+
+ + + + + Sensor Data +
+ +
+
+ Temperature + No Data +
+
+ Humidity + No Data +
+
+ Air Pressure + No Data +
+
+
@@ -321,6 +363,30 @@ + +
+
+ + + + + Raw Peripheral State +
+
No data yet
+
diff --git a/edge-apps/peripheral-integration-demo/package.json b/edge-apps/peripheral-integration-demo/package.json index 1c6eb7055..de2baa13b 100644 --- a/edge-apps/peripheral-integration-demo/package.json +++ b/edge-apps/peripheral-integration-demo/package.json @@ -31,5 +31,8 @@ "npm-run-all2": "^8.0.4", "prettier": "^3.8.1", "typescript": "^5.9.3" + }, + "dependencies": { + "highlight.js": "^11.11.1" } } diff --git a/edge-apps/peripheral-integration-demo/src/core/screen.ts b/edge-apps/peripheral-integration-demo/src/core/screen.ts index ed4db3291..76149ecf1 100644 --- a/edge-apps/peripheral-integration-demo/src/core/screen.ts +++ b/edge-apps/peripheral-integration-demo/src/core/screen.ts @@ -6,8 +6,16 @@ import { getTimeZone, } from '@screenly/edge-apps' import { Hardware } from '@screenly/edge-apps' +import hljs from 'highlight.js/lib/core' +import json from 'highlight.js/lib/languages/json' -import { subscribe, getState, setScreen, type ScreenType } from './state' +import { + subscribe, + getState, + setScreen, + type ScreenType, + getLastPeripheralState, +} from './state' import { waitForScreenDataPrepared, dispatchScreenDataPrepared, @@ -15,6 +23,8 @@ import { import { getNetworkStatus } from '../utils/network' import { updateOperatorDashboard } from '../features/operator-dashboard' +hljs.registerLanguage('json', json) + function delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)) } @@ -52,7 +62,8 @@ function syncScreensToState(state: ReturnType) { } const publicTemp = getEl('public-temperature') - publicTemp.textContent = `${Math.round(state.temperature)}°C` + publicTemp.textContent = + state.temperature !== null ? `${Math.round(state.temperature)}°C` : '--' if (state.currentScreen === 'maintenance') { getEl('maintenance-network').textContent = getNetworkStatus() @@ -78,6 +89,14 @@ async function loadMaintenanceInfo() { } catch { getEl('maintenance-timezone').textContent = '—' } + + const rawState = getLastPeripheralState() + const codeEl = getEl('maintenance-raw-state').querySelector('code')! + if (rawState) { + codeEl.innerHTML = hljs.highlight(JSON.stringify(rawState, null, 2), { + language: 'json', + }).value + } } async function preloadScreenData(role: ScreenType): Promise { diff --git a/edge-apps/peripheral-integration-demo/src/core/state.ts b/edge-apps/peripheral-integration-demo/src/core/state.ts index 5e5e7aa6c..4c162d272 100644 --- a/edge-apps/peripheral-integration-demo/src/core/state.ts +++ b/edge-apps/peripheral-integration-demo/src/core/state.ts @@ -4,14 +4,18 @@ export interface AppState { currentScreen: ScreenType timezone: string locale: string - temperature: number + temperature: number | null + humidity: number | null + airPressure: number | null } const state: AppState = { currentScreen: 'public', timezone: 'UTC', locale: 'en', - temperature: 22, + temperature: null, + humidity: null, + airPressure: null, } type Listener = (state: AppState) => void @@ -45,11 +49,29 @@ export function setLocale(locale: string) { notify() } -export function setTemperature(value: number) { - state.temperature = value +export function setSensorReadings(readings: { + temperature?: number | null + humidity?: number | null + airPressure?: number | null +}) { + if (readings.temperature !== undefined) + state.temperature = readings.temperature + if (readings.humidity !== undefined) state.humidity = readings.humidity + if (readings.airPressure !== undefined) + state.airPressure = readings.airPressure notify() } export function getState(): Readonly { return { ...state } } + +let lastPeripheralState: unknown = null + +export function setLastPeripheralState(raw: unknown) { + lastPeripheralState = raw +} + +export function getLastPeripheralState(): unknown { + return lastPeripheralState +} diff --git a/edge-apps/peripheral-integration-demo/src/css/style.css b/edge-apps/peripheral-integration-demo/src/css/style.css index e3d3690b9..db998c5da 100644 --- a/edge-apps/peripheral-integration-demo/src/css/style.css +++ b/edge-apps/peripheral-integration-demo/src/css/style.css @@ -370,6 +370,14 @@ auto-scaler { /* Maintenance rows */ +#screen-maintenance { + flex: 1; +} + +#screen-maintenance .glass-card:last-child { + flex: 0 1 auto; +} + .maintenance-rows { display: flex; flex-direction: column; @@ -425,3 +433,20 @@ auto-scaler { font-weight: 600; color: #e33876; } + +.maintenance-raw-state { + width: 100%; + flex: 1; + overflow: hidden; + border-radius: 16px; + font-size: 18px; + line-height: 1.6; + margin: 0; +} + +.maintenance-raw-state code { + display: block; + height: 100%; + padding: 1.5rem; + white-space: pre; +} diff --git a/edge-apps/peripheral-integration-demo/src/features/operator-dashboard.ts b/edge-apps/peripheral-integration-demo/src/features/operator-dashboard.ts index e2e8f3737..edb813e05 100644 --- a/edge-apps/peripheral-integration-demo/src/features/operator-dashboard.ts +++ b/edge-apps/peripheral-integration-demo/src/features/operator-dashboard.ts @@ -92,17 +92,31 @@ function stopUpdates() { } } +function updateSensorData(state: ReturnType) { + getEl('sensor-temperature').textContent = + state.temperature !== null ? `${state.temperature.toFixed(2)}°C` : 'No Data' + getEl('sensor-humidity').textContent = + state.humidity !== null ? `${state.humidity.toFixed(2)}%` : 'No Data' + getEl('sensor-air-pressure').textContent = + state.airPressure !== null + ? `${state.airPressure.toFixed(2)} hPa` + : 'No Data' +} + function onStateChange(state: ReturnType) { if (state.currentScreen === 'operator') { startUpdates() } else { stopUpdates() } + updateSensorData(state) } export function initOperatorDashboard() { subscribe(onStateChange) - if (getState().currentScreen === 'operator') startUpdates() + const state = getState() + if (state.currentScreen === 'operator') startUpdates() + updateSensorData(state) } export function updateOperatorDashboard() { diff --git a/edge-apps/peripheral-integration-demo/src/features/peripherals.ts b/edge-apps/peripheral-integration-demo/src/features/peripherals.ts index 68f844f4c..b8993c1a0 100644 --- a/edge-apps/peripheral-integration-demo/src/features/peripherals.ts +++ b/edge-apps/peripheral-integration-demo/src/features/peripherals.ts @@ -1,7 +1,11 @@ import { createPeripheralClient } from '@screenly/edge-apps' import type { PeripheralStateMessage } from '@screenly/edge-apps' -import { getState, setTemperature } from '../core/state' +import { + getState, + setSensorReadings, + setLastPeripheralState, +} from '../core/state' import { showWelcomeThenSwitch } from '../core/screen' import { restartLogoutTimer } from '../core/timer' import { authenticate } from './authenticate' @@ -14,12 +18,25 @@ export function initPeripherals() { client.register(edgeAppId) client.watchState((msg: PeripheralStateMessage) => { + setLastPeripheralState(msg.request.edge_app_source_state) + const readings = msg.request.edge_app_source_state.states const tempReading = readings.find((r) => 'ambient_temperature' in r) - if (tempReading) { - setTemperature(tempReading.ambient_temperature as number) - } + const humidityReading = readings.find((r) => 'humidity' in r) + const pressureReading = readings.find((r) => 'air_pressure' in r) + + setSensorReadings({ + temperature: tempReading + ? (tempReading.ambient_temperature as number) + : undefined, + humidity: humidityReading + ? (humidityReading.humidity as number) + : undefined, + airPressure: pressureReading + ? (pressureReading.air_pressure as number) + : undefined, + }) const cardReading = readings.find((r) => 'secure_card' in r) if (cardReading) { diff --git a/edge-apps/peripheral-integration-demo/src/main.ts b/edge-apps/peripheral-integration-demo/src/main.ts index c0fbf2e17..93e9a1f83 100644 --- a/edge-apps/peripheral-integration-demo/src/main.ts +++ b/edge-apps/peripheral-integration-demo/src/main.ts @@ -1,4 +1,5 @@ import './css/style.css' +import 'highlight.js/styles/monokai.css' import '@screenly/edge-apps/components' import {