Software
API Reference
WebSocket API

WebSocket API

Plain WebSocket, UTF-8 JSON frames on ws://<host>:1616. No SDK needed.

Connection

ScenarioURL
No authws://raspberrypi.local:1616
With authws://raspberrypi.local:1616?token=<ws-token>

Authenticated Connection

With --auth enabled: POST /auth with the 6-digit code → GET /auth/ws-token → connect with ?token=....

const dashboardBase = "http://raspberrypi.local:1617";
const wsBase = "ws://raspberrypi.local:1616";
 
async function connectAuthenticated(code: string): Promise<WebSocket> {
  await fetch(`${dashboardBase}/auth`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    credentials: "include",
    body: JSON.stringify({ code }),
  });
 
  const { token } = await fetch(`${dashboardBase}/auth/ws-token`, {
    credentials: "include",
  }).then((r) => r.json());
 
  return new WebSocket(`${wsBase}?token=${encodeURIComponent(token)}`);
}

Commands (Client → Server)

Filter

{"cmd": "set_filter", "enabled": true, "lowcut": 1.0, "highcut": 40.0}
{"cmd": "set_filter", "enabled": false}

Recording

{"cmd": "start_record"}
{"cmd": "stop_record"}

Webhooks

{"cmd": "webhook_list"}
{"cmd": "webhook_create", "rule": {
  "name": "Alpha alert",
  "trigger_type": "band_power_above",
  "params": {"band": "alpha", "threshold": 20},
  "url": "https://example.com/hook",
  "method": "POST",
  "headers": {"Authorization": "Bearer ..."},
  "cooldown": 30
}}
{"cmd": "webhook_update", "rule": {"id": "abc123", "enabled": false}}
{"cmd": "webhook_delete", "id": "abc123"}
{"cmd": "webhook_test", "id": "abc123"}

VRChat OSC

{"cmd": "osc_start"}
{"cmd": "osc_stop"}
{"cmd": "osc_configure", "host": "127.0.0.1", "port": 9000, "mode": "both", "interval": 0.25}
{"cmd": "osc_status"}

Lab Streaming Layer

{"cmd": "lsl_start"}
{"cmd": "lsl_stop"}
{"cmd": "lsl_status"}

Spike Filter

Configure the hardware delta-threshold spike rejection (SPI boards only):

{"cmd": "spike_config"}
{"cmd": "spike_config", "config": {"threshold": 200, "reset_after": 50}}

Hampel Filter

Device-agnostic per-channel Hampel spike filter (works on all devices):

{"cmd": "hampel_config"}
{"cmd": "hampel_config", "config": {"enabled": true, "window_size": 5, "n_sigma": 3.0}}
FieldTypeDefaultDescription
enabledbooltrueToggle the filter on/off
window_sizeint5Sliding window length (odd, ≥ 3)
n_sigmafloat3.0Outlier threshold in MAD units (≥ 1.0)

Server responds with the current config including replaced_count.

ADS1299 Registers

Real-time register configuration for PiEEG boards. See Register Configuration for full details.

{"cmd": "reg_read"}
{"cmd": "reg_write", "regs": {"0x05": "0x00", "0x06": "0x01"}}
{"cmd": "reg_preset", "preset": "internal_short"}
{"cmd": "noise_test", "duration": 3}

Presets: normal, internal_short, test_signal, temp_sensor.

Messages (Server → Client)

EEG Frame

Sent at 250 Hz:

{"t": 1711234567.123, "n": 42, "channels": [12.34, -5.67, 8.90, ...]}

Welcome Message

Sent on connect:

{
  "status": "connected",
  "sample_rate": 250,
  "channels": 16,
  "filter": false,
  "recording": false,
  "engine": { "native": true, "engine": "pieeg-core", "version": "0.1.1" },
  "spike_config": { "enabled": true, "threshold": 200 },
  "hampel_config": { "enabled": true, "window_size": 5, "n_sigma": 3.0, "replaced_count": 0 }
}

The channels field reflects the selected device: 16 for pieeg16 (default), 8 for pieeg8 or ironbci8.

The engine field reports which DSP implementation is active. When the optional pieeg-core (opens in a new tab) wheel is installed (pip install 'pieeg-server[fast]'), native is true and version is the pieeg-core version. Otherwise engine is "python", native is false, and version is null — the pure-Python fallback is in use.

Record Status

{
  "record_status": {
    "recording": true,
    "stopped": {
      "filename": "pieeg_20260401_143136.csv",
      "frames": 1000,
      "duration": 4.0,
      "path": "/path/to/file"
    }
  }
}

Webhook Event

{
  "webhook_event": {
    "rule_id": "abc",
    "value": 12.5,
    "ts": 1711234567.123456
  }
}