Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions edge-apps/peripheral-integration-demo/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions edge-apps/peripheral-integration-demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,48 @@
</div>
</div>
</div>

<!-- Sensor Data -->
<div class="glass-card glass-card--left">
<div class="glass-card-heading">
<!-- Activity icon -->
<svg
class="glass-card-icon"
width="48"
height="48"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
</svg>
<span class="glass-card-title">Sensor Data</span>
</div>

<div class="operator-grid">
<div class="operator-field">
<span class="operator-label">Temperature</span>
<span id="sensor-temperature" class="operator-value"
>No Data</span
>
</div>
<div class="operator-field">
<span class="operator-label">Humidity</span>
<span id="sensor-humidity" class="operator-value"
>No Data</span
>
</div>
<div class="operator-field">
<span class="operator-label">Air Pressure</span>
<span id="sensor-air-pressure" class="operator-value"
>No Data</span
>
</div>
</div>
</div>
</div>

<!-- Maintenance Screen -->
Expand Down Expand Up @@ -321,6 +363,30 @@
</div>
</div>
</div>

<div class="glass-card glass-card--left">
<div class="glass-card-heading">
<svg
class="glass-card-icon"
width="48"
height="48"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="16 18 22 12 16 6" />
<polyline points="8 6 2 12 8 18" />
</svg>
<span class="glass-card-title">Raw Peripheral State</span>
</div>
<pre
id="maintenance-raw-state"
class="maintenance-raw-state"
><code class="language-json">No data yet</code></pre>
</div>
</div>
</main>

Expand Down
3 changes: 3 additions & 0 deletions edge-apps/peripheral-integration-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@
"npm-run-all2": "^8.0.4",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
},
"dependencies": {
"highlight.js": "^11.11.1"
}
}
23 changes: 21 additions & 2 deletions edge-apps/peripheral-integration-demo/src/core/screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@ 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,
} from './screen-events'
import { getNetworkStatus } from '../utils/network'
import { updateOperatorDashboard } from '../features/operator-dashboard'

hljs.registerLanguage('json', json)

function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
}
Expand Down Expand Up @@ -52,7 +62,8 @@ function syncScreensToState(state: ReturnType<typeof getState>) {
}

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()
Expand All @@ -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<void> {
Expand Down
30 changes: 26 additions & 4 deletions edge-apps/peripheral-integration-demo/src/core/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<AppState> {
return { ...state }
}

let lastPeripheralState: unknown = null

export function setLastPeripheralState(raw: unknown) {
lastPeripheralState = raw
}

export function getLastPeripheralState(): unknown {
return lastPeripheralState
}
25 changes: 25 additions & 0 deletions edge-apps/peripheral-integration-demo/src/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,31 @@ function stopUpdates() {
}
}

function updateSensorData(state: ReturnType<typeof getState>) {
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<typeof getState>) {
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() {
Expand Down
25 changes: 21 additions & 4 deletions edge-apps/peripheral-integration-demo/src/features/peripherals.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions edge-apps/peripheral-integration-demo/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './css/style.css'
import 'highlight.js/styles/monokai.css'
import '@screenly/edge-apps/components'

import {
Expand Down
Loading