10. Event Stream Listener
Connects to the events channel (port 10020) and prints system events with optional filtering by level, name pattern, and exclusions. Read-only — no session profile, no commands sent.
What you'll learn:
- Subscribing to the events WebSocket (port
10020) as a passive observer - Parsing event JSON —
level,name,category,data,message - Filtering by minimum severity level
- Wildcard name matching and exclusion
- Using this tutorial to benchmark the control loop alongside any other tutorial
Workflow
- Parse CLI flags (
--level,--name,--exclude,--no-hide-rate,--port). - Connect to
ws://localhost:10020(the events channel) — no session profile is sent since this is a read-only observer. - For each incoming message, parse the event JSON and apply filters:
- Drop if
levelis below the minimum severity. - If
--namepatterns are given, drop unless one matches (against eithernamealone orcategory/name). - If any
--excludepattern matches, drop.
- Drop if
- Print surviving events with their level, qualified name, optional message, and optional
datapayload.
Parameters
| Flag | Default | Purpose |
|---|---|---|
--port | 10020 | Event channel port |
-l, --level | info | Minimum severity — one of info, notice, warning, error, critical, panic |
-n, --name | (none) | Only show events matching pattern (glob, repeatable) |
-x, --exclude | (none) | Hide events matching pattern (glob, repeatable) |
--no-hide-rate | false | Include system-rate-report events (hidden by default — very noisy) |
Run any tutorial (04-hello-floor, 05-position-control, 06-combined...) in one terminal, and the event listener with --no-hide-rate in another. The system-rate-report event includes the effective device tick rate — compare across the Python / C++ nlohmann / C++ Glaze variants of the same tutorial to observe serialization overhead on a ~4 kHz control loop.
# Terminal 1 — pick a variant
./04-haply-inverse-hello-floor # C++ nlohmann
./04-haply-inverse-hello-floor-glz # C++ Glaze
python 04-haply-inverse-hello-floor.py # Python
# Terminal 2 — watch the rate
./10-haply-inverse-events --no-hide-rate
State fields read
Each incoming message is a single event:
level— string severity (info...panic)name— event name (e.g.device-connected,system-rate-report)category— category (e.g.device,session,system)message— optional human-readable stringdata— optional nested JSON payload (shape depends on the event)
Full event catalogue: Events & Monitoring.
Send / receive
Passive subscription — only recv() is used; nothing is sent back. All three variants implement the same filter pipeline; the interesting Inverse-API-specific part is the event struct shape.
- Python
- C++ (nlohmann)
- C++ (Glaze)
async for msg in websocket is the shortest form of the receive-only loop.
async with websockets.connect(uri) as websocket:
async for msg in websocket:
try:
event = json.loads(msg)
except json.JSONDecodeError:
continue
if accept_event(event, min_level_index, name_patterns, exclude_patterns):
print(format_event(event))
libhv callback model — onmessage runs on the I/O thread; the main thread blocks on ENTER. Events are treated as untyped JSON — each field is read via .value(...) with a default.
ws.onmessage = [&](const std::string &msg) {
json event;
try { event = json::parse(msg); } catch (...) { return; }
if (accept_event(event, opts.min_level, opts.name_patterns, opts.exclude_patterns))
print_event(event);
};
ws.open("ws://localhost:10020");
while (std::cin.get() != '\n') {} // block main thread
The known event fields map to a typed event_data struct. The data field stays as glz::raw_json_view so arbitrary nested payloads (which depend on the event) can be forwarded as-is without defining a full schema.
// Struct model — known fields typed, nested `data` kept as raw JSON
struct event_data {
std::string level;
std::string name;
std::string category;
std::string message;
glz::raw_json_view data{};
};
// Send / receive
ws.onmessage = [&](const std::string &msg) {
event_data event{};
if (glz::read<glz_settings>(event, msg)) return; // drop malformed
if (accept_event(event, opts.min_level, opts.name_patterns, opts.exclude_patterns))
print_event(event);
};
ws.open("ws://localhost:10020");
while (std::cin.get() != '\n') {} // block main thread
Source: Python · C++ · C++ Glaze
Related: Events & Monitoring · WebSocket Protocol