You are the canonical SDK reference for the Even Hub G2 smart glasses SDK (@evenrealities/even_hub_sdk). When asked about $ARGUMENTS, locate and explain the relevant API, type, interface, enum, or method from this document. If no argument is given, provide a structured overview of all available APIs.
Architecture
Web App (your code) <-> EvenAppBridge (SDK) <-> Even App (host) <-> G2 Glasses
Your app is a standard HTML + TypeScript web page running inside a Flutter WebView hosted by the Even companion app. No special framework is required — plain Vite + TypeScript works perfectly.
Installation
npm install @evenrealities/even_hub_sdk
Current version: 0.0.12
Initialization
waitForEvenAppBridge() — async, recommended
Resolves when the native bridge is ready. Always prefer this over the sync singleton.
import { waitForEvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = await waitForEvenAppBridge()
EvenAppBridge.getInstance() — sync singleton
Only call this after the bridge has already been initialized (e.g. inside a callback that fires after waitForEvenAppBridge() resolves).
import { EvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = EvenAppBridge.getInstance()
Required Call Order
waitForEvenAppBridge()— wait for the bridgebridge.createStartUpPageContainer(container)— call exactly once at startup (required for glasses-mic audio and IMU)- Everything else:
audioControl,imuControl,rebuildPageContainer, event listeners, location, photo picker / camera, etc.
audioControl(true, AudioInputSource.Glasses) and imuControl will fail if createStartUpPageContainer has not succeeded first. audioControl(true, AudioInputSource.Phone), location APIs, pickImageFromAlbum, and captureImageFromCamera do not require the startup page — they route through the phone, not the glasses.
Complete Method Reference
| Method | Returns | Description |
|---|---|---|
bridge.getUserInfo() |
Promise<UserInfo> |
Get the signed-in user's uid, name, avatar URL, and country |
bridge.getDeviceInfo() |
Promise<DeviceInfo | null> |
Get connected glasses model, serial number, and status. Returns null if no device. |
bridge.setLocalStorage(key, value) |
Promise<boolean> |
Persist a key-value string pair to the companion app's storage |
bridge.getLocalStorage(key) |
Promise<string> |
Read a stored string by key; returns empty string if key does not exist |
bridge.createStartUpPageContainer(container) |
Promise<StartUpPageCreateResult> |
One-shot startup call to define all UI containers. 0=success, 1=invalid, 2=oversize, 3=outOfMemory |
bridge.rebuildPageContainer(container) |
Promise<boolean> |
Tear down and fully redraw all containers |
bridge.textContainerUpgrade(container) |
Promise<boolean> |
In-place text update without full redraw (max 2000 chars per call) |
bridge.updateImageRawData(data) |
Promise<ImageRawDataUpdateResult> |
Send raw pixel data to fill an image container. LZ4-compressed internally by the SDK (0.0.12+) — pass raw pixels as before. Calls must be serial — await each before the next. |
bridge.shutDownPageContainer(exitMode?) |
Promise<boolean> |
Close the app. exitMode 0=exit immediately, 1=show confirmation dialog |
bridge.onLaunchSource(cb) |
() => void |
Subscribe to launch source event. Fires exactly once: 'appMenu' or 'glassesMenu'. Returns unsubscribe function. |
bridge.onDeviceStatusChanged(cb) |
() => void |
Subscribe to device status updates (connect type, battery, wearing, charging). Returns unsubscribe function. |
bridge.onEvenHubEvent(cb) |
() => void |
Subscribe to all hub events: listEvent, textEvent, sysEvent, audioEvent. Returns unsubscribe function. |
bridge.audioControl(isOpen, source?) |
Promise<boolean> |
Open (true) or close (false) the microphone. source is an AudioInputSource value — Glasses (default, G2 four-mic array) or Phone (phone mic). Audio delivered as PCM via audioEvent with the source field set. |
bridge.imuControl(isOpen, reportFrq?) |
Promise<boolean> |
Enable/disable IMU sensor. reportFrq is an ImuReportPace value (P100–P1000). |
bridge.getAppLocation(options?) |
Promise<AppLocation | null> |
One-shot location read. options accepts accuracy, timeoutMs. Returns null if no fix in time, permission denied, or invalid coords. |
bridge.startAppLocationUpdates(options?) |
Promise<boolean> |
Begin continuous location updates. options accepts accuracy, intervalMs, distanceFilter. Subscribe with onAppLocationChanged to receive pushes. |
bridge.stopAppLocationUpdates() |
Promise<boolean> |
Stop continuous location updates started via startAppLocationUpdates. |
bridge.onAppLocationChanged(cb) |
() => void |
Subscribe to continuous-location updates. Callback receives an AppLocation. Returns an unsubscribe function. |
bridge.pickImageFromAlbum() |
Promise<AppImageAsset | null> |
Open the phone's photo album for single-image selection. Returns null if the user cancels or denies the album permission. |
bridge.captureImageFromCamera() |
Promise<AppImageAsset | null> |
Open the phone camera and capture one image. Returns null if the user cancels or denies the camera permission. The G2 has no on-glasses camera. |
bridge.callEvenApp(method, params?) |
Promise<any> |
Low-level direct call to native bridge method. Use when higher-level methods aren't available. |
TypeScript Interfaces
TextContainerProperty
Defines a text display container.
All fields are optional in the SDK constructor (accepts Partial<T>). Populate the fields your layout requires.
class TextContainerProperty {
xPosition?: number // 0–576, horizontal offset from left edge
yPosition?: number // 0–288, vertical offset from top edge
width?: number // 0–576, container width
height?: number // 0–288, container height
borderWidth?: number // 0–5, border thickness in pixels
borderColor?: number // 0–15, greyscale colour index
borderRadius?: number // 0–10, corner radius
paddingLength?: number // 0–32, inner padding in pixels
containerID?: number // unique integer ID for this container
containerName?: string // max 16 characters
isEventCapture?: number // 0 or 1; exactly one container per page must be 1
zOrderIndex?: number // stacking order, larger = front; all-or-nothing per page (0.0.12+, see Z-Order Rules)
content?: string // initial text content, max 1000 characters
}
ListContainerProperty
Defines a scrollable list container.
class ListContainerProperty {
xPosition?: number // 0–576
yPosition?: number // 0–288
width?: number // 0–576
height?: number // 0–288
borderWidth?: number // 0–5
borderColor?: number // 0–15
borderRadius?: number // 0–10
paddingLength?: number // 0–32
containerID?: number
containerName?: string // max 16 characters
isEventCapture?: number // 0 or 1
zOrderIndex?: number // stacking order, larger = front; all-or-nothing per page (0.0.12+, see Z-Order Rules)
itemContainer?: ListItemContainerProperty
}
ListItemContainerProperty
Defines the items within a list container.
class ListItemContainerProperty {
itemCount?: number // 1–20, number of items
itemWidth?: number // item width; 0 = auto (fills container)
isItemSelectBorderEn?: number // 0 or 1; 1 = show selection border around highlighted item
itemName?: string[] // array of item label strings; max 20 items, max 64 chars each
}
ImageContainerProperty
Defines a placeholder image container. Must be populated with updateImageRawData after creation. Image containers do not support isEventCapture — use a text container as the event-capture layer when combining with images.
class ImageContainerProperty {
xPosition?: number // 0–576
yPosition?: number // 0–288
width?: number // 20–288
height?: number // 20–144
containerID?: number
containerName?: string // max 16 characters
zOrderIndex?: number // stacking order, larger = front; all-or-nothing per page (0.0.12+, see Z-Order Rules)
}
TextContainerUpgrade
Payload for bridge.textContainerUpgrade().
class TextContainerUpgrade {
containerID?: number
containerName?: string // max 16 characters
contentOffset?: number // character offset to begin writing
contentLength?: number // number of characters to replace
content?: string // replacement text, max 2000 characters total
}
ImageRawDataUpdate
Payload for bridge.updateImageRawData().
class ImageRawDataUpdate {
containerID?: number
containerName?: string
imageData?: number[] | string | Uint8Array | ArrayBuffer // raw pixel data in 4-bit greyscale
}
Since 0.0.12 the SDK compresses image raw data with LZ4 internally before transfer to reduce size and update latency. Pass raw pixel data exactly as before — there is no compression flag to set.
CreateStartUpPageContainer
Parameter type for bridge.createStartUpPageContainer().
class CreateStartUpPageContainer {
containerTotalNum?: number // 1–12, total number of containers
widgetId?: number // auto-assigned; omit to let SDK assign
listObject?: ListContainerProperty[] // list containers
textObject?: TextContainerProperty[] // text containers, max 8
imageObject?: ImageContainerProperty[] // image containers, max 4
}
RebuildPageContainer
Parameter type for bridge.rebuildPageContainer(). Same shape as CreateStartUpPageContainer but without widgetId.
class RebuildPageContainer {
containerTotalNum?: number // 1–12, total number of containers
listObject?: ListContainerProperty[] // list containers
textObject?: TextContainerProperty[] // text containers, max 8
imageObject?: ImageContainerProperty[] // image containers, max 4
}
Z-Order Rules (0.0.12+)
zOrderIndex controls front/back stacking across all containers on a page. The SDK validates these rules before the payload reaches the native bridge:
- All-or-nothing per page — either every list/text/image container on the page sets
zOrderIndex, or none do. Omitting it everywhere keeps pre-0.0.12 behavior (declaration order determines overlap). - Unique per page — no two containers on the same page may share a value. There is no tie-break.
- Larger = closer to the front.
On violation the SDK logs an EvenHubPageContainerValidationErrorCode error and never calls native: createStartUpPageContainer returns StartUpPageCreateResult.invalid and rebuildPageContainer returns false.
enum EvenHubPageContainerValidationErrorCode {
MissingZOrderIndex = 'MISSING_Z_ORDER_INDEX', // some containers set zOrderIndex, others omit it
InvalidZOrderIndex = 'INVALID_Z_ORDER_INDEX', // value is not a valid number
DuplicateZOrderIndex = 'DUPLICATE_Z_ORDER_INDEX' // two containers share a value
}
// Run the same validation yourself before sending a page payload:
function validateEvenHubPageContainerZOrder(container: EvenHubPageContainerLike): EvenHubPageContainerValidationResult
// Human-readable message for an invalid result:
function formatEvenHubPageContainerValidationError(result): string
UserInfo
interface UserInfo {
uid: number // numeric user ID
name: string // display name
avatar: string // URL to avatar image
country: string // ISO country code or name
}
DeviceInfo
interface DeviceInfo {
readonly model: DeviceModel // glasses model identifier
readonly sn: string // serial number
status: DeviceStatus // current connection status object
}
DeviceStatus
interface DeviceStatus {
sn: string
connectType: DeviceConnectType
isWearing?: boolean // true if glasses are being worn
batteryLevel?: number // 0–100, battery percentage
isCharging?: boolean // true if glasses are charging
isInCase?: boolean // true if glasses are in the charging case
// Instance helpers
isNone(): boolean
isConnected(): boolean
isConnecting(): boolean
isDisconnected(): boolean
isConnectionFailed(): boolean
// Static factory methods
static fromJson(json: Record<string, any>): DeviceStatus
static createDefault(sn?: string): DeviceStatus
}
AppLocation
Location payload returned by getAppLocation and pushed via onAppLocationChanged.
interface AppLocation {
latitude: number // degrees
longitude: number // degrees
accuracy?: number // horizontal accuracy in meters
altitude?: number // meters above sea level
speed?: number // meters per second
heading?: number // degrees from true north
timestamp?: number // Unix milliseconds
}
AppLocationOptions
Options object accepted by getAppLocation and startAppLocationUpdates. All fields optional.
interface AppLocationOptions {
accuracy?: AppLocationAccuracy // Low | Medium | High
timeoutMs?: number // one-shot only
intervalMs?: number // continuous only
distanceFilter?: number // continuous only; meters — host skips smaller pushes
}
AppImageAsset
Image payload returned by pickImageFromAlbum and captureImageFromCamera.
interface AppImageAsset {
path: string // host-side path; opaque to the WebView
name: string // original filename
mimeType: string // e.g. "image/jpeg", "image/png"
size: number // bytes
base64: string // inline data, ready for <img src="data:..."> or further processing
}
Event Models
EvenHubEvent
The callback type for bridge.onEvenHubEvent(cb).
interface EvenHubEvent {
listEvent?: List_ItemEvent
textEvent?: Text_ItemEvent
sysEvent?: Sys_ItemEvent
audioEvent?: AudioEventPayload
jsonData?: Record<string, any> // raw payload passthrough
}
AudioEventPayload
Microphone audio payload on event.audioEvent. source identifies which mic the buffer came from — useful when toggling audioControl(true, ...) between sources at runtime.
interface AudioEventPayload {
source: AudioInputSource // Glasses | Phone
audioPcm: Uint8Array // PCM 16 kHz, signed 16-bit little-endian, mono
}
Text_ItemEvent
Fires when a text container receives a user interaction.
interface Text_ItemEvent {
containerID?: number
containerName?: string
eventType?: OsEventTypeList
}
List_ItemEvent
Fires when a list item is selected or scrolled.
interface List_ItemEvent {
containerID?: number
containerName?: string
currentSelectItemName?: string // label of the currently selected item
currentSelectItemIndex?: number // 0-based index of the selected item
eventType?: OsEventTypeList
}
Sys_ItemEvent
System-level events including IMU data and lifecycle signals.
interface Sys_ItemEvent {
eventType?: OsEventTypeList
eventSource?: EventSourceType
imuData?: IMU_Report_Data
systemExitReasonCode?: number
}
IMU_Report_Data
Raw accelerometer/gyro values from the glasses' IMU sensor.
interface IMU_Report_Data {
x?: number // X-axis value
y?: number // Y-axis value
z?: number // Z-axis value
}
LaunchSource
type LaunchSource = 'appMenu' | 'glassesMenu'
Enums
enum OsEventTypeList {
CLICK_EVENT = 0,
SCROLL_TOP_EVENT = 1,
SCROLL_BOTTOM_EVENT = 2,
DOUBLE_CLICK_EVENT = 3,
FOREGROUND_ENTER_EVENT = 4,
FOREGROUND_EXIT_EVENT = 5,
ABNORMAL_EXIT_EVENT = 6,
SYSTEM_EXIT_EVENT = 7,
IMU_DATA_REPORT = 8
}
enum DeviceConnectType {
None = 'none',
Connecting = 'connecting',
Connected = 'connected',
Disconnected = 'disconnected',
ConnectionFailed = 'connectionFailed'
}
enum StartUpPageCreateResult {
success = 0,
invalid = 1,
oversize = 2,
outOfMemory = 3
}
enum ImageRawDataUpdateResult {
success = "success",
imageException = "imageException",
imageSizeInvalid = "imageSizeInvalid",
imageToGray4Failed = "imageToGray4Failed",
sendFailed = "sendFailed"
}
enum EvenAppMethod {
GetUserInfo = 'getUserInfo',
GetGlassesInfo = 'getGlassesInfo',
SetLocalStorage = 'setLocalStorage',
GetLocalStorage = 'getLocalStorage',
GetAppLocation = 'getAppLocation',
StartAppLocationUpdates = 'startAppLocationUpdates',
StopAppLocationUpdates = 'stopAppLocationUpdates',
PickImageFromAlbum = 'pickImageFromAlbum',
CaptureImageFromCamera = 'captureImageFromCamera',
CreateStartUpPageContainer = 'createStartUpPageContainer',
RebuildPageContainer = 'rebuildPageContainer',
UpdateImageRawData = 'updateImageRawData',
TextContainerUpgrade = 'textContainerUpgrade',
AudioControl = 'audioControl',
ImuControl = 'imuControl',
ShutDownPageContainer = 'shutDownPageContainer'
}
enum AudioInputSource {
Glasses = 'glasses', // G2 four-mic array (default; requires createStartUpPageContainer first)
Phone = 'phone' // phone microphone (no startup-page requirement)
}
enum AppLocationAccuracy {
Low = 'low', // city-level — cheapest, kindest to battery
Medium = 'medium', // block-level — balanced default
High = 'high' // best available fix — most battery
}
// ImuReportPace — reporting frequency for IMU sensor data
// Value = milliseconds between reports (P100 = 100 ms = 10 Hz, P1000 = 1000 ms = 1 Hz)
enum ImuReportPace {
P100 = 100,
P200 = 200,
P300 = 300,
P400 = 400,
P500 = 500,
P600 = 600,
P700 = 700,
P800 = 800,
P900 = 900,
P1000 = 1000
}
enum EventSourceType {
TOUCH_EVENT_FORM_DUMMY_NULL = 0,
TOUCH_EVENT_FROM_GLASSES_R = 1,
TOUCH_EVENT_FROM_RING = 2,
TOUCH_EVENT_FROM_GLASSES_L = 3
}
enum DeviceModel {
G1 = 'g1',
G2 = 'g2',
Ring1 = 'ring1'
}
Result Codes
createStartUpPageContainer / StartUpPageCreateResult
| Code | Name | Meaning |
|---|---|---|
| 0 | success | Containers created successfully |
| 1 | invalid | Invalid container configuration |
| 2 | oversize | Total container size exceeds display limits |
| 3 | outOfMemory | Insufficient memory on device |
updateImageRawData / ImageRawDataUpdateResult
| Name | Meaning |
|---|---|
| success | Image data accepted and rendered |
| imageException | Generic image processing error |
| imageSizeInvalid | Image dimensions do not match container dimensions |
| imageToGray4Failed | Could not convert image to 4-bit greyscale |
| sendFailed | Transport error sending data to glasses |
Critical Rules
createStartUpPageContaineris one-shot — call it exactly once at startup; calling it again will not work. UserebuildPageContainerfor subsequent full redraws.- Exactly one container must have
isEventCapture: 1— this designates which container receives user input. Having zero or more than one causes undefined behavior. zOrderIndexis all-or-nothing per page (0.0.12+) — if any container sets it, every list/text/image container on that page must set a unique value; larger renders in front. Violations fail SDK-side validation:createStartUpPageContainerreturnsinvalid,rebuildPageContainerreturnsfalse.- Container limits —
containerTotalNummust be 1–12;textObjectarray max 8 items;imageObjectarray max 4 items. - Image sends must be serial —
updateImageRawDatacalls must be queued and awaited one at a time; concurrent calls are not supported and will cause errors. - Image containers are placeholders — after
createStartUpPageContainersucceeds, image containers are empty until populated viaupdateImageRawData. - Glasses-mic
audioControlandimuControlrequire startup to succeed —audioControl(true, AudioInputSource.Glasses)andimuControlwill fail if called beforecreateStartUpPageContainerreturnsStartUpPageCreateResult.success.audioControl(true, AudioInputSource.Phone), location APIs,pickImageFromAlbum, andcaptureImageFromCameraroute through the phone and do not require the startup page. - Always unsubscribe event listeners on teardown —
onEvenHubEvent,onDeviceStatusChanged, andonLaunchSourceall return an unsubscribe function; call it when your component/page is destroyed. onLaunchSourcefires only once — register the listener early (before or immediately afterwaitForEvenAppBridge) to avoid missing the event.
Canvas Specifications
| Property | Value |
|---|---|
| Resolution | 576 × 288 px |
| Colour depth | 4-bit greyscale (16 shades, 0 = black, 15 = white) |
| Coordinate origin | (0, 0) at top-left |
| X axis | Increases rightward |
| Y axis | Increases downward |
Host Push Format (Simulator / Testing)
The companion app (and simulator) push events into the WebView via window.postMessage. You generally do not need to handle these directly — the SDK processes them internally — but the formats are useful when writing tests or a custom simulator.
// Format 1 — named event type with jsonData wrapper
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: { type: 'listEvent', jsonData: { /* event payload */ } } }
// Format 2 — snake_case event type with data wrapper
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: { type: 'list_event', data: { /* event payload */ } } }
// Format 3 — array format [eventType, payload]
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: ['list_event', { /* event payload */ }] }
// Audio event — audioPcm is an array of PCM sample integers; source identifies the mic
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: { type: 'audioEvent', jsonData: { source: 'glasses', audioPcm: [/* numbers */] } } }
// Device status changed
{ type: 'listen_even_app_data', method: 'deviceStatusChanged', data: { sn: 'ABC123', connectType: 'connected', isWearing: true, batteryLevel: 80, isCharging: false } }
// Launch source (fires once on app open)
{ method: 'evenAppLaunchSource', data: { launchSource: 'appMenu' } }
// App location changed (pushed continuously after startAppLocationUpdates)
{ method: 'appLocationChanged', data: { latitude: 37.7749, longitude: -122.4194, accuracy: 5, timestamp: 1750000000000 } }
Task
Look up SDK reference for: $ARGUMENTS