expo-meta-wearables-dat

This package enables developers to utilize Meta's AI glasses (Meta Wearables DAT) in Expo + React Native applications.


Keywords
react-native, expo, expo-meta-wearables-dat, EMWDAT, meta, wearables, glasses, ray-ban, camera, streaming, ios, android, bluetooth, dat
License
MIT
Install
npm install expo-meta-wearables-dat@1.2.0

Documentation

expo-meta-wearables-dat

npm version CI license platform: iOS | Android

Expo native module for integrating Meta Wearables DAT (Ray-Ban Meta smart glasses) into React Native apps. Provides device registration, permissions, session-based camera streaming, photo capture, and a React hook — bridged from the official Meta Wearables DAT SDK 0.6 on both iOS and Android.

Official SDK docs: Meta Wearables DAT — Developer Documentation

You must register your app in the Meta Wearables Developer Center to obtain your App ID and Client Token.

Disclaimer: This project is not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc. It is an independent, community-maintained wrapper around the publicly available Meta Wearables DAT SDK.

Non-goals

  • Background streaming — the SDK doesn't support it
  • Expo Go — requires a development build (native code)

Features

  • Device registration / unregistration via Meta AI app
  • Permission management (camera)
  • Device discovery and link state monitoring
  • Session-based camera streaming with native view
  • Compressed HEVC video streaming (Android)
  • Photo capture (JPEG / HEIC)
  • useMetaWearables React hook with full state management
  • Mock device simulation for testing (debug builds) with permission mocking and phone camera feed
  • Expo config plugin (auto-configures Info.plist, AndroidManifest, URL schemes, deployment target)

Compatibility

Requirement Version
React Native 0.76+
Expo SDK 52+
iOS 16.0+
Android API 31+
Xcode 16+
Swift 5.9+
DAT SDK 0.6
New Architecture Untested

Supported Devices

  • Ray-Ban Meta (verified)
  • Ray-Ban Meta Optics (untested)
  • Meta Ray-Ban Display (untested)
  • Oakley Meta HSTN / Vanguard (untested)

Installation

npx expo install expo-meta-wearables-dat

Or manually:

# pnpm
pnpm add expo-meta-wearables-dat

# yarn
yarn add expo-meta-wearables-dat

# npm
npm install expo-meta-wearables-dat

Setup

Config plugin

Add the plugin to your app.json / app.config.js:

{
  "plugins": [
    [
      "expo-meta-wearables-dat",
      {
        "urlScheme": "myapp",
        "metaAppId": "YOUR_META_APP_ID",
        "clientToken": "YOUR_CLIENT_TOKEN",
        "bluetoothUsageDescription": "This app uses Bluetooth to connect to Meta Wearables."
      }
    ]
  ]
}
Prop Required Description
urlScheme Yes URL scheme for Meta AI app callback (e.g. "myapp"). Do not include :// — only the scheme name
metaAppId No Meta App ID from Wearables Developer Center. Omit for Developer Mode
clientToken No Client Token from Wearables Developer Center
bluetoothUsageDescription No Custom Bluetooth usage description (iOS only)
githubToken No GitHub token for Maven packages (Android). Falls back to GITHUB_TOKEN env var

iOS

The plugin automatically configures:

  • CFBundleURLTypes (URL scheme)
  • LSApplicationQueriesSchemes (fb-viewapp)
  • UISupportedExternalAccessoryProtocols (com.meta.ar.wearable)
  • UIBackgroundModes (bluetooth-peripheral, external-accessory)
  • NSBluetoothAlwaysUsageDescription
  • MWDAT configuration dictionary (including TeamID auto-resolved from Xcode's DEVELOPMENT_TEAM signing setting)
  • iOS deployment target to 16.0
  • Embeds MWDATCamera & MWDATCore dynamic frameworks

Note: The native Meta Wearables DAT iOS SDK states iOS 17.0+ as its minimum. The podspec targets 16.0 and builds successfully, but runtime behavior on iOS 16 devices is unverified. We recommend iOS 17.0+ for production use.

Android

The plugin automatically configures:

  • <meta-data> entries for APPLICATION_ID and CLIENT_TOKEN in AndroidManifest.xml
  • Deep link <intent-filter> on MainActivity with the configured URL scheme
  • Bluetooth permissions (BLUETOOTH, BLUETOOTH_CONNECT)

The Android SDK dependencies are resolved via Maven from GitHub Packages. The config plugin injects the Maven repository automatically. You need either:

  • GITHUB_ACTOR and GITHUB_TOKEN environment variables set, or
  • The githubToken plugin prop configured

Prebuild

After adding the plugin, generate the native projects:

npx expo prebuild

If you change plugin configuration later, regenerate with --clean to ensure native projects are fully updated:

npx expo prebuild --clean

Prerequisites

  • The user must have the Meta AI app installed and paired with their glasses
  • A physical device is required (no simulator/emulator support)
  • iOS: Xcode 16+ with a valid signing team
  • Android: Android Studio with SDK installed, minSdk 31 (Android 12+)

Quick Start

import { View, Button, Text } from "react-native";
import { useMetaWearables, EMWDATStreamView } from "expo-meta-wearables-dat";
import { useState } from "react";

export default function App() {
  const [sessionId, setSessionId] = useState<string | null>(null);
  const {
    isConfigured,
    registrationState,
    devices,
    startRegistration,
    createSession,
    startSession,
    stopSession,
    addStreamToSession,
    capturePhoto,
  } = useMetaWearables({
    onPhotoCaptured: (photo) => console.log("Photo saved:", photo.filePath),
    onStreamStateChange: (state) => console.log("Stream:", state),
  });

  const handleStartStream = async () => {
    const id = await createSession();
    setSessionId(id);
    await startSession(id);
    await addStreamToSession(id, { resolution: "medium", frameRate: 24 });
  };

  const handleStopStream = async () => {
    if (sessionId) {
      await stopSession(sessionId);
      setSessionId(null);
    }
  };

  return (
    <View style={{ flex: 1, padding: 20, paddingTop: 60, gap: 10 }}>
      <Text>Configured: {String(isConfigured)}</Text>
      <Text>Registration: {registrationState}</Text>
      <Text>Devices: {devices.length}</Text>

      <Button title="Register" onPress={() => startRegistration()} />
      <Button title="Start Stream" onPress={handleStartStream} />
      <Button title="Stop Stream" onPress={handleStopStream} />
      <Button title="Capture Photo" onPress={() => capturePhoto("jpeg")} />

      <EMWDATStreamView isActive={!!sessionId} resizeMode="contain" style={{ flex: 1 }} />
    </View>
  );
}

API Reference

useMetaWearables(options?)

React hook that manages the full lifecycle of Meta Wearables integration.

Options (UseMetaWearablesOptions):

Option Type Default Description
autoConfig boolean true Auto-call configure() on mount
logLevel LogLevel "info" Initial log level
onRegistrationStateChange (state) => void Registration state changed
onDevicesChange (devices) => void Device list updated
onLinkStateChange (deviceId, linkState) => void Device connection changed
onStreamStateChange (state) => void Stream state changed
onVideoFrame (metadata) => void Video frame received
onPhotoCaptured (photo) => void Photo captured
onStreamError (error) => void Stream error occurred
onPermissionStatusChange (permission, status) => void Permission status changed
onCompatibilityChange (deviceId, compatibility) => void Device compatibility changed
onDeviceSessionStateChange (sessionId, state) => void Device session state changed
onDeviceSessionError (sessionId, error, message?) => void Device session error
onCapabilityStateChange (sessionId, state) => void Capability state changed

Returned state:

Field Type Description
isConfigured boolean SDK configured
isConfiguring boolean true while configuring
configError Error | null Error from last configure
registrationState RegistrationState Registration lifecycle state
permissionStatus PermissionStatus "granted" | "denied"
devices Device[] Connected devices
deviceSessionStates Record<string, DeviceSessionState> Per-session states
deviceSessionErrors Record<string, { error, message? }> Per-session errors
capabilityStates Record<string, CapabilityState> Per-session capability state

Returned actions:

Action Signature Description
configure () => Promise<void> Initialize SDK
setLogLevel (level: LogLevel) => void Change log level
startRegistration () => Promise<void> Open Meta AI app for registration
startUnregistration () => Promise<void> Unregister from Meta AI
checkPermissionStatus (permission) => Promise<PermissionStatus> Check permission
requestPermission (permission) => Promise<PermissionStatus> Request permission
getDevice (id) => Promise<Device | null> Get device by identifier
refreshDevices () => Promise<Device[]> Refresh device list
createSession (deviceId?) => Promise<string> Create a device session
startSession (sessionId) => Promise<void> Start a session
stopSession (sessionId) => Promise<void> Stop a session (terminal)
addStreamToSession (sessionId, config?) => Promise<void> Attach camera stream capability
removeStreamFromSession (sessionId) => Promise<void> Remove stream capability
capturePhoto (format?) => Promise<void> Capture photo
enableMockDeviceKit (config?) => Promise<void> Enable mock device kit
disableMockDeviceKit () => Promise<void> Disable mock device kit
isMockDeviceKitEnabled () => Promise<boolean> Check if mock kit is enabled
pairMockDevice () => Promise<string> Pair a mock device
unpairMockDevice (deviceId) => Promise<void> Unpair a mock device
mockSetPermissionStatus (permission, status) => Promise<void> Set mock permission status
mockSetPermissionRequestResult (permission, result) => Promise<void> Set mock permission request result
mockDeviceSetCameraFeedFromCamera (id, facing) => Promise<void> Set mock camera from phone camera

Module Functions

These can be imported directly for lower-level control:

import {
  EMWDATModule,
  configure,
  setLogLevel,
  startRegistration,
  startUnregistration,
  handleUrl,
  checkPermissionStatus,
  requestPermission,
  getDevices,
  getDevice,
  getRegistrationState,
  getRegistrationStateAsync,
  createSession,
  startSession,
  stopSession,
  addStreamToSession,
  removeStreamFromSession,
  capturePhoto,
  addListener,
  // Mock device kit
  enableMockDeviceKit,
  disableMockDeviceKit,
  isMockDeviceKitEnabled,
  pairMockDevice,
  unpairMockDevice,
  getMockDevices,
  mockDevicePowerOn,
  mockDevicePowerOff,
  mockDeviceDon,
  mockDeviceDoff,
  mockDeviceFold,
  mockDeviceUnfold,
  mockDeviceSetCameraFeed,
  mockDeviceSetCapturedImage,
  mockDeviceSetCameraFeedFromCamera,
  mockSetPermissionStatus,
  mockSetPermissionRequestResult,
} from "expo-meta-wearables-dat";

Events

Subscribe via addListener or hook callbacks:

Event Payload
onRegistrationStateChange { state: RegistrationState }
onDevicesChange { devices: Device[] }
onLinkStateChange { deviceId: string, linkState: LinkState }
onStreamStateChange { state: StreamSessionState }
onVideoFrame { timestamp, width, height, isCompressed? }
onPhotoCaptured { filePath, format, timestamp, width?, height?, base64? }
onStreamError StreamSessionError (discriminated union)
onPermissionStatusChange { permission: Permission, status: PermissionStatus }
onCompatibilityChange { deviceId: string, compatibility: Compatibility }
onDeviceSessionStateChange { sessionId: string, state: DeviceSessionState }
onDeviceSessionError { sessionId: string, error: DeviceSessionErrorCode, message?: string }
onCapabilityStateChange { sessionId: string, state: CapabilityState }

EMWDATStreamView

Native view component for rendering the camera stream.

Prop Type Default Description
isActive boolean false Whether to render frames
resizeMode "contain" | "cover" | "stretch" "contain" How frames fit the view
style ViewStyle Standard React Native style

Types

Key types exported from the package:

  • LogLevel"debug" | "info" | "warn" | "error" | "none"
  • RegistrationState"unavailable" | "available" | "registering" | "registered"
  • Permission"camera"
  • PermissionStatus"granted" | "denied"
  • Device{ identifier, name, linkState, deviceType, compatibility }
  • DeviceType"rayBanMeta" | "oakleyMetaHSTN" | "oakleyMetaVanguard" | "metaRayBanDisplay" | "rayBanMetaOptics" | "unknown"
  • LinkState"connected" | "disconnected" | "connecting"
  • Compatibility"compatible" | "undefined" | "deviceUpdateRequired" | "sdkUpdateRequired"
  • DeviceSessionState"idle" | "starting" | "started" | "paused" | "stopping" | "stopped"
  • DeviceSessionErrorCode"noEligibleDevice" | "sessionAlreadyStopped" | "sessionAlreadyExists" | "sessionIdle" | "capabilityAlreadyActive" | "capabilityNotFound" | "unexpectedError"
  • CapabilityState"active" | "stopped"
  • StreamSessionConfig{ videoCodec, resolution, frameRate, deviceId?, compressVideo?, skipAppLaunch? }
  • StreamSessionState"stopped" | "waitingForDevice" | "starting" | "streaming" | "paused" | "stopping"
  • StreamSessionError — Discriminated union: internalError | deviceNotFound | deviceNotConnected | timeout | videoStreamingError | permissionDenied | hingesClosed | thermalCritical
  • PhotoData{ filePath, format, timestamp, width?, height?, base64? }
  • PhotoCaptureFormat"jpeg" | "heic"
  • VideoFrameMetadata{ timestamp, width, height, isCompressed? }
  • StreamingResolution"high" | "medium" | "low"
  • VideoCodec"raw" | "hvc1"
  • CameraFacing"front" | "back"
  • MockDeviceKitConfig{ initiallyRegistered?, initialPermissionsGranted? }
  • CaptureError"deviceDisconnected" | "notStreaming" | "captureInProgress" | "captureFailed"
  • StreamViewResizeMode"contain" | "cover" | "stretch"
  • EMWDATPluginProps — Config plugin options
  • Error code types: WearablesErrorCode, RegistrationErrorCode, UnregistrationErrorCode, PermissionErrorCode, DecoderError

See src/EMWDAT.types.ts for the full list.

Mock Device API (Testing)

Functions for simulating Meta Wearables devices during development using the SDK's mock device framework. Only available in debug builds.

import {
  // Kit lifecycle
  enableMockDeviceKit,
  disableMockDeviceKit,
  isMockDeviceKitEnabled,
  // Device pairing
  pairMockDevice,
  unpairMockDevice,
  getMockDevices,
  // Device simulation
  mockDevicePowerOn,
  mockDevicePowerOff,
  mockDeviceDon,
  mockDeviceDoff,
  mockDeviceFold,
  mockDeviceUnfold,
  mockDeviceSetCameraFeed,
  mockDeviceSetCapturedImage,
  mockDeviceSetCameraFeedFromCamera,
  // Permission mocking
  mockSetPermissionStatus,
  mockSetPermissionRequestResult,
} from "expo-meta-wearables-dat";
Function Signature Description
enableMockDeviceKit (config?: MockDeviceKitConfig) => Promise<void> Enable mock kit with optional config
disableMockDeviceKit () => Promise<void> Disable mock kit and remove fakes
isMockDeviceKitEnabled () => Promise<boolean> Check if mock kit is enabled
pairMockDevice () => Promise<string> Pair a mock Ray-Ban Meta, returns ID
unpairMockDevice (id: string) => Promise<void> Unpair a mock device
getMockDevices () => Promise<string[]> List active mock device IDs
mockDevicePowerOn (id: string) => Promise<void> Power on
mockDevicePowerOff (id: string) => Promise<void> Power off
mockDeviceDon (id: string) => Promise<void> Simulate putting glasses on
mockDeviceDoff (id: string) => Promise<void> Simulate taking glasses off
mockDeviceFold (id: string) => Promise<void> Fold hinges
mockDeviceUnfold (id: string) => Promise<void> Unfold hinges
mockDeviceSetCameraFeed (id: string, fileUrl: string) => Promise<void> Set camera feed from local video file
mockDeviceSetCapturedImage (id: string, fileUrl: string) => Promise<void> Set captured image from local file
mockDeviceSetCameraFeedFromCamera (id: string, facing: CameraFacing) => Promise<void> Use phone camera as mock feed
mockSetPermissionStatus (permission, status) => Promise<void> Set mock permission check result
mockSetPermissionRequestResult (permission, result) => Promise<void> Set mock permission request result

Example App

The example/ directory contains a full demo app:

  1. Copy the example credentials and fill in your values:

    cd example

    Edit app.json and replace the placeholders:

    • YOUR_APPLE_TEAM_ID — your Apple Developer Team ID
    • YOUR_META_APP_ID — from the Meta Wearables Developer Center
    • YOUR_CLIENT_TOKEN — from the same Developer Center page
  2. Build and run:

    npx expo prebuild --clean
    npx expo run:ios --device
    # or
    npx expo run:android --device

Requires a physical device with a paired Meta Wearables device.

Upgrading to 1.2.0 (SDK 0.6)

1.2.0 migrates to Meta Wearables DAT SDK 0.6, introducing a session-based streaming model.

Deprecated (removed):

  • startStream(config?) — use createSession()startSession(id)addStreamToSession(id, config)
  • stopStream() — use stopSession(sessionId)
  • getStreamState() — observe stream state via onStreamStateChange event
  • streamState and lastError from hook return — use events and deviceSessionStates/deviceSessionErrors
  • SessionState type — replaced by DeviceSessionState
  • createMockDevice() / removeMockDevice() — use enableMockDeviceKit() + pairMockDevice() / unpairMockDevice()

Added:

  • Session management: createSession, startSession, stopSession, addStreamToSession, removeStreamFromSession
  • DeviceSessionState, DeviceSessionErrorCode, CapabilityState types
  • compressVideo and skipAppLaunch in StreamSessionConfig
  • isCompressed in VideoFrameMetadata
  • rayBanMetaOptics device type
  • Mock device kit lifecycle: enableMockDeviceKit, disableMockDeviceKit, isMockDeviceKitEnabled
  • Mock permissions: mockSetPermissionStatus, mockSetPermissionRequestResult
  • Mock phone camera: mockDeviceSetCameraFeedFromCamera with CameraFacing type
  • New events: onDeviceSessionError, onCapabilityStateChange

Troubleshooting

Pod install fails / autolinking skips EMWDAT

Ensure iOS deployment target is 16.0. The config plugin sets this automatically, but if you ran expo prebuild --clean, check that ios/Podfile.properties.json contains:

{ "ios.deploymentTarget": "16.0" }

MWDATCamera / MWDATCore framework not found at runtime

The config plugin adds a build phase to embed these dynamic frameworks. Run npx expo prebuild --clean to regenerate the Xcode project.

Registration opens Meta AI app but callback doesn't return

Verify your urlScheme matches the one registered in the Meta Wearables Developer Center, and that CFBundleURLTypes in Info.plist contains it. The config plugin handles this, but double-check after prebuild.

Stream starts but no video frames

Ensure the glasses hinges are open and the device is connected (linkState: "connected"). Check onStreamError for hingesClosed or deviceNotConnected errors.

expo prebuild --clean breaks the build

This wipes Podfile.properties.json. Re-run prebuild (the config plugin will re-inject the deployment target) and then pod install.

Android: Mock device stream shows no frames

The mock video feed must be HEVC (H.265) encoded. The SDK requests video/hevc mime type and rejects H.264 (AVC) videos. Resolution does not matter — only the codec.

Privacy & Data

  • The library does not store, persist, or log personally identifiable information
  • No network requests are made beyond what the Meta Wearables DAT SDK itself performs
  • Debug logging is disabled by default (logLevel: "info") — logs stay on the device console
  • Photos are saved to a local file path and never uploaded by the library
  • Video frames are rendered on-device and not transmitted or stored

See SECURITY.md for the vulnerability reporting process.

Roadmap

  • Background streaming (pending SDK support)
  • New Architecture validation

License

MIT