Even Hub G2で最初のアプリを作る:クイックスタートとハマりどころ
Even Hub の公式クイックスタートを参考に、Even Realities G2スマートグラス向けの最初のアプリ「my-first-app」を作ってみました。
一通りやってみると、公式サンプルのままではタップが効かないという地味にハマるポイントがありました。最初はシミュレータ特有の問題かと思いましたが、実機で再検証したところ同じ挙動でした。本記事では、プロジェクト作成から配布までの手順に加えて、その原因調査と修正の過程(シミュレータ・実機の両方での検証)を記録として残しておきます。
1. プロジェクト作成
Vite の Vanilla TypeScript テンプレートでプロジェクトを作成します。
npm create vite@latest my-first-app -- --template vanilla-ts
cd my-first-app
npm install
2. Even Hub SDKの導入
npm install @evenrealities/even_hub_sdk@latest
3. Even Hubマニフェストの作成
evenhub init
これで app.json が生成されます。package_id・バージョン・permissions などのアプリのメタデータを保持するマニフェストファイルです。
4. main.ts の実装
クイックスタートの「Write main.ts」に沿って、src/main.ts を Even Hub SDK を使った実装に置き換えます。要点は次の3つです。
waitForEvenAppBridge()でブリッジの初期化を待つ- 576×288 いっぱいの text container を
createStartUpPageContainerで描画する onEvenHubEventでタップ/ダブルタップを購読し、カウントアップと終了処理を実装する
あわせて、Vite テンプレートに含まれる未使用ファイルを削除しておきます。
counter.tsstyle.cssassets/配下の画像public/icons.svg
5. シミュレータでの動作確認とハマりポイント
シミュレータをインストールして、開発サーバーを起動した状態でシミュレータから読み込みます。
npm install -g @evenrealities/evenhub-simulator
npm run dev
evenhub-simulator -g http://localhost:5173
修正後、シミュレータ上でタップするとカウントが増えるようになった様子が以下です。下部の Up / Down / Click / Double Click のボタンで G2 の操作をエミュレートできます。

問題:クリックしても反応しない
ここで問題が発生しました。シミュレータ上でクリック/ダブルクリックしても、アプリがまったく反応しません。
調査:HTTP自動化APIで実際のイベントを観測する
何が起きているのかを直接確認するため、シミュレータの HTTP 自動化 API を有効化しました。--automation-port を指定すると、HTTP 経由で入力を送ったりコンソールログを取得したりできます。
evenhub-simulator -g http://localhost:5173 --automation-port 9898
curl -s -X POST http://127.0.0.1:9898/api/input -H "Content-Type: application/json" -d '{"action":"click"}'
curl -s "http://127.0.0.1:9898/api/console"
クリックを送るたびに /api/console で観測されたイベントは以下の通りでした。いずれも textEvent ではなく sysEvent として届いています。
// クリック(シングルタップ)
{"jsonData":{"eventSource":1},"sysEvent":{"eventSource":1}}
// ダブルクリック(ダブルタップ)
{"jsonData":{"eventType":3,"eventSource":1},"sysEvent":{"eventType":3,"eventSource":1}}
OsEventTypeList は CLICK_EVENT=0・DOUBLE_CLICK_EVENT=3 なので、eventType:3 はダブルクリックに一致します。シングルクリックの方は eventType キー自体が省略されていました(SDK が 0 を undefined に正規化する挙動)。そして textEvent は一度も観測されませんでした。
原因判明
公式クイックスタートのサンプルコードは、クリックが常に event.textEvent(コンテナ単位のイベント、containerID 付き)として届く前提になっていました。
// 公式サンプルの前提(動かなかったコード)
bridge.onEvenHubEvent((event) => {
const textEvent = event.textEvent
if (!textEvent || textEvent.containerID !== 1) return // ← textEventは常にundefinedなので毎回ここでreturnしていた
switch (textEvent.eventType) {
case OsEventTypeList.CLICK_EVENT:
case undefined:
// ...カウントアップ処理...
ところが evenhub-simulator v0.7.1 では、StartUpPage(ルートページ)いっぱいに置いた単一の text container へのタップは event.sysEvent(containerID を持たず、eventType のみ)として配信されていました。そのため event.textEvent は常に undefined となり、ガード節で毎回早期 return してクリックが無視されていたのです。
そもそもルートページに置いたコンテナが1つだけの場合は、containerID によるコンテナの絞り込み自体が不要とも言えます。
修正
onEvenHubEvent 内で event.textEvent と event.sysEvent の両方を見るように変更しました。textEvent が存在する場合のみ containerID を確認し、どちらの形でイベントが来ても eventType でルーティングできるようにしています。
bridge.onEvenHubEvent((event) => {
const source = event.textEvent ?? event.sysEvent
if (!source) return
if (event.textEvent && event.textEvent.containerID !== 1) return
switch (source.eventType) {
case OsEventTypeList.CLICK_EVENT:
case undefined:
// ...カウントアップ処理...
この時点では「シミュレータ特有の挙動かもしれない」「実機では textEvent 経由で届く可能性も残っている」と考えていたため、どちらの経路でも動くようにしてハードウェアとの互換性も保つ形にしました。
実機での再検証
シミュレータの --automation-port のような外部 API は実機にはありません。そこで代わりに、Vite dev server の HMR 用 WebSocket を使って、イベントを Mac 側のターミナルに中継する一時的な仕組みを作りました。
// vite.config.ts(デバッグ用、検証後は削除)
function evenHubEventLogger(): Plugin {
return {
name: 'evenhub-event-logger',
configureServer(server) {
server.ws.on('evenhub:event', (data) => {
console.log('[evenhub event]', JSON.stringify(data, null, 2))
})
},
}
}
// main.ts の onEvenHubEvent 内(デバッグ用、検証後は削除)
import.meta.hot?.send('evenhub:event', event)
npm run dev -- --host で LAN 公開し、実機の Even Hub コンパニオンアプリから接続してタップ/ダブルタップ/スワイプを実行、Mac 側のターミナルに出力されたログを確認しました。
// クリック(シングルタップ)
{ "sysEvent": { "eventSource": 1 } }
// ダブルクリック(ダブルタップ)
{ "sysEvent": { "eventType": 3, "eventSource": 1 } }
// 上スワイプ
{ "textEvent": { "containerID": 1, "containerName": "main", "eventType": 1 } }
// 下スワイプ
{ "textEvent": { "containerID": 1, "containerName": "main", "eventType": 2 } }
結論は次の通りです。
- クリック/ダブルクリックは実機でも
sysEvent(containerIDなし)として届く。 シミュレータだけの挙動ではなく、event.textEvent ?? event.sysEventの修正は実機でも必須でした。 - 一方でスワイプ(上下スクロール、
eventType:1/2)はtextEvent(containerID付き)として届く。 同じonEvenHubEventでも、操作の種類によってtextEvent/sysEventのどちらに乗るかが変わります。
検証後、デバッグ用の vite.config.ts と import.meta.hot?.send 呼び出しは削除済みです。
6. ドキュメント整備
再現・引き継ぎしやすいように、ドキュメントを用意しました。
- リポジトリルートに
README.md(セットアップ手順、ハマりポイントの記録) - リポジトリルートに
CLAUDE.md(コマンド一覧、アーキテクチャ、上記の既知の癖)
7. コミット・PR・レビュー対応
ブランチを切ってコミットし、PR を作成します。
git checkout -b my-first-app-quickstart
git add README.md CLAUDE.md my-first-app
git commit -m "Add my-first-app Even Hub quickstart and docs"
git push -u origin my-first-app-quickstart
gh pr create ...
Gemini Code Assist のレビューで2件の指摘を受け、対応しました。
app.jsonの未使用のnetwork/location権限を削除("permissions": [])main.tsでcreateStartUpPageContainer失敗時、console.errorではなくthrow new Error(...)で処理を止めるように変更
修正をコミット・プッシュし、各インラインコメントに返信。PR マージ後、main をチェックアウトして最新化しました。
8. 実機での動作確認
実機で確認するには、LAN 上の自分の IP に開発サーバーを公開し、QR コードから読み込みます。
ipconfig getifaddr en0 # LAN上の自分のIPを確認
evenhub qr --url "http://<自分のIP>:5173" # 実機用QRコードを生成
npm run dev -- --host # --host でLANに公開
生成した QR コードを Even Hub のコンパニオンアプリでスキャンし、実機での表示・動作を確認しました。
9. ビルドとパッケージング
ビルドして .ehpk ファイルにパッケージングします。
npm run build # tsc + vite build → dist/
npx evenhub pack app.json dist -o g2-demo.ehpk # .ehpk にパッケージング
.ehpk ファイルはビルド成果物なので、my-first-app/.gitignore に *.ehpk を追加して git 管理外にしました。
なお、app.json の package_id を com.example.g2demo から dev.y16ra.g2demo に変更した際は、npm run build → npx evenhub pack ... を再実行してパッケージを作り直す必要があります。
10. 配布
最後に、.ehpk ファイルを Even Hub developer portal に提出して審査・公開を依頼します。提出は Web ポータル上での手動アップロードが必要で、CLI に提出コマンドはありません。
まとめ
Even Hub G2 の最初のアプリづくりで学んだことを整理します。
- セットアップ: Vite(vanilla-ts)でプロジェクトを作り、Even Hub SDK を入れて
evenhub initでapp.jsonを生成する - ハマりどころ: ルートページの単一コンテナへのタップは
sysEvent(containerIDなし)として届く。公式サンプルのtextEvent前提のコードだと反応しない。これはシミュレータ特有ではなく実機でも同じだった - 操作による違い: クリック/ダブルクリックは
sysEvent、スワイプ(上下スクロール)はtextEvent(containerID付き)と、操作の種類でイベントの届き方が変わる - 調査のコツ: シミュレータでは
--automation-portの HTTP 自動化 API と/api/console、実機では Vite の HMR WebSocket でログを中継して、実際のイベントを直接観測すると原因にたどり着ける - 修正方針:
textEventとsysEventの両方を見てeventTypeでルーティングすれば、シミュレータ・実機のどちらでも動く - 配布:
.ehpkを作って developer portal に手動アップロード
公式ドキュメント通りに進めても、イベントの届き方の違いでつまずくことがあります。困ったときはブラックボックスにせず、自動化 API やログで一次情報を観測するのが解決への近道でした。シミュレータで見つけた挙動を実機でも裏取りできたのが今回の一番の収穫です。