关于 sdk-reference
sdk-reference 是供 Even Hub G2 智能眼镜 SDK Web 应用开发者查阅的参考技能,涵盖桥接 API、数据类型、初始化顺序和容器校验规则。
主要功能
- 01桥接初始化与调用顺序说明
- 02API 方法和类型参考
- 03文本、列表及图像容器定义
- 04页面容器层级校验说明
适用场景
- 01查找任务对应的 SDK 方法
- 02构建并更新眼镜应用界面
- 03发送前检查页面容器数据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ even-realities/ everything-evenhub --skill sdk-reference安装后在 Claude Code 中输入/sdk-reference确认已加载。
详细介绍
你是 Even Hub G2 智能眼镜 SDK(@evenrealities/even_hub_sdk)的权威参考。有人询问 $ARGUMENTS 时,请从本文档中查找并解释相关的 API、类型、接口、枚举或方法。如果未提供参数,请按结构概述所有可用 API。
架构
Web App (your code) <-> EvenAppBridge (SDK) <-> Even App (host) <-> G2 Glasses
你的应用是一个标准的 HTML + TypeScript 网页,在 Even 配套应用托管的 Flutter WebView 中运行。无需使用特殊框架——直接使用 Vite + TypeScript 即可。
安装
npm install @evenrealities/even_hub_sdk
当前版本:0.0.12
初始化
waitForEvenAppBridge() — 异步,推荐使用
当原生桥接准备就绪时解析。始终优先使用此方法,而不是同步单例。
import { waitForEvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = await waitForEvenAppBridge()
EvenAppBridge.getInstance() — 同步单例
仅在桥接已初始化后调用(例如,在 waitForEvenAppBridge() 解析后触发的回调中调用)。
import { EvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = EvenAppBridge.getInstance()
必需的调用顺序
waitForEvenAppBridge()— 等待桥接就绪bridge.createStartUpPageContainer(container)— 在启动时仅调用一次(眼镜麦克风音频和 IMU 必需)- 其他所有操作:
audioControl、imuControl、rebuildPageContainer、事件监听器、定位、照片选择器 / 相机等
如果未先成功调用 createStartUpPageContainer,audioControl(true, AudioInputSource.Glasses) 和 imuControl 将会失败。audioControl(true, AudioInputSource.Phone)、定位 API、pickImageFromAlbum 和 captureImageFromCamera 不需要启动页面——它们通过手机而非眼镜进行操作。
完整方法参考
| 方法 | 返回值 | 说明 |
|---|---|---|
bridge.getUserInfo() | Promise<UserInfo> | 获取已登录用户的 uid、姓名、头像 URL 和国家/地区 |
bridge.getDeviceInfo() | Promise<DeviceInfo | null> | 获取已连接眼镜的型号、序列号和状态。没有设备时返回 null。 |
bridge.setLocalStorage(key, value) | Promise<boolean> | 将键值字符串对持久化到配套应用的存储中 |
bridge.getLocalStorage(key) | Promise<string> | 按键读取存储的字符串;键不存在时返回空字符串 |
bridge.createStartUpPageContainer(container) | Promise<StartUpPageCreateResult> | 一次性启动调用,用于定义所有 UI 容器。0=成功,1=无效,2=超出大小限制,3=内存不足 |
bridge.rebuildPageContainer(container) | Promise<boolean> | 拆除并完整重绘所有容器 |
bridge.textContainerUpgrade(container) | Promise<boolean> | 原位更新文本,无需完整重绘(每次调用最多 2000 个字符) |
bridge.updateImageRawData(data) | Promise<ImageRawDataUpdateResult> | 发送原始像素数据以填充图像容器。SDK 内部会使用 LZ4 压缩(0.0.12+)——仍像之前一样传入原始像素。调用必须串行——每次调用后都要等待完成再进行下一次。 |
bridge.shutDownPageContainer(exitMode?) | Promise<boolean> | 关闭应用。exitMode 0=立即退出,1=显示确认对话框 |
bridge.onLaunchSource(cb) | () => void | 订阅启动来源事件。仅触发一次:'appMenu' 或 'glassesMenu'。返回取消订阅函数。 |
bridge.onDeviceStatusChanged(cb) | () => void | 订阅设备状态更新(连接类型、电池、佩戴状态、充电状态)。返回取消订阅函数。 |
bridge.onEvenHubEvent(cb) | () => void | 订阅所有中心事件:listEvent、textEvent、sysEvent、audioEvent。返回取消订阅函数。 |
bridge.audioControl(isOpen, source?) | Promise<boolean> | 打开(true)或关闭(false)麦克风。source 是一个 AudioInputSource 值——Glasses(默认,G2 四麦克风阵列)或 Phone(手机麦克风)。音频会以 PCM 格式通过 audioEvent 传递,并设置 source 字段。 |
bridge.imuControl(isOpen, reportFrq?) | Promise<boolean> | 启用/停用 IMU 传感器。reportFrq 是一个 ImuReportPace 值(P100–P1000)。 |
bridge.getAppLocation(options?) | Promise<AppLocation | null> | 单次读取位置。options 接受 accuracy、timeoutMs。如果在规定时间内未获取到定位、权限被拒绝或坐标无效,则返回 null。 |
bridge.startAppLocationUpdates(options?) | Promise<boolean> | 开始持续更新位置。options 接受 accuracy、intervalMs、distanceFilter。订阅 onAppLocationChanged 以接收推送。 |
bridge.stopAppLocationUpdates() | Promise<boolean> | 停止通过 startAppLocationUpdates 启动的持续位置更新。 |
bridge.onAppLocationChanged(cb) | () => void | 订阅持续位置更新。回调会接收一个 AppLocation。返回取消订阅函数。 |
bridge.pickImageFromAlbum() | Promise<AppImageAsset | null> | 打开手机相册以选择单张图片。如果用户取消操作或拒绝 album 权限,则返回 null。 |
bridge.captureImageFromCamera() | Promise<AppImageAsset | null> | 打开手机摄像头并拍摄一张图片。如果用户取消操作或拒绝 camera 权限,则返回 null。G2 没有眼镜端摄像头。 |
bridge.callEvenApp(method, params?) | Promise<any> | 直接调用原生桥接方法的底层接口。当没有更高级别的方法可用时使用。 |
TypeScript 接口
TextContainerProperty
定义一个文本显示容器。
SDK 构造函数中的所有字段都是可选的(接受 Partial<T>)。根据布局需求填写字段。
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
定义一个可滚动列表容器。
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
定义列表容器中的条目。
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
定义占位图像容器。创建后必须使用 updateImageRawData 填充。图像容器不支持 isEventCapture —— 与图像组合使用时,请使用文本容器作为事件捕获层。
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
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
bridge.updateImageRawData() 的负载。
class ImageRawDataUpdate {
containerID?: number
containerName?: string
imageData?: number[] | string | Uint8Array | ArrayBuffer // raw pixel data in 4-bit greyscale
}
自 0.0.12 起,SDK 会在传输前在内部使用 LZ4 压缩图像原始数据,以减小数据量并缩短更新延迟。照常传入原始像素数据即可——无需设置压缩标志。
CreateStartUpPageContainer
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
bridge.rebuildPageContainer() 的参数类型。结构与 CreateStartUpPageContainer 相同,但不包含 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 轴顺序规则(0.0.12+)
zOrderIndex 控制页面上所有容器的前后层叠顺序。在负载到达原生桥接层之前,SDK 会验证以下规则:
- 每页要么全部设置,要么全部不设置 —— 页面上的每个列表/文本/图像容器都必须设置
zOrderIndex,或者都不设置。若全部省略,则保持 0.0.12 之前的行为(重叠顺序由声明顺序决定)。 - 每页唯一 —— 同一页面上的容器不得共享相同的值。没有平局处理规则。
- 数值越大,越靠前。
违反规则时,SDK 会记录 EvenHubPageContainerValidationErrorCode 错误,且不会调用原生层:createStartUpPageContainer 返回 StartUpPageCreateResult.invalid,rebuildPageContainer 返回 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
getAppLocation 返回并通过 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
getAppLocation 和 startAppLocationUpdates 接受的选项对象。所有字段均为可选。
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
pickImageFromAlbum 和 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
}
事件模型
EvenHubEvent
bridge.onEvenHubEvent(cb) 的回调类型。
interface EvenHubEvent {
listEvent?: List_ItemEvent
textEvent?: Text_ItemEvent
sysEvent?: Sys_ItemEvent
audioEvent?: AudioEventPayload
jsonData?: Record<string, any> // raw payload passthrough
}
AudioEventPayload
event.audioEvent 上的麦克风音频负载。source 用于标识缓冲区来自哪个麦克风——在运行时于不同来源之间切换 audioControl(true, ...) 时很有用。
interface AudioEventPayload {
source: AudioInputSource // Glasses | Phone
audioPcm: Uint8Array // PCM 16 kHz, signed 16-bit little-endian, mono
}
Text_ItemEvent
文本容器收到用户交互时触发。
interface Text_ItemEvent {
containerID?: number
containerName?: string
eventType?: OsEventTypeList
}
List_ItemEvent
列表条目被选中或滚动时触发。
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
系统级事件,包括 IMU 数据和生命周期信号。
interface Sys_ItemEvent {
eventType?: OsEventTypeList
eventSource?: EventSourceType
imuData?: IMU_Report_Data
systemExitReasonCode?: number
}
IMU_Report_Data
眼镜 IMU 传感器提供的原始加速度计/陀螺仪数值。
interface IMU_Report_Data {
x?: number // X-axis value
y?: number // Y-axis value
z?: number // Z-axis value
}
LaunchSource
type LaunchSource = 'appMenu' | 'glassesMenu'
枚举
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'
}
结果代码
createStartUpPageContainer / StartUpPageCreateResult
| 代码 | 名称 | 含义 |
|---|---|---|
| 0 | success | 容器创建成功 |
| 1 | invalid | 容器配置无效 |
| 2 | oversize | 容器总尺寸超出显示限制 |
| 3 | outOfMemory | 设备内存不足 |
updateImageRawData / ImageRawDataUpdateResult
| 名称 | 含义 |
|---|---|
| success | 图像数据已接收并渲染 |
| imageException | 通用图像处理错误 |
| imageSizeInvalid | 图像尺寸与容器尺寸不匹配 |
| imageToGray4Failed | 无法将图像转换为 4 位灰度图 |
| sendFailed | 向眼镜发送数据时发生传输错误 |
关键规则
createStartUpPageContainer只能调用一次 — 启动时必须且只能调用一次;再次调用无效。后续需要完整重绘时,请使用rebuildPageContainer。- 必须且只能有一个容器设置
isEventCapture: 1— 这决定哪个容器接收用户输入。设置为零个或多个都会导致未定义行为。 - 每个页面的
zOrderIndex必须全部设置或全部不设置(0.0.12+) — 如果有任何容器设置了该属性,则该页面上的每个列表、文本和图像容器都必须设置唯一值;数值越大,渲染层级越靠前。违反此规则会导致 SDK 端验证失败:createStartUpPageContainer返回invalid,rebuildPageContainer返回false。 - 容器数量限制 —
containerTotalNum必须为 1–12;textObject数组最多包含 8 项;imageObject数组最多包含 4 项。 - 图像发送必须串行 — 必须将
updateImageRawData调用排队,并逐个等待完成;不支持并发调用,否则会导致错误。 - 图像容器是占位容器 —
createStartUpPageContainer成功后,图像容器仍为空,直到通过updateImageRawData填充内容。 - 眼镜麦克风的
audioControl和imuControl需要启动成功 — 在createStartUpPageContainer返回StartUpPageCreateResult.success之前调用audioControl(true, AudioInputSource.Glasses)和imuControl会失败。audioControl(true, AudioInputSource.Phone)、位置 API、pickImageFromAlbum和captureImageFromCamera通过手机处理,不需要启动页面。 - 销毁时务必取消订阅事件监听器 —
onEvenHubEvent、onDeviceStatusChanged和onLaunchSource都会返回取消订阅函数;组件或页面销毁时请调用该函数。 onLaunchSource只触发一次 — 请尽早注册监听器(在调用waitForEvenAppBridge之前或紧接其后),以免错过该事件。
画布规格
| 属性 | 值 |
|---|---|
| 分辨率 | 576 × 288 px |
| 色深 | 4 位灰度(16 个灰度级,0 = 黑色,15 = 白色) |
| 坐标原点 | 左上角的 (0, 0) |
| X 轴 | 向右递增 |
| Y 轴 | 向下递增 |
主机推送格式(模拟器 / 测试)
配套应用(以及模拟器)会通过 window.postMessage 将事件推送到 WebView。通常无需直接处理这些事件 — SDK 会在内部处理;不过,在编写测试或自定义模拟器时,这些格式会很有用。
// 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 } }
任务
查找 SDK 参考文档:$ARGUMENTS