From everything-evenhub
Automates EvenHub glasses simulator via localhost HTTP API: sends glasses inputs (up, down, click, double-click), captures RGBA glasses/webview screenshots, polls console logs. Use for programmatic testing.
npx claudepluginhub even-realities/everything-evenhub --plugin everything-evenhubThis skill is limited to using the following tools:
The simulator exposes an HTTP API on localhost when launched with `--automation-port <PORT>`.
Test and debug Even Hub G2 apps using the desktop simulator—launch, configure, debug, take screenshots, and understand simulator vs hardware differences. Use when testing apps without physical glasses.
Automates GUI interactions via screen capture, mouse clicks, typing, scrolling for UI testing, visual verification, and non-browser apps. Bridges Playwright to user browsers using extensions or CDP endpoints.
Automates Android, iOS, Aurora OS, and Desktop via CLI: screenshots, annotations, taps/swipes/text input, app install/launch/stop/uninstall, file push/pull, shell commands, device info queries.
Share bugs, ideas, or general feedback.
The simulator exposes an HTTP API on localhost when launched with --automation-port <PORT>.
evenhub-simulator https://my-app.example.com --automation-port 9898
Base URL: http://127.0.0.1:<PORT>
GET /api/ping
→ "pong"
Returns the current LVGL framebuffer as image/png (576×288, RGBA).
GET /api/screenshot/glasses
→ image/png binary (RGBA)
RGBA format — critical for image analysis:
(R=0, G=255, B=0, A=0) — transparent (alpha = 0)(R=0, G=255, B=0, A=255) — opaque (alpha = 255)Always work in RGBA mode. Do NOT convert to RGB — it drops the alpha channel and makes background pixels indistinguishable from text pixels (both appear as pure green).
from PIL import Image
img = Image.open('glasses.png') # keep as RGBA — do NOT call img.convert('RGB')
pixels = img.load()
# Lit pixel test:
is_lit = lambda px: px[3] > 0 # alpha > 0
Captures the main browser webview using html2canvas. Returns image/png. May take a few seconds; times out after 10s.
GET /api/screenshot/webview
→ image/png binary
Returns captured console.* output, uncaught exceptions, unhandled promise rejections, and failed fetch calls from the main webview.
GET /api/console
→ { "entries": [...], "total": N }
Each entry:
{ "id": 0, "level": "log|warn|error|info|debug|trace", "message": "...", "ts": 1712150400000 }
Prefixes for non-console sources:
[uncaught] ... — uncaught exception[unhandledrejection] ... — unhandled promise rejection[fetch] ... — failed fetch (non-ok status or network error)Poll for new entries only:
GET /api/console?since_id=42
→ entries with id > 42
since_id must be a non-negative integer. Passing a negative value (e.g. -1) causes
the server to return an error, not an empty list. For the first poll, omit the parameter
entirely to retrieve all current entries, then track last_id from the response:
# First poll — no since_id
resp = requests.get(f"{BASE_URL}/api/console")
entries = resp.json()["entries"]
last_id = max((e["id"] for e in entries), default=0)
# Subsequent polls
resp = requests.get(f"{BASE_URL}/api/console?since_id={last_id}")
Clear the buffer:
DELETE /api/console
Timing caution: Startup logs (e.g. a "ready" signal) are emitted once and lost if you clear the buffer before reading them. Pattern: poll for the ready signal first, then clear if needed.
# Safe: check for ready signal BEFORE clearing
poll_until(target="APP_READY")
requests.delete(f"{BASE_URL}/api/console") # only if you need a clean slate after
Send a touchpad action to the glasses display.
POST /api/input
Content-Type: application/json
{ "action": "up" | "down" | "click" | "double_click" }
Response: { "ok": true }
Actions map to the glasses touchpad:
up / down — scroll through list items or textclick — select the current itemdouble_click — triggers a system-level double-click event (typically "back" or "dismiss")GET /api/ping/api/screenshot/glasses (RGBA) or /api/console to check state.POST /api/input. After each action, wait briefly (~300ms) then confirm state changed.since_id when polling console to avoid re-reading old entries.double_click is typically used to go back or dismiss the current view.createStartUpPageContainer take time.$ARGUMENTS