refactor bluetooth device scan alogrithm.
This commit is contained in:
parent
6c8bd6c524
commit
63c7d49943
|
@ -1,8 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use btleplug::{
|
||||
api::{Central, CentralState, Manager as _, Peripheral as _, ScanFilter},
|
||||
platform::{Adapter, Manager, Peripheral},
|
||||
api::{Central, CentralState, Manager as _, Peripheral, ScanFilter},
|
||||
platform::{Adapter, Manager},
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use tauri::{
|
||||
|
@ -10,37 +10,37 @@ use tauri::{
|
|||
AppHandle, Emitter, State,
|
||||
};
|
||||
|
||||
use crate::{errors, state::AppState};
|
||||
use crate::{cmd::PeripheralItem, errors, state::AppState};
|
||||
|
||||
pub async fn handle_bluetooth_events(
|
||||
app_handle: Arc<AppHandle>,
|
||||
app_state: Arc<RwLock<AppState>>,
|
||||
) -> Result<JoinHandle<()>, errors::AppError> {
|
||||
let state = app_state.read().await;
|
||||
let adapter = state.central_adapter.read().await;
|
||||
let adapter = state.get_central_adapter().await;
|
||||
let app = Arc::clone(&app_handle);
|
||||
if let Some(adapter) = &*adapter {
|
||||
if let Some(adapter) = adapter {
|
||||
let adapter = adapter.clone();
|
||||
let app_state = Arc::clone(&app_state);
|
||||
Ok(async_runtime::spawn(async move {
|
||||
let mut event_stream = adapter.events().await.unwrap();
|
||||
while let Some(event) = event_stream.next().await {
|
||||
match event {
|
||||
btleplug::api::CentralEvent::DeviceDiscovered(_id) => {
|
||||
app.emit("peripheral_found", ()).unwrap();
|
||||
}
|
||||
btleplug::api::CentralEvent::DeviceConnected(_id) => {
|
||||
btleplug::api::CentralEvent::DeviceDiscovered(id)
|
||||
| btleplug::api::CentralEvent::DeviceUpdated(id) => {
|
||||
let peripheral = adapter.peripheral(&id).await;
|
||||
if let Ok(peripheral) = peripheral {
|
||||
let properties = peripheral.properties().await.ok().flatten();
|
||||
let state = app_state.write().await;
|
||||
let peripherals = adapter.peripherals().await;
|
||||
if let Ok(peripherals) = peripherals {
|
||||
for peripheral in peripherals {
|
||||
if peripheral.id() == _id {
|
||||
state.set_connected_peripheral(peripheral).await;
|
||||
state.update_found_peripherals(id.clone()).await;
|
||||
let item = PeripheralItem::new(&peripheral, properties.as_ref()).await;
|
||||
app.emit("peripheral_found", item).unwrap();
|
||||
}
|
||||
}
|
||||
btleplug::api::CentralEvent::DeviceConnected(id) => {
|
||||
let state = app_state.write().await;
|
||||
state.set_connected_peripheral(id).await;
|
||||
app.emit("peripheral_connected", ()).unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
btleplug::api::CentralEvent::DeviceDisconnected(_id) => {
|
||||
let state = app_state.write().await;
|
||||
|
@ -92,6 +92,7 @@ pub async fn start_scan(
|
|||
.await
|
||||
.map_err(|_| errors::AppError::UnableToStartScan)?;
|
||||
state.set_scanning(true).await;
|
||||
state.clear_found_peripherals().await;
|
||||
app_handle.emit("scanning_started", ()).unwrap();
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -117,18 +118,3 @@ pub async fn stop_scan(
|
|||
Err(errors::AppError::NoAvailableBluetoothAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_peripherals(
|
||||
app_state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<Vec<Peripheral>, errors::AppError> {
|
||||
let state = app_state.read().await; // Change from lock() to read()
|
||||
let adapter = state.get_central_adapter().await;
|
||||
if let Some(adapter) = adapter {
|
||||
Ok(adapter
|
||||
.peripherals()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripherals)?)
|
||||
} else {
|
||||
Err(errors::AppError::NoAvailableBluetoothAdapter)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use btleplug::api::{Central as _, Peripheral as _};
|
||||
use state::{CentralState, ChannelState, PeripheralItem};
|
||||
pub use state::{CentralState, ChannelState, PeripheralItem};
|
||||
use tauri::{async_runtime::RwLock, AppHandle, Emitter, State};
|
||||
|
||||
use crate::{bluetooth, errors, state::AppState};
|
||||
|
@ -44,12 +44,7 @@ pub async fn central_device_state(
|
|||
Ok(CentralState {
|
||||
is_ready: central_device_state == btleplug::api::CentralState::PoweredOn,
|
||||
is_scanning: *state.scanning.lock().await,
|
||||
connected: state
|
||||
.connected_peripheral
|
||||
.lock()
|
||||
.await
|
||||
.clone()
|
||||
.map(|p| p.id()),
|
||||
connected: state.get_connected_peripheral().await,
|
||||
})
|
||||
} else {
|
||||
Ok(CentralState::default())
|
||||
|
@ -61,72 +56,27 @@ pub async fn connected_peripheral_state(
|
|||
app_state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<Option<PeripheralItem>, errors::AppError> {
|
||||
let state = app_state.read().await;
|
||||
let connected_peripheral = state.connected_peripheral.lock().await.clone();
|
||||
if let Some(peripheral) = connected_peripheral {
|
||||
let connected_peripheral = state
|
||||
.connected_peripheral
|
||||
.lock()
|
||||
.await
|
||||
.clone()
|
||||
.ok_or(errors::AppError::NoConnectedPeripheral)?;
|
||||
let central = state
|
||||
.get_central_adapter()
|
||||
.await
|
||||
.ok_or(errors::AppError::BluetoothAdapterNotFound)?;
|
||||
if let Ok(peripheral) = central.peripheral(&connected_peripheral).await {
|
||||
let properties = peripheral
|
||||
.properties()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripheralProperties)?;
|
||||
if let Some(properties) = properties {
|
||||
let represent = properties
|
||||
.local_name
|
||||
.unwrap_or_else(|| properties.address.to_string());
|
||||
Ok(Some(PeripheralItem {
|
||||
id: peripheral.id(),
|
||||
address: properties.address.to_string(),
|
||||
represent,
|
||||
is_connected: peripheral
|
||||
.is_connected()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripheralProperties)?,
|
||||
rssi: properties.rssi,
|
||||
battery: properties.tx_power_level,
|
||||
}))
|
||||
Ok(Some(
|
||||
PeripheralItem::new(&peripheral, properties.as_ref()).await,
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn found_peripherals(
|
||||
app_state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<Vec<PeripheralItem>, errors::AppError> {
|
||||
let state = app_state.read().await;
|
||||
let central = state.get_central_adapter().await;
|
||||
let mut peripherals: Vec<PeripheralItem> = vec![];
|
||||
if let Some(central) = central {
|
||||
let found_peripherals = central
|
||||
.peripherals()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripherals)?;
|
||||
for peripheral in found_peripherals {
|
||||
let properties = peripheral
|
||||
.properties()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripheralProperties)?;
|
||||
if let Some(properties) = properties {
|
||||
let represent = properties
|
||||
.local_name
|
||||
.unwrap_or_else(|| properties.address.to_string());
|
||||
let item = PeripheralItem {
|
||||
id: peripheral.id(),
|
||||
address: properties.address.to_string(),
|
||||
represent,
|
||||
is_connected: peripheral
|
||||
.is_connected()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripheralProperties)?,
|
||||
rssi: properties.rssi,
|
||||
battery: properties.tx_power_level,
|
||||
};
|
||||
peripherals.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(peripherals)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use btleplug::platform::PeripheralId;
|
||||
use btleplug::{
|
||||
api::{Peripheral as _, PeripheralProperties},
|
||||
platform::{Peripheral, PeripheralId},
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::playlist::PlayMode;
|
||||
|
@ -33,3 +36,18 @@ pub struct ChannelState {
|
|||
pub boost_limit: u32,
|
||||
pub play_mode: PlayMode,
|
||||
}
|
||||
|
||||
impl PeripheralItem {
|
||||
pub async fn new(periperial: &Peripheral, properties: Option<&PeripheralProperties>) -> Self {
|
||||
Self {
|
||||
id: periperial.id(),
|
||||
address: periperial.address().to_string(),
|
||||
represent: properties
|
||||
.and_then(|p| p.local_name.clone())
|
||||
.unwrap_or(String::from("Unknown")),
|
||||
is_connected: periperial.is_connected().await.unwrap_or(false),
|
||||
rssi: properties.and_then(|p| p.rssi),
|
||||
battery: properties.and_then(|p| p.tx_power_level),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ pub enum AppError {
|
|||
BluetoothAdapterNotFound,
|
||||
#[error("No available Bluetooth adapter, maybe not powered on")]
|
||||
NoAvailableBluetoothAdapter,
|
||||
#[error("No connected peripheral")]
|
||||
NoConnectedPeripheral,
|
||||
#[error("Unable to start scan devices")]
|
||||
UnableToStartScan,
|
||||
#[error("Unable to stop scan devices")]
|
||||
|
|
|
@ -55,7 +55,6 @@ pub fn run() {
|
|||
.invoke_handler(generate_handler![
|
||||
cmd::central_device_state,
|
||||
cmd::connected_peripheral_state,
|
||||
cmd::found_peripherals,
|
||||
cmd::channel_a_state,
|
||||
cmd::channel_b_state,
|
||||
cmd::activate_central_adapter,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
use btleplug::platform::{Adapter, Peripheral};
|
||||
use btleplug::platform::{Adapter, PeripheralId};
|
||||
use tauri::{
|
||||
async_runtime::{JoinHandle, Mutex, RwLock},
|
||||
AppHandle, Runtime,
|
||||
|
@ -12,8 +12,9 @@ pub struct AppState {
|
|||
pub db: Arc<Mutex<sled::Db>>,
|
||||
pub central_adapter: Arc<RwLock<Option<Adapter>>>,
|
||||
pub central_event_handler: Arc<Mutex<Option<JoinHandle<()>>>>,
|
||||
pub found_peripherals: Arc<RwLock<HashSet<PeripheralId>>>,
|
||||
pub scanning: Arc<Mutex<bool>>,
|
||||
pub connected_peripheral: Arc<Mutex<Option<Arc<Peripheral>>>>,
|
||||
pub connected_peripheral: Arc<Mutex<Option<PeripheralId>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AppState {}
|
||||
|
@ -27,6 +28,7 @@ impl AppState {
|
|||
db: Arc::new(Mutex::new(db)),
|
||||
central_adapter: Arc::new(RwLock::new(None)),
|
||||
central_event_handler: Arc::new(Mutex::new(None)),
|
||||
found_peripherals: Arc::new(RwLock::new(HashSet::new())),
|
||||
scanning: Arc::new(Mutex::new(false)),
|
||||
connected_peripheral: Arc::new(Mutex::new(None)),
|
||||
})
|
||||
|
@ -59,14 +61,29 @@ impl AppState {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn get_found_peripherals(&self) -> HashSet<PeripheralId> {
|
||||
let found_peripherals = self.found_peripherals.read().await;
|
||||
found_peripherals.clone()
|
||||
}
|
||||
|
||||
pub async fn update_found_peripherals(&self, peripheral: PeripheralId) {
|
||||
let mut found_peripherals = self.found_peripherals.write().await;
|
||||
found_peripherals.insert(peripheral);
|
||||
}
|
||||
|
||||
pub async fn clear_found_peripherals(&self) {
|
||||
let mut found_peripherals = self.found_peripherals.write().await;
|
||||
found_peripherals.clear();
|
||||
}
|
||||
|
||||
pub async fn set_scanning(&self, scanning_state: bool) {
|
||||
let mut scanning = self.scanning.lock().await;
|
||||
*scanning = scanning_state;
|
||||
}
|
||||
|
||||
pub async fn set_connected_peripheral(&self, peripheral: Peripheral) {
|
||||
pub async fn set_connected_peripheral(&self, peripheral: PeripheralId) {
|
||||
let mut connected_peripheral = self.connected_peripheral.lock().await;
|
||||
*connected_peripheral = Some(Arc::new(peripheral));
|
||||
*connected_peripheral = Some(peripheral);
|
||||
}
|
||||
|
||||
pub async fn clear_connected_peripheral(&self) {
|
||||
|
@ -74,13 +91,8 @@ impl AppState {
|
|||
*connected_peripheral = None;
|
||||
}
|
||||
|
||||
pub async fn get_connected_peripheral(&self) -> Option<Arc<Peripheral>> {
|
||||
pub async fn get_connected_peripheral(&self) -> Option<PeripheralId> {
|
||||
let connected_peripheral = self.connected_peripheral.lock().await;
|
||||
|
||||
if let Some(peripheral) = connected_peripheral.clone() {
|
||||
Some(Arc::clone(&peripheral))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
connected_peripheral.clone()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user