# Direct bluetooth device connection

{% hint style="warning" %}
Please note that **ThryveBLE** module is in beta; ReactNative and Flutter support will follow.
{% endhint %}

With Thryve, you can connect blood glucose monitors and heart rate monitors directly via Bluetooth Low Energy (BLE). The ThryveBLE module abstracts the complexity of Bluetooth integration by providing:

* **Effortless connectivity:** Automatically handles permissions, device discovery, connections, and communication.
* **Automated data access:** Retrieves health data from connected devices and uploads it to the Thryve data warehouse, ensuring your application always has up-to-date metrics.
* **Comprehensive device handling:** Manages device interactions end-to-end, from connection stability to reliable data acquisition.

<figure><img src="/files/DoOtviIJT3BLn8pOuEXL" alt="" width="188"><figcaption></figcaption></figure>

This guide explains how to let users connect and disconnect devices and includes UI integration examples. For a best-practice implementation, refer to the Thryve Sample App.

{% hint style="info" %}
The module is compatible with [Blood Glucose Monitors](https://www.bluetooth.com/specifications/specs/glp-1-0-1/) and [Heart Rate Monitors](https://www.bluetooth.com/specifications/specs/heart-rate-profile-1-0/) that adhere to open Bluetooth device profiles. Devices from manufacturers that do not follow Bluetooth standards may not function properly.
{% endhint %}

<details>

<summary>Verified Blood Glucose Monitors</summary>

Correct functionality has been verified for the following devices:

* Roche ACCU-CHEK Instant
* Roche ACCU-CHEK Guide
* FORA 6 Connect
* Contour Care

</details>

<details>

<summary>Verified Heart Rate Monitors</summary>

Correct functionality has been verified for the following devices:

* Coros Heart Rate Monitor
* Garmin HRM-Dual
* Polar H7 Heart Rate Sensor
* Polar H9 Heart Rate Sensor
* Polar H10 Heart Rate Sensor
* Suunto Smart Heart Rate Belt
* Whoop 4.0 (requires [enabled Heart Rate Broadcast](https://support.whoop.com/s/article/Heart-Rate-Broadcast?language=en_US))
* Whoop 5.0 (requires [enabled Heart Rate Broadcast](https://support.whoop.com/s/article/Heart-Rate-Broadcast?language=en_US))

</details>

## Integrate Thryve BLE with your application

To be able to  integrate `ThryveBLE` module you need to:

{% tabs %}
{% tab title="iOS" %}

1. **Integrate the `ThryveBLE` module**\
   Make sure to also add `ThryveCore` and `ThryveCommons` modules. Follow either the [cocoapods](/integrate-your-mobile-app/setup-thryve-sdk/ios.md#integration-via-cocoapods) or [framework files](/integrate-your-mobile-app/setup-thryve-sdk/ios.md#integration-via-frameworks) instructions in the [iOS section](/integrate-your-mobile-app/setup-thryve-sdk/ios.md).&#x20;

2. **Add Bluetooth Capability**\
   In Xcode: Navigate to *Target → Signing & Capabilities → add (+) → Bluetooth*.

   <figure><img src="/files/6H5mGS9Ge6itxrjos28H" alt=""><figcaption></figcaption></figure>

3. **Add `Privacy - Bluetooth Always Usage Description` to Info.plist**\
   `<key>NSBluetoothAlwaysUsageDescription</key>`

   `<string>Bluetooth permission for Thryve</string>`
   {% endtab %}

{% tab title="Android" %}

1. **Integrate the `thryve_module_ble` module**\
   Make sure to also add `thryve_core_sdk` and `thryve_module_commons` modules. Follow the instructions in either  the [Thryve Repository Dependencies ](/integrate-your-mobile-app/setup-thryve-sdk/android.md#integrate-using-thryve-repository-dependencies)or  [Thryve .aar Libraries](https://docs.thryve.health/integrate-your-mobile-app/pages/gd15Xn3eNpfQhy8TrHU5#integrate-using-thryve-.aar-libraries) in the [Android section](https://open.gitbook.com/~site/site_WRtC1/mobile-apps/setup-thryve-sdk/android)
2. **Add the following `BLUETOOTH` permissions to the `AndroidManifest.xml`**

   ```
   <uses-permission android:name="android.permission.BLUETOOTH" />
   <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
   <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
   <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
   ```
3. **Add the `ACCESS_FINE_LOCATION` permission to `AndroidManifest.xml`**\
   This is required for all applications targeting Android devices running below Android 12

   ```
   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
   ```

{% endtab %}
{% endtabs %}

### Configure the `ThryveBLE` module

When initializing Thryve SDK make sure to define the `deviceTypes` and an optional `ThryveBLEEventListener` in the `ThryveBLEConfig` .

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons
import ThryveObservability
import ThryveBLE

let bleConfig = ThryveBLEConfig(
    //set the BLE device types. It defaults to all ThryveBLEDeviceTypes
    deviceTypes: Set<ThryveBLEDeviceType> = Set(ThryveBLEDeviceType.allCases),
    eventsListener: self // Optional: Receives callbacks for device found/connected/disconnected/data `ThryveBLEEventListener` protocol and implements `onDeviceFound(response: ThryveResponse<ThryveBLEDevice>)`, `onDeviceConnecting(response: ThryveResponse<ThryveBLEDevice>)`, `onDeviceConnected(response: ThryveResponse<ThryveBLEDevice>)`, onDeviceDisconnected(response: ThryveResponse<ThryveBLEDevice>) and, `onHeartRateDataReceived(device: ThryveBLEDevice, response: ThryveResponse<ThryveBLEHeartRateMeasurement>)` functions
)

let thryveSDKConfig = ThryveSDKConfig(
    authId: "AUTH_ID",
    authSecret: "AUTH_SECRET",
    endUserAlias: "YOUR_UNIQUE_USER_IDENTIFIER",
    endUserId: nil, 
    locale: "de",
    configs: [bleConfig],
    observability: ObservabilityConfig(tracingEnabled: true, crashReportingEnabled: true)
)
        
let thryveSDK = await ThryveSDK.getOrCreate(thryveSDKConfig)

// Optionally, when ThryveSDK is initialized from a non-async context (AppDelegate, legacy code, etc.), it is recommended to use the getOrCreate(...) callback to know when the SDK is ready.

ThryveSDK.getOrCreate(thryveSDKConfig) { initResult in
    if initResult.successful {
        Logger.i("ThryveSDK ready, you can start calling API methods")
    } else if let error = initResult.errors?.first {
        Logger.e("ThryveSDK init failed: \(error.errorMessage ?? "Unknown error")")
    }
} 
```

{% endtab %}

{% tab title="Android" %}

```kotlin
 val bleConfig = ThryveBLEConfig(
    deviceTypes = ThryveBLEDeviceType.entries.toList(),
    eventsListener = object : ThryveBLEEventListener {
            override fun onDeviceFound(response: ThryveResponse<ThryveBLEDevice>) {}
            override fun onDeviceConnecting(response: ThryveResponse<ThryveBLEDevice>) {}
            override fun onDeviceConnected(response: ThryveResponse<ThryveBLEDevice>) {}
            override fun onDeviceDisconnected(response: ThryveResponse<ThryveBLEDevice>) {}
            override fun onHeartRateDataReceived(response: ThryveResponse<ThryveBLEGattData.Measurement>) {}
        }
 )
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="150.45184326171875">Parameter</th><th width="419.3529052734375">Description</th><th>Mandatory</th></tr></thead><tbody><tr><td><code>deviceTypes</code></td><td>Limits the BLE device types the host application supports (e.g. heart rate monitor). Only devices matching the <code>ThryveBLEDeviceType</code>  will appear in discovery.</td><td>no</td></tr><tr><td><code>eventsListener</code></td><td><p>An object of <code>ThryveBLEEventListener</code> that will have to implement  the following functions to receive events from the ThryveBLE  module.</p><pre><code>onDeviceFound(response: ThryveResponse&#x3C;ThryveBLEDevice>) 
onDeviceConnecting(response: ThryveResponse&#x3C;ThryveBLEDevice>) 
onDeviceConnected(response: ThryveResponse&#x3C;ThryveBLEDevice>)
onDeviceDisconnected(response: ThryveResponse&#x3C;ThryveBLEDevice>) 
onHeartRateDataReceived(device: ThryveBLEDevice, response: ThryveResponse&#x3C;ThryveBLEHeartRateMeasurement>)
</code></pre></td><td>no</td></tr></tbody></table>

## Allow users to connect to devices

To enable Bluetooth connectivity for users, implement the following features:

* Display a list of nearby devices available for connection.
* Allow users to connect to and disconnect from devices.
* Provide connection status and troubleshooting information.

Using `ThryveBLE`, these functionalities can be achieved quickly with just a few functions to expedite your initial integration.

### Start device discovery

To discover nearby BLE devices, initiate `startDeviceDiscovery`. Handle any found devices using `onDeviceFound` in the `ThryveBLEEventListener`. The discovery will end when `stopDeviceDiscovery` is called or the discovery process timed out as per configuration.

The Thryve SDK only returns Bluetooth devices that match the configured Bluetooth Device Profiles, automatically filtering out unsupported devices.

{% hint style="info" %}
Be aware that devices may appear compatible if manufacturers advertise open Bluetooth Device Profiles in device discovery, yet choose proprietary integration methods for connection and data access.
{% endhint %}

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons
import ThryveBLE

class BLEViewModel: ObservableObject, ThryveBLEEventListener {
---
    func startScan() {
        Task { @MainActor in
               let response = await ThryveSDK.get().startDeviceDiscovery()
                guard response.successful else {
                  // Process all [ThryveErrors] in response.errors
                  return 
                }
                
                //returns a list of ThryveBLEDevice found
                let listOfThryveBLEDevices = response.data  
          }
    }
    
    //onDeviceFound method is a ThryveBLEEventListener event listener callback.
    //It is automatically triggered by ThryveSDK each time a ThryveBLEDevice is found 
    //during the scanning session in ThryveSDK.get().startDeviceDiscovery()
    func onDeviceFound(response: ThryveResponse<ThryveBLEDevice>) {
            Task { @MainActor in
                guard let bleDevice = response.data else {
                    // Process all [ThryveErrors] in response.errors
                    return
                }
                //process the ThryveBLEDevice found in the startDeviceDiscovery process
                //example update UI with the bleDevice object 
            }
    }
    
    //Call stopDeviceDiscovery to stop the BLE discovery or scanning process
    func stopScan() {
      Task { @MainActor in
          let response = await ThryveSDK.get().stopDeviceDiscovery()
        }
    }
---
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble

...
fun startScan(activity: ComponentActivity) {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().startDeviceDiscovery(activity) { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
             
   //returns a list of ThryveBLEDevice found
   val listOfThryveBLEDevices = response.data               
}

 //onDeviceFound method is a ThryveBLEEventListener event listener callback.
 //It is automatically triggered by ThryveSDK each time a ThryveBLEDevice is found 
 //during the scanning session in ThryveSDK.get().startDeviceDiscovery(activity)
override fun onDeviceFound(response: ThryveResponse<ThryveBLEDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    
    val bleDevice = response.data
    //process the ThryveBLEDevice found in the startDeviceDiscovery process
    //example update UI with the bleDevice object 
}

//Call stopDeviceDiscovery to stop the BLE discovery or scanning process
fun stopScan() {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().stopDeviceDiscovery { response ->
            continuation.resume(response)
        }
    }
                
    if (response.successful) {
      // device discovery process has stopped.
    }else{
      // Process all [ThryveErrors] in response.errors
    } 
}
...
```

{% endtab %}
{% endtabs %}

**Recommendation:**

Manage the scanning process carefully. Avoid continuous scanning or high scanning timeouts when no devices are available or users opt not to connect a device. Continuous scanning can significantly drain the smartphone's battery, leading to user frustration and potential app uninstallation.

### Connect to BLE device

To enable users to connect to a BLE device upon a button click, utilize the `connectDevice` method from the `ThryveBLE`. Handle the connection status through the `ThryveBLEEventListener` interface, using the `onDeviceConnecting` and `onDeviceConnected` callback methods for processing connection events effectively.

<figure><img src="/files/BDMlHxv5M58hamqGl51X" alt="Showing Bluetooth Device Pairing Process" width="188"><figcaption><p>Pairing process with Roche ACCU-CHEK Guide</p></figcaption></figure>

Once connected, all available data is automatically retrieved and uploaded to the Thryve backend. For broadcasting devices like heart rate monitors, [heart rate data is accessible locally in real-time](/integrate-your-mobile-app/real-time-heart-rate-data.md) and is automatically uploaded every 5 minutes, as long as the connection remains active.

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons
import ThryveBLE

class BLEViewModel: ObservableObject, ThryveBLEEventListener {
---
    func connectDevice() {
        Task { @MainActor in
               let response: ThryveResponse<Bool> = await ThryveSDK.get().connectDevice(device.id)
                guard response.successful else {
                  // Process all [ThryveErrors] in response.errors
                  return 
                }
                
                //process the connection status in the response object 
          }
    }
    
    //onDeviceConnecting method is a ThryveBLEEventListener event listener callback.
    //It is automatically triggered by ThryveSDK each time a ThryveBLEDevice is connecting 
    //during the execution of ThryveSDK.get().connectDevice
    func onDeviceConnecting(response: ThryveResponse<ThryveBLEDevice>) {
            guard response.successful else {
                  // Process all [ThryveErrors] in response.errors
                  return 
                }
                
                //process response.data. example update UI
    }
    
    //onDeviceConnected method is a ThryveBLEEventListener event listener callback.
    //It is automatically triggered by ThryveSDK each time a ThryveBLEDevice is connected 
    //during the execution of ThryveSDK.get().connectDevice
    //Data is automatically uploaded when a BLE device is connected.
    func onDeviceConnected(response: ThryveResponse<ThryveBLEDevice>) {
            guard response.successful else {
                  // Process all [ThryveErrors] in response.errors
                  return 
                }
                
                //process response.data. example update UI
    }
---
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble

...
fun connectDevice(activity: ComponentActivity, bleDevice: ThryveBLEDDevice) {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().connectDevice(activity, bleDevice.id) { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
             
   //bleDevice has connected. update application UI          
}

 //onDeviceConnecting method is a ThryveBLEEventListener event listener callback.
 //It is automatically triggered by ThryveSDK each time a ThryveBLEDevice is connecting 
 //during the execution of ThryveSDK.get().connectDevice
 override fun onDeviceConnecting(response: ThryveResponse<ThryveBLEDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    
    //ThryveBLEDevice is connecting.
}

 //onDeviceConnected method is a ThryveBLEEventListener event listener callback.
 //It is automatically triggered by ThryveSDK each time a ThryveBLEDevice is connected 
 //during the execution of ThryveSDK.get().connectDevice
 //Data is automatically uploaded when a BLE device is connected.
 override fun onDeviceConnected(response: ThryveResponse<ThryveBLEDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    //ThryveBLEDevice is connected.process response.data. example update UI
}

...
```

{% endtab %}
{% endtabs %}

#### Disconnect BLE device

To enable users to manually disconnect their BLE device, use `disconnectDevice`. Handle the disconnection status with the `ThryveBLEEventListener`'s `onDeviceDisconnected` callback.

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons
import ThryveBLE

class BLEViewModel: ObservableObject, ThryveBLEEventListener {
---
    func disconnectDevice(device: ThryveBLEDevice) {
        Task { @MainActor in
               let response = await ThryveSDK.get().disconnectDevice(device.id)
                guard response.successful else {
                  // Process all [ThryveErrors] in response.errors
                  return 
                }
                
                //process the disconnection boolean result in response.data
          }
    }
    
    //onDeviceDisconnected method is a ThryveBLEEventListener event listener callback.
    //It is automatically triggered by ThryveSDK when a ThryveBLEDevice is disconnected 
    func onDeviceDisconnected(response: ThryveResponse<ThryveBLEDevice>) {
            Task { @MainActor in
                guard response.successful else {
                  // Process all [ThryveErrors] in response.errors
                  return 
                }
                //process the ThryveBLEDevice that was disconnected. response.data
            }
    }
---
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble

...
fun onDeviceDisconnected(activity: ComponentActivity, bleDevice: ThryveBLEDDevice) {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().disconnectDevice(activity, bleDevice.id) { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
             
   //bleDevice has disconnected. update application UI          
}

//onDeviceDisconnected method is a ThryveBLEEventListener event listener callback.
//It is automatically triggered by ThryveSDK when a ThryveBLEDevice is disconnected
override fun onDeviceDisconnected(response: ThryveResponse<ThryveBLEDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    //ThryveBLEDevice is disconnected.process response.data. example update UI
}
...
```

{% endtab %}
{% endtabs %}

## Show device connection status

We recommend to transparently display users actively connected devices as well as known, previously connected devices and allow them to re-connect those.

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons
import ThryveBLE

class BLEViewModel: ObservableObject, ThryveBLEEventListener {
---
    func getActiveBLEDevices() {
        Task { @MainActor in
               let activedDevices = await ThryveSDK.get().getActiveDevices() 
                //Process all active or connected [ThryveBLEDevice] in activeDevices
          }
    }
    
    func getPreviousBLEDevices() {
        Task { @MainActor in
               let previousDevices = await ThryveSDK.get().getPreviousDevices()
                //Process all previous [ThryveBLEDevice] in previousDevices
          }
    }
---
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble
...

fun getActiveBLEDevices() {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().getActiveDevices { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
    //Process all active or connected [ThryveBLEDevice] in response.data         
}

fun getPreviousBLEDevices() {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().getPreviousDevices { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
    //Process all previous [ThryveBLEDevice] in response.data         
}

...

```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thryve.health/integrate-your-mobile-app/direct-bluetooth-device-connection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
