Pairing via BLE
Pre-requisite: Ensure the app has all necessary runtime permissions to access Bluetooth services.
- Android 11 (API 30) and below: the
ACCESS_FINE_LOCATIONruntime permission is required (the SDK manifest declares the legacyBLUETOOTH/BLUETOOTH_ADMINpermissions up to API 30).- Android 12 (API 31) and above: the
BLUETOOTH_SCANandBLUETOOTH_CONNECTruntime permissions are required.- Bluetooth must be enabled before starting BLE operations.
- A pairing session key is required before the Wi-Fi configuration is sent. See Pairing a Camera.
The app connects to the camera over Bluetooth and writes the Wi-Fi credentials and the pairing session key to it. The camera then joins the network and claims the session.
The call order
The calls below are a sequence, and each step is driven by the callback of the one before it:
release() → registerCallback() → startScan() → collect scanResults → stopScan()
→ connectToDevice() → onConnected() → startServiceDiscovery()
→ onDeviceIdReceived() / onDeviceReady() → sendWifiScanCommand() → collect wifiNetworks
→ createPairingSession() → sendWifiConfig() → onWifiConfigSent()
→ poll getPairingSessionStatus() until PROCESSED
✅ Best Practice: Call
release()beforeregisterCallback()when starting a flow.release()clears every registered callback, so registering first loses the callback.
Start Scanning for BLE Devices
To begin scanning for nearby BLE devices:
InstaVision.bleService.startScan()
Optional: Customize BLE Configuration You can configure BLE scan and connection behavior using BleConfig.
val config = BleConfig(
scanTimeout = 90_000L, // Scan timeout in ms, defaults to 90 seconds
connectionTimeout = 90_000L, // Connection timeout in ms, defaults to 90 seconds
)
InstaVision.bleService.startScan(bleConfig = config)
Observe Scan Results
You can observe the scan results using a Flow:
InstaVision.bleService.scanResults.collectLatest { deviceList ->
// Filter or display device info
}
The SDK applies no scan filter, so the list contains every nearby BLE device. Filter it by device name yourself.
Stop BLE Scanning
BLE scan will automatically stop after the configured timeout (BleConfig.scanTimeout), defaulting to 90 seconds.
To manually stop scanning before timeout:
InstaVision.bleService.stopScan()
✅ Best Practice: Always call
stopScan()before initiating a connection.
Connect to BLE Device
Once you have selected a BluetoothDevice from scan results:
InstaVision.bleService.connectToDevice(device)
You will receive the onConnected() callback if the connection is successful.
Once the device ID is read off the connected device, you’ll receive onDeviceIdReceived(deviceId). You can also read the last known device ID at any time via:
val deviceId = InstaVision.bleService.connectedDID
Not every camera exposes the device ID characteristic. When it does not, only onDeviceReady() fires and connectedDID stays null.
Start Service Discovery
After connecting, initiate service discovery:
InstaVision.bleService.startServiceDiscovery()
This will trigger the BLE device to discover services and characteristics. Once completed, you’ll receive:
BleCallback.onDeviceReady()
Check Available Wifi (Optional)
After the device is connected and services are discovered, you can send a command to initiate a WiFi scan:
InstaVision.bleService.sendWifiScanCommand()
This triggers the BLE device to start scanning for available WiFi networks.
You can collect the list of available networks using:
InstaVision.bleService.wifiNetworks.collectLatest { networks ->
networks.forEach { network ->
Log.d("BLE", "SSID: ${network.ssid}")
}
}
The list updates as new networks are discovered by the BLE device.
Send WiFi Configuration
To update WiFi credentials the device needs to connect to, use:
InstaVision.bleService.sendWifiConfig(
ssid = "YourNetwork",
password = "YourPassword",
sessionKey = "session_key",
region = ServerRegions.US,
env = Environment.RELEASE
)
sessionKey is the PairingSession.sessionKey created earlier — see Pairing a Camera.
Upon success, the callback onWifiConfigSent() will be invoked — and the SDK then calls release() itself, clearing registered callbacks, the GATT connection and the scan / Wi-Fi flows.
Poll getPairingSessionStatus from here to learn whether the camera actually joined the network and claimed the session.
Release BLE Resources
The SDK releases everything automatically after onWifiConfigSent(). Call release() yourself only when abandoning a session early or restarting one:
InstaVision.bleService.release()
This ensures clean disconnection, cancels any timers, clears callbacks, and closes GATT connections.
Registering BLE Callback
To receive BLE state updates and responses, register your callback:
val bleCallback = object: BleCallback() {
override fun onConnected() {
Log.d("BLE", "Connected to BLE device")
}
override fun onDeviceReady() {
Log.d("BLE", "Device is ready")
}
override fun onDeviceIdReceived(deviceId: String) {
Log.d("BLE", "Device id received: $deviceId")
}
override fun onWifiConfigSent() {
Log.d("BLE", "WiFi config successfully sent")
}
override fun onDeviceConnectionFailed() {
Log.e("BLE", "Connection failed")
}
override fun onScanStopped() {
Log.e("BLE", "Ble scanning stopped")
}
}
InstaVision.bleService.registerCallback(bleCallback)
onDeviceConnectionFailed() is the single error channel for the whole flow — connection timeouts, GATT disconnects, failed service discovery and missing characteristics all arrive through it, with no error payload.
✅ Best Practice: Track whether the Wi-Fi configuration has already been sent, and ignore
onDeviceConnectionFailed()after that point. BecausesendWifiConfigreleases the connection on success, the disconnect that follows a successful write would otherwise be reported as a failure.
Unregistering BLE Callback
Once you no longer need BLE updates (for example, when the pairing screen is destroyed), unregister the callback to avoid leaks:
InstaVision.bleService.unregisterCallback(bleCallback)