@evenrealities/even_hub_sdk
v0.0.12
Published
TypeScript SDK for Even Hub developers to communicate with Even App
Readme
@evenrealities/even_hub_sdk
Even App WebView 页面使用的 TypeScript SDK。
English | 中文
⚡️ 快速开始
npm install @evenrealities/even_hub_sdkimport { waitForEvenAppBridge } from '@evenrealities/even_hub_sdk';
const bridge = await waitForEvenAppBridge();
const user = await bridge.getUserInfo();
const device = await bridge.getDeviceInfo();
await bridge.setLocalStorage('theme', 'dark');
const theme = await bridge.getLocalStorage('theme');
console.log(user.name, device?.model, theme);当 Web 页面运行在 Even App WebView 内,并需要这些能力时使用:
- App 桥接:用户、设备、本地存储。
- App 能力:定位、相册、相机。
- 眼镜 UI:启动页容器、重建、文本/图片更新。
- 传感器与事件:MIC、IMU、启动来源、设备状态。
📦 安装
npm install @evenrealities/even_hub_sdk
# 或
pnpm add @evenrealities/even_hub_sdk
# 或
yarn add @evenrealities/even_hub_sdk要求:
- SDK 版本:
0.0.12 - Node.js:
^20.0.0 || >=22.0.0 - 运行环境:提供
window.flutter_inappwebview.callHandler的 Even App WebView
🔧 常用能力
启动来源
宿主在 WebView loading 完成后推送一次启动来源。
const bridge = await waitForEvenAppBridge();
const unsubscribe = bridge.onLaunchSource((source) => {
if (source === 'glassesMenu') {
console.log('从眼镜菜单打开');
}
});
// unsubscribe();取值:
appMenuglassesMenu
设备状态
import {
DeviceConnectType,
waitForEvenAppBridge,
} from '@evenrealities/even_hub_sdk';
const bridge = await waitForEvenAppBridge();
const unsubscribe = bridge.onDeviceStatusChanged((status) => {
if (status.connectType === DeviceConnectType.Connected) {
console.log('电量:', status.batteryLevel);
}
});
// unsubscribe();App 定位
import {
AppLocationAccuracy,
waitForEvenAppBridge,
} from '@evenrealities/even_hub_sdk';
const bridge = await waitForEvenAppBridge();
const location = await bridge.getAppLocation({
accuracy: AppLocationAccuracy.High,
timeoutMs: 5000,
});
if (location) {
console.log(location.latitude, location.longitude);
}连续定位:
await bridge.startAppLocationUpdates({
accuracy: AppLocationAccuracy.Medium,
intervalMs: 1000,
distanceFilter: 5,
});
const unsubscribeLocation = bridge.onAppLocationChanged((location) => {
console.log('位置:', location.latitude, location.longitude);
});
// await bridge.stopAppLocationUpdates();
// unsubscribeLocation();相册与相机
相册只支持单选。
const albumImage = await bridge.pickImageFromAlbum();
if (albumImage) {
console.log(albumImage.name, albumImage.mimeType, albumImage.size);
// Web 侧使用 albumImage.base64
}
const cameraImage = await bridge.captureImageFromCamera();
if (cameraImage) {
console.log('拍摄成功:', cameraImage.name);
}AppImageAsset:
type AppImageAsset = {
path: string;
name: string;
mimeType: string;
size: number;
base64: string;
};MIC 来源
可选择眼镜 MIC 或手机 MIC。
import {
AudioInputSource,
waitForEvenAppBridge,
} from '@evenrealities/even_hub_sdk';
const bridge = await waitForEvenAppBridge();
await bridge.audioControl(true, AudioInputSource.Glasses);
// await bridge.audioControl(true, AudioInputSource.Phone);
const unsubscribeAudio = bridge.onEvenHubEvent((event) => {
const audio = event.audioEvent;
if (!audio) return;
console.log(audio.source); // AudioInputSource.Glasses | AudioInputSource.Phone
console.log(audio.audioPcm.length); // Uint8Array
});
// await bridge.audioControl(false);
// unsubscribeAudio();说明:
- 默认来源:
AudioInputSource.Glasses。 - 音频数据通过
onEvenHubEvent下发。 - 使用眼镜 MIC 前,先创建启动页。
IMU
import {
ImuReportPace,
OsEventTypeList,
waitForEvenAppBridge,
} from '@evenrealities/even_hub_sdk';
const bridge = await waitForEvenAppBridge();
await bridge.imuControl(true, ImuReportPace.P500);
const unsubscribeImu = bridge.onEvenHubEvent((event) => {
const sys = event.sysEvent;
if (sys?.eventType !== OsEventTypeList.IMU_DATA_REPORT) return;
if (!sys.imuData) return;
console.log(sys.imuData.x, sys.imuData.y, sys.imuData.z);
});
// await bridge.imuControl(false);
// unsubscribeImu();ImuReportPace 是协议档位:P100 到 P1000。
🕶️ 眼镜 UI
先调用 createStartUpPageContainer,再做其他眼镜 UI 操作。
import {
ImageContainerProperty,
ImageRawDataUpdateResult,
ListContainerProperty,
StartUpPageCreateResult,
TextContainerProperty,
waitForEvenAppBridge,
} from '@evenrealities/even_hub_sdk';
const bridge = await waitForEvenAppBridge();
const listObject: ListContainerProperty[] = [{
xPosition: 100,
yPosition: 50,
width: 200,
height: 150,
containerID: 1,
containerName: 'list-1',
zOrderIndex: 1,
itemContainer: {
itemCount: 3,
itemName: ['Item 1', 'Item 2', 'Item 3'],
},
isEventCapture: 1,
}];
const textObject: TextContainerProperty[] = [{
xPosition: 100,
yPosition: 220,
width: 200,
height: 50,
containerID: 2,
containerName: 'text-1',
zOrderIndex: 2,
content: 'Hello',
isEventCapture: 0,
}];
const imageObject: ImageContainerProperty[] = [{
xPosition: 320,
yPosition: 50,
width: 100,
height: 80,
containerID: 3,
containerName: 'image-1',
zOrderIndex: 3,
}];
const result = await bridge.createStartUpPageContainer({
containerTotalNum: 3,
listObject,
textObject,
imageObject,
});
if (result === StartUpPageCreateResult.success) {
await bridge.textContainerUpgrade({
containerID: 2,
containerName: 'text-1',
content: 'Updated',
});
}更新图片内容:
图片原始数据由 SDK 内部使用 LZ4 压缩,以减少传输体积,同时保持设备端快速解码。
const imageResult = await bridge.updateImageRawData({
containerID: 3,
containerName: 'image-1',
imageData: [/* 灰度图片字节 */],
});
if (imageResult !== ImageRawDataUpdateResult.success) {
console.warn('图片更新失败:', imageResult);
}重建页面:
await bridge.rebuildPageContainer({
containerTotalNum: 1,
textObject: [{
xPosition: 100,
yPosition: 80,
width: 240,
height: 60,
containerID: 4,
containerName: 'status-text',
content: 'Ready',
isEventCapture: 1,
}],
});监听列表、文本和系统事件:
const unsubscribeHub = bridge.onEvenHubEvent((event) => {
if (event.listEvent) {
console.log('选中:', event.listEvent.currentSelectItemName);
}
if (event.textEvent) {
console.log('文本事件:', event.textEvent.containerName);
}
if (event.sysEvent) {
console.log('系统事件:', event.sysEvent.eventType);
}
});
// unsubscribeHub();关闭眼镜页面:
await bridge.shutDownPageContainer(0);
// await bridge.shutDownPageContainer(1); // 交给前台交互层决定是否退出规则:
- 坐标原点:左上角。
containerTotalNum:1到12。textObject:最多8个。- 只能有一个容器使用
isEventCapture: 1。 - 图片容器创建后,需要再调用
updateImageRawData。 zOrderIndex只有在同一个画面的所有容器都不填写时才可以省略,用于兼容旧版本 SDK 页面。- 同一个画面里,只要任意容器填写了
zOrderIndex,所有 list/text/image 容器都必须填写。 - 同一个画面内
zOrderIndex数值必须唯一;数值越大,画面层级越靠前。 - 违反上述
zOrderIndex规则时,SDK 会在调用原生前输出EvenHubPageContainerValidationErrorCode错误日志。createStartUpPageContainer返回StartUpPageCreateResult.invalid,rebuildPageContainer返回false。
📚 API 速查
Bridge
| API | 返回 |
| --- | --- |
| waitForEvenAppBridge() | Promise<EvenAppBridge> |
| EvenAppBridge.getInstance() | EvenAppBridge |
| callEvenApp(method, params?) | Promise<any> |
App
| API | 返回 |
| --- | --- |
| getUserInfo() | Promise<UserInfo> |
| getDeviceInfo() | Promise<DeviceInfo \| null> |
| setLocalStorage(key, value) | Promise<boolean> |
| getLocalStorage(key) | Promise<string> |
| getAppLocation(options?) | Promise<AppLocation \| null> |
| startAppLocationUpdates(options?) | Promise<boolean> |
| stopAppLocationUpdates() | Promise<boolean> |
| pickImageFromAlbum() | Promise<AppImageAsset \| null> |
| captureImageFromCamera() | Promise<AppImageAsset \| null> |
事件
| API | 事件 |
| --- | --- |
| onLaunchSource(callback) | appMenu / glassesMenu |
| onDeviceStatusChanged(callback) | 设备状态 |
| onAppLocationChanged(callback) | App 位置 |
| onEvenHubEvent(callback) | list / text / sys / audio |
EvenHub
| API | 返回 |
| --- | --- |
| createStartUpPageContainer(container) | Promise<StartUpPageCreateResult> |
| rebuildPageContainer(container) | Promise<boolean> |
| updateImageRawData(data) | Promise<ImageRawDataUpdateResult> |
| textContainerUpgrade(container) | Promise<boolean> |
| audioControl(isOpen, source?: AudioInputSource) | Promise<boolean> |
| imuControl(isOpen, reportFrq?) | Promise<boolean> |
| shutDownPageContainer(exitMode?) | Promise<boolean> |
🧯 排障
| 现象 | 处理 |
| --- | --- |
| Flutter handler not available | 需要运行在 Even App WebView 内,普通浏览器不能调用原生能力。 |
| 收不到启动来源 | 尽早注册 onLaunchSource,宿主只在加载完成后推送一次。 |
| 眼镜 MIC 返回 false | 先创建启动页,再调用 audioControl(true, AudioInputSource.Glasses)。 |
| 连续定位没有回调 | 先调用 startAppLocationUpdates,并保持 onAppLocationChanged 订阅。 |
| 眼镜图片不显示 | 先创建/重建图片容器,再调用 updateImageRawData。 |
📜 更新日志
0.0.12
- 新增列表、文本、图片容器的
zOrderIndex支持,用于控制页面中多个容器的前后叠放顺序。 - 图片原始数据更新由 SDK 内部使用 LZ4 压缩,减少传输体积,同时保持快速编解码,降低图片更新延迟。
0.0.11
- 新增 App 定位 API:单次定位和连续定位。
- 新增 App 相册图片选择,只支持单选。
- 新增 App 相机拍摄 API。
- 新增 MIC 来源选择:
glasses或phone。
0.0.10
- 增强 WebView 后台保活能力。
0.0.9
- 优化
EventSourceType兼容性。 - 增加默认来源枚举兜底。
- 提升事件来源解析一致性。
0.0.8
- 新增启动来源事件:
appMenu/glassesMenu。 - 启动页容器数量从
4扩展到12。 - 新增 IMU 控制和 IMU 数据事件。
0.0.1
- 初始桥接、本地存储、设备信息、EvenHub 协议和事件 API。
