← All work

In progress

OPC-UA Web Dashboard

Live multi-station factory dashboard. Python + SvelteKit, real-time over WebSocket.

OPC-UAIndustrial IoTDashboard asyncuaFastAPISvelteKitTailwindPostgresTimescaleDBDocker

What it is

A reference implementation for streaming OPC-UA telemetry from a small fleet of stations into a modern web dashboard, with first-class operator UX and a backend that survives network partitions without losing data.

The point of this project is to show — concretely, in a public repo — how the “automation engineering ↔ web engineering” handoff should look in 2026.

What it solves

A common failure mode in industrial dashboards is that they’re built by web engineers who treat OPC-UA like a REST API, or by automation engineers who treat the web like a glorified PLC HMI. Either way the result is fragile: too chatty, too unreliable under network blips, and too hard to extend.

This project takes a position on each of those:

  • Subscription-driven, not poll-driven. The dashboard subscribes to OPC-UA monitored items; the gateway translates value-change notifications into a deduplicated WebSocket stream.
  • Backend buffers; UI doesn’t. A Postgres + TimescaleDB pair provides the source of truth; the UI is a view onto recent state, not a state machine.
  • Operator-first interaction. Confirmation dialogs, status banding, and history windows behave the way line operators expect.

Architecture

flowchart LR
    PLC[OPC-UA Server<br/>Station 1..N] -->|subscriptions| GW[Gateway<br/>Python · asyncua]
    GW -->|WebSocket| WEB[SvelteKit Dashboard]
    GW -->|writes| TS[(TimescaleDB)]
    WEB -->|history queries| API[FastAPI]
    API --> TS

The gateway and the API are two services so we can scale or restart them independently. The UI doesn’t care which one is up — degraded mode is explicit.

Stack notes

  • asyncua for the OPC-UA client. Reliable, well-maintained, async-native.
  • FastAPI for the read API — historical queries, fleet config, operator events.
  • SvelteKit (Svelte 5 runes) for the UI. Static assets via Cloudflare; auth via a signed cookie issued by the API.
  • TimescaleDB for time-series storage with continuous aggregates. The dashboard asks for “last 60 seconds at 250ms resolution” or “last 24h at 1min” and the right view is selected automatically.

What’s interesting

The non-obvious part is the gateway’s backpressure handling. When a UI is slow or a client disconnects, OPC-UA notifications must not pile up indefinitely on the server side, and Postgres writes must not lag behind the live stream. The gateway runs three separate consumers fed from a single ring buffer: WebSocket fanout, durable write, and alarm evaluation. They drop independently when overloaded; the UI is told what’s stale.

Status

Currently building (weeks 3–5 of the public timeline). Once live, this card flips to status: "live" and the demo URL becomes real. Source will be public from day one.