Skip to content
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
2b6f1a8
feat(edge-apps): add peripheral integration POC
nicomiguelino Mar 2, 2026
21fcf10
chore(edge-apps): apply formatting fixes to peripheral integration POC
nicomiguelino Mar 2, 2026
6d4ec84
chore(edge-apps): fix linting errors in edge-apps-library
nicomiguelino Mar 2, 2026
170c717
chore: exclude peripheral-integration-poc from Prettier
nicomiguelino Mar 2, 2026
f65f9a8
refactor(edge-apps): update peripheral integration to use snapshot model
nicomiguelino Mar 3, 2026
a83ea72
docs(edge-apps): fix markdown table separator style in POC.md
nicomiguelino Mar 3, 2026
1cf256b
Merge branch 'master' into feat/peripheral-integration-poc
nicomiguelino Mar 3, 2026
97129fa
feat(edge-apps): add peripheral integration POC with watchState API
nicomiguelino Mar 3, 2026
66f41d9
feat(edge-apps): scaffold peripheral-integration-demo Edge App
nicomiguelino Mar 3, 2026
4d1a982
feat(edge-apps): wire peripheral temperature into demo app
nicomiguelino Mar 3, 2026
8e585db
feat(edge-apps): wire card reader auth into peripheral-integration-demo
nicomiguelino Mar 3, 2026
f3c0f5b
feat(edge-apps): update peripheral protocol to edge_app_source_state
nicomiguelino Mar 3, 2026
41a3df7
chore(edge-apps): retire peripheral-integration-poc
nicomiguelino Mar 4, 2026
328f295
docs(peripheral-demo): update README with accurate API snippet and se…
nicomiguelino Mar 4, 2026
87ce1ba
refactor(edge-apps): deliver full wire message in watchState callback
nicomiguelino Mar 4, 2026
0e4ec87
feat(edge-apps): add edge_app_registration phase to peripheral integr…
nicomiguelino Mar 4, 2026
2196671
chore(peripheral-demo): remove temporary role switch buttons
nicomiguelino Mar 4, 2026
6f02124
refactor(edge-apps): move peripheral WS client into edge-apps-library
nicomiguelino Mar 4, 2026
be9ff8c
refactor(edge-apps): rename PeripheralReading to PeripheralState
nicomiguelino Mar 4, 2026
429e24a
feat(edge-apps): update secure_card wire format to nested uid object
nicomiguelino Mar 4, 2026
157b403
feat(peripheral-demo): reject stale card reads older than 1 minute
nicomiguelino Mar 4, 2026
e3c000d
feat(peripheral-demo): redesign public view to match Figma design
nicomiguelino Mar 4, 2026
bf58746
feat(peripheral-demo): redesign operator view to match Figma design
nicomiguelino Mar 4, 2026
5f4288e
feat(peripheral-demo): redesign maintenance view to match Figma design
nicomiguelino Mar 4, 2026
57f2a01
feat(peripheral-demo): redesign welcome overlay to match Figma design
nicomiguelino Mar 4, 2026
a08ccec
chore(peripheral-demo): remove emojis from safety carousel tips
nicomiguelino Mar 4, 2026
73953f8
refactor(peripheral-demo): rename TS files to kebab-case and simplify…
nicomiguelino Mar 4, 2026
cdb290d
fix(peripheral-demo): prevent SVG icons from flashing large before CS…
nicomiguelino Mar 4, 2026
5a1c6c4
fix(peripheral-demo): round temperature to nearest integer in public …
nicomiguelino Mar 4, 2026
7e92ca2
fix(peripheral-demo): fix portrait layout by locking orientation and …
nicomiguelino Mar 4, 2026
ab7323a
fix(peripheral-demo): fix maintenance view not populating on card scan
nicomiguelino Mar 5, 2026
9229487
fix(edge-apps): exclude fonts from asset inlining to avoid WAF false …
nicomiguelino Mar 5, 2026
d135e2d
feat(peripheral-demo): add categories to manifest files
nicomiguelino Mar 5, 2026
ce2c33f
fix(peripheral-demo): update layout and styling in index.html
514sid Mar 5, 2026
a5ad76f
fix(edge-apps): remove identification ACK dependency in peripheral cl…
nicomiguelino Mar 5, 2026
585b8bf
fix(edge-apps): simplify peripheral init sequence per protocol correc…
nicomiguelino Mar 5, 2026
1937989
refactor(edge-apps): align type names with Peri Protocol
nicomiguelino Mar 5, 2026
6688369
docs(edge-apps): improve comments in peripheral client and types
nicomiguelino Mar 5, 2026
3a846d2
chore: update Prettier ignore to exclude peripheral-integration-demo
nicomiguelino Mar 5, 2026
ed2941e
refactor(edge-apps-library): improve peripheral client robustness and…
nicomiguelino Mar 5, 2026
b58c98a
chore(peripheral-integration-demo): add app and instance IDs to manif…
nicomiguelino Mar 5, 2026
b46ab16
fix(edge-apps-library): remove downstream_node_event ACK handler
nicomiguelino Mar 5, 2026
7665190
refactor(edge-apps): revert type renames to original names
nicomiguelino Mar 5, 2026
6034f80
refactor(peripheral-integration-demo): replace Tailwind with plain CSS
nicomiguelino Mar 6, 2026
ab63f00
style(peripheral-integration-demo): adjust margins for date and time …
514sid Mar 6, 2026
ead28f5
fix(peripheral-integration): handle binary WebSocket messages and upd…
salmanfarisvp Mar 6, 2026
13d8d46
fix ts lint
salmanfarisvp Mar 6, 2026
e7dd6b1
fix(peripheral-integration): prevent logout timer from restarting on …
salmanfarisvp Mar 6, 2026
eb682de
feat(peripheral-integration): restart timer when same card is scanned…
salmanfarisvp Mar 6, 2026
20349db
fix ts lint
salmanfarisvp Mar 6, 2026
afef88e
Merge branch 'master' into feat/peripheral-integration-poc
nicomiguelino Mar 6, 2026
dd9d8d5
fix(peripheral-integration): sync dev server card UIDs with authentic…
nicomiguelino Mar 6, 2026
4c642cd
fix(peripheral-integration): prevent UI flash when switching between …
salmanfarisvp Mar 9, 2026
b04313d
fix(peripheral-integration): add proper spacing for hardware name in …
salmanfarisvp Mar 9, 2026
c84b3ad
feat(peripheral-demo): add Sensor Data card to operator view
nicomiguelino Mar 9, 2026
ee6cce6
fix(peripheral-demo): display sensor values with 2 decimal places
nicomiguelino Mar 9, 2026
6285ffa
refactor(peripheral-demo): widen sensor state setters to accept null
nicomiguelino Mar 9, 2026
86e5699
refactor(peripheral-demo): batch sensor state updates into single not…
nicomiguelino Mar 9, 2026
092e6fe
feat(peripheral-demo): add Raw Peripheral State card to maintenance view
nicomiguelino Mar 10, 2026
83809de
Merge branch 'master' into feat/peripheral-integration-poc
nicomiguelino Mar 10, 2026
5f5d9a1
Merge branch 'feat/peripheral-integration-poc' into ew-demo/additiona…
nicomiguelino Mar 10, 2026
8445984
Merge branch 'master' into feat/peripheral-integration-poc
nicomiguelino Mar 10, 2026
d33ff6f
Merge branch 'feat/peripheral-integration-poc' into ew-demo/additiona…
nicomiguelino Mar 10, 2026
39f51eb
Merge pull request #725 from Screenly/ew-demo/additional-sensor-data-…
salmanfarisvp Mar 10, 2026
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
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ edge-apps/.bun-create/
edge-apps/cap-alerting/
edge-apps/clock/
edge-apps/edge-apps-library/
edge-apps/peripheral-integration-demo/
edge-apps/grafana/
edge-apps/powerbi-legacy/
edge-apps/powerbi-legacy/**
Expand Down
9 changes: 9 additions & 0 deletions edge-apps/edge-apps-library/bun.lock

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

5 changes: 4 additions & 1 deletion edge-apps/edge-apps-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
"offline-geocode-city": "^1.0.2",
"panic-overlay": "^1.0.51",
"sharp": "^0.34.5",
"tailwindcss": "^4.2.1"
"tailwindcss": "^4.2.1",
"ulid": "^3.0.2"
},
"peerDependencies": {
"@playwright/test": "^1.58.0"
Expand All @@ -94,6 +95,7 @@
"@types/jsdom": "^28.0.0",
"@types/node": "^25.3.0",
"@types/sharp": "^0.32.0",
"@types/ws": "^8.18.1",
"bun-types": "^1.3.9",
"eslint": "^10.0.2",
"jiti": "^2.6.1",
Expand All @@ -102,6 +104,7 @@
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1",
"vite": "^7.3.1",
"ws": "^8.19.0",
"yaml": "^2.8.2"
}
}
3 changes: 3 additions & 0 deletions edge-apps/edge-apps-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ export * from './components/index.js'

// Types
export * from './types/index.js'

// Peripheral integration
export * from './peripherals/index.js'
110 changes: 110 additions & 0 deletions edge-apps/edge-apps-library/src/peripherals/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { ulid } from 'ulid'

import type { EdgeAppSourceState } from '../types/index.js'

// WebSocket address of the hardware integration service on the Screenly player.
// Port 9010 is a fixed port defined in the player firmware for Edge App connections.
const PERIPHERAL_WS_URL = 'ws://127.0.0.1:9010'

const ETB = '\x17'

export interface PeripheralClient {
register: (edgeAppId: string) => void
watchState: (callback: (msg: EdgeAppSourceState) => void) => void
}

// eslint-disable-next-line max-lines-per-function
export function createPeripheralClient(): PeripheralClient {
let ws: WebSocket | null = null
const subscribers: Array<(msg: EdgeAppSourceState) => void> = []
const readings: Record<string, unknown> = {}

function send(payload: unknown) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(payload) + ETB)
}
}

function notifySubscribers() {
const msg: EdgeAppSourceState = {
request: {
id: ulid(),
edge_app_source_state: {
states: Object.values(
readings,
) as EdgeAppSourceState['request']['edge_app_source_state']['states'],
},
},
}
subscribers.forEach((cb) => cb(msg))
}

function connect() {
ws = new WebSocket(PERIPHERAL_WS_URL)

ws.onmessage = (e: MessageEvent) => {
const text = (e.data as string).replace(ETB, '')
let msg: Record<string, unknown>
try {
msg = JSON.parse(text) as Record<string, unknown>
} catch {
return
}

const request = msg.request as Record<string, unknown> | undefined
if (request?.edge_app_source_state) {
const state = request.edge_app_source_state as {
states: Array<{ name: string }>
}
state.states.forEach((s) => {
readings[s.name] = s
})
notifySubscribers()
send({
response: { request_id: request.id, ok: 'edge_app_source_state' },
})
return
}

if (request?.downstream_node_event) {
send({
response: { request_id: request.id, ok: 'downstream_node_event' },
})
return
}
}

ws.onerror = () => console.warn('[screenly] Peripheral WS error')
ws.onclose = () => {
setTimeout(connect, 2000)
}
}

return {
register(edgeAppId: string) {
if (!ws) connect()
const sendRegistration = () => {
send({
request: {
id: ulid(),
edge_app_registration: {
id: edgeAppId,
secret: '',
requested_source_channels: [],
},
},
})
}
if (ws!.readyState === WebSocket.OPEN) {
sendRegistration()
} else {
ws!.addEventListener('open', sendRegistration, { once: true })
}
},

watchState(callback: (msg: EdgeAppSourceState) => void) {
if (!ws) connect()
subscribers.push(callback)
},
}
}
28 changes: 28 additions & 0 deletions edge-apps/edge-apps-library/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ export interface ScreenlySettings extends Record<string, unknown> {
sentry_dsn?: string
}

/**
* A single peripheral channel reading as delivered by the hardware integration service.
* The sensor-specific value is carried under its wire key (e.g. ambient_temperature, humidity).
*/
export interface SourceChannelStateReport {
/** Channel name as configured on the player (e.g. "my_living_room_temp") */
name: string
/** Unix epoch timestamp in milliseconds */
timestamp: number
/** Physical unit (e.g. "°C", "%", "hPa") — omitted when not applicable */
unit?: string
/** Sensor-specific value field (e.g. ambient_temperature, humidity, byte_array) */
[wireKey: string]: unknown
}

/**
* Full wire message delivered by the hardware integration service.
* Matches the `edge_app_source_state` push event shape from the hardware integration service.
*/
export interface EdgeAppSourceState {
request: {
id: string
edge_app_source_state: {
states: SourceChannelStateReport[]
}
}
}

/**
* The global screenly object provided by screenly.js
*/
Expand Down
Loading