GridSight

Full-Stack
2026-05-03T15:33:23Z
AngularTypeScriptNode.jsExpressBackend

GridSight is a vertical slice of a real-feeling utility tool: a fleet view of eight plausibly-Swedish substation transformers and feeder breakers, each emitting SCADA-style telemetry (top-oil and winding temperatures, load factor, voltage, dissolved-gas readings) every 5 seconds via an in-process simulator. Threshold rules referenced to IEEE C57.91 (transformer loading) and IEEE C57.104 (DGA condition limits) raise alerts as soon as a reading crosses warning or alarm levels. The Angular frontend subscribes to a single Server-Sent Events stream and updates the dashboard, asset detail charts, and alert feed live, with reconnect-with-exponential-backoff baked into the SSE client.

The backend is Node 20 + Express + strict TypeScript, with SQLite (better-sqlite3, WAL mode) for storage, Zod at every API boundary, pino for structured logging, prom-client for /metrics, and a hand-authored OpenAPI 3.1 spec served at /openapi. The simulator and the alert engine are pure functions over a TelemetryReading type, with vitest unit tests covering the threshold matrix and asserting that compound failures (overload + high oil temp) raise multiple alerts. Helmet, CORS allowlist, express-rate-limit, and graceful SIGTERM shutdown round out the security and operability defaults.

The AI feature is the differentiator and the security-conscious bit. The recommendations endpoint is env-gated: if ANTHROPIC_API_KEY is set, the route calls Claude with a tool-use schema enforcing the response shape (urgency, root_cause, recommended_actions[], confidence, references); if the key is missing, it serves a hand-authored fixture matched to the alert rule. The five fixtures (healthy, oil-temp-high, load-imbalance, winding-fault, dga-spike) are written to read like recommendations a maintenance planner could actually act on, citing IEEE references and explicit priority levels (now / soon / planned). The portfolio page narrative around this is the point: real integration in the repo, fixtures in the public demo, key never leaves local dev.

The Angular frontend is intentionally modern: standalone components, signals (state), control-flow @if/@for, OnPush change detection, lazy-loaded routes, RxJS via takeUntilDestroyed for cleanup, Angular Material for chrome, and a hand-rolled inline-SVG sparkline component instead of pulling in a chart library. The whole bundle stays under 500kb initial. Architecture is env-driven and stateless, so although the demo runs on Vercel, the same binaries are Azure App Service portable. That trade-off is documented on this page rather than papered over.

  • Eight simulated substation assets (transformers, MV/LV substations, feeder breakers) across the Solna metro area, with IEC 60076 field naming
  • SCADA-style telemetry every 5 s: oil + winding temperature, load factor, voltage, current, DGA gases (H₂, CH₄, C₂H₂)
  • Threshold-based anomaly detection referenced to IEEE C57.91 (oil-temp warn 95 °C / alarm 105 °C; winding alarm 130 °C) and IEEE C57.104 (DGA condition 2/3 limits)
  • Server-Sent Events fan-out for telemetry + alert events; client reconnects with exponential backoff
  • Env-gated AI recommendation endpoint that calls Claude with tool-use schema enforcement when ANTHROPIC_API_KEY is set, otherwise serves hand-authored fixtures
  • Five fixtures (healthy, oil-temp-high, load-imbalance, winding-fault, dga-spike) authored to read like real maintenance planner output, with priority levels and IEEE references
  • Angular 18 standalone components + signals + lazy routes + Angular Material; hand-rolled inline-SVG sparkline (no chart library)
  • Express + TypeScript strict mode + Zod request validation + helmet + CORS allowlist + express-rate-limit + graceful SIGTERM shutdown
  • OpenAPI 3.1 spec hand-authored and served at /openapi; vitest + supertest cover routes and threshold matrix
  • Stateless API + env-driven config = Azure App Service portable (deployed to Vercel, deliberately documented as the trade-off)

Live snapshot: Råsunda T11 TR-RASUNDA-11

A reading captured mid-fault. Same data shape that flows over the SSE stream and into the AI recommendation endpoint.

Telemetry reading

Oil temp
102.4 °C alarm 105 °C
Winding temp
118.7 °C warn 110 °C
Load factor
93% warn 100%
Voltage
0.984 pu
DGA H₂
64 ppm
DGA C₂H₂
0.60 ppm
high oil_temp_high Top-oil temperature 102.4 °C exceeds IEEE C57.91 alarm threshold (105 °C).

AI recommendation

demo fixture

Top-oil temperature has crossed the IEEE C57.91 alarm threshold (105 °C) while load factor remained near rated capacity. The combination indicates the cooling system is under-performing relative to thermal demand, most commonly a fouled radiator surface, degraded fan operation, or low oil level.

  1. now Reduce loading to ≤ 70% of rated kVA until oil temperature returns below 90 °C.

    Sustained operation above 105 °C accelerates insulation aging by ~2× per 6 °C (Arrhenius). Load reduction is the only corrective lever available remotely.

  2. soon Dispatch field crew to verify cooling fan status, radiator surface cleanliness, and oil level.

    Three of the four most common causes of high-oil-temp events are cooling-system mechanical and can be diagnosed in a single 30-minute site visit.

  3. planned Schedule infrared thermography sweep of all radiator panels during the next available outage window.

    Thermography reveals partial blockages and uneven flow that are not detectable from oil-temp telemetry alone.

confidence 84% refs: IEEE C57.91-2011 §7.2.2 · IEEE C57.91-2011 Annex A

The integration code in api/src/domain/recommender.ts calls Claude with a tool-use schema enforcing this exact shape. The deployed demo doesn't ship the API key. It serves five hand-authored fixtures matched to the alert rule. Flip ANTHROPIC_API_KEY in env to switch paths.

§ Engineering Notes

How it ships

AI the way enterprises ship it

The recommendations endpoint is env-gated: with ANTHROPIC_API_KEY set, the route calls Claude with a tool-use schema enforcing the response shape; without it, the route serves one of five hand-authored fixtures. Same JSON contract, two paths. The deployed demo never holds the key.

Domain-literate, deploy-portable

Field naming follows IEC 60076; threshold rules reference IEEE C57.91 (oil 95/105 °C, winding 130 °C) and IEEE C57.104 (DGA condition 2/3). The simulator injects compound failures and the alert engine raises the right combinations.

Stateless API + env-driven config = Azure App Service portable. The demo runs on Vercel; moving it to Azure is a Bicep file and a connection string.