├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── layout │ │ │ └── activity_main.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── drawable │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ └── com │ │ │ └── welie │ │ │ └── blessedexample │ │ │ ├── SensorContactFeature.kt │ │ │ ├── TemperatureType.kt │ │ │ ├── ObservationUnit.kt │ │ │ ├── BloodPressureMeasurementStatus.kt │ │ │ ├── TemperatureMeasurement.kt │ │ │ ├── WeightMeasurement.kt │ │ │ ├── GlucoseMeasurement.kt │ │ │ ├── HeartRateMeasurement.kt │ │ │ ├── PulseOximeterSpotMeasurement.kt │ │ │ ├── BloodPressureMeasurement.kt │ │ │ └── PulseOximeterContinuousMeasurement.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── blessed ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── welie │ │ │ └── blessed │ │ │ ├── Phy.kt │ │ │ ├── ReadResponse.kt │ │ │ ├── GattException.kt │ │ │ ├── ConnectionFailedException.kt │ │ │ ├── PhyOptions.kt │ │ │ ├── ConnectionState.kt │ │ │ ├── WriteType.kt │ │ │ ├── PhyType.kt │ │ │ ├── ConnectionPriority.kt │ │ │ ├── PeripheralType.kt │ │ │ ├── ScanMode.kt │ │ │ ├── BondState.kt │ │ │ ├── AdvertiseError.kt │ │ │ ├── ScanFailure.kt │ │ │ ├── Extensions.kt │ │ │ ├── BluetoothCentral.kt │ │ │ ├── BluetoothCentralManagerCallback.kt │ │ │ ├── Logger.kt │ │ │ ├── GattStatus.kt │ │ │ └── BluetoothPeripheralCallback.kt │ └── test │ │ └── java │ │ └── com │ │ └── welie │ │ └── blessed │ │ └── BluetoothBytesParserTest.kt ├── proguard-rules.pro └── build.gradle ├── docs ├── package-list ├── script.js ├── allclasses-noframe.html ├── index.html ├── com │ └── welie │ │ └── blessed │ │ ├── package-frame.html │ │ └── package-tree.html ├── allclasses-frame.html ├── deprecated-list.html ├── index-files │ ├── index-14.html │ ├── index-6.html │ ├── index-9.html │ ├── index-8.html │ ├── index-11.html │ ├── index-7.html │ ├── index-1.html │ └── index-16.html ├── overview-tree.html ├── constant-values.html └── help-doc.html ├── jitpack.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .github └── workflows │ └── gradle.yml ├── LICENSE ├── .gitignore ├── gradle.properties ├── gradlew.bat └── SERVER.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /blessed/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /docs/package-list: -------------------------------------------------------------------------------- 1 | com.welie.blessed 2 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk17 3 | -------------------------------------------------------------------------------- /blessed/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | blessed 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BlessedExample 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weliem/blessed-android-coroutines/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | 16 | include ':app', ':blessed' 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/SensorContactFeature.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | /** 4 | * Enum that contains all sensor contact feature as specified here: 5 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml 6 | */ 7 | enum class SensorContactFeature { 8 | NotSupported, SupportedNoContact, SupportedAndContact 9 | } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/TemperatureType.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | enum class TemperatureType(val value: Int) { 4 | Unknown(0), Armpit(1), Body(2), Ear(3), Finger(4), GastroIntestinalTract(5), Mouth(6), Rectum(7), Toe(8), Tympanum(9); 5 | 6 | companion object { 7 | fun fromValue(value: Int): TemperatureType { 8 | for (type in values()) { 9 | if (type.value == value) return type 10 | } 11 | return Unknown 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/ObservationUnit.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | enum class ObservationUnit(val notation: String, val mdc: String) { 4 | BeatsPerMinute("bpm", "MDC_DIM_BEAT_PER_MIN"), 5 | Celsius("\u00B0C", "MDC_DIM_DEGC"), 6 | Fahrenheit("\u00B0F", "MDC_DIM_FAHR"), 7 | Inches("inch", "MDC_DIM_INCH"), 8 | Kilograms("Kg", "MDC_DIM_KILO_G"), 9 | KgM2("kg/m2", "MDC_DIM_KG_PER_M_SQ"), 10 | KPA("kPa", "MDC_DIM_KILO_PASCAL"), 11 | Meters("m", "MDC_DIM_M"), 12 | MiligramPerDeciliter("mg/dL", "MDC_DIM_MILLI_G_PER_DL"), 13 | MmolPerLiter("mmol/L", "MDC_DIM_MILLI_MOLE_PER_L"), 14 | MMHG("mmHg", "MDC_DIM_MMHG"), 15 | Percent("%", "MDC_DIM_PERCENT"), 16 | Pounds("lbs", "MDC_DIM_LB"), 17 | } -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Android Build 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Java 17 20 | uses: actions/setup-java@v2 21 | with: 22 | distribution: "temurin" 23 | java-version: 17 24 | - name: Grant execute permission for gradlew 25 | run: chmod +x gradlew 26 | - name: Build with Gradle 27 | run: ./gradlew build 28 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /blessed/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /docs/script.js: -------------------------------------------------------------------------------- 1 | function show(type) 2 | { 3 | count = 0; 4 | for (var key in methods) { 5 | var row = document.getElementById(key); 6 | if ((methods[key] & type) != 0) { 7 | row.style.display = ''; 8 | row.className = (count++ % 2) ? rowColor : altColor; 9 | } 10 | else 11 | row.style.display = 'none'; 12 | } 13 | updateTabs(type); 14 | } 15 | 16 | function updateTabs(type) 17 | { 18 | for (var value in tabs) { 19 | var sNode = document.getElementById(tabs[value][0]); 20 | var spanNode = sNode.firstChild; 21 | if (value == type) { 22 | sNode.className = activeTableTab; 23 | spanNode.innerHTML = tabs[value][1]; 24 | } 25 | else { 26 | sNode.className = tableTab; 27 | spanNode.innerHTML = "" + tabs[value][1] + ""; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/BloodPressureMeasurementStatus.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | class BloodPressureMeasurementStatus internal constructor(measurementStatus: Int) { 4 | /** 5 | * Body Movement Detected 6 | */ 7 | val isBodyMovementDetected: Boolean 8 | /** 9 | * Cuff is too loose 10 | */ 11 | val isCuffTooLoose: Boolean 12 | /** 13 | * Irregular pulse detected 14 | */ 15 | val isIrregularPulseDetected: Boolean 16 | /** 17 | * Pulse is not in normal range 18 | */ 19 | val isPulseNotInRange: Boolean 20 | /** 21 | * Improper measurement position 22 | */ 23 | val isImproperMeasurementPosition: Boolean 24 | 25 | init { 26 | isBodyMovementDetected = measurementStatus and 0x0001 > 0 27 | isCuffTooLoose = measurementStatus and 0x0002 > 0 28 | isIrregularPulseDetected = measurementStatus and 0x0004 > 0 29 | isPulseNotInRange = measurementStatus and 0x0008 > 0 30 | isImproperMeasurementPosition = measurementStatus and 0x0020 > 0 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Martijn van Welie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /blessed/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/ 38 | 39 | # Keystore files 40 | # Uncomment the following line if you do not want to check your keystore files in. 41 | #*.jks 42 | 43 | # External native build folder generated in Android Studio 2.2 and later 44 | .externalNativeBuild 45 | 46 | # Google Services (e.g. APIs or Firebase) 47 | google-services.json 48 | 49 | # Freeline 50 | freeline.py 51 | freeline/ 52 | freeline_project_description.json 53 | 54 | # fastlane 55 | fastlane/report.xml 56 | fastlane/Preview.html 57 | fastlane/screenshots 58 | fastlane/test_output 59 | fastlane/readme.md 60 | 61 | .classpath 62 | .project 63 | **/org.eclipse.** 64 | -------------------------------------------------------------------------------- /blessed/src/test/java/com/welie/blessed/BluetoothBytesParserTest.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessed 2 | 3 | import android.content.Context 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_FLOAT 5 | import io.mockk.* 6 | import org.junit.* 7 | import org.junit.Assert.* 8 | import java.nio.ByteOrder 9 | 10 | 11 | class BluetoothBytesParserTest { 12 | 13 | @Test 14 | fun first_test() { 15 | val byteParser = BluetoothBytesParser(byteArrayOf(0xFF.toByte(), 0x00, 0x01, 0x6c), ByteOrder.BIG_ENDIAN) 16 | assertEquals(byteParser.getFloatValue(FORMAT_FLOAT), 36.4f) 17 | } 18 | 19 | @Test 20 | fun second_test() { 21 | var parser = BluetoothBytesParser(ByteOrder.LITTLE_ENDIAN) 22 | parser.setFloatValue(364, -1, FORMAT_FLOAT, 0) 23 | parser.offset = 0 24 | assertEquals(36.4f, parser.getFloatValue(FORMAT_FLOAT)) 25 | 26 | parser = BluetoothBytesParser(ByteOrder.LITTLE_ENDIAN) 27 | parser.setFloatValue(5.3f, 1) 28 | parser.setFloatValue(36.86f, 2) 29 | 30 | parser.offset = 0 31 | assertEquals(5.3f, parser.getFloatValue(FORMAT_FLOAT)) 32 | assertEquals(36.86f, parser.getFloatValue(FORMAT_FLOAT)) 33 | } 34 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/Phy.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | data class Phy(val tx: PhyType, val rx: PhyType) -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/ReadResponse.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | class ReadResponse(val status: GattStatus, val value: ByteArray) -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/GattException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | class GattException(val status: GattStatus) : RuntimeException("GATT error $status (${status.value})") -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/ConnectionFailedException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import java.lang.RuntimeException 26 | 27 | class ConnectionFailedException(val status: HciStatus) : RuntimeException("connection failed: $status (${status.value})") -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | id 'com.android.application' 5 | id 'org.jetbrains.kotlin.android' 6 | } 7 | 8 | android { 9 | namespace 'com.welie.blessedexample' 10 | 11 | compileSdk = 36 12 | 13 | defaultConfig { 14 | minSdk = 26 15 | targetSdk = 36 16 | versionCode = 1 17 | versionName "1.0" 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | kotlin { 28 | compilerOptions { 29 | jvmTarget = JvmTarget.JVM_1_8 30 | } 31 | } 32 | 33 | compileOptions { 34 | sourceCompatibility JavaVersion.VERSION_1_8 35 | targetCompatibility JavaVersion.VERSION_1_8 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(include: ['*.jar'], dir: 'libs') 41 | implementation 'androidx.appcompat:appcompat:1.7.1' 42 | implementation 'androidx.constraintlayout:constraintlayout:2.2.1' 43 | implementation 'com.jakewharton.timber:timber:5.0.1' 44 | 45 | implementation "androidx.core:core-ktx:1.17.0" 46 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2" 47 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2" 48 | 49 | 50 | implementation project(':blessed') 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/TemperatureMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_FLOAT 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 6 | import java.nio.ByteOrder 7 | import java.util.* 8 | 9 | data class TemperatureMeasurement( 10 | val temperatureValue: Float, 11 | val unit: ObservationUnit, 12 | val timestamp: Date?, 13 | val type: TemperatureType, 14 | val createdAt: Date = Calendar.getInstance().time 15 | ) { 16 | companion object { 17 | fun fromBytes(value: ByteArray): TemperatureMeasurement { 18 | val parser = BluetoothBytesParser(value, ByteOrder.LITTLE_ENDIAN) 19 | val flags = parser.getIntValue(FORMAT_UINT8) 20 | val unit = if (flags and 0x01 > 0) ObservationUnit.Fahrenheit else ObservationUnit.Celsius 21 | val timestampPresent = flags and 0x02 > 0 22 | val typePresent = flags and 0x04 > 0 23 | 24 | val temperatureValue = parser.getFloatValue(FORMAT_FLOAT) 25 | val timestamp = if (timestampPresent) parser.dateTime else null 26 | val type = if (typePresent) TemperatureType.fromValue(parser.getIntValue(FORMAT_UINT8)) else TemperatureType.Unknown 27 | 28 | return TemperatureMeasurement( 29 | unit = unit, 30 | temperatureValue = temperatureValue, 31 | timestamp = timestamp, 32 | type = type 33 | ) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 11 | 14 | 15 | 16 | 17 | 20 | 23 | 24 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/PhyOptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | /** 26 | * This class represents the possible Phy options 27 | */ 28 | enum class PhyOptions(val value: Int) { 29 | /** 30 | * No preferred option. Use this value in combination with PHY_LE_1M and PHY_LE_2M 31 | */ 32 | NO_PREFERRED(0), 33 | 34 | /** 35 | * Prefer 2x range option with throughput of +/- 500 Kbps 36 | */ 37 | S2(1), 38 | 39 | /** 40 | * Prefer 4x range option with throughput of +/- 125 Kbps 41 | */ 42 | S8(2); 43 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/ConnectionState.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | /** 26 | * This class represents the possible connection states 27 | */ 28 | enum class ConnectionState(val value: Int) { 29 | /** 30 | * The peripheral is disconnected 31 | */ 32 | DISCONNECTED(0), 33 | 34 | /** 35 | * The peripheral is connecting 36 | */ 37 | CONNECTING(1), 38 | 39 | /** 40 | * The peripheral is connected 41 | */ 42 | CONNECTED(2), 43 | 44 | /** 45 | * The peripheral is disconnecting 46 | */ 47 | DISCONNECTING(3); 48 | 49 | companion object { 50 | fun fromValue(value: Int): ConnectionState { 51 | for (type in values()) { 52 | if (type.value == value) { 53 | return type 54 | } 55 | } 56 | return DISCONNECTED 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/WeightMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT16 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 6 | import com.welie.blessedexample.ObservationUnit.Kilograms 7 | import com.welie.blessedexample.ObservationUnit.Pounds 8 | import java.util.* 9 | import kotlin.math.round 10 | 11 | data class WeightMeasurement( 12 | val weight: Float, 13 | val unit: ObservationUnit, 14 | val timestamp: Date?, 15 | val userID: Int?, 16 | val bmi: Float?, 17 | val heightInMetersOrInches: Float?, 18 | val createdAt: Date = Calendar.getInstance().time 19 | ) { 20 | companion object { 21 | fun fromBytes(value: ByteArray): WeightMeasurement { 22 | val parser = BluetoothBytesParser(value) 23 | val flags = parser.getIntValue(FORMAT_UINT8) 24 | val unit = if (flags and 0x01 > 0) Pounds else Kilograms 25 | val timestampPresent = flags and 0x02 > 0 26 | val userIDPresent = flags and 0x04 > 0 27 | val bmiAndHeightPresent = flags and 0x08 > 0 28 | 29 | val weightMultiplier = if (unit == Kilograms) 0.005f else 0.01f 30 | val weight = parser.getIntValue(FORMAT_UINT16) * weightMultiplier 31 | val timestamp = if (timestampPresent) parser.dateTime else null 32 | val userID = if (userIDPresent) parser.getIntValue(FORMAT_UINT8) else null 33 | val bmi = if (bmiAndHeightPresent) parser.getIntValue(FORMAT_UINT16) * 0.1f else null 34 | val heightMultiplier = if (unit == Kilograms) 0.001f else 0.1f 35 | val height = if (bmiAndHeightPresent) parser.getIntValue(FORMAT_UINT16) * heightMultiplier else null 36 | 37 | return WeightMeasurement( 38 | weight = round(weight * 100) / 100, 39 | unit = unit, 40 | timestamp = timestamp, 41 | userID = userID, 42 | bmi = bmi, 43 | heightInMetersOrInches = height 44 | ) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /blessed/build.gradle: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | id 'com.android.library' 5 | id 'maven-publish' 6 | id 'org.jetbrains.kotlin.android' 7 | } 8 | 9 | android { 10 | namespace 'com.welie.blessed' 11 | compileSdk = 36 12 | 13 | defaultConfig { 14 | minSdk = 26 15 | targetSdk = 36 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlin { 31 | compilerOptions { 32 | jvmTarget = JvmTarget.JVM_1_8 33 | } 34 | } 35 | testOptions { 36 | unitTests { 37 | includeAndroidResources = true 38 | } 39 | } 40 | publishing { 41 | singleVariant('release') { 42 | withSourcesJar() 43 | } 44 | } 45 | } 46 | 47 | dependencies { 48 | implementation "androidx.core:core-ktx:1.17.0" 49 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2" 50 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2" 51 | implementation 'com.jakewharton.timber:timber:5.0.1' 52 | 53 | testImplementation 'junit:junit:4.13.2' 54 | testImplementation "org.robolectric:robolectric:4.16" 55 | testImplementation "org.mockito:mockito-core:5.20.0" 56 | testImplementation 'androidx.test:core:1.7.0' 57 | testImplementation "io.mockk:mockk:1.14.6" 58 | } 59 | 60 | afterEvaluate { 61 | publishing { 62 | publications { 63 | // Creates a Maven publication called "release". 64 | release(MavenPublication) { 65 | from components.release 66 | groupId = 'com.github.weliem' 67 | artifactId = 'blessed-android-coroutines' 68 | version = '1.0' 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/WriteType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.BluetoothGattCharacteristic 26 | 27 | /** 28 | * WriteType describes the type of write that can be done 29 | */ 30 | enum class WriteType(val writeType: Int, val property: Int) { 31 | /** 32 | * Write characteristic and requesting acknowledgement by the remote peripheral 33 | */ 34 | WITH_RESPONSE(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, BluetoothGattCharacteristic.PROPERTY_WRITE), 35 | 36 | /** 37 | * Write characteristic without requiring a response by the remote peripheral 38 | */ 39 | WITHOUT_RESPONSE(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE), 40 | 41 | /** 42 | * Write characteristic including authentication signature 43 | */ 44 | SIGNED(BluetoothGattCharacteristic.WRITE_TYPE_SIGNED, BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE); 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/GlucoseMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_SFLOAT 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_SINT16 6 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT16 7 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 8 | import com.welie.blessedexample.ObservationUnit.MiligramPerDeciliter 9 | import com.welie.blessedexample.ObservationUnit.MmolPerLiter 10 | import java.util.* 11 | 12 | data class GlucoseMeasurement( 13 | val value: Float?, 14 | val unit: ObservationUnit, 15 | val timestamp: Date?, 16 | val sequenceNumber: Int, 17 | val contextWillFollow: Boolean, 18 | val createdAt: Date = Calendar.getInstance().time 19 | ) { 20 | companion object { 21 | fun fromBytes(value: ByteArray): GlucoseMeasurement { 22 | val parser = BluetoothBytesParser(value) 23 | val flags: Int = parser.getIntValue(FORMAT_UINT8) 24 | val timeOffsetPresent = flags and 0x01 > 0 25 | val typeAndLocationPresent = flags and 0x02 > 0 26 | val unit = if (flags and 0x04 > 0) MmolPerLiter else MiligramPerDeciliter 27 | val contextWillFollow = flags and 0x10 > 0 28 | 29 | val sequenceNumber = parser.getIntValue(FORMAT_UINT16) 30 | var timestamp = parser.dateTime 31 | if (timeOffsetPresent) { 32 | val timeOffset: Int = parser.getIntValue(FORMAT_SINT16) 33 | timestamp = Date(timestamp.time + timeOffset * 60000) 34 | } 35 | 36 | val multiplier = if (unit === MiligramPerDeciliter) 100000 else 1000 37 | val glucoseValue = if (typeAndLocationPresent) parser.getFloatValue(FORMAT_SFLOAT) * multiplier else null 38 | 39 | return GlucoseMeasurement( 40 | unit = unit, 41 | timestamp = timestamp, 42 | sequenceNumber = sequenceNumber, 43 | value = glucoseValue, 44 | contextWillFollow = contextWillFollow 45 | ) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/PhyType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | /** 26 | * This class represents the possible Phy types 27 | */ 28 | enum class PhyType(val value: Int, val mask: Int) { 29 | /** 30 | * A Physical Layer (PHY) connection of 1 mbit. Compatible with Bluetooth 4.0, 4.1, 4.2 and 5.0 31 | */ 32 | LE_1M(1, 1), 33 | 34 | /** 35 | * A Physical Layer (PHY) connection of 2 mbit. Requires Bluetooth 5 36 | */ 37 | LE_2M(2, 2), 38 | 39 | /** 40 | * A Physical Layer (PHY) connection with long range. Requires Bluetooth 5 41 | */ 42 | LE_CODED(3, 4), 43 | 44 | /** 45 | * Unknown Phy Type. Not to be used. 46 | */ 47 | UNKNOWN_PHY_TYPE(-1, -1); 48 | 49 | companion object { 50 | fun fromValue(value: Int): PhyType { 51 | for (type in values()) { 52 | if (type.value == value) return type 53 | } 54 | return UNKNOWN_PHY_TYPE 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/ConnectionPriority.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.BluetoothGatt 26 | 27 | enum class ConnectionPriority(val value: Int) { 28 | /** 29 | * Use the connection parameters recommended by the Bluetooth SIG. 30 | * This is the default value if no connection parameter update 31 | * is requested. 32 | */ 33 | BALANCED(BluetoothGatt.CONNECTION_PRIORITY_BALANCED), 34 | 35 | /** 36 | * Request a high priority, low latency connection. 37 | * An application should only request high priority connection parameters to transfer large 38 | * amounts of data over LE quickly. Once the transfer is complete, the application should 39 | * request BALANCED connection parameters to reduce energy use. 40 | */ 41 | HIGH(BluetoothGatt.CONNECTION_PRIORITY_HIGH), 42 | 43 | /** 44 | * Request low power, reduced data rate connection parameters. 45 | */ 46 | LOW_POWER(BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER); 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/HeartRateMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT16 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 6 | import java.nio.ByteOrder 7 | import java.util.* 8 | 9 | data class HeartRateMeasurement( 10 | val pulse: Int, 11 | val energyExpended: Int?, 12 | val rrIntervals: IntArray, 13 | val sensorContactStatus: SensorContactFeature, 14 | val createdAt: Date = Calendar.getInstance().time 15 | ) { 16 | companion object { 17 | fun fromBytes(value: ByteArray): HeartRateMeasurement { 18 | val parser = BluetoothBytesParser(value, ByteOrder.LITTLE_ENDIAN) 19 | val flags = parser.getIntValue(FORMAT_UINT8) 20 | val pulse = if (flags and 0x01 == 0) parser.getIntValue(FORMAT_UINT8) else parser.getIntValue(FORMAT_UINT16) 21 | val sensorContactStatusFlag = flags and 0x06 shr 1 22 | val energyExpenditurePresent = flags and 0x08 > 0 23 | val rrIntervalPresent = flags and 0x10 > 0 24 | 25 | val sensorContactStatus = when (sensorContactStatusFlag) { 26 | 0, 1 -> SensorContactFeature.NotSupported 27 | 2 -> SensorContactFeature.SupportedNoContact 28 | 3 -> SensorContactFeature.SupportedAndContact 29 | else -> SensorContactFeature.NotSupported 30 | } 31 | 32 | val energyExpended = if (energyExpenditurePresent) parser.getIntValue(FORMAT_UINT16) else null 33 | 34 | val rrArray = ArrayList() 35 | if (rrIntervalPresent) { 36 | while (parser.offset < value.size) { 37 | val rrInterval = parser.getIntValue(FORMAT_UINT16) 38 | rrArray.add((rrInterval.toDouble() / 1024.0 * 1000.0).toInt()) 39 | } 40 | } 41 | 42 | return HeartRateMeasurement( 43 | pulse = pulse, 44 | energyExpended = energyExpended, 45 | sensorContactStatus = sensorContactStatus, 46 | rrIntervals = rrArray.toIntArray() 47 | ) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/PeripheralType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.BluetoothDevice 26 | 27 | /** 28 | * This class represents the possible peripheral types 29 | */ 30 | enum class PeripheralType(val value: Int) { 31 | /** 32 | * Unknown peripheral type, peripheral is not cached 33 | */ 34 | UNKNOWN(BluetoothDevice.DEVICE_TYPE_UNKNOWN), 35 | 36 | /** 37 | * Classic - BR/EDR peripheral 38 | */ 39 | CLASSIC(BluetoothDevice.DEVICE_TYPE_CLASSIC), 40 | 41 | /** 42 | * Bluetooth Low Energy peripheral 43 | */ 44 | LE(BluetoothDevice.DEVICE_TYPE_LE), 45 | 46 | /** 47 | * Dual Mode - BR/EDR/LE 48 | */ 49 | DUAL(BluetoothDevice.DEVICE_TYPE_DUAL); 50 | 51 | companion object { 52 | fun fromValue(value: Int): PeripheralType { 53 | for (type in values()) { 54 | if (type.value == value) { 55 | return type 56 | } 57 | } 58 | return UNKNOWN 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/PulseOximeterSpotMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_SFLOAT 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT16 6 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 7 | import java.util.* 8 | 9 | data class PulseOximeterSpotMeasurement( 10 | val spO2: Float, 11 | val pulseRate: Float, 12 | val pulseAmplitudeIndex: Float?, 13 | val timestamp: Date?, 14 | val isDeviceClockSet: Boolean, 15 | val measurementStatus: Int?, 16 | val sensorStatus: Int?, 17 | val createdAt: Date = Calendar.getInstance().time 18 | ) { 19 | companion object { 20 | fun fromBytes(value: ByteArray): PulseOximeterSpotMeasurement { 21 | val parser = BluetoothBytesParser(value) 22 | val flags = parser.getIntValue(FORMAT_UINT8) 23 | val timestampPresent = flags and 0x01 > 0 24 | val measurementStatusPresent = flags and 0x02 > 0 25 | val sensorStatusPresent = flags and 0x04 > 0 26 | val pulseAmplitudeIndexPresent = flags and 0x08 > 0 27 | val isDeviceClockSet = flags and 0x10 == 0 28 | 29 | val spO2 = parser.getFloatValue(FORMAT_SFLOAT) 30 | val pulseRate = parser.getFloatValue(FORMAT_SFLOAT) 31 | val timestamp = if (timestampPresent) parser.dateTime else null 32 | val measurementStatus = if (measurementStatusPresent) parser.getIntValue(FORMAT_UINT16) else null 33 | val sensorStatus = if (sensorStatusPresent) parser.getIntValue(FORMAT_UINT16) else null 34 | if (sensorStatusPresent) parser.getIntValue(FORMAT_UINT8) // Reserved byte 35 | val pulseAmplitudeIndex = if (pulseAmplitudeIndexPresent) parser.getFloatValue(FORMAT_SFLOAT) else null 36 | 37 | return PulseOximeterSpotMeasurement( 38 | spO2 = spO2, 39 | pulseRate = pulseRate, 40 | measurementStatus = measurementStatus, 41 | sensorStatus = sensorStatus, 42 | pulseAmplitudeIndex = pulseAmplitudeIndex, 43 | timestamp = timestamp, 44 | isDeviceClockSet = isDeviceClockSet 45 | ) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/BloodPressureMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_SFLOAT 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT16 6 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 7 | import java.nio.ByteOrder 8 | import java.util.* 9 | 10 | data class BloodPressureMeasurement( 11 | val systolic: Float, 12 | val diastolic: Float, 13 | val meanArterialPressure: Float, 14 | val unit: ObservationUnit, 15 | val timestamp: Date?, 16 | val pulseRate: Float?, 17 | val userID: Int?, 18 | val measurementStatus: BloodPressureMeasurementStatus?, 19 | val createdAt: Date = Calendar.getInstance().time 20 | ) { 21 | companion object { 22 | fun fromBytes(value: ByteArray): BloodPressureMeasurement { 23 | val parser = BluetoothBytesParser(value, ByteOrder.LITTLE_ENDIAN) 24 | val flags = parser.getIntValue(FORMAT_UINT8) 25 | val unit = if (flags and 0x01 > 0) ObservationUnit.KPA else ObservationUnit.MMHG 26 | val timestampPresent = flags and 0x02 > 0 27 | val pulseRatePresent = flags and 0x04 > 0 28 | val userIdPresent = flags and 0x08 > 0 29 | val measurementStatusPresent = flags and 0x10 > 0 30 | 31 | val systolic = parser.getFloatValue(FORMAT_SFLOAT) 32 | val diastolic = parser.getFloatValue(FORMAT_SFLOAT) 33 | val meanArterialPressure = parser.getFloatValue(FORMAT_SFLOAT) 34 | val timestamp = if (timestampPresent) parser.dateTime else null 35 | val pulseRate = if (pulseRatePresent) parser.getFloatValue(FORMAT_SFLOAT) else null 36 | val userID = if (userIdPresent) parser.getIntValue(FORMAT_UINT8) else null 37 | val status = if (measurementStatusPresent) BloodPressureMeasurementStatus(parser.getIntValue(FORMAT_UINT16)) else null 38 | 39 | return BloodPressureMeasurement( 40 | systolic = systolic, 41 | diastolic = diastolic, 42 | meanArterialPressure = meanArterialPressure, 43 | unit = unit, 44 | timestamp = timestamp, 45 | pulseRate = pulseRate, 46 | userID = userID, 47 | measurementStatus = status 48 | ) 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/ScanMode.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.le.ScanSettings 26 | 27 | /** 28 | * This class represents the possible scan modes 29 | */ 30 | enum class ScanMode(val value: Int) { 31 | /** 32 | * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for 33 | * other scan results without starting BLE scans themselves. 34 | */ 35 | OPPORTUNISTIC(-1), 36 | 37 | /** 38 | * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that 39 | * provides a good trade-off between scan frequency and power consumption. 40 | */ 41 | BALANCED(ScanSettings.SCAN_MODE_BALANCED), 42 | 43 | /** 44 | * Scan using highest duty cycle. It's recommended to only use this mode when the application is 45 | * running in the foreground. 46 | */ 47 | LOW_LATENCY(ScanSettings.SCAN_MODE_LOW_LATENCY), 48 | 49 | /** 50 | * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the 51 | * least power. This mode is enforced if the scanning application is not in foreground. 52 | */ 53 | LOW_POWER(ScanSettings.SCAN_MODE_LOW_POWER); 54 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/BondState.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.BluetoothDevice 26 | 27 | /** 28 | * The class represents the various possible bond states 29 | */ 30 | enum class BondState(val value: Int) { 31 | /** 32 | * Indicates the remote peripheral is not bonded. 33 | * There is no shared link key with the remote peripheral, so communication 34 | * (if it is allowed at all) will be unauthenticated and unencrypted. 35 | */ 36 | NONE(BluetoothDevice.BOND_NONE), 37 | 38 | /** 39 | * Indicates bonding is in progress with the remote peripheral. 40 | */ 41 | BONDING(BluetoothDevice.BOND_BONDING), 42 | 43 | /** 44 | * Indicates the remote peripheral is bonded. 45 | * A shared link keys exists locally for the remote peripheral, so 46 | * communication can be authenticated and encrypted. 47 | */ 48 | BONDED(BluetoothDevice.BOND_BONDED), 49 | 50 | BOND_LOST(13), 51 | BONDING_FAILED(14); 52 | 53 | companion object { 54 | fun fromValue(value: Int): BondState { 55 | for (type in values()) { 56 | if (type.value == value) { 57 | return type 58 | } 59 | } 60 | return NONE 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/AdvertiseError.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.le.AdvertiseCallback 26 | 27 | /** 28 | * This enum describes all possible errors that can occur when trying to start advertising 29 | */ 30 | enum class AdvertiseError(val value: Int) { 31 | /** 32 | * Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes. 33 | */ 34 | DATA_TOO_LARGE(AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE), 35 | 36 | /** 37 | * Failed to start advertising because no advertising instance is available. 38 | */ 39 | TOO_MANY_ADVERTISERS(AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS), 40 | 41 | /** 42 | * Failed to start advertising as the advertising is already started. 43 | */ 44 | ALREADY_STARTED(AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED), 45 | 46 | /** 47 | * Operation failed due to an internal error. 48 | */ 49 | INTERNAL_ERROR(AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR), 50 | 51 | /** 52 | * This feature is not supported on this platform. 53 | */ 54 | FEATURE_UNSUPPORTED(AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED), UNKNOWN_ERROR(-1); 55 | 56 | companion object { 57 | @JvmStatic 58 | fun fromValue(value: Int): AdvertiseError { 59 | for (type in values()) { 60 | if (type.value == value) return type 61 | } 62 | return UNKNOWN_ERROR 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/ScanFailure.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.le.ScanCallback 26 | 27 | /** 28 | * This class represents the possible scan failure reasons 29 | */ 30 | enum class ScanFailure(val value: Int) { 31 | /** 32 | * Failed to start scan as BLE scan with the same settings is already started by the app. 33 | */ 34 | ALREADY_STARTED(ScanCallback.SCAN_FAILED_ALREADY_STARTED), 35 | 36 | /** 37 | * Failed to start scan as app cannot be registered. 38 | */ 39 | APPLICATION_REGISTRATION_FAILED(ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED), 40 | 41 | /** 42 | * Failed to start scan due an internal error 43 | */ 44 | INTERNAL_ERROR(ScanCallback.SCAN_FAILED_INTERNAL_ERROR), 45 | 46 | /** 47 | * Failed to start power optimized scan as this feature is not supported. 48 | */ 49 | FEATURE_UNSUPPORTED(ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED), 50 | 51 | /** 52 | * Failed to start scan as it is out of hardware resources. 53 | */ 54 | OUT_OF_HARDWARE_RESOURCES(5), 55 | 56 | /** 57 | * Failed to start scan as application tries to scan too frequently. 58 | */ 59 | SCANNING_TOO_FREQUENTLY(6), UNKNOWN(-1); 60 | 61 | companion object { 62 | fun fromValue(value: Int): ScanFailure { 63 | for (type in values()) { 64 | if (type.value == value) { 65 | return type 66 | } 67 | } 68 | return UNKNOWN 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/Extensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.BluetoothGattCharacteristic 26 | import android.bluetooth.BluetoothGattCharacteristic.* 27 | 28 | fun ByteArray.asString() : String { 29 | if(this.isEmpty()) return "" 30 | val parser = BluetoothBytesParser(this) 31 | return parser.stringValue 32 | } 33 | 34 | fun ByteArray.asHexString() : String { 35 | return BluetoothBytesParser.bytes2String(this) 36 | } 37 | 38 | fun ByteArray.asUInt8() : UInt? { 39 | if (this.isEmpty()) return null 40 | val parser = BluetoothBytesParser(this) 41 | return parser.getIntValue(FORMAT_UINT8).toUInt() 42 | } 43 | 44 | fun BluetoothGattCharacteristic.supportsReading(): Boolean { 45 | return properties and PROPERTY_READ > 0 46 | } 47 | 48 | fun BluetoothGattCharacteristic.supportsWritingWithResponse(): Boolean { 49 | return properties and PROPERTY_WRITE > 0 50 | } 51 | 52 | fun BluetoothGattCharacteristic.supportsWritingWithoutResponse(): Boolean { 53 | return properties and PROPERTY_WRITE_NO_RESPONSE > 0 54 | } 55 | 56 | fun BluetoothGattCharacteristic.supportsNotifying(): Boolean { 57 | return properties and PROPERTY_NOTIFY > 0 || properties and PROPERTY_INDICATE > 0 58 | } 59 | 60 | fun BluetoothGattCharacteristic.supportsWriteType(writeType: WriteType): Boolean { 61 | val writeProperty: Int = when (writeType) { 62 | WriteType.WITH_RESPONSE -> PROPERTY_WRITE 63 | WriteType.WITHOUT_RESPONSE -> PROPERTY_WRITE_NO_RESPONSE 64 | else -> 0 65 | } 66 | return properties and writeProperty > 0 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/welie/blessedexample/PulseOximeterContinuousMeasurement.kt: -------------------------------------------------------------------------------- 1 | package com.welie.blessedexample 2 | 3 | import com.welie.blessed.BluetoothBytesParser 4 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_SFLOAT 5 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT16 6 | import com.welie.blessed.BluetoothBytesParser.Companion.FORMAT_UINT8 7 | import java.util.* 8 | 9 | data class PulseOximeterContinuousMeasurement( 10 | val spO2: Float, 11 | val pulseRate: Float, 12 | val spO2Fast: Float?, 13 | val pulseRateFast: Float?, 14 | val spO2Slow: Float?, 15 | val pulseRateSlow: Float?, 16 | val pulseAmplitudeIndex: Float?, 17 | val measurementStatus: Int?, 18 | val sensorStatus: Int?, 19 | val createdAt: Date = Calendar.getInstance().time 20 | ) { 21 | companion object { 22 | fun fromBytes(value: ByteArray): PulseOximeterContinuousMeasurement { 23 | val parser = BluetoothBytesParser(value) 24 | val flags = parser.getIntValue(FORMAT_UINT8) 25 | val spo2FastPresent = flags and 0x01 > 0 26 | val spo2SlowPresent = flags and 0x02 > 0 27 | val measurementStatusPresent = flags and 0x04 > 0 28 | val sensorStatusPresent = flags and 0x08 > 0 29 | val pulseAmplitudeIndexPresent = flags and 0x10 > 0 30 | 31 | val spO2 = parser.getFloatValue(FORMAT_SFLOAT) 32 | val pulseRate = parser.getFloatValue(FORMAT_SFLOAT) 33 | val spO2Fast = if (spo2FastPresent) parser.getFloatValue(FORMAT_SFLOAT) else null 34 | val pulseRateFast = if (spo2FastPresent) parser.getFloatValue(FORMAT_SFLOAT) else null 35 | val spO2Slow = if (spo2SlowPresent) parser.getFloatValue(FORMAT_SFLOAT) else null 36 | val pulseRateSlow = if (spo2SlowPresent) parser.getFloatValue(FORMAT_SFLOAT) else null 37 | val measurementStatus = if (measurementStatusPresent) parser.getIntValue(FORMAT_UINT16) else null 38 | val sensorStatus = if (sensorStatusPresent) parser.getIntValue(FORMAT_UINT16) else null 39 | if (sensorStatusPresent) parser.getIntValue(FORMAT_UINT8) // Reserved byte 40 | val pulseAmplitudeIndex = if (pulseAmplitudeIndexPresent) parser.getFloatValue(FORMAT_SFLOAT) else null 41 | 42 | return PulseOximeterContinuousMeasurement( 43 | spO2 = spO2, 44 | pulseRate = pulseRate, 45 | spO2Fast = spO2Fast, 46 | pulseRateFast = pulseRateFast, 47 | spO2Slow = spO2Slow, 48 | pulseRateSlow = pulseRateSlow, 49 | measurementStatus = measurementStatus, 50 | sensorStatus = sensorStatus, 51 | pulseAmplitudeIndex = pulseAmplitudeIndex 52 | ) 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /docs/allclasses-noframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes 7 | 8 | 9 | 10 | 11 | 12 |

All Classes

13 |
14 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Generated Documentation (Untitled) 7 | 60 | 61 | 62 | 63 | 64 | 65 | <noscript> 66 | <div>JavaScript is disabled on your browser.</div> 67 | </noscript> 68 | <h2>Frame Alert</h2> 69 | <p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="com/welie/blessed/package-summary.html">Non-frame version</a>.</p> 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/BluetoothCentral.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.annotation.SuppressLint 26 | import android.bluetooth.BluetoothDevice 27 | import java.util.* 28 | 29 | /** 30 | * This class represent a remote Central 31 | */ 32 | @SuppressLint("MissingPermission") 33 | @Suppress("unused") 34 | class BluetoothCentral internal constructor(val device: BluetoothDevice) { 35 | var currentMtu = 23 36 | 37 | val address: String 38 | get() = device.address 39 | val name: String 40 | get() = if (device.name == null) "" else device.name 41 | val bondState: BondState 42 | get() = BondState.fromValue(device.bondState) 43 | 44 | /** 45 | * Create a bond 46 | */ 47 | fun createBond(): Boolean { 48 | return device.createBond() 49 | } 50 | 51 | /** 52 | * Confirm pairing 53 | */ 54 | fun setPairingConfirmation(confirm: Boolean): Boolean { 55 | return device.setPairingConfirmation(confirm) 56 | } 57 | 58 | /** 59 | * Get maximum length of byte array that can be written depending on WriteType 60 | * 61 | * This value is derived from the current negotiated MTU or the maximum characteristic length (512) 62 | */ 63 | fun getMaximumWriteValueLength(writeType: WriteType): Int { 64 | return when (writeType) { 65 | WriteType.WITH_RESPONSE -> 512 66 | WriteType.SIGNED -> currentMtu - 15 67 | else -> currentMtu - 3 68 | } 69 | } 70 | 71 | override fun equals(other: Any?): Boolean { 72 | if (this === other) return true 73 | if (other == null || javaClass != other.javaClass) return false 74 | val that = other as BluetoothCentral 75 | return device.address == that.device.address 76 | } 77 | 78 | override fun hashCode(): Int { 79 | return Objects.hash(device) 80 | } 81 | } -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/BluetoothCentralManagerCallback.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.le.ScanResult 26 | 27 | /** 28 | * Callbacks for the BluetoothCentralManager class 29 | */ 30 | internal abstract class BluetoothCentralManagerCallback { 31 | /** 32 | * Successfully connected with a peripheral. 33 | * 34 | * @param peripheral the peripheral that was connected. 35 | */ 36 | open fun onConnectedPeripheral(peripheral: BluetoothPeripheral) {} 37 | 38 | /** 39 | * Connecting with the peripheral has failed. 40 | * 41 | * @param peripheral the peripheral for which the connection was attempted 42 | * @param status the status code for the connection failure 43 | */ 44 | open fun onConnectionFailed(peripheral: BluetoothPeripheral, status: HciStatus) {} 45 | 46 | /** 47 | * Peripheral disconnected 48 | * 49 | * @param peripheral the peripheral that disconnected. 50 | * @param status the status code for the disconnection 51 | */ 52 | open fun onDisconnectedPeripheral(peripheral: BluetoothPeripheral, status: HciStatus) {} 53 | 54 | /** 55 | * Discovered a peripheral 56 | * 57 | * @param peripheral the peripheral that was found 58 | * @param scanResult the scanResult describing the peripheral 59 | */ 60 | open fun onDiscoveredPeripheral(peripheral: BluetoothPeripheral, scanResult: ScanResult) {} 61 | 62 | /** 63 | * Scanning failed 64 | * 65 | * @param scanFailure the status code for the scanning failure 66 | */ 67 | open fun onScanFailed(scanFailure: ScanFailure) {} 68 | 69 | /** 70 | * Bluetooth adapter status changed 71 | * 72 | * @param state the current status code for the adapter 73 | */ 74 | open fun onBluetoothAdapterStateChanged(state: Int) {} 75 | 76 | /** 77 | * NULL class to deal with nullability 78 | */ 79 | internal class NULL : BluetoothCentralManagerCallback() 80 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /docs/com/welie/blessed/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.welie.blessed 7 | 8 | 9 | 10 | 11 | 12 |

com.welie.blessed

13 |
14 |

Classes

15 | 25 |

Enums

26 | 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/allclasses-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes 7 | 8 | 9 | 10 | 11 | 12 |

All Classes

13 |
14 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/deprecated-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Deprecated List 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
71 |

Deprecated API

72 |

Contents

73 |
74 | 75 |
76 | 77 | 78 | 79 | 80 | 81 | 82 | 90 |
91 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/Logger.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.util.Log 26 | import timber.log.Timber 27 | 28 | internal object Logger { 29 | var enabled = true 30 | 31 | /** 32 | * Send a verbose log message. 33 | * 34 | * @param tag Used to identify the source of a log message. It usually identifies 35 | * the class or activity where the log call occurs. 36 | * @param msg The message you would like logged. 37 | */ 38 | fun v(tag: String, msg: String) { 39 | triggerLogger(Log.VERBOSE, tag, msg) 40 | } 41 | 42 | /** Log an verbose message with optional format args. */ 43 | fun v(tag: String, msg: String, vararg args: Any) { 44 | triggerLogger(Log.VERBOSE, tag, msg, *args) 45 | } 46 | 47 | /** 48 | * Send a debug log message. 49 | * 50 | * @param tag Used to identify the source of a log message. It usually identifies 51 | * the class or activity where the log call occurs. 52 | * @param msg The message you would like logged. 53 | */ 54 | fun d(tag: String, msg: String) { 55 | triggerLogger(Log.DEBUG, tag, msg) 56 | } 57 | 58 | /** Log an debug message with optional format args. */ 59 | fun d(tag: String, msg: String, vararg args: Any) { 60 | triggerLogger(Log.DEBUG, tag, msg, *args) 61 | } 62 | 63 | /** 64 | * Send an info log message. 65 | * 66 | * @param tag Used to identify the source of a log message. It usually identifies 67 | * the class or activity where the log call occurs. 68 | * @param msg The message you would like logged. 69 | */ 70 | fun i(tag: String, msg: String) { 71 | triggerLogger(Log.INFO, tag, msg) 72 | } 73 | 74 | /** Log an info message with optional format args. */ 75 | fun i(tag: String, msg: String, vararg args: Any) { 76 | triggerLogger(Log.INFO, tag, msg, *args) 77 | } 78 | 79 | /** 80 | * Send a warn log message. 81 | * 82 | * @param tag Used to identify the source of a log message. It usually identifies 83 | * the class or activity where the log call occurs. 84 | * @param msg The message you would like logged. 85 | */ 86 | fun w(tag: String, msg: String) { 87 | triggerLogger(Log.WARN, tag, msg) 88 | } 89 | 90 | /** Log an warn message with optional format args. */ 91 | fun w(tag: String, msg: String, vararg args: Any) { 92 | triggerLogger(Log.WARN, tag, msg, *args) 93 | } 94 | 95 | /** 96 | * Send an error log message. 97 | * 98 | * @param tag Used to identify the source of a log message. It usually identifies 99 | * the class or activity where the log call occurs. 100 | * @param msg The message you would like logged. 101 | */ 102 | fun e(tag: String, msg: String) { 103 | triggerLogger(Log.ERROR, tag, msg) 104 | } 105 | 106 | /** Log an error message with optional format args. */ 107 | fun e(tag: String, msg: String, vararg args: Any) { 108 | triggerLogger(Log.ERROR, tag, msg, *args) 109 | } 110 | 111 | /** 112 | * What a Terrible Failure: Report a condition that should never happen. 113 | * The error will always be logged at level severe with the call stack. 114 | * @param tag Used to identify the source of a log message. 115 | * @param msg The message you would like logged. 116 | */ 117 | fun wtf(tag: String, msg: String) { 118 | triggerLogger(Log.ASSERT, tag, msg) 119 | } 120 | 121 | /** Log an wtf message with optional format args. */ 122 | fun wtf(tag: String, msg: String, vararg args: Any) { 123 | triggerLogger(Log.ASSERT, tag, msg, *args) 124 | } 125 | 126 | private fun triggerLogger(priority: Int, tag: String, msg: String, vararg args: Any) { 127 | if (enabled) { 128 | triggerLogger(priority, tag, String.format(msg, *args)) 129 | } 130 | } 131 | 132 | private fun triggerLogger(priority: Int, tag: String, msg: String) { 133 | if (enabled) { 134 | Timber.tag(tag).log(priority, msg) 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /docs/index-files/index-14.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | T-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

T

74 |
75 |
toString() - Method in class com.welie.blessed.BluetoothBytesParser
76 |
 
77 |
78 | A B C F G H I M N O P R S T V W 
79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 95 |
96 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /docs/index-files/index-6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | H-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

H

74 |
75 |
HciStatus - Enum in com.welie.blessed
76 |
77 |
This class describes the HCI error codes as defined in the Bluetooth Standard, Volume 1, Part F, 1.3 HCI Error Code, pages 364-377.
78 |
79 |
80 | A B C F G H I M N O P R S T V W 
81 | 82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 97 |
98 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /docs/index-files/index-9.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | N-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

N

74 |
75 |
notifyCharacteristicChanged(byte[], BluetoothGattCharacteristic) - Method in class com.welie.blessed.BluetoothPeripheralManager
76 |
77 |
Send a notification or indication that a local characteristic has been 78 | updated
79 |
80 |
81 | A B C F G H I M N O P R S T V W 
82 | 83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 98 |
99 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /docs/index-files/index-8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | M-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

M

74 |
75 |
mask - Variable in enum com.welie.blessed.PhyType
76 |
 
77 |
MAX_MTU - Static variable in class com.welie.blessed.BluetoothPeripheral
78 |
79 |
Max MTU that Android can handle
80 |
81 |
mergeArrays(byte[]...) - Static method in class com.welie.blessed.BluetoothBytesParser
82 |
83 |
Merge multiple arrays intro one array
84 |
85 |
86 | A B C F G H I M N O P R S T V W 
87 | 88 |
89 | 90 | 91 | 92 | 93 | 94 | 95 | 103 |
104 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /docs/index-files/index-11.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | P-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

P

74 |
75 |
PeripheralType - Enum in com.welie.blessed
76 |
77 |
This class represents the possible peripheral types
78 |
79 |
PhyOptions - Enum in com.welie.blessed
80 |
81 |
This class represents the possible Phy options
82 |
83 |
PhyType - Enum in com.welie.blessed
84 |
85 |
This class represents the possible Phy types
86 |
87 |
property - Variable in enum com.welie.blessed.WriteType
88 |
 
89 |
90 | A B C F G H I M N O P R S T V W 
91 | 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 107 |
108 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/index-files/index-7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | I-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

I

74 |
75 |
isBluetoothEnabled() - Method in class com.welie.blessed.BluetoothCentralManager
76 |
77 |
Check if Bluetooth is enabled
78 |
79 |
isNotifying(BluetoothGattCharacteristic) - Method in class com.welie.blessed.BluetoothPeripheral
80 |
81 |
Boolean to indicate if the specified characteristic is currently notifying or indicating.
82 |
83 |
isScanning() - Method in class com.welie.blessed.BluetoothCentralManager
84 |
85 |
Check if a scanning is active
86 |
87 |
88 | A B C F G H I M N O P R S T V W 
89 | 90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 105 |
106 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /docs/index-files/index-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | A-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

A

74 |
75 |
add(BluetoothGattService) - Method in class com.welie.blessed.BluetoothPeripheralManager
76 |
77 |
Add a service to the peripheral
78 |
79 |
AdvertiseError - Enum in com.welie.blessed
80 |
81 |
This enum describes all possible errors that can occur when trying to start advertising
82 |
83 |
autoConnectPeripheral(BluetoothPeripheral, BluetoothPeripheralCallback) - Method in class com.welie.blessed.BluetoothCentralManager
84 |
85 |
Automatically connect to a peripheral when it is advertising.
86 |
87 |
autoConnectPeripheralsBatch(Map<BluetoothPeripheral, BluetoothPeripheralCallback>) - Method in class com.welie.blessed.BluetoothCentralManager
88 |
89 |
Autoconnect to a batch of peripherals.
90 |
91 |
92 | A B C F G H I M N O P R S T V W 
93 | 94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | 109 |
110 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /docs/index-files/index-16.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | W-Index 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
A B C F G H I M N O P R S T V W  71 | 72 | 73 |

W

74 |
75 |
writeCharacteristic(UUID, UUID, byte[], WriteType) - Method in class com.welie.blessed.BluetoothPeripheral
76 |
77 |
Write a value to a characteristic using the specified write type.
78 |
79 |
writeCharacteristic(BluetoothGattCharacteristic, byte[], WriteType) - Method in class com.welie.blessed.BluetoothPeripheral
80 |
81 |
Write a value to a characteristic using the specified write type.
82 |
83 |
writeDescriptor(BluetoothGattDescriptor, byte[]) - Method in class com.welie.blessed.BluetoothPeripheral
84 |
85 |
Write a value to a descriptor.
86 |
87 |
WriteType - Enum in com.welie.blessed
88 |
89 |
WriteType describes the type of write that can be done
90 |
91 |
writeType - Variable in enum com.welie.blessed.WriteType
92 |
 
93 |
94 | A B C F G H I M N O P R S T V W 
95 | 96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | 111 |
112 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/GattStatus.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | /** 26 | * The GattStatus describes the result of a GATT operation. 27 | * 28 | * 29 | * Note that most of these error codes correspond to the ATT error codes as defined in the Bluetooth Standard, Volume 3, Part F, 3.4.1 Error handling p1491) 30 | * See https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=478726 31 | * 32 | * 33 | * 34 | * Gatt status values as defined in the Android source code: 35 | * https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h 36 | * 37 | */ 38 | enum class GattStatus(val value: Int) { 39 | /** 40 | * Operation completed successfully 41 | */ 42 | SUCCESS(0x00), 43 | 44 | /** 45 | * The attribute handle given was not valid on this server 46 | */ 47 | INVALID_HANDLE(0x01), 48 | 49 | /** 50 | * The attribute cannot be read. 51 | */ 52 | READ_NOT_PERMITTED(0x02), 53 | 54 | /** 55 | * The attribute cannot be written. 56 | */ 57 | WRITE_NOT_PERMITTED(0x03), 58 | 59 | /** 60 | * The attribute PDU was invalid. 61 | */ 62 | INVALID_PDU(0x04), 63 | 64 | /** 65 | * The attribute requires authentication before it can be read or written. 66 | */ 67 | INSUFFICIENT_AUTHENTICATION(0x05), 68 | 69 | /** 70 | * Attribute server does not support the request received from the client. 71 | */ 72 | REQUEST_NOT_SUPPORTED(0x06), 73 | 74 | /** 75 | * Offset specified was past the end of the attribute. 76 | */ 77 | INVALID_OFFSET(0x07), 78 | 79 | /** 80 | * The attribute requires authorization before it can be read or written. 81 | */ 82 | INSUFFICIENT_AUTHORIZATION(0x08), 83 | 84 | /** 85 | * Too many prepare writes have been queued. 86 | */ 87 | PREPARE_QUEUE_FULL(0x09), 88 | 89 | /** 90 | * No attribute found within the given attribute handle range. 91 | */ 92 | ATTRIBUTE_NOT_FOUND(0x0A), 93 | 94 | /** 95 | * The attribute cannot be read using the ATT_READ_BLOB_REQ PDU. 96 | */ 97 | ATTRIBUTE_NOT_LONG(0x0B), 98 | 99 | /** 100 | * The Encryption Key Size used for encrypting this link is insufficient. 101 | */ 102 | INSUFFICIENT_ENCRYPTION_KEY_SIZE(0x0C), 103 | 104 | /** 105 | * The attribute value length is invalid for the operation. 106 | */ 107 | INVALID_ATTRIBUTE_VALUE_LENGTH(0x0D), 108 | 109 | /** 110 | * The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested. 111 | */ 112 | UNLIKELY_ERROR(0x0E), 113 | 114 | /** 115 | * The attribute requires encryption before it can be read or written. 116 | */ 117 | INSUFFICIENT_ENCRYPTION(0x0F), 118 | 119 | /** 120 | * The attribute type is not a supported grouping attribute as defined by a higher layer specification. 121 | */ 122 | UNSUPPORTED_GROUP_TYPE(0x10), 123 | 124 | /** 125 | * Insufficient Resources to complete the request. 126 | */ 127 | INSUFFICIENT_RESOURCES(0x11), 128 | 129 | /** 130 | * The server requests the client to rediscover the database. 131 | */ 132 | DATABASE_OUT_OF_SYNC(0x12), 133 | 134 | /** 135 | * The attribute parameter value was not allowed 136 | */ 137 | VALUE_NOT_ALLOWED(0x13), 138 | 139 | // (0x80 – 0x9F) - Application error code defined by a higher layer specification. 140 | // So the following codes are Android specific 141 | 142 | /** 143 | * No resources 144 | */ 145 | NO_RESOURCES(0x80), 146 | 147 | /** 148 | * An internal error has occurred 149 | */ 150 | INTERNAL_ERROR(0x81), 151 | 152 | /** 153 | * Wrong state 154 | */ 155 | WRONG_STATE(0x82), 156 | 157 | /** 158 | * Database is full 159 | */ 160 | DB_FULL(0x83), 161 | 162 | /** 163 | * Busy 164 | */ 165 | BUSY(0x84), 166 | 167 | /** 168 | * Undefined GATT error occurred 169 | */ 170 | ERROR(0x85), 171 | 172 | /** 173 | * Command has been queued up 174 | */ 175 | CMD_STARTED(0x86), 176 | 177 | /** 178 | * Illegal parameter 179 | */ 180 | ILLEGAL_PARAMETER(0x87), 181 | 182 | /** 183 | * Operation is pending 184 | */ 185 | PENDING(0x88), 186 | 187 | /** 188 | * Authorization failed, typically because bonding failed 189 | */ 190 | AUTHORIZATION_FAILED(0x89), 191 | 192 | /** 193 | * More 194 | */ 195 | MORE(0x8a), 196 | 197 | /** 198 | * Invalid configuration 199 | */ 200 | INVALID_CFG(0x8b), 201 | 202 | /** 203 | * Service started 204 | */ 205 | SERVICE_STARTED(0x8c), 206 | 207 | /** 208 | * No Man-in-the-middle protection 209 | */ 210 | ENCRYPTED_NO_MITM(0x8d), 211 | 212 | /** 213 | * Not encrypted 214 | */ 215 | NOT_ENCRYPTED(0x8e), 216 | 217 | /** 218 | * Command is sent but L2CAP channel is congested 219 | */ 220 | CONNECTION_CONGESTED(0x8f), // (0xE0 – 0xFF) - Common profile and service error codes defined in Core Specification Supplement, Part B. 221 | 222 | /** 223 | * Client Characteristic Configuration Descriptor error 224 | */ 225 | CCCD_CFG_ERROR(0x00FD), 226 | 227 | /** 228 | * Procedure in progress 229 | */ 230 | PROCEDURE_IN_PROGRESS(0x00FE), 231 | 232 | /** 233 | * Value out of range 234 | */ 235 | VALUE_OUT_OF_RANGE(0x00FF), // Other errors codes that are Android specific 236 | 237 | /** 238 | * L2CAP connection cancelled 239 | */ 240 | CONNECTION_CANCELLED(0x0100), 241 | 242 | /** 243 | * Failure to register client when trying to connect 244 | */ 245 | FAILURE(0x101), 246 | 247 | /** 248 | * Used when status code is not defined in the class 249 | */ 250 | UNKNOWN_STATUS_CODE(0xFFFF); 251 | 252 | companion object { 253 | @JvmStatic 254 | fun fromValue(value: Int): GattStatus { 255 | for (type in values()) { 256 | if (type.value == value) return type 257 | } 258 | return UNKNOWN_STATUS_CODE 259 | } 260 | } 261 | } -------------------------------------------------------------------------------- /SERVER.md: -------------------------------------------------------------------------------- 1 | # Creating your own peripheral 2 | 3 | This guide describes how to create your own peripheral using the `BluetoothPeripheralManager` class. This class has been designed to make it as easy as possible to develop your own peripheral. An [example project](https://github.com/weliem/bluetooth-server-example) is available. 4 | 5 | ## Setting up your peripheral 6 | 7 | The first thing to do is to create an instance of the `BluetoothPeripheralManager` class: 8 | 9 | ```java 10 | BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); 11 | 12 | BluetoothPeripheralManager peripheralManager = new BluetoothPeripheralManager(context, bluetoothManager, peripheralManagerCallback); 13 | ``` 14 | 15 | Not all Android phones support creating a peripheral. Most recent phones support it but some older ones may not. So make sure you do a check like this: 16 | 17 | ```java 18 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 19 | 20 | if (!bluetoothAdapter.isMultipleAdvertisementSupported()) { 21 | Timber.e("not supporting advertising"); 22 | } 23 | ``` 24 | 25 | ## Adding services 26 | 27 | Now that you created your `BluetoothPeripheralManager` you need to add some services. Which services you add is up to you of course. 28 | 29 | Setting up a heartrate service could be done like this: 30 | 31 | ```java 32 | UUID HRS_SERVICE_UUID = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb"); 33 | UUID HEARTRATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb"); 34 | 35 | BluetoothGattService service = new BluetoothGattService(HRS_SERVICE_UUID, SERVICE_TYPE_PRIMARY); 36 | BluetoothGattCharacteristic measurement = new BluetoothGattCharacteristic(HEARTRATE_MEASUREMENT_CHARACTERISTIC_UUID, PROPERTY_READ | PROPERTY_INDICATE, PERMISSION_READ); 37 | service.addCharacteristic(measurement); 38 | peripheralManager.add(service); 39 | ``` 40 | 41 | The `add()` method is asynchronous and you will receive a callback via the `BluetoothPeripheralManagerCallback` class on the method `onServiceAdded()`. The `add()` call is enqueued so you can call it several times in a row. 42 | 43 | ## Starting advertising 44 | 45 | To start advertising you call the method `startAdvertising` with the advertise settings, advertise data and scan response. Here is an example: 46 | 47 | ```java 48 | AdvertiseSettings advertiseSettings = new AdvertiseSettings.Builder() 49 | .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) 50 | .setConnectable(true) 51 | .setTimeout(0) 52 | .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) 53 | .build(); 54 | 55 | AdvertiseData advertiseData = new AdvertiseData.Builder() 56 | .setIncludeTxPowerLevel(true) 57 | .addServiceUuid(new ParcelUuid(serviceUUID)) 58 | .build(); 59 | 60 | AdvertiseData scanResponse = new AdvertiseData.Builder() 61 | .setIncludeDeviceName(true) 62 | .build(); 63 | 64 | peripheralManager.startAdvertising(advertiseSettings, scanResponse, advertiseData); 65 | ``` 66 | 67 | ## Implementing characteristic read or write requests 68 | 69 | When a remote central connects and tries to read a characteristic, you get a callback on `onCharacteristicRead`. You don't necessarily need to implement it if the characterist already has the proper value. But if you want to update it just before it get returned, you need to override this method. For example, if you implement the current time characteristic and you want to make sure the characteristic value is updated before it is returned, you can do: 70 | 71 | ```java 72 | @Override 73 | public void onCharacteristicRead(@NotNull BluetoothCentral central, @NotNull BluetoothGattCharacteristic characteristic) { 74 | currentTime.setValue(getCurrentTime()); 75 | } 76 | ``` 77 | 78 | When a write request happens, you get a callback on `onCharacteristicWrite`. If you want to validate the value before it is assigned to the characteristic you can do that by overriding this method. If you consider the value valid, you must return GattStatus.SUCCESS and otherwise you return some other GattStatus value that represents the error. After you return GattStatus.SUCCESS, the value is assigned to the characteristic. Otherwise the characteristics's value will remain unchanged and the remote central will receive an error. For example: 79 | 80 | ```java 81 | @Override 82 | public GattStatus onCharacteristicWrite(@NotNull BluetoothCentral central, @NotNull BluetoothGattCharacteristic characteristic, @NotNull byte[] value) { 83 | if (isValid(value, characteristic)) { 84 | return GattStatus.SUCCESS; 85 | } else { 86 | // Return an error, typical INVALID_ATTRIBUTE_VALUE_LENGTH or VALUE_NOT_ALLOWED 87 | return GattStatus.VALUE_NOT_ALLOWED; 88 | } 89 | } 90 | ``` 91 | 92 | ## Implementing descriptor read or write requests 93 | 94 | Read or write request for descriptors work in the same way as for characteristics. The only exception is when the descriptor happens to be the CCC descriptor, which is used to turn on/off notifications 95 | 96 | ## Enabling or disabling of notifications 97 | 98 | If you have a characteristic with PROPERTY_INDICATE or PROPERTY_NOTIFY and a CCC descriptor added, then a remote central may 'enable notifications'. Blessed will doublecheck if the the correct descriptor values are written, and if correct it will call either `onNotifyingEnabled` or `onNotifyingDisabled`. It is then your responsibility to actually follow up and do the notifications. 99 | 100 | 101 | ```java 102 | @Override 103 | public void onNotifyingEnabled(@NotNull BluetoothCentral central, @NotNull BluetoothGattCharacteristic characteristic) { 104 | if (characteristic.getUuid().equals(CURRENT_TIME_CHARACTERISTIC_UUID)) { 105 | notifyCurrentTime(); 106 | } 107 | } 108 | 109 | @Override 110 | public void onNotifyingDisabled(@NotNull BluetoothCentral central, @NotNull BluetoothGattCharacteristic characteristic) { 111 | if (characteristic.getUuid().equals(CURRENT_TIME_CHARACTERISTIC_UUID)) { 112 | stopNotifying(); 113 | } 114 | } 115 | ``` 116 | 117 | ## Sending notifications 118 | 119 | Once notifications have been enabled, you can send notifactions by calling: 120 | 121 | ```java 122 | peripheralManager.notifyCharacteristicChanged(value, characteristic); 123 | ``` 124 | 125 | Note that you have to pass the value for the characteristic. That is there so that you can do high speed notifications as each call to notifyCharacteristicChanged() will be queued up. So you can call this function in a loop and then each command is executed the value of the characteristic will be updated and sent to the remote central. 126 | 127 | ## Connecting and disconnecting centrals 128 | 129 | When a remote central connects or disconnects, the following callbacks are called: 130 | 131 | ```java 132 | @Override 133 | public void onCentralConnected(@NotNull BluetoothCentral central) { 134 | // Do something, e.g. initialization of characteristics 135 | } 136 | 137 | @Override 138 | public void onCentralDisconnected(@NotNull BluetoothCentral central) { 139 | if (noCentralsConnected()) { 140 | stopNotifying(); 141 | } 142 | } 143 | ``` 144 | 145 | Typically, when a central disconnects, you stop notifying and clean up. 146 | 147 | ## Long reads and writes 148 | 149 | The BluetoothPeripheralManager class supports long reads and writes. It will take care of splitting up characteristic byte arrays in smaller chunks and re-assembling them. Hence, nothing special is needed and they function the same way as normal read and writes. 150 | 151 | 152 | -------------------------------------------------------------------------------- /docs/overview-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Class Hierarchy 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
71 |

Hierarchy For All Packages

72 | Package Hierarchies: 73 | 76 |
77 |
78 |

Class Hierarchy

79 | 93 |

Enum Hierarchy

94 | 116 |
117 | 118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 133 |
134 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /blessed/src/main/java/com/welie/blessed/BluetoothPeripheralCallback.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Martijn van Welie 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | package com.welie.blessed 24 | 25 | import android.bluetooth.BluetoothGattCharacteristic 26 | import android.bluetooth.BluetoothGattDescriptor 27 | 28 | /** 29 | * Callbacks for the BluetoothPeripheral class 30 | */ 31 | @Suppress("UNUSED_PARAMETER") 32 | internal abstract class BluetoothPeripheralCallback { 33 | /** 34 | * Callback invoked when the list of remote services, characteristics and descriptors 35 | * for the remote peripheral has been discovered. 36 | * 37 | */ 38 | open fun onServicesDiscovered(peripheral: BluetoothPeripheral) {} 39 | 40 | /** 41 | * Callback invoked when the notification state of a characteristic has changed. 42 | * 43 | * 44 | * Use [BluetoothPeripheral.isNotifying] to get the current notification state of the characteristic 45 | * 46 | * @param peripheral the peripheral 47 | * @param characteristic the characteristic for which the notification state changed 48 | * @param status GATT status code 49 | */ 50 | open fun onNotificationStateUpdate(peripheral: BluetoothPeripheral, characteristic: BluetoothGattCharacteristic, status: GattStatus) {} 51 | 52 | /** 53 | * Callback invoked as the result of a characteristic read operation or notification/indication 54 | * 55 | * @param peripheral the peripheral 56 | * @param value the new value received 57 | * @param characteristic the characteristic for which the new value was received 58 | * @param status GATT status code 59 | */ 60 | open fun onCharacteristicUpdate(peripheral: BluetoothPeripheral, value: ByteArray, characteristic: BluetoothGattCharacteristic, status: GattStatus) {} 61 | 62 | /** 63 | * Callback invoked as the result of a characteristic write operation. 64 | * 65 | * @param peripheral the peripheral 66 | * @param value the value that was written 67 | * @param characteristic the characteristic written to 68 | * @param status GATT status code 69 | */ 70 | open fun onCharacteristicWrite(peripheral: BluetoothPeripheral, value: ByteArray, characteristic: BluetoothGattCharacteristic, status: GattStatus) {} 71 | 72 | /** 73 | * Callback invoked as the result of a characteristic read operation 74 | * 75 | * @param peripheral the peripheral 76 | * @param value the value that was read 77 | * @param characteristic the characteristic read 78 | * @param status GATT status code 79 | */ 80 | open fun onCharacteristicRead(peripheral: BluetoothPeripheral, value: ByteArray, characteristic: BluetoothGattCharacteristic, status: GattStatus) {} 81 | 82 | /** 83 | * Callback invoked as the result of a descriptor read operation 84 | * 85 | * @param peripheral the peripheral 86 | * @param value the read value 87 | * @param descriptor the descriptor that was read 88 | * @param status GATT status code 89 | */ 90 | open fun onDescriptorRead(peripheral: BluetoothPeripheral, value: ByteArray, descriptor: BluetoothGattDescriptor, status: GattStatus) {} 91 | 92 | /** 93 | * Callback invoked as the result of a descriptor write operation. 94 | * This callback is not called for the Client Characteristic Configuration descriptor. Instead the [BluetoothPeripheralCallback.onNotificationStateUpdate] will be called 95 | * 96 | * @param peripheral the peripheral 97 | * @param value the value that was written 98 | * @param descriptor the descriptor written to 99 | * @param status the GATT status code 100 | */ 101 | open fun onDescriptorWrite(peripheral: BluetoothPeripheral, value: ByteArray, descriptor: BluetoothGattDescriptor, status: GattStatus) {} 102 | 103 | /** 104 | * Callback invoked when the bonding process is started 105 | * 106 | * @param peripheral the peripheral 107 | */ 108 | open fun onBondingStarted(peripheral: BluetoothPeripheral) {} 109 | 110 | /** 111 | * Callback invoked when the bonding process has succeeded 112 | * 113 | * @param peripheral the peripheral 114 | */ 115 | open fun onBondingSucceeded(peripheral: BluetoothPeripheral) {} 116 | 117 | /** 118 | * Callback invoked when the bonding process has failed 119 | * 120 | * @param peripheral the peripheral 121 | */ 122 | open fun onBondingFailed(peripheral: BluetoothPeripheral) {} 123 | 124 | /** 125 | * Callback invoked when a bond has been lost and the peripheral is not bonded anymore. 126 | * 127 | * @param peripheral the peripheral 128 | */ 129 | open fun onBondLost(peripheral: BluetoothPeripheral) {} 130 | 131 | /** 132 | * Callback invoked as the result of a read RSSI operation 133 | * 134 | * @param peripheral the peripheral 135 | * @param rssi the RSSI value 136 | * @param status GATT status code 137 | */ 138 | open fun onReadRemoteRssi(peripheral: BluetoothPeripheral, rssi: Int, status: GattStatus) {} 139 | 140 | /** 141 | * Callback invoked as the result of a MTU request operation 142 | * 143 | * @param peripheral the peripheral 144 | * @param mtu the new MTU 145 | * @param status GATT status code 146 | */ 147 | open fun onMtuChanged(peripheral: BluetoothPeripheral, mtu: Int, status: GattStatus) {} 148 | 149 | /** 150 | * Callback invoded as the result of a connection priority request 151 | * 152 | * @param peripheral the peripheral 153 | */ 154 | open fun onRequestedConnectionPriority(peripheral: BluetoothPeripheral) {} 155 | 156 | /** 157 | * Callback invoke as result of readPhy or setPhy operation 158 | * 159 | * @param peripheral the peripheral 160 | * @param txPhy the transmitter PHY in use. 161 | * @param rxPhy the receiver PHY in use 162 | * @param status GATT status code 163 | */ 164 | open fun onPhyUpdate(peripheral: BluetoothPeripheral, txPhy: PhyType, rxPhy: PhyType, status: GattStatus) {} 165 | 166 | /** 167 | * Callback invoked when the connection parameters are updated. 168 | * 169 | * This can happen as a result of requestConnectionPriority() or when the stack/peripheral decides to change the connection parameters. 170 | * This callback is only called for Android 8 (Oreo) or newer. 171 | * 172 | * @param peripheral the peripheral 173 | * @param interval Connection interval used on this connection, 1.25ms unit. 174 | * Valid range is from 6 (7.5ms) to 3200 (4000ms). 175 | * @param latency Slave latency for the connection in number of connection events. 176 | * Valid range is from 0 to 499. 177 | * @param timeout Supervision timeout for this connection, in 10ms unit. 178 | * Valid range is from 10 (0.1s) to 3200 (32s). 179 | * @param status GATT status code 180 | */ 181 | open fun onConnectionUpdated(peripheral: BluetoothPeripheral, interval: Int, latency: Int, timeout: Int, status: GattStatus) {} 182 | 183 | /** 184 | * NULL class to deal with nullability 185 | */ 186 | internal class NULL : BluetoothPeripheralCallback() 187 | } -------------------------------------------------------------------------------- /docs/com/welie/blessed/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.welie.blessed Class Hierarchy 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
71 |

Hierarchy For Package com.welie.blessed

72 |
73 |
74 |

Class Hierarchy

75 | 89 |

Enum Hierarchy

90 | 112 |
113 | 114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 129 |
130 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /docs/constant-values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Constant Field Values 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
71 |

Constant Field Values

72 |

Contents

73 | 76 |
77 |
78 | 79 | 80 |

com.welie.*

81 | 170 |
171 | 172 |
173 | 174 | 175 | 176 | 177 | 178 | 179 | 187 |
188 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /docs/help-doc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | API Help 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 |
42 | 69 | 70 |
71 |

How This API Document Is Organized

72 |
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
73 |
74 |
75 | 170 | This help file applies to API documentation generated using the standard doclet.
171 | 172 |
173 | 174 | 175 | 176 | 177 | 178 | 179 | 187 |
188 | 215 | 216 | 217 | 218 | --------------------------------------------------------------------------------