Bluetooth Behavior

Native Android ==> Bluetooth module

Most supported Bluetooth devices function similarly and you can follow the instructions in Bluetooth permissions, Bluetooth Operations and Passive Read to work with them.

However, a few of our supported devices have additional requirements and/or considerations that you need to be aware of. Those are covered below:

Agamatrix

📘

Approval Required from i-SENS

The Agamatrix Jazz Wireless 2 glucose meter, supported via a custom Bluetooth integration (peripheralID = 32), requires an authentication key, which is provided by Agamatrix, in order to communicate with the meter. To configure your Agamatrix authentication key for use with the Agamatrix meter in the Validic Mobile SDK:

  1. Create a file named agamatrix.license
  2. File contents should be your Agamatrix key without the hex prefix notation 0x. For example, if your key is 0x011234567890987654321 then your file contains 011234567890987654321.
  3. Add the agamatrix.license file in the application's /app/src/main/assets/ folder.

ChoiceMMed Pulse Oximeter model MD300C228

ChoiceMMed sells several pulse oximeter variants under the model MD300C228. While they appear identical, they broadcast different Bluetooth names and use different BLE communication protocols. The Validic Inform SDK currently supports only the variants that advertise as iChoice or MD300C228. Any other variants cannot be read using the Validic Inform SDK.

CVS Health Blood Glucose Meter

📘

Approval Required from i-SENS

The CVS Health Advanced Bluetooth Glucose Meter, supported via a custom Bluetooth integration (peripheralID = 33), requires an authentication key, which is provided by Agamatrix, in order to communicate with the meter. To configure your Agamatrix authentication key for use with the CVS Health meter in the Validic Mobile SDK:

  1. Create a file named cvs_advanced.license
  2. File contents should be your Agamatrix key without the hex prefix notation 0x. For example, if your key is 0x055234567890987654987 then your file contains 055234567890987654987.
  3. Add the cvs_advanced.license file in the application's /app/src/main/assets/ folder.

CVS Health Body Composition Scale

The CVS Health Digital Glass Body Analysis Scale (peripheralID = 39) and Beurer BF600/BF720 (peripheralID = 41) support body weight and body composition metrics including body fat percentage, muscle percentage, body water, BMR, and lean mass (reported from the device's fat-free mass value). Full body composition metrics are Inform-only — v1 only reports body weight, height, and BMI; see User Profile Number below for the flavor breakdown.

During pairing, a user slot is created on the scale. On subsequent pairings, the SDK will reuse the existing user slot if the consent code is still valid, falling back to creating a new user if needed. Historical readings stored on the scale are synced during each read connection.

User Profile Data

For accurate body composition calculations, the scale requires the user's demographics. Store these values against the device address using the public key constants from ValidicBluetooth.kt before pairing or reading:

val peripheral = BluetoothPeripheral.getPeripheralForID(41) // CVS Health Scale
val address = bluetoothDevice.address

// KEY_USER_DATE_OF_BIRTH: ISO date string "YYYY-MM-DD"
peripheral.storePeripheralDataForDevice(address, KEY_USER_DATE_OF_BIRTH, "1990-05-15")

// KEY_USER_GENDER: Int — 0 = Male, 1 = Female, 2 = Unspecified
peripheral.storePeripheralDataForDevice(address, KEY_USER_GENDER, 0)

// KEY_USER_HEIGHT_CM: Int — height in whole centimeters (1–254)
peripheral.storePeripheralDataForDevice(address, KEY_USER_HEIGHT_CM, 175)

// KEY_USER_ACTIVITY_LEVEL: Int — 1 (sedentary) to 5 (very active)
peripheral.storePeripheralDataForDevice(address, KEY_USER_ACTIVITY_LEVEL, 3)

All four fields are optional. Only non-null values are written to the scale.

Profile Sync Behavior

The SDK treats its stored values as the source of truth and syncs them to the scale during both pairing and reading connections:

  • SDK has user data stored: The SDK reads the scale's current profile values and writes only fields that differ, minimizing unnecessary BLE writes. The Database Change Increment (0x2A99) is bumped after any write, per the BLE SIG User Data Service specification.
  • SDK has no user data stored: The SDK never reads the scale's own profile to seed itself — it is always the source of truth. Only body weight is reported; height, BMI, and body composition values are suppressed to prevent inaccurate results, even if the scale already has a customized profile from its own app (Database Change Increment > 0).

Note: If the user modifies their profile via the Beurer or CVS Health app between readings, the SDK detects the change via the Database Change Increment and reasserts its stored values on the next connection.

User Profile Number

The scale itself pairs and reads on both v1 and Inform, but what gets reported from a reading differs by flavor.

Full body composition metrics (body fat, muscle mass, body water, BMR, lean mass — beyond weight, height, and BMI) are emitted in Inform records, and operationMetadata is exposed on PairResult/ReadResult.

The scale assigns the paired user to one of up to 8 on-device profile slots during pairing. On a first measurement — where no prior reading exists for the scale to auto-match within its weight tolerance — the device manual requires the user to explicitly select this profile number on the scale itself using its up/down buttons before stepping on. If the wrong profile is selected, the measurement is either associated with the wrong user or dropped.

The assigned/reused profile number is surfaced on the PairResult returned from pairing via operationMetadata.userDesignation, so host apps can display it to the user immediately after pairing completes:

val pairResult = PairRequest(peripheral, device).enqueue()
val userDesignation = pairResult.operationMetadata.userDesignation // e.g. "4", or null if not applicable

if (userDesignation != null) {
    // Show the user: "On the scale, select profile #4 before stepping on for your first weigh-in."
}

operationMetadata.userDesignation is null for peripherals that don't use device-side user profiles. It
matches the SIG User Index/User ID value the scale reports per-reading, and the user_designation tag on
the resulting Inform record. operationMetadata exposes all device-specific metadata gathered during the
operation and mirrors the operationMetadata parameter on BluetoothPeripheralControllerListener.onSuccess
for listener-based consumers.

Roche CoaguChek INR Meter

📘

Approval Required from Roche

The Roche CoaguChek integration requires pre-approval for Roche's CoaguChek SDK. Please reach out to Validic Support at [email protected] if you are interested in integrating with Roche CoaguChek INR meters.

The Roche CoaguChek Vantus INR meter Meter (peripheralID = 51) requires a license file which is provided by Validic, in order to communicate with the meter. To configure your Validic License for use with the Roche CoaguChek INR meter in the Validic Mobile SDK:

  1. Add validic.license provided via Validic Support in the application's /app/src/main/assets/` folder.
  2. Then in your application before pairing, provide the device encryption key. The key is required to decrypt readings.
  3. Not every Android phone has the necessary encryption alogorithm, so add a dependency on SpongyCastle for support in your application's build.gradle
dependencies {  
  implementation: 'com.madgag.spongycastle:prov:1.58.0.0'  
}

The Roche CoaguChek Vantus INR meter has an uncommon reading workflow which requires that the end user provide their meter's unique encryption key. That encryption key will be used to decrypt the reading values captured from the user's meter. Without a valid encryption key, foreground and passive reads with the INR meter will fail.

Because this workflow is specific to the Roche INR meter, any Validic clients implementing the INR meter Bluetooth integration must build a user experience (modal or screen) to capture the meter's encryption key from the user. This key will then be passed by the client to the Validic SDK where it will be saved and used to decrypt readings. Note: the key is a long string of hexadecimal characters, so it is recommended to implement a QR code reading to capture the code displayed on the meter.

A code sample follows to illustrate what this could look like:

// This example requests the encryption key from inside a function for simplicity.  
// This example uses a AppCompatEditText text field to collect the encryption key from the user. It is recommended to use a QR code reader to reduce the likelihood of errors.

private fun pair() {  
    viewLifecycleOwner.lifecycleScope.launch {  
        try {  
            PairRequest(peripheral, device).enqueue()  
            if (peripheral.peripheralID == 51) { // Add Encryption Key dialog for Roche CoaguChek  
              addButton("Save Encryption Key") {  
                val editText = AppCompatEditText(requireActivity())  
                editText.isAllCaps = true  
                editText.inputType =  
                    InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS  
                dialog =  
                    AlertDialog.Builder(requireActivity())  
                        .setView(editText)  
                        .setTitle("Enter CoaguChek Encryption Key")  
                        .setPositiveButton("Ok") { _, _ ->  
                          val value = editText.text?.toString()  
                          value?.let {  
                            if (value.isNotEmpty()) {  
                              peripheral.storePeripheralDataForDevice(  
                                  device.address,  
                                  KEY_ENCRYPTION_KEY,  
                                  editText.text?.toString()?.filter { it.isLetterOrDigit() }  
                              )  
                            }  
                            dialog = null  
                          }  
                        }  
                        .setNegativeButton("Cancel") { d, _ ->  
                          d?.cancel()  
                          dialog = null  
                        }  
                        .show()  
              }  
            }
      
} catch (e: ValidicBluetoothException) {
      dialog?.dismiss()
    }
}
}

Welch Allyn Series 1500/1700

The Welch Allyn series 1500 blood pressure cuffs sometimes fail to pair on Android 10 phones. Several retries may be needed to receive a successful bond. The behavior is improved on Android 11+ and also in the Welch Allyn series 1700 but it's not perfect.

The Validic Mobile SDK already attempts to communicate with the bp cuff more than once to help prevent an error, however, you should plan to guide the user through retrying if an error does occur.

Passive Read Considerations

There's some specific behavior you need to be aware of related to passive read. Please review Passive Read for more details.


Did this page help you?