diff --git a/src-tauri/src/bluetooth.rs b/src-tauri/src/bluetooth.rs index f7f7f12..ee7af03 100644 --- a/src-tauri/src/bluetooth.rs +++ b/src-tauri/src/bluetooth.rs @@ -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,38 +10,38 @@ 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, app_state: Arc>, ) -> Result, 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) => { - 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; - app.emit("peripheral_connected", ()).unwrap(); - break; - } - } + 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; + 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(); + } btleplug::api::CentralEvent::DeviceDisconnected(_id) => { let state = app_state.write().await; state.clear_connected_peripheral().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>>, -) -> Result, 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) - } -} diff --git a/src-tauri/src/cmd/mod.rs b/src-tauri/src/cmd/mod.rs index bba1794..15b4bb2 100644 --- a/src-tauri/src/cmd/mod.rs +++ b/src-tauri/src/cmd/mod.rs @@ -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,74 +56,29 @@ pub async fn connected_peripheral_state( app_state: State<'_, Arc>>, ) -> Result, 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, - })) - } else { - Ok(None) - } + Ok(Some( + PeripheralItem::new(&peripheral, properties.as_ref()).await, + )) } else { Ok(None) } } -#[tauri::command] -pub async fn found_peripherals( - app_state: State<'_, Arc>>, -) -> Result, errors::AppError> { - let state = app_state.read().await; - let central = state.get_central_adapter().await; - let mut peripherals: Vec = 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] pub async fn channel_a_state( _app_state: State<'_, Arc>>, diff --git a/src-tauri/src/cmd/state.rs b/src-tauri/src/cmd/state.rs index 2fd8e1d..9718464 100644 --- a/src-tauri/src/cmd/state.rs +++ b/src-tauri/src/cmd/state.rs @@ -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), + } + } +} diff --git a/src-tauri/src/errors.rs b/src-tauri/src/errors.rs index 859d41b..eed7a58 100644 --- a/src-tauri/src/errors.rs +++ b/src-tauri/src/errors.rs @@ -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")] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 37df5c4..a8e5bcd 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -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, diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 5208d82..4abe5fe 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -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>, pub central_adapter: Arc>>, pub central_event_handler: Arc>>>, + pub found_peripherals: Arc>>, pub scanning: Arc>, - pub connected_peripheral: Arc>>>, + pub connected_peripheral: Arc>>, } 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 { + 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> { + pub async fn get_connected_peripheral(&self) -> Option { let connected_peripheral = self.connected_peripheral.lock().await; - - if let Some(peripheral) = connected_peripheral.clone() { - Some(Arc::clone(&peripheral)) - } else { - None - } + connected_peripheral.clone() } }