Source of truth: daemon/src/eventermidex/protocol.py. The glasses client mirrors
these shapes but shares no code (CODEMAP invariant #1). One JSON object per ws
text message.
| Type | Shape | Notes |
|---|---|---|
hello |
{"type":"hello","v":1} |
Sent once on connect, before the first frame; the only message carrying the version |
frame |
{"type":"frame","lines":["…"]} |
Full HUD frame replacement; client redraws the whole container |
frame (list) |
{"type":"frame","lines":["…"],"list":{"items":["…"],"selected":0}} |
Additive (Phase 4 scroll fix): on menu/picker screens the daemon also sends the raw items + selected index. List-capable clients render a native OS list (the OS moves a selection border — no page-scroll animation) with lines[0] as the title; older clients ignore list and render lines |
mic |
{"type":"mic","open":true|false} |
Client opens/closes the glasses mic (audioControl); on open failure it sends back a voice error |
exit |
{"type":"exit"} |
Client shuts down its page container and closes the ws without reconnecting. Currently unsent — the "exit app" menu entry was dropped (Even's hold-to-exit covers it); the shape stays in the contract |
On connect the daemon sends hello, then immediately the current frame.
| Type | Shape | Notes |
|---|---|---|
gesture |
{"type":"gesture","name":"tap"|"double-tap"|"scroll-up"|"scroll-down"} |
The four gestures the G2 client emits |
gesture (list) |
{"type":"gesture","name":"…","item":3} |
Additive: on a native-list screen the OS reports which index its selection border landed on; the daemon adopts it as the highlight truth (scroll = sync only; tap executes that item). Must be an int if present |
voice |
{"type":"voice","chunk":"<base64 PCM>"} |
One mic PCM chunk (assumed 16 kHz 16-bit mono); daemon accumulates while recording, ignores otherwise |
voice |
{"type":"voice","error":"mic-open-failed"} |
Mic could not be opened; daemon aborts the dictation |
command |
{"type":"command","name":"…"} |
Reserved — parsed and logged only until Phase 4 |
Anything else raises ProtocolError on the daemon and is logged, never fatal.
- The version lives only in
hello. Frames are the hot path over a bandwidth/MTU-bound BLE link and stay lean — no per-messagev. - Receivers must silently ignore message types they don't recognize. This
makes new message types additive: the deployed spike client (which ignores
non-
framemessages) keeps working unchanged. - Additive fields/types do not bump
v; breaking changes to existing shapes do.
S: {"type":"hello","v":1}
S: {"type":"frame","lines":["evenTermidex","…"]}
C: {"type":"gesture","name":"tap"}
S: {"type":"frame","lines":["…updated…"]}
Dictation (menu → "dictate prompt"):
S: {"type":"mic","open":true}
C: {"type":"voice","chunk":"AAAA…"} (repeated while speaking)
C: {"type":"gesture","name":"tap"} (wearer taps: done)
S: {"type":"mic","open":false}
S: {"type":"frame","lines":["send this prompt?","…transcript…"]}
C: {"type":"gesture","name":"tap"} (send) — or double-tap (discard)