Platform Architecture

How HOP Sensors connects substation SCADA systems to the cloud, detects anomalies before they become outages, and delivers real-time operational intelligence to grid operators.

v5 — June 2026 React · FastAPI · InfluxDB 3 · Python ML Cloud-hosted SaaS
Contents

00Executive Summary

HOP Sensors is a cloud-native SCADA observability platform built for electric utilities. Substation systems transmit data over HTTPS to a REST API. A machine learning engine scores every substation for anomalies every 60 seconds. Operators view results in a live web dashboard refreshing every 5 seconds. Automated email alerts notify staff of feeder trips and abnormal conditions — no on-premises hardware required.

5s
Dashboard refresh rate
60s
ML scoring interval
0
On-prem hardware needed
v5
Current generation
CapabilityDetails
Data transmissionHTTPS REST API with pre-shared API key authentication
Alert deliveryEmail — anomaly alerts, feeder trips, and restore confirmations
User access modelRole-based, per-utility, per-substation access control
Platform generationv5 — React dashboard, replaced Grafana-based v4

01System Overview

The v5 platform is a six-service architecture running on DigitalOcean cloud infrastructure. Each service is containerized with Docker and orchestrated via Docker Compose, split between two servers: a SCADA droplet running all data pipeline services, and a mail droplet handling the public website and email delivery.

End-to-End Data Flow

  Substation SCADA Device  (RTU / IED / DMS)
          |
          |  HTTPS + API Key
          v
    FastAPI Ingest API  -------->  InfluxDB 3 Core
                                        |
                                 ML Service  (z-score, every 60s)
                                        |
                                 Alerting Service  ------->  Email (SendGrid)

    React Dashboard  <--------  FastAPI Query API  <--------  InfluxDB 3
    portal.hopsensorsllc.com -- refreshes every 5s
      

Service Inventory

Core API

FastAPI (v5-fastapi)

REST API handling ingest from substations and query requests from the dashboard. Authenticates all requests via X-API-Key header.

PythonPort 8000REST
Storage

InfluxDB 3 Core (v5-influxdb)

Purpose-built time-series database storing telemetry, events, outages, and ML scores. Queried via SQL.

InfluxDB 3Port 8181SQL
Intelligence

ML Service (v5-ml)

Z-score anomaly detection on frequency readings every 60 seconds. Dynamically discovers substations and writes scores back through FastAPI.

PythonPort 9000APScheduler
Notifications

Alerting Service (v5-alerting)

Polls for anomalies and feeder events. Per-source cooldowns and global rate limits prevent alert fatigue. Delivers via mail droplet API.

PythonInternal
Frontend

React Dashboard (v5-frontend)

Single-page React + Vite app served by nginx. Six tabs: Telemetry, Alarms, Frequency, ML Anomaly, Executive View, Maps. Refreshes every 5s.

ReactVitePort 3001
Identity

Auth Service (v5-auth)

Flask app providing login, session management, user administration, and per-user substation access control. Backed by SQLite.

FlaskSQLitePort 5001

02Data Ingestion

Substation systems transmit data over HTTPS using a pre-shared API key. Three endpoints handle the three data types the platform tracks.

EndpointData TypeKey Fields
POST /ingest/telemetryReal-time readings per feeder/substationvoltage_kv, current_a, power_mw, frequency_hz
POST /ingest/eventDiscrete grid events (feeder trips, restores)device_id, feeder_id, severity, event_type, old_state, new_state
POST /ingest/outageOutage records with customer impactoutage_id, feeder_id, customers_out, mw_interrupted, status, cause

Example Payload

// POST /ingest/telemetry  --  Header: X-API-Key: <key>
{
  "substation_id": "SUB001",
  "feeder_id":     "FDR002",
  "device_id":     "SUB001-FDR002",
  "site_name":     "Portland North",
  "latitude":      45.5231,
  "longitude":     -122.6765,
  "voltage_kv":    229.8,
  "current_a":     1187.4,
  "power_mw":      274.2,
  "frequency_hz":  60.002
}

Integration Note: Any system capable of HTTPS POST requests can integrate — existing SCADA platforms, DMS, RTU data collectors, or custom middleware. No proprietary hardware or protocol adapters required.

03Time-Series Storage

HOP Sensors v5 uses InfluxDB 3 Core, a purpose-built time-series database optimized for high-frequency, append-only measurement data. The platform queries via SQL, replacing the Flux query language used in v4.

Database Schema

MeasurementTags (indexed)Fields
scada_gridsubstation_id, feeder_id, device_id, site_namevoltage_kv, current_a, power_mw, frequency_hz, latitude, longitude, anomaly_score, is_anomaly, z_score, baseline_mean, baseline_std, latest_value, model_version
scada_eventsdevice_id, feeder_id, severity, event_typeold_state, new_state, message, latitude, longitude
outagesoutage_id, feeder_id, status, region, causecustomers_out, mw_interrupted, etr_time, latitude, longitude

v4 to v5: Migrated from Grafana + Flux queries to a custom React dashboard over SQL through FastAPI — replacing proprietary Grafana config with fully version-controlled application code.

04ML Anomaly Detection

The ML service runs a statistical z-score anomaly detector on grid frequency. Every 60 seconds it fetches the most recent 20 readings per substation, computes a rolling baseline, and scores the latest reading. No training data or GPU resources required — fully interpretable and auditable.

Scoring Algorithm

# For each substation -- last N=20 frequency readings:
mean  = mean(readings)
std   = stddev(readings)
z     = (latest - mean) / std          # standard deviations from baseline
score = min(abs(z) / Z_THRESHOLD, 1.0) # normalized to [0.0, 1.0]

# Classification:
NORMAL   : score <  0.4   # z < 1.0 sigma
ELEVATED : score <  0.7   # 1.0 sigma to 1.75 sigma
ANOMALY  : score >= 0.7   # z >= 1.75 sigma  --  triggers alert email
Field Written to DBDescription
anomaly_scoreNormalized 0.0–1.0. Higher = more anomalous.
is_anomalyBoolean. True when |z| >= Z_THRESHOLD (default 2.5 sigma).
z_scoreRaw z-score in standard deviations from baseline mean.
baseline_meanMean of the scoring window (rolling average).
baseline_stdStandard deviation of the scoring window.
latest_valueMost recent frequency reading scored.
model_versionModel identifier for auditability (e.g. zscore-v1).

05Alerting Pipeline

The alerting service polls InfluxDB and the event API every 60 seconds. Three alert types with independent rate-limiting logic prevent alert fatigue. Delivery is via SendGrid SMTP through the mail droplet.

Alert TypeTriggerPer-Source CooldownGlobal Cap
ANOMALY ALERTanomaly_score >= 0.7 on any substation1 hour per substation2 / hour
FEEDER TRIPFEEDER_TRIP event with severity=ALARM30 min per feeder3 / hour
FEEDER RESTOREDFEEDER_RESTORE, only if a TRIP alert was sent for that feederNoneShared with TRIP

The SCADA droplet authenticates to the mail droplet's API with an X-Alert-Token shared secret. Email content includes substation ID, detected values, z-score, baseline context, and a direct link to the portal dashboard.

Design note: Alerting is fully decoupled from the dashboard. Operators receive email alerts even when not logged in, and delivery does not depend on any browser session.

06Operator Dashboard

The dashboard is a React single-page application at portal.hopsensorsllc.com. It auto-refreshes every 5 seconds and requires only a modern web browser — no plugins, extensions, or client software.

TabPurposeKey Components
TelemetryLive readings per substationLive panel (anomaly score, voltage, current, power, frequency), time-series charts
Alarms & OutagesOperational event feedColor-coded event log, active outage table with MW and customer impact
FrequencyGrid frequency vs. NERC boundsPer-substation dials (59.85–60.15 Hz normal), multi-substation threshold plot
ML AnomalyAnomaly scores and trendScore cards (Normal / Elevated / Anomaly), trend scatter plot, z-score detail table
Executive ViewSingle-screen grid healthGrid health %, active outages, MW interrupted, anomalies detected, per-substation status
MapsGeographic visualizationLeaflet map, substation markers, outage polygon overlays, GIS data input

Features: light/dark mode, time range selector (5m / 15m / 30m / 1h / 6h / 24h / 7d), role-based default tab (executives land on Executive View, operators on Telemetry).

07Authentication & Multi-Tenancy

Authentication is handled by a Flask app backed by SQLite. Users log in at portal.hopsensorsllc.com/login with work email and password. Sessions are server-side cookies; passwords are bcrypt hashed.

FieldDescription
emailUnique work email. Used as login username.
nameDisplay name shown in the dashboard header.
org_nameUtility organization (e.g. Pacific Power Co.)
roleoperator or admin. Admins access the /admin user management panel.
activeBoolean. Deactivated users cannot log in; records are retained.
substationsList of substation IDs the user is authorized to view.

Per-user substation filtering is enforced in the React frontend via session data injected at login. Admins manage users at /admin without SSH access.

08Infrastructure

The platform runs on two DigitalOcean Droplets. All v5 services run as Docker containers with restart: unless-stopped, orchestrated by Docker Compose.

ServerHostnameServices
SCADA Droplethopsensors-scadav5-fastapi, v5-influxdb, v5-ml, v5-alerting, v5-auth, v5-frontend, v5-simulator
Mail Dropletmail (do-linpaws)hopsensors-api (systemd), nginx, Postfix, Dovecot

Public DNS points hopsensorsllc.com and portal.hopsensorsllc.com to the mail droplet's nginx. The portal subdomain is reverse-proxied to the auth service on the SCADA droplet. All containers share the v5-hopsensors-net Docker bridge network. InfluxDB (8181) and FastAPI (8000) are not exposed to the public internet.

09Security

ControlImplementation
Transport encryptionAll public endpoints over HTTPS/TLS via nginx. Internal container traffic on isolated Docker bridge network.
Ingest API authEvery request requires X-API-Key header, managed via environment variables.
Alert channel authX-Alert-Token shared secret between SCADA and mail droplets. Channel not publicly exposed.
Portal authSession-based login. Bcrypt hashed passwords. Inactive users hard-blocked.
Brute-force protectionfail2ban on both droplets monitoring SSH and nginx access logs.
Attack surface reductionInfluxDB and FastAPI not publicly exposed. Legacy Grafana disabled and removed.
Data isolationPer-user substation access enforced. Users see only their assigned substations.

Roadmap: SOC 2 Type I audit logging planned for a future release, targeting enterprise utility customers with formal compliance requirements.

10API Reference

All endpoints require X-API-Key header. Base URL: https://portal.hopsensorsllc.com/api

Ingest

Method + PathDescription
POST /ingest/telemetryWrite a telemetry reading for a substation/feeder
POST /ingest/eventWrite a discrete grid event (trip, restore, etc.)
POST /ingest/outageWrite or update an outage record

Query

Method + PathDescription
GET /query/telemetry/latestLatest reading per substation (last 1 minute)
GET /query/telemetry/timeseriesVoltage/power/frequency time series — params: substation_id, minutes
GET /query/ml/latestLatest ML anomaly score per substation
GET /query/ml/trendAnomaly score trend for all substations — param: minutes
GET /query/eventsRecent events/alarms — param: minutes
GET /query/outages/activeCurrently active outages
GET /query/frequency/thresholdsFrequency readings for threshold plot — param: minutes
GET /query/substationsDiscover all active substation IDs and coordinates
GET /healthAPI health check

Ready to connect your substations to real-time intelligence?

Request a Demo