mcpField
S
sdk-reference

sdk-reference

github.com/even-realities开发79136 次安装1 周前更新
代码仓库

关于 sdk-reference

sdk-reference 是供 Even Hub G2 智能眼镜 SDK Web 应用开发者查阅的参考技能,涵盖桥接 API、数据类型、初始化顺序和容器校验规则。

主要功能

  1. 01桥接初始化与调用顺序说明
  2. 02API 方法和类型参考
  3. 03文本、列表及图像容器定义
  4. 04页面容器层级校验说明

适用场景

  1. 01查找任务对应的 SDK 方法
  2. 02构建并更新眼镜应用界面
  3. 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()

必需的调用顺序

  1. waitForEvenAppBridge() — 等待桥接就绪
  2. bridge.createStartUpPageContainer(container) — 在启动时仅调用一次(眼镜麦克风音频和 IMU 必需)
  3. 其他所有操作: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
代码名称含义
0success容器创建成功
1invalid容器配置无效
2oversize容器总尺寸超出显示限制
3outOfMemory设备内存不足
updateImageRawData / ImageRawDataUpdateResult
名称含义
success图像数据已接收并渲染
imageException通用图像处理错误
imageSizeInvalid图像尺寸与容器尺寸不匹配
imageToGray4Failed无法将图像转换为 4 位灰度图
sendFailed向眼镜发送数据时发生传输错误

关键规则

  1. createStartUpPageContainer 只能调用一次 — 启动时必须且只能调用一次;再次调用无效。后续需要完整重绘时,请使用 rebuildPageContainer。
  2. 必须且只能有一个容器设置 isEventCapture: 1 — 这决定哪个容器接收用户输入。设置为零个或多个都会导致未定义行为。
  3. 每个页面的 zOrderIndex 必须全部设置或全部不设置(0.0.12+) — 如果有任何容器设置了该属性,则该页面上的每个列表、文本和图像容器都必须设置唯一值;数值越大,渲染层级越靠前。违反此规则会导致 SDK 端验证失败:createStartUpPageContainer 返回 invalid,rebuildPageContainer 返回 false。
  4. 容器数量限制 — containerTotalNum 必须为 1–12;textObject 数组最多包含 8 项;imageObject 数组最多包含 4 项。
  5. 图像发送必须串行 — 必须将 updateImageRawData 调用排队,并逐个等待完成;不支持并发调用,否则会导致错误。
  6. 图像容器是占位容器 — createStartUpPageContainer 成功后,图像容器仍为空,直到通过 updateImageRawData 填充内容。
  7. 眼镜麦克风的 audioControl 和 imuControl 需要启动成功 — 在 createStartUpPageContainer 返回 StartUpPageCreateResult.success 之前调用 audioControl(true, AudioInputSource.Glasses) 和 imuControl 会失败。audioControl(true, AudioInputSource.Phone)、位置 API、pickImageFromAlbum 和 captureImageFromCamera 通过手机处理,不需要启动页面。
  8. 销毁时务必取消订阅事件监听器 — onEvenHubEvent、onDeviceStatusChanged 和 onLaunchSource 都会返回取消订阅函数;组件或页面销毁时请调用该函数。
  9. 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

评论

支持 Markdown