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 @@
+
+
+
+
+
+
+
+ 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 {