Skip to content

Commit

Permalink
Merge pull request #1 from MnlPhlp/add_characs
Browse files Browse the repository at this point in the history
Improvements
  • Loading branch information
MnlPhlp authored Dec 18, 2024
2 parents ecfa563 + 218a59c commit 5bb4a38
Show file tree
Hide file tree
Showing 19 changed files with 700 additions and 270 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-blec"
license = "MIT OR Apache-2.0"
version = "0.2.0"
version = "0.3.0"
authors = ["Manuel Philipp"]
description = "BLE-Client plugin for Tauri"
edition = "2021"
Expand All @@ -22,6 +22,7 @@ uuid = "1.11.0"
once_cell = "1.20.2"
tracing = "0.1.40"
futures = { version = "0.3.31", default-features = false }
enumflags2 = { version = "0.7", features = ["serde"] }

# [target.'cfg(target_os = "android")'.dependencies]
async-trait = "0.1.83"
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ let address = ...
await connect(address, () => console.log('disconnected'))
// send some text to a characteristic
const CHARACTERISTIC_UUID = '51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B'
await sendString(CHARACTERISTIC_UUID, 'Test')
await sendString(CHARACTERISTIC_UUID, 'Test', 'withResponse')
```

## Usage in Backend
Expand All @@ -75,14 +75,13 @@ This means if you connect from the frontend you can send data from rust without

```rs
use uuid::{uuid, Uuid};
use tauri_plugin_blec::models::WriteType;

const CHARACTERISTIC_UUID: Uuid = uuid!("51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B");
const DATA: [u8; 500] = [0; 500];
let handler = tauri_plugin_blec::get_handler().unwrap();
handler
.lock()
.await
.send_data(CHARACTERISTIC_UUID, &DATA)
.send_data(CHARACTERISTIC_UUID, &DATA, WriteType::WithResponse)
.await
.unwrap();
```
46 changes: 38 additions & 8 deletions android/src/main/java/BleClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,15 @@ import Peripheral
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import app.tauri.annotation.InvokeArg
import app.tauri.plugin.Invoke

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanSettings
import android.bluetooth.le.ScanFilter.Builder;
import android.bluetooth.le.ScanFilter.Builder
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context.MODE_PRIVATE
import android.content.Intent
import android.content.SharedPreferences
Expand All @@ -26,18 +21,24 @@ import android.net.Uri
import android.os.Build
import android.os.ParcelUuid
import android.provider.Settings
import android.util.SparseArray
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.startActivityForResult
import androidx.core.content.ContextCompat.getSystemService
import app.tauri.annotation.InvokeArg
import app.tauri.plugin.Channel
import app.tauri.plugin.Invoke
import app.tauri.plugin.JSArray
import app.tauri.plugin.JSObject

class BleDevice(
val address: String,
private val name: String,
private val rssi: Int,
private val connected: Boolean,
private val manufacturerData: SparseArray<ByteArray>?,
private val services: List<ParcelUuid>?
){
fun toJsObject():JSObject{
val obj = JSObject()
Expand All @@ -46,6 +47,33 @@ class BleDevice(
obj.put("name",name)
obj.put("connected",connected)
obj.put("rssi",rssi)
// create Json Array from services
val services = if (services != null) {
val arr = JSArray();
for (service in services){
arr.put(service)
}
arr
} else { null }
obj.put("services",services)
// crate object from sparse Array
val manufacturerData = if (manufacturerData != null) {
val subObj = JSObject()
for (i in 0 until manufacturerData.size()) {
val key = manufacturerData.keyAt(i)
// get the object by the key.
val value = manufacturerData.get(key)
val arr = JSArray()
for (element in value){
// toInt is needed to generate number in Json
// the UByte is serialized as string
arr.put(element.toUByte().toInt())
}
subObj.put(key.toString(),arr)
}
subObj
} else { null }
obj.put("manufacturerData",manufacturerData)
return obj
}
}
Expand Down Expand Up @@ -170,7 +198,9 @@ class BleClient(private val activity: Activity, private val plugin: BleClientPlu
result.device.address,
name,
result.rssi,
connected
connected,
result.scanRecord?.manufacturerSpecificData,
result.scanRecord?.serviceUuids
)
this@BleClient.plugin.devices[device.address] = Peripheral(this@BleClient.activity, result.device, this@BleClient.plugin)
val res = JSObject()
Expand Down
6 changes: 1 addition & 5 deletions android/src/main/java/Peripheral.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class Peripheral(private val activity: Activity, private val device: BluetoothDe
value: ByteArray,
status: Int
) {
val id = characteristic?.uuid ?: return
val id = characteristic.uuid ?: return
val invoke = this@Peripheral.onReadInvoke[id]!!
if (status != BluetoothGatt.GATT_SUCCESS){
invoke.reject("Read from characteristic $id failed with status $status")
Expand Down Expand Up @@ -246,10 +246,6 @@ class Peripheral(private val activity: Activity, private val device: BluetoothDe
invoke.resolve(res)
}

class Notification(
uuid: UUID,
data: Array<Byte>
)
fun setNotifyChannel(channel: Channel){
this.notifyChannel = channel;
}
Expand Down
49 changes: 24 additions & 25 deletions examples/plugin-blec-example/package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
{
"name": "plugin-blec-example",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@mnlphlp/plugin-blec": ">=0.3.0",
"@saeris/vue-spinners": "^1.0.8",
"@tauri-apps/api": ">=2.0.0",
"@tauri-apps/plugin-log": "~2",
"@tauri-apps/plugin-shell": ">=2.0.0",
"vue": "^3.3.4"
},
"devDependencies": {
"@tauri-apps/cli": ">=2.0.0",
"@vitejs/plugin-vue": "^5.0.5",
"typescript": "^5.2.2",
"vite": "^5.3.1",
"vue-tsc": "^2.0.22"
}
"name": "plugin-blec-example",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@mnlphlp/plugin-blec": "file:../..",
"@tauri-apps/api": ">=2.0.0",
"@tauri-apps/plugin-log": "~2",
"@tauri-apps/plugin-shell": ">=2.0.0",
"vue": "^3.3.4"
},
"devDependencies": {
"@tauri-apps/cli": ">=2.0.0",
"@vitejs/plugin-vue": "^5.0.5",
"typescript": "~5.6.3",
"vite": "^5.3.1",
"vue-tsc": "^2.1.10"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ android {
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
}
signingConfigs {
create("release") {
keyAlias = "example"
keyPassword = "123456"
storeFile = rootProject.file("example_key.jks")
storePassword = "123456"
}
}
buildTypes {
getByName("debug") {
manifestPlaceholders["usesCleartextTraffic"] = "true"
Expand All @@ -38,6 +46,7 @@ android {
}
getByName("release") {
isMinifyEnabled = true
signingConfig = signingConfigs.getByName("release")
proguardFiles(
*fileTree(".") { include("**/*.pro") }
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
password=123456
keyAlias=example
storeFile=./example_key.jks
15 changes: 6 additions & 9 deletions examples/plugin-blec-example/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@ async fn test() -> bool {
let handler = tauri_plugin_blec::get_handler().unwrap();
let start = std::time::Instant::now();
handler
.lock()
.await
.send_data(CHARACTERISTIC_UUID, &DATA)
.await
.unwrap();
let response = handler
.lock()
.await
.recv_data(CHARACTERISTIC_UUID)
.send_data(
CHARACTERISTIC_UUID,
&DATA,
tauri_plugin_blec::models::WriteType::WithoutResponse,
)
.await
.unwrap();
let response = handler.recv_data(CHARACTERISTIC_UUID).await.unwrap();
let time = start.elapsed();
info!("Time elapsed: {:?}", time);
assert_eq!(response, DATA);
Expand Down
24 changes: 20 additions & 4 deletions examples/plugin-blec-example/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { BleDevice, getConnectionUpdates, startScan, sendString, readString, uns
import { onMounted, ref } from 'vue';
import BleDev from './components/BleDev.vue'
import { invoke } from '@tauri-apps/api/core'
import { BarLoader } from '@saeris/vue-spinners'
const devices = ref<BleDevice[]>([])
Expand Down Expand Up @@ -46,6 +45,8 @@ async function test() {
console.error(e)
}
}
const showServices = ref(false);
</script>

<template>
Expand Down Expand Up @@ -87,9 +88,14 @@ async function test() {
<button class="ml" :onclick="subscribe">{{ notifyData ? "Unsubscribe" : "Subscribe" }}</button>
</div>
</div>
<div v-else v-for="device in devices" class="row">
<BleDev :key="device.address" :device="device"
:onclick="() => connect(device.address, () => console.log('disconnected'))" />
<div v-else>
<label id="show-services-label" for="show-services">Show Services</label>
<input v-model="showServices" type="checkbox" id="show-services" />
<div v-for="device in devices" class="row">
<BleDev :key="device.address" :device="device"
:show-services="showServices"
:onclick="() => connect(device.address, () => console.log('disconnected'))" />
</div>
</div>
</div>
</template>
Expand Down Expand Up @@ -200,6 +206,16 @@ button {
margin-right: 5px;
}
#show-services-label {
margin-top: 5px;
font-size: 1.3em;
}
#show-services {
margin: 5px;
height: 2em;
width: 2em;
}
:root {
color: #f6f6f6;
background-color: #2f2f2f;
Expand Down
14 changes: 11 additions & 3 deletions examples/plugin-blec-example/src/components/BleDev.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
<script setup lang="ts">
import { BleDevice } from 'tauri-plugin-blec'
import { BleDevice } from '@mnlphlp/plugin-blec'
const props = defineProps<{
defineProps<{
device: BleDevice
showServices: boolean
}>()
console.log(props.device)
</script>

<template>
<div class="box">
<h1>{{ device.name }}</h1>
<p>{{ device.address }}</p>
<div>
Manufacturer data:
<p v-for="[key, value] in Object.entries(device.manufacturerData)">{{ key }}: {{ value }}</p>
</div>
<div v-if="showServices">
Services:
<p v-for="service in device.services">{{ service }}</p>
</div>
<p>{{ device.isConnected ? "Connected" : "Not Connected" }}</p>
</div>
</template>
Expand Down
13 changes: 8 additions & 5 deletions guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export type BleDevice = {
address: string;
name: string;
isConnected: boolean;
services: string[];
manufacturerData: Record<number, Uint8Array>;
};

/**
Expand Down Expand Up @@ -74,7 +76,6 @@ export async function connect(address: string, onDisconnect: (() => void) | null
})
} catch (e) {
console.error(e)
await disconnect()
}
}

Expand All @@ -83,10 +84,11 @@ export async function connect(address: string, onDisconnect: (() => void) | null
* @param characteristic UUID of the characteristic to write to
* @param data Data to write to the characteristic
*/
export async function send(characteristic: string, data: Uint8Array) {
export async function send(characteristic: string, data: Uint8Array, writeType: 'withResponse' | 'withoutResponse' = 'withResponse') {
await invoke('plugin:blec|send', {
characteristic,
data
data,
writeType,
})
}

Expand All @@ -95,10 +97,11 @@ export async function send(characteristic: string, data: Uint8Array) {
* @param characteristic UUID of the characteristic to write to
* @param data Data to write to the characteristic
*/
export async function sendString(characteristic: string, data: string) {
export async function sendString(characteristic: string, data: string, writeType: 'withResponse' | 'withoutResponse' = 'withResponse') {
await invoke('plugin:blec|send_string', {
characteristic,
data
data,
writeType,
})
}

Expand Down
Loading

0 comments on commit 5bb4a38

Please sign in to comment.