| name | sdk-reference |
| description | Complete Even Hub SDK API reference — all methods, types, interfaces, enums, and event models for G2 smart glasses development. Use when looking up specific API signatures, parameters, return types, or type definitions. |
| allowed-tools | ["Read","Grep","Glob","Bash","Write","Edit"] |
| argument-hint | ["API or type to look up"] |
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 bridge
bridge.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
yPosition?: number
width?: number
height?: number
borderWidth?: number
borderColor?: number
borderRadius?: number
paddingLength?: number
containerID?: number
containerName?: string
isEventCapture?: number
zOrderIndex?: number
content?: string
}
ListContainerProperty
Defines a scrollable list container.
class ListContainerProperty {
xPosition?: number
yPosition?: number
width?: number
height?: number
borderWidth?: number
borderColor?: number
borderRadius?: number
paddingLength?: number
containerID?: number
containerName?: string
isEventCapture?: number
zOrderIndex?: number
itemContainer?: ListItemContainerProperty
}
ListItemContainerProperty
Defines the items within a list container.
class ListItemContainerProperty {
itemCount?: number
itemWidth?: number
isItemSelectBorderEn?: number
itemName?: string[]
}
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
yPosition?: number
width?: number
height?: number
containerID?: number
containerName?: string
zOrderIndex?: number
}
TextContainerUpgrade
Payload for bridge.textContainerUpgrade().
class TextContainerUpgrade {
containerID?: number
containerName?: string
contentOffset?: number
contentLength?: number
content?: string
}
ImageRawDataUpdate
Payload for bridge.updateImageRawData().
class ImageRawDataUpdate {
containerID?: number
containerName?: string
imageData?: number[] | string | Uint8Array | ArrayBuffer
}
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
widgetId?: number
listObject?: ListContainerProperty[]
textObject?: TextContainerProperty[]
imageObject?: ImageContainerProperty[]
}
RebuildPageContainer
Parameter type for bridge.rebuildPageContainer(). Same shape as CreateStartUpPageContainer but without widgetId.
class RebuildPageContainer {
containerTotalNum?: number
listObject?: ListContainerProperty[]
textObject?: TextContainerProperty[]
imageObject?: ImageContainerProperty[]
}
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',
InvalidZOrderIndex = 'INVALID_Z_ORDER_INDEX',
DuplicateZOrderIndex = 'DUPLICATE_Z_ORDER_INDEX'
}
function validateEvenHubPageContainerZOrder(container: EvenHubPageContainerLike): EvenHubPageContainerValidationResult
function formatEvenHubPageContainerValidationError(result): string
UserInfo
interface UserInfo {
uid: number
name: string
avatar: string
country: string
}
DeviceInfo
interface DeviceInfo {
readonly model: DeviceModel
readonly sn: string
status: DeviceStatus
}
DeviceStatus
interface DeviceStatus {
sn: string
connectType: DeviceConnectType
isWearing?: boolean
batteryLevel?: number
isCharging?: boolean
isInCase?: boolean
isNone(): boolean
isConnected(): boolean
isConnecting(): boolean
isDisconnected(): boolean
isConnectionFailed(): boolean
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
longitude: number
accuracy?: number
altitude?: number
speed?: number
heading?: number
timestamp?: number
}
AppLocationOptions
Options object accepted by getAppLocation and startAppLocationUpdates. All fields optional.
interface AppLocationOptions {
accuracy?: AppLocationAccuracy
timeoutMs?: number
intervalMs?: number
distanceFilter?: number
}
AppImageAsset
Image payload returned by pickImageFromAlbum and captureImageFromCamera.
interface AppImageAsset {
path: string
name: string
mimeType: string
size: number
base64: string
}
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>
}
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
audioPcm: Uint8Array
}
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
currentSelectItemIndex?: number
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
y?: number
z?: number
}
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',
Phone = 'phone'
}
enum AppLocationAccuracy {
Low = 'low',
Medium = 'medium',
High = 'high'
}
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
createStartUpPageContainer is one-shot — call it exactly once at startup; calling it again will not work. Use rebuildPageContainer for 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.
zOrderIndex is 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: createStartUpPageContainer returns invalid, rebuildPageContainer returns false.
- Container limits —
containerTotalNum must be 1–12; textObject array max 8 items; imageObject array max 4 items.
- Image sends must be serial —
updateImageRawData calls must be queued and awaited one at a time; concurrent calls are not supported and will cause errors.
- Image containers are placeholders — after
createStartUpPageContainer succeeds, image containers are empty until populated via updateImageRawData.
- Glasses-mic
audioControl and imuControl require startup to succeed — audioControl(true, AudioInputSource.Glasses) and imuControl will fail if called before createStartUpPageContainer returns StartUpPageCreateResult.success. audioControl(true, AudioInputSource.Phone), location APIs, pickImageFromAlbum, and captureImageFromCamera route through the phone and do not require the startup page.
- Always unsubscribe event listeners on teardown —
onEvenHubEvent, onDeviceStatusChanged, and onLaunchSource all return an unsubscribe function; call it when your component/page is destroyed.
onLaunchSource fires only once — register the listener early (before or immediately after waitForEvenAppBridge) 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.
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: { type: 'listEvent', jsonData: { } } }
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: { type: 'list_event', data: { } } }
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: ['list_event', { }] }
{ type: 'listen_even_app_data', method: 'evenHubEvent', data: { type: 'audioEvent', jsonData: { source: 'glasses', audioPcm: [] } } }
{ type: 'listen_even_app_data', method: 'deviceStatusChanged', data: { sn: 'ABC123', connectType: 'connected', isWearing: true, batteryLevel: 80, isCharging: false } }
{ method: 'evenAppLaunchSource', data: { launchSource: 'appMenu' } }
{ method: 'appLocationChanged', data: { latitude: 37.7749, longitude: -122.4194, accuracy: 5, timestamp: 1750000000000 } }
Task
Look up SDK reference for: $ARGUMENTS