Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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