Drive a Claude Code session from Even Realities G2 smart glasses.
evenTermidex turns the G2's heads-up display into a hands-free cockpit for a coding agent. Claude Code runs on a real machine (a Mac or a home server); the glasses give you an ambient, notification-driven view: a menu to steer from, a blank "comm screen" while the agent works, and a heads-up notification the moment it finishes or needs your approval. You dictate prompts with the glasses mic (transcribed locally with Whisper on the host) and answer permission prompts with a tap. A Bluetooth keyboard is there for the moments you need to type real code.
Status: early and experimental, but the full loop works on hardware — dictate a prompt → send → glance away while the HUD stays blank →
! input needed [Write(file.py)]→ tap to approve →* done - tap to read. Phases 0–4 are complete (the Phase-4 native-list client is packed but still awaiting its on-hardware pass); next up is Phase 5 (hardening: ws auth, reconnect, run as a service).
Two hard constraints shape the entire design:
- A phone terminal can't speak Bluetooth LE. Android exposes BLE only to native apps, so a Termux process can't talk to the glasses directly. A small native app owns the BLE link and relays over a socket; the official Even Realities app can't be repurposed for this.
- The HUD is tiny and keyboardless — roughly 640×350, monochrome green, about 5–7 lines, driven by temple taps/swipes, the R1 ring, and a mic. So evenTermidex does not mirror a raw terminal. It renders meaningful state (mode, session, latest message, pending prompt) and maps gestures + voice to actions.
The upside of running the agent remotely: Claude Code lives in a full environment, the daemon is plain Python, and the phone just carries bytes over a Tailscale mesh — no exposed ports, no port-forwarding, works off your home network.
[G2 glasses]
│ BLE
[Phone] glasses client (owns BLE, renders the HUD, emits gestures/voice)
│ │ websocket ws://<host-tailnet-ip>:8765
│ Tailscale (WireGuard mesh)
│
[Host: Mac / home server]
eventermidex daemon (Python)
• websocket server frames out / events (gestures, mic PCM) in
• tmux driver capture-pane (read) · send-keys (inject)
• renderer Claude state → ~9-line HUD frames
• controller gestures / voice → Claude actions
• stt mic PCM → faster-whisper → prompt text
└─ tmux session running `claude`
(SSH is no longer in the data path — Tailscale replaced the ssh -L tunnel in Phase 0. You
can still SSH into the host over the same tailnet for an interactive shell.)
| Directory | Runtime | Role |
|---|---|---|
daemon/ |
Python 3.12 | Host-side brain: websocket server, renderer, controller, tmux driver |
glasses-client/ |
Node / Even Hub SDK | The BLE-owning app that renders the HUD (form decided in Phase 0) |
phone/ |
Termux (Android) | Tailscale transport notes (SSH for an interactive shell) |
spike/ |
mixed | Phase 0 throwaway "text out / gesture in" round-trip test |
docs/ |
— | Architecture, the websocket contract, gesture map |
Maintenance docs: CODEMAP.md (structure + dependencies) and
AUDIT.md (change log).
Navigation is hub-and-spoke: the menu is home and the only way between screens. Scroll moves the highlight (wraps at the edges), tap runs the entry, and a double-tap on any screen returns to the menu — it never touches Claude.
menu - mode: plan ← live agent mode
> comm screen ← ambient home: blank while Claude works
dictate prompt ← glasses mic → Whisper → review → tap to send
quick actions ← canned prompts: /clear, /compact, continue, fix tests
mode ← mode picker; each switch is verified against the pane
sessions ← switch / create / kill tmux sessions
send Esc ← interrupt the agent (always explicit, never a gesture)
output ← Claude's last answer
terminal ← raw pane tail (debugging)
The menu and the pickers render as native OS lists — the daemon sends the items and the glasses OS draws the selection border, so scrolling moves a highlight instead of scroll-animating the whole page (text containers on the G2 always animate, which made the old text-rendered menu jump). The sessions picker lists every tmux session, creates new auto-named ones, and can kill any session except the active one; the mode picker doesn't just fire blind keystrokes — it steps Claude's mode and re-reads the pane until the requested mode is actually showing.
The comm screen stays empty until something needs you, then notifies at the top:
! input needed - tap to view with the tool being approved ([Write(probe.txt)]) and the
question — tap opens the full prompt, tap again approves the default. When a turn finishes:
* done - tap to read.
- Phase 0 — Spike: on-device round-trip verified on real G2; client = Even Hub SDK app, transport = Tailscale
- Phase 1 — Foundation: tmux
claudelauncher, daemon websocket server + tmux driver, v1 message schema; tap-to-approve verified on hardware - Phase 2 — Renderer: parse the session into {mode, activity, latest message, pending prompt, tail}; reflow to the 9×36 HUD budget
- Phase 3 — Controller & voice: hub-and-spoke menu, comm-screen notifications, prompt approval with tool context, dictation via glasses mic + local faster-whisper (review-before-send); full loop verified on hardware
- Phase 4 — Sessions & shortcuts: session picker (switch/create/kill), verified mode picker, canned quick-actions, native OS-list menus, debounced done-notifications;
serveauto-creates its session (native-list client packed, HW verify pending) - Phase 5 — Resilience & hardening: ws token auth, reconnect/keep-alive, run as a service, latency tuning
Requires a modern host (macOS or Linux) with Python ≥ 3.11 and tmux. The glasses reach the host over Tailscale; a Bluetooth keyboard is optional.
git clone https://github.com/aplaino/evenTermidex.git
cd evenTermidex/daemon
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,voice]" # [voice] = faster-whisper, needed for dictation
eventermidex # environment self-check ("doctor")
eventermidex serve # ws server on the host's tailnet IP :8765serve auto-creates the claude tmux session if it doesn't exist (eventermidex launch
still exists if you want to start it separately).
serve --stt-model tiny trades transcription accuracy for speed (default: small; the
model downloads on first start). Without the [voice] extra everything works except
dictation.
Verify the pipeline locally, no glasses needed — the probe stands in for the client:
python spike/host/probe_client.py ws://<host-tailnet-ip>:8765The glasses client lives in glasses-client/ (Even Hub SDK app); build
and pack with npm run build && npx evenhub pack app.json dist -o eventermidex.ehpk, then
sideload via the Even Hub dashboard. It targets ws://100.115.25.7:8765 by default —
override with ?ws= in the dev server, or repoint it for your own host. The manifest must
request the g2-microphone permission or the mic will refuse to open (already in
app.json); if the mic fails after a reinstall, force-quit and reopen the Even app.
Built on the shoulders of the Even Realities community:
- men-g2-ble-gateway — Python/bleak G2 gateway; protocol reference
- g2mirror — terminal-to-G2 mirroring, prior art
- MentraOS — open smart-glasses OS supporting the G1/G2
- awesome-even-realities-g2 — curated index
- Even Realities' official Even Hub SDK & simulator
This is an independent, unofficial project. It is not affiliated with, endorsed by, or supported by Even Realities. "Even Realities" and "G2" are trademarks of their respective owner. Some approaches rely on community reverse-engineering of the glasses' protocol; use at your own risk, and review your device's terms. Provided as is, without warranty — see the license.