npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

insta-360-expo-module

v0.1.2

Published

Expo plugin used for insta 360 android and ios sdk

Readme

Insta360 Expo Module

A comprehensive Expo module that provides React Native bindings for the Insta360 Camera SDK, enabling seamless integration of Insta360 camera functionality into your Expo/React Native applications.

📋 Table of Contents

✨ Features

This module provides full access to Insta360 camera capabilities:

🔌 Connection Management

  • Bluetooth Low Energy (BLE) - Wireless connection with automatic device scanning
  • WiFi Connection - Direct WiFi connectivity
  • USB Connection - Wired connection support

📷 Camera Operations

  • Photo Capture - Standard photo capture with various modes
  • Video Recording - High-quality video recording
  • HDR Capture - High Dynamic Range photography
  • Timelapse - Time-lapse video recording
  • Interval Shooting - Automatic interval-based photo capture
  • Bullet Time - 360° bullet time effect

ℹ️ Camera Information

  • Real-time battery level monitoring
  • Storage state and SD card status
  • Camera serial number and type
  • Supported capture modes
  • Connection status

🎥 Preview & Live Streaming

  • Real-time camera preview streaming
  • Preview status monitoring

📁 File Management

  • List files on camera
  • Delete files from camera
  • File metadata (size, duration, resolution)

🔔 Real-time Events

  • Connection state changes
  • Battery level updates
  • Capture progress
  • Preview status
  • Storage changes
  • BLE device discovery

📦 Installation

npm install insta-360-expo-module

or

yarn add insta-360-expo-module

🛠 Platform Setup

Android

1. Add Insta360 SDK to your project

Add the Insta360 SDK repository to your android/build.gradle:

allprojects {
    repositories {
        // ... other repositories
        maven {
            url 'https://github.com/Insta360Develop/CameraSDK-Android-Release/raw/master/'
        }
    }
}

2. Add SDK dependencies

In your android/app/build.gradle:

dependencies {
    // Insta360 SDK
    implementation 'com.arashivision.sdk:sdkcamera:1.x.x'
    implementation 'com.arashivision.sdk:sdkmedia:1.x.x'

    // Required dependencies
    implementation 'com.github.Jasonchenlijian:FastBle:2.4.0'
}

3. Add required permissions

Add the following permissions to your AndroidManifest.xml:

<!-- Bluetooth permissions for Android 12+ (API 31+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- Bluetooth permissions for older Android versions -->
<uses-permission android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />

<!-- Location permission required for BLE scanning -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- WiFi and Network -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<!-- Storage -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- Camera -->
<uses-permission android:name="android.permission.CAMERA" />

4. Request runtime permissions

For Android 6.0+, request permissions at runtime:

import { PermissionsAndroid, Platform } from "react-native";

const requestBlePermissions = async () => {
  if (Platform.OS !== "android") return true;

  const apiLevel = Platform.Version;

  if (apiLevel >= 31) {
    // Android 12+
    const granted = await PermissionsAndroid.requestMultiple([
      PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
      PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
      PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
    ]);

    return (
      granted["android.permission.BLUETOOTH_SCAN"] === "granted" &&
      granted["android.permission.BLUETOOTH_CONNECT"] === "granted" &&
      granted["android.permission.ACCESS_FINE_LOCATION"] === "granted"
    );
  } else {
    // Android 11 and below
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
    );
    return granted === "granted";
  }
};

iOS

1. Add Insta360 SDK

Add the Insta360 SDK to your ios/Podfile:

pod 'INSCameraSDK', '~> 1.x.x'

2. Add required permissions

Add the following to your Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app needs camera access to connect to Insta360 cameras</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to save captured media</string>

<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs Bluetooth to connect to Insta360 cameras</string>

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth to connect to Insta360 cameras</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access for Bluetooth scanning</string>

3. Install pods

cd ios && pod install

🚀 Quick Start

import React, { useEffect, useState } from 'react';
import * as Insta360 from 'insta-360-expo-module';

function App() {
  const [isConnected, setIsConnected] = useState(false);

  useEffect(() => {
    // Initialize SDK
    const initSDK = async () => {
      try {
        await Insta360.initializeSDK();
        console.log('SDK initialized');
      } catch (error) {
        console.error('Failed to initialize SDK:', error);
      }
    };

    initSDK();

    // Listen for connection events
    const subscription = Insta360.addCameraConnectedListener((event) => {
      console.log('Camera connected:', event.type);
      setIsConnected(true);
    });

    return () => subscription.remove();
  }, []);

  const scanAndConnect = async () => {
    try {
      // Start BLE scan
      await Insta360.startBleScan();

      // Listen for found devices
      const deviceSubscription = Insta360.addBleDeviceFoundListener((device) => {
        console.log('Found device:', device.name);
        // Connect to the first device found
        Insta360.connectBle(device.mac);
      });
    } catch (error) {
      console.error('Scan error:', error);
    }
  };

  const capturePhoto = async () => {
    if (!isConnected) return;

    try {
      await Insta360.capturePhoto();
      console.log('Photo capture initiated');
    } catch (error) {
      console.error('Capture error:', error);
    }
  };

  return (
    // Your UI here
  );
}

📖 API Reference

SDK Initialization

isSDKInitialized(): boolean

Check if the SDK is initialized.

const isInit = Insta360.isSDKInitialized();

initializeSDK(): Promise<{ success: boolean }>

Initialize the Insta360 SDK. This should be called before any other operations.

await Insta360.initializeSDK();

Connection Methods

checkBlePermissions(): boolean

Check if Bluetooth permissions are granted.

const hasPermissions = Insta360.checkBlePermissions();

isBluetoothEnabled(): boolean

Check if Bluetooth is enabled on the device.

const isEnabled = Insta360.isBluetoothEnabled();

startBleScan(): Promise<{ success: boolean }>

Start scanning for nearby Insta360 cameras via BLE.

await Insta360.startBleScan();

Prerequisites:

  • Bluetooth must be enabled
  • Location permissions must be granted (Android)
  • BLUETOOTH_SCAN permission (Android 12+)

stopBleScan(): Promise<{ success: boolean }>

Stop the BLE scan.

await Insta360.stopBleScan();

connectBle(deviceMac: string): Promise<{ success: boolean; deviceName?: string }>

Connect to a camera via Bluetooth using its MAC address.

await Insta360.connectBle("AA:BB:CC:DD:EE:FF");

connectWiFi(): Promise<{ success: boolean; cameraType?: string }>

Connect to a camera via WiFi. The device must be connected to the camera's WiFi network.

await Insta360.connectWiFi();

connectUSB(): Promise<{ success: boolean }>

Connect to a camera via USB.

await Insta360.connectUSB();

disconnectCamera(): Promise<{ success: boolean }>

Disconnect from the currently connected camera.

await Insta360.disconnectCamera();

isConnected(): boolean

Check if a camera is currently connected.

const connected = Insta360.isConnected();

getConnectionType(): ConnectionType

Get the current connection type.

type ConnectionType = "none" | "ble" | "wifi" | "usb" | "unknown";

const connType = Insta360.getConnectionType();

getCameraType(): string

Get the connected camera model/type.

const cameraType = Insta360.getCameraType();
// e.g., "ONE X2", "ONE RS", etc.

Camera Information

fetchCameraOptions(): Promise<CameraOptions>

Fetch comprehensive camera information from the connected device.

interface CameraOptions {
  success: boolean;
  batteryLevel: number; // 0-100
  storageState: number; // 0 = no card, 1 = card available
  mediaTime: number; // Available recording time in seconds
}

const options = await Insta360.fetchCameraOptions();

getBatteryLevel(): number

Get current battery level (0-100).

const battery = Insta360.getBatteryLevel();

getStorageState(): number

Get SD card state (0 = not available, 1 = available).

const storage = Insta360.getStorageState();

isBatteryCharging(): boolean

Check if the camera is currently charging.

const isCharging = Insta360.isBatteryCharging();

isSdCardEnabled(): boolean

Check if an SD card is inserted and enabled.

const hasCard = Insta360.isSdCardEnabled();

getSerialNumber(): string

Get the camera's serial number.

const serial = Insta360.getSerialNumber();

getSupportedCaptureModes(): CaptureMode[]

Get all capture modes supported by the connected camera.

type CaptureMode =
  | "CAPTURE_NORMAL" // Standard photo
  | "RECORD_NORMAL" // Standard video
  | "HDR_CAPTURE" // HDR photo
  | "HDR_RECORD" // HDR video
  | "INTERVAL_SHOOTING" // Interval photos
  | "TIMELAPSE" // Timelapse video
  | "BULLETTIME" // Bullet time
  | "TIME_SHIFT" // TimeShift
  | "LOOPER_RECORDING" // Loop recording
  | "SUPER_RECORD" // Super resolution
  | "SLOW_MOTION" // Slow motion
  | "SELFIE_RECORD" // Selfie mode
  | "PURE_RECORD" // Pure shot
  | "BURST" // Burst mode
  | "NIGHT_SCENE" // Night mode
  | "STARLAPSE_SHOOTING"; // Star lapse

const modes = Insta360.getSupportedCaptureModes();

getCurrentCaptureMode(): string

Get the currently active capture mode.

const mode = Insta360.getCurrentCaptureMode();

Preview Operations

startPreview(): Promise<{ success: boolean }>

Start the camera preview stream.

await Insta360.startPreview();

stopPreview(): Promise<{ success: boolean }>

Stop the camera preview stream.

await Insta360.stopPreview();

isPreviewStreaming(): boolean

Check if preview is currently streaming.

const streaming = Insta360.isPreviewStreaming();

Capture Operations

capturePhoto(): Promise<{ success: boolean }>

Capture a photo with current settings.

await Insta360.capturePhoto();

Listen to onCaptureFinished event to get the file paths.

startRecording(): Promise<{ success: boolean }>

Start video recording.

await Insta360.startRecording();

stopRecording(): Promise<{ success: boolean }>

Stop video recording.

await Insta360.stopRecording();

isRecording(): boolean

Check if camera is currently recording.

const recording = Insta360.isRecording();

Special Capture Modes

captureHDR(): Promise<{ success: boolean }>

Capture an HDR photo.

await Insta360.captureHDR();

startTimelapse(): Promise<{ success: boolean }>

Start timelapse recording.

await Insta360.startTimelapse();

stopTimelapse(): Promise<{ success: boolean }>

Stop timelapse recording.

await Insta360.stopTimelapse();

startIntervalShooting(): Promise<{ success: boolean }>

Start interval shooting (captures photos at set intervals).

await Insta360.startIntervalShooting();

stopIntervalShooting(): Promise<{ success: boolean }>

Stop interval shooting.

await Insta360.stopIntervalShooting();

startBulletTime(): Promise<{ success: boolean }>

Start bullet time recording.

await Insta360.startBulletTime();

stopBulletTime(): Promise<{ success: boolean }>

Stop bullet time recording.

await Insta360.stopBulletTime();

File Management

getCameraFileList(): Promise<{ files: CameraFile[] }>

Get list of all files on the camera.

interface CameraFile {
  name: string; // File name
  uri: string; // File URI
  isVideo: boolean; // true = video, false = photo
  width: number; // Resolution width
  height: number; // Resolution height
  duration: number; // Duration in ms (video only)
  createTime: number; // Creation timestamp
  size: number; // File size in bytes
  urls: string[]; // Array of file URLs
}

const { files } = await Insta360.getCameraFileList();

deleteCameraFile(fileUrls: string[]): Promise<{ success: boolean }>

Delete files from the camera.

await Insta360.deleteCameraFile(["file://path/to/file1.jpg"]);

🔔 Event Listeners

All event listeners return an EventSubscription object with a remove() method for cleanup.

Connection Events

addCameraConnectedListener(listener: (event: CameraConnectedEvent) => void)

Fired when camera successfully connects.

interface CameraConnectedEvent {
  type: ConnectionType; // 'ble' | 'wifi' | 'usb'
}

const subscription = Insta360.addCameraConnectedListener((event) => {
  console.log(`Camera connected via ${event.type}`);
});

// Cleanup
subscription.remove();

addCameraDisconnectedListener(listener: (event: CameraDisconnectedEvent) => void)

Fired when camera disconnects.

interface CameraDisconnectedEvent {
  type?: string;
  error?: boolean;
  errorCode?: number;
}

const subscription = Insta360.addCameraDisconnectedListener((event) => {
  console.log("Camera disconnected", event);
});

BLE Scan Events

addBleScanStartListener(listener: (event: BleScanStartEvent) => void)

Fired when BLE scan starts.

interface BleScanStartEvent {
  status: "started";
}

const subscription = Insta360.addBleScanStartListener((event) => {
  console.log("BLE scan started");
});

addBleScanFailedListener(listener: (event: BleScanFailedEvent) => void)

Fired when BLE scan fails to start.

interface BleScanFailedEvent {
  status: "failed";
}

addBleDeviceFoundListener(listener: (event: BleDeviceFoundEvent) => void)

Fired when a BLE device is discovered during scanning.

interface BleDeviceFoundEvent {
  name: string; // Device name
  mac: string; // MAC address
  rssi: number; // Signal strength
}

const subscription = Insta360.addBleDeviceFoundListener((device) => {
  console.log(`Found device: ${device.name} (${device.mac})`);
});

addBleScanFinishedListener(listener: (event: BleScanFinishedEvent) => void)

Fired when BLE scan completes.

interface BleScanFinishedEvent {
  devices: BleDevice[];
}

Preview Events

addPreviewOpenedListener(listener: (event: PreviewOpenedEvent) => void)

Fired when preview stream opens successfully.

const subscription = Insta360.addPreviewOpenedListener((event) => {
  console.log("Preview opened");
});

addPreviewClosedListener(listener: (event: PreviewClosedEvent) => void)

Fired when preview stream closes.

const subscription = Insta360.addPreviewClosedListener((event) => {
  console.log("Preview closed");
});

addPreviewErrorListener(listener: (event: PreviewErrorEvent) => void)

Fired when preview encounters an error.

const subscription = Insta360.addPreviewErrorListener((event) => {
  console.error("Preview error:", event.error);
});

Capture Events

addCaptureStartingListener(listener: (event: CaptureStartingEvent) => void)

Fired when capture is starting.

const subscription = Insta360.addCaptureStartingListener((event) => {
  console.log("Capture starting...");
});

addCaptureWorkingListener(listener: (event: CaptureWorkingEvent) => void)

Fired when capture is in progress.

const subscription = Insta360.addCaptureWorkingListener((event) => {
  console.log("Capture in progress");
});

addCaptureStoppingListener(listener: (event: CaptureStoppingEvent) => void)

Fired when capture is stopping.

const subscription = Insta360.addCaptureStoppingListener((event) => {
  console.log("Capture stopping...");
});

addCaptureFinishedListener(listener: (event: CaptureFinishedEvent) => void)

Fired when capture completes successfully.

interface CaptureFinishedEvent {
  status: "finished";
  files: string[]; // Array of captured file paths
}

const subscription = Insta360.addCaptureFinishedListener((event) => {
  console.log(`Captured ${event.files.length} file(s)`);
  event.files.forEach((file) => console.log(file));
});

addCaptureErrorListener(listener: (event: CaptureErrorEvent) => void)

Fired when capture fails.

interface CaptureErrorEvent {
  error: boolean;
  errorCode: number;
}

const subscription = Insta360.addCaptureErrorListener((event) => {
  console.error(`Capture error: ${event.errorCode}`);
});

addCaptureTimeChangedListener(listener: (event: CaptureTimeChangedEvent) => void)

Fired periodically during video recording with elapsed time.

interface CaptureTimeChangedEvent {
  time: number; // Elapsed time in milliseconds
}

const subscription = Insta360.addCaptureTimeChangedListener((event) => {
  const seconds = Math.floor(event.time / 1000);
  console.log(`Recording: ${seconds}s`);
});

Camera Status Events

addBatteryLevelChangedListener(listener: (event: BatteryLevelChangedEvent) => void)

Fired when battery level changes.

interface BatteryLevelChangedEvent {
  level: number; // 0-100
  isCharging: boolean;
}

const subscription = Insta360.addBatteryLevelChangedListener((event) => {
  console.log(`Battery: ${event.level}% ${event.isCharging ? "⚡" : "🔋"}`);
});

addStorageStateChangedListener(listener: (event: StorageStateChangedEvent) => void)

Fired when SD card state changes.

interface StorageStateChangedEvent {
  enabled: boolean;
}

const subscription = Insta360.addStorageStateChangedListener((event) => {
  console.log(`SD Card: ${event.enabled ? "Available" : "Not Available"}`);
});

💡 Usage Examples

Complete Connection Flow

import React, { useEffect, useState } from 'react';
import { View, Button, Text, FlatList, Alert } from 'react-native';
import * as Insta360 from 'insta-360-expo-module';

function CameraConnection() {
  const [devices, setDevices] = useState([]);
  const [isScanning, setIsScanning] = useState(false);
  const [isConnected, setIsConnected] = useState(false);

  useEffect(() => {
    // Initialize SDK
    Insta360.initializeSDK();

    // Setup event listeners
    const connectedSub = Insta360.addCameraConnectedListener((event) => {
      Alert.alert('Success', `Connected via ${event.type}`);
      setIsConnected(true);
    });

    const disconnectedSub = Insta360.addCameraDisconnectedListener(() => {
      Alert.alert('Info', 'Camera disconnected');
      setIsConnected(false);
    });

    const deviceFoundSub = Insta360.addBleDeviceFoundListener((device) => {
      setDevices(prev => {
        if (!prev.find(d => d.mac === device.mac)) {
          return [...prev, device];
        }
        return prev;
      });
    });

    const scanFinishedSub = Insta360.addBleScanFinishedListener(() => {
      setIsScanning(false);
    });

    return () => {
      connectedSub.remove();
      disconnectedSub.remove();
      deviceFoundSub.remove();
      scanFinishedSub.remove();
    };
  }, []);

  const startScan = async () => {
    try {
      setDevices([]);
      setIsScanning(true);
      await Insta360.startBleScan();
    } catch (error) {
      setIsScanning(false);
      Alert.alert('Error', error.message);
    }
  };

  const connectToDevice = async (mac) => {
    try {
      await Insta360.stopBleScan();
      await Insta360.connectBle(mac);
    } catch (error) {
      Alert.alert('Error', error.message);
    }
  };

  return (
    <View>
      <Button
        title={isScanning ? "Scanning..." : "Scan for Cameras"}
        onPress={startScan}
        disabled={isScanning || isConnected}
      />

      <FlatList
        data={devices}
        keyExtractor={(item) => item.mac}
        renderItem={({ item }) => (
          <Button
            title={`${item.name} (${item.rssi}dB)`}
            onPress={() => connectToDevice(item.mac)}
          />
        )}
      />

      {isConnected && (
        <Button
          title="Disconnect"
          onPress={() => Insta360.disconnectCamera()}
        />
      )}
    </View>
  );
}

Photo Capture with Progress

function PhotoCapture() {
  const [isCapturing, setIsCapturing] = useState(false);

  useEffect(() => {
    const startingSub = Insta360.addCaptureStartingListener(() => {
      setIsCapturing(true);
    });

    const finishedSub = Insta360.addCaptureFinishedListener((event) => {
      setIsCapturing(false);
      Alert.alert('Success', `Photo saved: ${event.files[0]}`);
    });

    const errorSub = Insta360.addCaptureErrorListener((event) => {
      setIsCapturing(false);
      Alert.alert('Error', `Capture failed: ${event.errorCode}`);
    });

    return () => {
      startingSub.remove();
      finishedSub.remove();
      errorSub.remove();
    };
  }, []);

  const takePhoto = async () => {
    try {
      await Insta360.capturePhoto();
    } catch (error) {
      Alert.alert('Error', error.message);
    }
  };

  return (
    <View>
      <Button
        title={isCapturing ? "Capturing..." : "Take Photo"}
        onPress={takePhoto}
        disabled={isCapturing}
      />
    </View>
  );
}

Video Recording with Timer

function VideoRecording() {
  const [isRecording, setIsRecording] = useState(false);
  const [recordingTime, setRecordingTime] = useState(0);

  useEffect(() => {
    const workingSub = Insta360.addCaptureWorkingListener(() => {
      setIsRecording(true);
    });

    const finishedSub = Insta360.addCaptureFinishedListener((event) => {
      setIsRecording(false);
      setRecordingTime(0);
      Alert.alert('Success', `Video saved: ${event.files[0]}`);
    });

    const timeSub = Insta360.addCaptureTimeChangedListener((event) => {
      setRecordingTime(Math.floor(event.time / 1000));
    });

    return () => {
      workingSub.remove();
      finishedSub.remove();
      timeSub.remove();
    };
  }, []);

  const toggleRecording = async () => {
    try {
      if (isRecording) {
        await Insta360.stopRecording();
      } else {
        await Insta360.startRecording();
      }
    } catch (error) {
      Alert.alert('Error', error.message);
    }
  };

  return (
    <View>
      <Text>{recordingTime}s</Text>
      <Button
        title={isRecording ? "Stop Recording" : "Start Recording"}
        onPress={toggleRecording}
      />
    </View>
  );
}

Battery Monitor

function BatteryMonitor() {
  const [battery, setBattery] = useState(0);
  const [isCharging, setIsCharging] = useState(false);

  useEffect(() => {
    const batterySub = Insta360.addBatteryLevelChangedListener((event) => {
      setBattery(event.level);
      setIsCharging(event.isCharging);
    });

    // Initial fetch
    const fetchBattery = async () => {
      if (Insta360.isConnected()) {
        const level = Insta360.getBatteryLevel();
        const charging = Insta360.isBatteryCharging();
        setBattery(level);
        setIsCharging(charging);
      }
    };
    fetchBattery();

    return () => batterySub.remove();
  }, []);

  return (
    <View>
      <Text>
        Battery: {battery}% {isCharging ? '⚡ Charging' : '🔋'}
      </Text>
    </View>
  );
}

🔧 Troubleshooting

Android

BLE Scan Fails to Start

Problem: startBleScan() fails with permission errors.

Solutions:

  1. Ensure Bluetooth is enabled on the device
  2. Check that all required permissions are granted:
    • Android 12+: BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION
    • Android 11-: ACCESS_FINE_LOCATION
  3. Request permissions at runtime before scanning
  4. Enable location services (required for BLE scanning on Android)

Camera Not Found During Scan

Problem: BLE scan completes but no devices found.

Solutions:

  1. Ensure camera is powered on and in pairing mode
  2. Check if camera is already connected to another device
  3. Verify camera is within Bluetooth range (~10 meters)
  4. Reset camera's Bluetooth by turning it off and on

Connection Fails After Scan

Problem: connectBle() fails even though device was found.

Solutions:

  1. Stop the scan before connecting: await Insta360.stopBleScan()
  2. Ensure only one connection attempt at a time
  3. Check if camera requires pairing in its settings first

Preview Not Starting

Problem: startPreview() succeeds but no preview appears.

Solutions:

  1. This module provides camera control, not preview rendering
  2. You need to implement your own preview view using the camera stream
  3. Use a third-party video player component with the preview stream URL

iOS

Camera Permissions Denied

Problem: Camera operations fail with permission errors.

Solutions:

  1. Ensure Info.plist contains required usage descriptions
  2. Request permissions before accessing camera
  3. Direct users to Settings if permissions were previously denied

Bluetooth Not Working

Problem: BLE operations fail on iOS.

Solutions:

  1. Add Bluetooth usage descriptions to Info.plist
  2. Ensure app has Bluetooth permissions
  3. Check iOS version compatibility (iOS 13+ recommended)

General Issues

SDK Initialization Fails

Problem: initializeSDK() returns error.

Solutions:

  1. Verify Insta360 SDK is properly installed
  2. Check that SDK version is compatible with your camera model
  3. Ensure application context is available (Android)
  4. Restart the app if SDK state is corrupted

Camera Disconnects Randomly

Problem: Camera disconnects unexpectedly during operation.

Solutions:

  1. Check camera battery level
  2. Verify stable Bluetooth/WiFi connection
  3. Keep device within connection range
  4. Disable battery optimization for your app (Android)
  5. Implement reconnection logic with event listeners

File Operations Fail

Problem: getCameraFileList() or file deletion fails.

Solutions:

  1. Ensure camera is connected
  2. Check SD card is inserted and formatted correctly
  3. Verify storage permissions are granted
  4. Some cameras may have file access limitations

Memory Issues

Problem: App crashes or runs out of memory.

Solutions:

  1. Clean up event listeners when components unmount
  2. Close preview streams when not needed
  3. Limit number of concurrent operations
  4. Process large file lists in batches

📱 Supported Cameras

This module supports all Insta360 cameras compatible with the Insta360 Camera SDK, including:

  • Insta360 ONE X2
  • Insta360 ONE RS
  • Insta360 ONE R
  • Insta360 ONE X
  • Insta360 ONE
  • Other compatible models

Check your camera's documentation for specific feature support.


🏗️ Architecture

Module Structure

insta-360-expo-module/
├── android/                      # Android native implementation
│   ├── src/main/java/expo/modules/insta360expomodule/
│   │   └── Insta360ExpoModule.kt    # Kotlin SDK wrapper
│   └── build.gradle
├── ios/                          # iOS native implementation
│   ├── Insta360ExpoModule.swift     # Swift SDK wrapper
│   └── Insta360ExpoModule.podspec
├── src/                          # TypeScript/JavaScript API
│   ├── index.ts                     # Main export
│   ├── Insta360ExpoModule.ts        # Module API
│   ├── Insta360ExpoModule.types.ts  # TypeScript definitions
│   └── Insta360ExpoModuleNative.ts  # Native bridge
└── example/                      # Example app
    └── App.tsx                      # Comprehensive demo

Native Implementation (Android)

The Android implementation (Insta360ExpoModule.kt) wraps the Insta360 Camera SDK with the following key features:

SDK Managers

  • InstaCameraManager: Core camera control and connection management
  • InstaMediaSDK: Media processing and file operations
  • WorkUtils: File and media work utilities

Callbacks Implemented

  • ICameraChangedCallback: Connection state changes
  • IPreviewStatusListener: Preview stream status
  • ICaptureStatusListener: Capture operation status
  • IScanBleListener: BLE device scanning
  • ICameraOperateCallback: Camera operation results

Key Features

  • Coroutine-based async operations
  • Automatic SDK initialization on module creation
  • Comprehensive error handling
  • Real-time event emission to JavaScript
  • Permission checking utilities

iOS Implementation

The iOS implementation provides basic camera functionality through native iOS APIs. For full Insta360 SDK features on iOS, additional integration work may be required.


🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


📄 License

MIT License - see LICENSE file for details.


🔗 Resources


📞 Support

For issues and questions:

  • Open an issue on GitLab
  • Check existing issues for solutions
  • Review the example app for implementation guidance

📝 Changelog

Version 0.1.0

  • Initial release
  • Full Android support with Insta360 Camera SDK
  • Basic iOS support
  • Complete BLE, WiFi, and USB connection support
  • Photo and video capture
  • Special capture modes (HDR, Timelapse, Interval, Bullet Time)
  • File management
  • Real-time event system
  • Comprehensive example app

Made with ❤️ by VT Netzwelt