refactor to global static variables to store application states.
This commit is contained in:
		@@ -1,52 +1,60 @@
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::{collections::HashSet, sync::Arc};
 | 
			
		||||
 | 
			
		||||
use btleplug::{
 | 
			
		||||
    api::{Central, CentralState, Manager as _, Peripheral, ScanFilter},
 | 
			
		||||
    platform::{Adapter, Manager},
 | 
			
		||||
    platform::{Adapter, Manager, PeripheralId},
 | 
			
		||||
};
 | 
			
		||||
use futures::StreamExt;
 | 
			
		||||
use tauri::{
 | 
			
		||||
    async_runtime::{self, JoinHandle, RwLock},
 | 
			
		||||
    AppHandle, Emitter, State,
 | 
			
		||||
    async_runtime::{self, JoinHandle},
 | 
			
		||||
    AppHandle, Emitter,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{cmd::PeripheralItem, errors, state::AppState};
 | 
			
		||||
use crate::{cmd::PeripheralItem, errors, state};
 | 
			
		||||
 | 
			
		||||
async fn notify_peripheral_update(app_handle: Arc<AppHandle>, peirpheral: &PeripheralId) {
 | 
			
		||||
    let adapter = state::get_central_adapter().await;
 | 
			
		||||
    if let Some(adapter) = adapter {
 | 
			
		||||
        let peripheral = adapter.peripheral(peirpheral).await;
 | 
			
		||||
        if let Ok(peripheral) = peripheral {
 | 
			
		||||
            let properties = peripheral.properties().await.ok().flatten();
 | 
			
		||||
            let services = state::get_found_setvices(peirpheral).await;
 | 
			
		||||
            let item =
 | 
			
		||||
                PeripheralItem::new_with_cache(&peripheral, properties.as_ref(), services).await;
 | 
			
		||||
            app_handle.emit("peripheral_updated", item).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.get_central_adapter().await;
 | 
			
		||||
    let adapter = state::get_central_adapter().await;
 | 
			
		||||
    let app = Arc::clone(&app_handle);
 | 
			
		||||
    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)
 | 
			
		||||
                    | 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();
 | 
			
		||||
                        }
 | 
			
		||||
                        notify_peripheral_update(Arc::clone(&app_handle), &id).await;
 | 
			
		||||
                        state::update_found_peripherals(id.clone()).await;
 | 
			
		||||
                    }
 | 
			
		||||
                    btleplug::api::CentralEvent::DeviceConnected(id) => {
 | 
			
		||||
                        let state = app_state.write().await;
 | 
			
		||||
                        state.set_connected_peripheral(id).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;
 | 
			
		||||
                        state::clear_connected_peripheral().await;
 | 
			
		||||
                        app.emit("peripheral_disconnected", ()).unwrap();
 | 
			
		||||
                    }
 | 
			
		||||
                    btleplug::api::CentralEvent::ServicesAdvertisement { id, services } => {
 | 
			
		||||
                        let services = HashSet::from_iter(services);
 | 
			
		||||
                        state::update_found_services(id.clone(), services).await;
 | 
			
		||||
                        notify_peripheral_update(Arc::clone(&app_handle), &id).await;
 | 
			
		||||
                    }
 | 
			
		||||
                    _ => {}
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -80,19 +88,15 @@ pub async fn get_central_adapter() -> Result<Adapter, errors::AppError> {
 | 
			
		||||
    found_adapter.ok_or(errors::AppError::NoAvailableBluetoothAdapter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn start_scan(
 | 
			
		||||
    app_handle: Arc<AppHandle>,
 | 
			
		||||
    app_state: State<'_, Arc<RwLock<AppState>>>,
 | 
			
		||||
) -> Result<(), errors::AppError> {
 | 
			
		||||
    let state = app_state.read().await;
 | 
			
		||||
    let adapter = state.get_central_adapter().await;
 | 
			
		||||
pub async fn start_scan(app_handle: Arc<AppHandle>) -> Result<(), errors::AppError> {
 | 
			
		||||
    let adapter = state::get_central_adapter().await;
 | 
			
		||||
    if let Some(adapter) = adapter {
 | 
			
		||||
        adapter
 | 
			
		||||
            .start_scan(ScanFilter::default())
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(|_| errors::AppError::UnableToStartScan)?;
 | 
			
		||||
        state.set_scanning(true).await;
 | 
			
		||||
        state.clear_found_peripherals().await;
 | 
			
		||||
        state::set_scanning(true).await;
 | 
			
		||||
        state::clear_found_peripherals().await;
 | 
			
		||||
        app_handle.emit("scanning_started", ()).unwrap();
 | 
			
		||||
        Ok(())
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -100,18 +104,14 @@ pub async fn start_scan(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn stop_scan(
 | 
			
		||||
    app_handle: Arc<AppHandle>,
 | 
			
		||||
    app_state: State<'_, Arc<RwLock<AppState>>>,
 | 
			
		||||
) -> Result<(), errors::AppError> {
 | 
			
		||||
    let state = app_state.read().await;
 | 
			
		||||
    let adapter = state.get_central_adapter().await;
 | 
			
		||||
pub async fn stop_scan(app_handle: Arc<AppHandle>) -> Result<(), errors::AppError> {
 | 
			
		||||
    let adapter = state::get_central_adapter().await;
 | 
			
		||||
    if let Some(adapter) = adapter {
 | 
			
		||||
        adapter
 | 
			
		||||
            .stop_scan()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(|_| errors::AppError::UnableToStopScan)?;
 | 
			
		||||
        state.set_scanning(false).await;
 | 
			
		||||
        state::set_scanning(false).await;
 | 
			
		||||
        app_handle.emit("scanning_stopped", ()).unwrap();
 | 
			
		||||
        Ok(())
 | 
			
		||||
    } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,10 @@ use btleplug::{
 | 
			
		||||
pub use state::{CentralState, ChannelState, PeripheralItem};
 | 
			
		||||
use tauri::{async_runtime::RwLock, AppHandle, Emitter, State};
 | 
			
		||||
 | 
			
		||||
use crate::{bluetooth, errors, state::AppState};
 | 
			
		||||
use crate::{
 | 
			
		||||
    bluetooth, errors,
 | 
			
		||||
    state::{self as global, AppState},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod state;
 | 
			
		||||
 | 
			
		||||
@@ -20,11 +23,10 @@ pub async fn activate_central_adapter(
 | 
			
		||||
    let adapter = bluetooth::get_central_adapter().await?;
 | 
			
		||||
    {
 | 
			
		||||
        let state = app_state.read().await;
 | 
			
		||||
        state.set_central_adapter(adapter).await;
 | 
			
		||||
        global::set_central_adapter(adapter).await;
 | 
			
		||||
        state.clear_central_event_handler().await;
 | 
			
		||||
    }
 | 
			
		||||
    let handle =
 | 
			
		||||
        bluetooth::handle_bluetooth_events(Arc::clone(&app), Arc::clone(&app_state)).await?;
 | 
			
		||||
    let handle = bluetooth::handle_bluetooth_events(Arc::clone(&app)).await?;
 | 
			
		||||
    {
 | 
			
		||||
        let state = app_state.write().await;
 | 
			
		||||
        state.set_central_event_handler(handle).await;
 | 
			
		||||
@@ -34,11 +36,8 @@ pub async fn activate_central_adapter(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tauri::command]
 | 
			
		||||
pub async fn central_device_state(
 | 
			
		||||
    app_state: State<'_, Arc<RwLock<AppState>>>,
 | 
			
		||||
) -> Result<CentralState, errors::AppError> {
 | 
			
		||||
    let state = app_state.read().await;
 | 
			
		||||
    let central = state.get_central_adapter().await;
 | 
			
		||||
pub async fn central_device_state() -> Result<CentralState, errors::AppError> {
 | 
			
		||||
    let central = global::get_central_adapter().await;
 | 
			
		||||
    if let Some(central) = central {
 | 
			
		||||
        let central_device_state = central
 | 
			
		||||
            .adapter_state()
 | 
			
		||||
@@ -46,8 +45,8 @@ pub async fn central_device_state(
 | 
			
		||||
            .map_err(|_| errors::AppError::BluetoothNotReady)?;
 | 
			
		||||
        Ok(CentralState {
 | 
			
		||||
            is_ready: central_device_state == btleplug::api::CentralState::PoweredOn,
 | 
			
		||||
            is_scanning: *state.scanning.lock().await,
 | 
			
		||||
            connected: state.get_connected_peripheral().await,
 | 
			
		||||
            is_scanning: global::get_scanning().await,
 | 
			
		||||
            connected: global::get_connected_peripheral().await,
 | 
			
		||||
        })
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(CentralState::default())
 | 
			
		||||
@@ -55,18 +54,12 @@ pub async fn central_device_state(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tauri::command]
 | 
			
		||||
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()
 | 
			
		||||
pub async fn connected_peripheral_state() -> Result<Option<PeripheralItem>, errors::AppError> {
 | 
			
		||||
    let connected_peripheral = global::get_connected_peripheral()
 | 
			
		||||
        .await
 | 
			
		||||
        .clone()
 | 
			
		||||
        .ok_or(errors::AppError::NoConnectedPeripheral)?;
 | 
			
		||||
    let central = state
 | 
			
		||||
        .get_central_adapter()
 | 
			
		||||
    let central = global::get_central_adapter()
 | 
			
		||||
        .await
 | 
			
		||||
        .ok_or(errors::AppError::BluetoothAdapterNotFound)?;
 | 
			
		||||
    if let Ok(peripheral) = central.peripheral(&connected_peripheral).await {
 | 
			
		||||
@@ -74,22 +67,23 @@ pub async fn connected_peripheral_state(
 | 
			
		||||
            .properties()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(|_| errors::AppError::UnableToRetrievePeripheralProperties)?;
 | 
			
		||||
        peripheral
 | 
			
		||||
            .discover_services()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(|_| errors::AppError::UnableToRetrievePeripheralServices)?;
 | 
			
		||||
        Ok(Some(
 | 
			
		||||
            PeripheralItem::new(&peripheral, properties.as_ref()).await,
 | 
			
		||||
            PeripheralItem::new_form_peripheral(&peripheral, properties.as_ref()).await,
 | 
			
		||||
        ))
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(None)
 | 
			
		||||
        Err(errors::AppError::NoConnectedPeripheral)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tauri::command]
 | 
			
		||||
pub async fn specific_peripheral_state(
 | 
			
		||||
    app_state: State<'_, Arc<RwLock<AppState>>>,
 | 
			
		||||
    id: PeripheralId,
 | 
			
		||||
) -> Result<PeripheralItem, errors::AppError> {
 | 
			
		||||
    let state = app_state.read().await;
 | 
			
		||||
    let central = state
 | 
			
		||||
        .get_central_adapter()
 | 
			
		||||
    let central = global::get_central_adapter()
 | 
			
		||||
        .await
 | 
			
		||||
        .ok_or(errors::AppError::BluetoothAdapterNotFound)?;
 | 
			
		||||
    let peripheral = central
 | 
			
		||||
@@ -97,7 +91,16 @@ pub async fn specific_peripheral_state(
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(|_| errors::AppError::UnableToRetrievePeripheralState)?;
 | 
			
		||||
    let properties = peripheral.properties().await.unwrap_or(None);
 | 
			
		||||
    Ok(PeripheralItem::new(&peripheral, properties.as_ref()).await)
 | 
			
		||||
    let peripheral_item = if peripheral.connect().await.is_ok() {
 | 
			
		||||
        peripheral.discover_services().await.unwrap_or_default();
 | 
			
		||||
        let item = PeripheralItem::new_form_peripheral(&peripheral, properties.as_ref()).await;
 | 
			
		||||
        peripheral.disconnect().await.unwrap();
 | 
			
		||||
        item
 | 
			
		||||
    } else {
 | 
			
		||||
        let services = global::get_found_setvices(&id).await;
 | 
			
		||||
        PeripheralItem::new_with_cache(&peripheral, properties.as_ref(), services).await
 | 
			
		||||
    };
 | 
			
		||||
    Ok(peripheral_item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tauri::command]
 | 
			
		||||
@@ -115,19 +118,13 @@ pub async fn channel_b_state(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tauri::command]
 | 
			
		||||
pub async fn start_scan_devices(
 | 
			
		||||
    app_handle: AppHandle,
 | 
			
		||||
    app_state: State<'_, Arc<RwLock<AppState>>>,
 | 
			
		||||
) -> Result<(), errors::AppError> {
 | 
			
		||||
    bluetooth::start_scan(Arc::new(app_handle), app_state).await?;
 | 
			
		||||
pub async fn start_scan_devices(app_handle: AppHandle) -> Result<(), errors::AppError> {
 | 
			
		||||
    bluetooth::start_scan(Arc::new(app_handle)).await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tauri::command]
 | 
			
		||||
pub async fn stop_scan_devices(
 | 
			
		||||
    app_handle: AppHandle,
 | 
			
		||||
    app_state: State<'_, Arc<RwLock<AppState>>>,
 | 
			
		||||
) -> Result<(), errors::AppError> {
 | 
			
		||||
    bluetooth::stop_scan(Arc::new(app_handle), app_state).await?;
 | 
			
		||||
pub async fn stop_scan_devices(app_handle: AppHandle) -> Result<(), errors::AppError> {
 | 
			
		||||
    bluetooth::stop_scan(Arc::new(app_handle)).await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
use std::{collections::HashSet, hash::Hash};
 | 
			
		||||
 | 
			
		||||
use btleplug::{
 | 
			
		||||
    api::{Peripheral as _, PeripheralProperties},
 | 
			
		||||
    platform::{Peripheral, PeripheralId},
 | 
			
		||||
};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::playlist::PlayMode;
 | 
			
		||||
 | 
			
		||||
@@ -24,6 +27,14 @@ pub struct PeripheralItem {
 | 
			
		||||
    pub is_connected: bool,
 | 
			
		||||
    pub rssi: Option<i16>,
 | 
			
		||||
    pub battery: Option<i16>,
 | 
			
		||||
    pub services: HashSet<PeripheralService>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Default, Eq, Serialize)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct PeripheralService {
 | 
			
		||||
    pub uuid: Uuid,
 | 
			
		||||
    pub characteristics: HashSet<Uuid>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Default, Serialize)]
 | 
			
		||||
@@ -38,8 +49,24 @@ pub struct ChannelState {
 | 
			
		||||
    pub play_mode: PlayMode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Hash for PeripheralService {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.uuid.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for PeripheralService {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.uuid == other.uuid
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PeripheralItem {
 | 
			
		||||
    pub async fn new(periperial: &Peripheral, properties: Option<&PeripheralProperties>) -> Self {
 | 
			
		||||
    pub async fn new(
 | 
			
		||||
        periperial: &Peripheral,
 | 
			
		||||
        properties: Option<&PeripheralProperties>,
 | 
			
		||||
        services: HashSet<PeripheralService>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            id: periperial.id(),
 | 
			
		||||
            address: periperial.address().to_string(),
 | 
			
		||||
@@ -48,6 +75,58 @@ impl PeripheralItem {
 | 
			
		||||
            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),
 | 
			
		||||
            services,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn new_with_cache(
 | 
			
		||||
        periperial: &Peripheral,
 | 
			
		||||
        properties: Option<&PeripheralProperties>,
 | 
			
		||||
        services: HashSet<Uuid>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let service = services
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|uuid| PeripheralService {
 | 
			
		||||
                uuid,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        Self {
 | 
			
		||||
            id: periperial.id(),
 | 
			
		||||
            address: periperial.address().to_string(),
 | 
			
		||||
            represent: properties.and_then(|p| p.local_name.clone()),
 | 
			
		||||
            is_unknown: properties.and_then(|p| p.local_name.clone()).is_none(),
 | 
			
		||||
            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),
 | 
			
		||||
            services: service,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn new_form_peripheral(
 | 
			
		||||
        periperial: &Peripheral,
 | 
			
		||||
        properties: Option<&PeripheralProperties>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let services = periperial
 | 
			
		||||
            .services()
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|s| {
 | 
			
		||||
                let characteristics = s.characteristics.iter().map(|c| c.uuid).collect();
 | 
			
		||||
                PeripheralService {
 | 
			
		||||
                    uuid: s.uuid,
 | 
			
		||||
                    characteristics,
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        Self {
 | 
			
		||||
            id: periperial.id(),
 | 
			
		||||
            address: periperial.address().to_string(),
 | 
			
		||||
            represent: properties.and_then(|p| p.local_name.clone()),
 | 
			
		||||
            is_unknown: properties.and_then(|p| p.local_name.clone()).is_none(),
 | 
			
		||||
            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),
 | 
			
		||||
            services,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,4 +21,6 @@ pub enum AppError {
 | 
			
		||||
    UnableToRetrievePeripheralProperties,
 | 
			
		||||
    #[error("Unable to retrieve peripheral state")]
 | 
			
		||||
    UnableToRetrievePeripheralState,
 | 
			
		||||
    #[error("Unable to retrieve peripheral services")]
 | 
			
		||||
    UnableToRetrievePeripheralServices,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,54 +1,45 @@
 | 
			
		||||
use std::{collections::HashSet, sync::Arc};
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::{HashMap, HashSet},
 | 
			
		||||
    sync::{Arc, LazyLock},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use btleplug::platform::{Adapter, PeripheralId};
 | 
			
		||||
use tauri::{
 | 
			
		||||
    async_runtime::{JoinHandle, Mutex, RwLock},
 | 
			
		||||
    AppHandle, Runtime,
 | 
			
		||||
};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::config_db;
 | 
			
		||||
 | 
			
		||||
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<PeripheralId>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe impl Send for AppState {}
 | 
			
		||||
unsafe impl Sync for AppState {}
 | 
			
		||||
 | 
			
		||||
static CENTRAL_ADAPTER: LazyLock<Arc<RwLock<Option<Adapter>>>> =
 | 
			
		||||
    LazyLock::new(|| Arc::new(RwLock::new(None)));
 | 
			
		||||
static CACHED_SERVICES: LazyLock<Arc<RwLock<HashMap<PeripheralId, HashSet<Uuid>>>>> =
 | 
			
		||||
    LazyLock::new(|| Arc::new(RwLock::new(HashMap::new())));
 | 
			
		||||
static FOUND_PERIPHERALS: LazyLock<Arc<RwLock<HashSet<PeripheralId>>>> =
 | 
			
		||||
    LazyLock::new(|| Arc::new(RwLock::new(HashSet::new())));
 | 
			
		||||
static SCANNING: LazyLock<Arc<Mutex<bool>>> = LazyLock::new(|| Arc::new(Mutex::new(false)));
 | 
			
		||||
static CONNECTED_PERIPHERAL: LazyLock<Arc<Mutex<Option<PeripheralId>>>> =
 | 
			
		||||
    LazyLock::new(|| Arc::new(Mutex::new(None)));
 | 
			
		||||
 | 
			
		||||
impl AppState {
 | 
			
		||||
    pub async fn new<R: Runtime>(app: &AppHandle<R>) -> anyhow::Result<Self> {
 | 
			
		||||
        let db = config_db::open_config_db(app)?;
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            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)),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_central_adapter(&self, adapter: Adapter) {
 | 
			
		||||
        let mut central_adapter = self.central_adapter.write().await;
 | 
			
		||||
        *central_adapter = Some(adapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn clear_central_adapter(&self) {
 | 
			
		||||
        let mut central_adapter = self.central_adapter.write().await;
 | 
			
		||||
        *central_adapter = None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_central_adapter(&self) -> Option<Adapter> {
 | 
			
		||||
        let central_adapter = self.central_adapter.read().await;
 | 
			
		||||
        central_adapter.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_central_event_handler(&self, handler: JoinHandle<()>) {
 | 
			
		||||
        let mut central_event_handler = self.central_event_handler.lock().await;
 | 
			
		||||
        *central_event_handler = Some(handler);
 | 
			
		||||
@@ -60,39 +51,71 @@ impl AppState {
 | 
			
		||||
            handler.abort();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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: PeripheralId) {
 | 
			
		||||
        let mut connected_peripheral = self.connected_peripheral.lock().await;
 | 
			
		||||
        *connected_peripheral = Some(peripheral);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn clear_connected_peripheral(&self) {
 | 
			
		||||
        let mut connected_peripheral = self.connected_peripheral.lock().await;
 | 
			
		||||
        *connected_peripheral = None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_connected_peripheral(&self) -> Option<PeripheralId> {
 | 
			
		||||
        let connected_peripheral = self.connected_peripheral.lock().await;
 | 
			
		||||
        connected_peripheral.clone()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_central_adapter() -> Option<Adapter> {
 | 
			
		||||
    let central_adapter = CENTRAL_ADAPTER.read().await;
 | 
			
		||||
    central_adapter.clone()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn set_central_adapter(adapter: Adapter) {
 | 
			
		||||
    let mut central_adapter = CENTRAL_ADAPTER.write().await;
 | 
			
		||||
    *central_adapter = Some(adapter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn cleaar_central_adapter() {
 | 
			
		||||
    let mut central_adapter = CENTRAL_ADAPTER.write().await;
 | 
			
		||||
    *central_adapter = None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_found_peripherals() -> HashSet<PeripheralId> {
 | 
			
		||||
    let found_peripherals = FOUND_PERIPHERALS.read().await;
 | 
			
		||||
    found_peripherals.clone()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn update_found_peripherals(peripheral: PeripheralId) {
 | 
			
		||||
    let mut found_peripherals = FOUND_PERIPHERALS.write().await;
 | 
			
		||||
    found_peripherals.insert(peripheral);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn clear_found_peripherals() {
 | 
			
		||||
    let mut found_peripherals = FOUND_PERIPHERALS.write().await;
 | 
			
		||||
    found_peripherals.clear();
 | 
			
		||||
    let mut cached_services = CACHED_SERVICES.write().await;
 | 
			
		||||
    cached_services.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_found_setvices(peripheral: &PeripheralId) -> HashSet<Uuid> {
 | 
			
		||||
    let cached_services = CACHED_SERVICES.read().await;
 | 
			
		||||
    cached_services.get(peripheral).cloned().unwrap_or_default()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn update_found_services(peripheral: PeripheralId, services: HashSet<Uuid>) {
 | 
			
		||||
    let mut cached_services = CACHED_SERVICES.write().await;
 | 
			
		||||
    cached_services.insert(peripheral, services);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_scanning() -> bool {
 | 
			
		||||
    let scanning = SCANNING.lock().await;
 | 
			
		||||
    *scanning
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn set_scanning(scanning: bool) {
 | 
			
		||||
    let mut scanning_state = SCANNING.lock().await;
 | 
			
		||||
    *scanning_state = scanning;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_connected_peripheral() -> Option<PeripheralId> {
 | 
			
		||||
    let connected_peripheral = CONNECTED_PERIPHERAL.lock().await;
 | 
			
		||||
    connected_peripheral.clone()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn set_connected_peripheral(peripheral: PeripheralId) {
 | 
			
		||||
    let mut connected_peripheral = CONNECTED_PERIPHERAL.lock().await;
 | 
			
		||||
    *connected_peripheral = Some(peripheral);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn clear_connected_peripheral() {
 | 
			
		||||
    let mut connected_peripheral = CONNECTED_PERIPHERAL.lock().await;
 | 
			
		||||
    *connected_peripheral = None;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ type PeripheralItem = {
 | 
			
		||||
  isConnected: boolean;
 | 
			
		||||
  rssi: number | null;
 | 
			
		||||
  battery: number | null;
 | 
			
		||||
  services: { uuid: string; characteristics: string[] }[];
 | 
			
		||||
};
 | 
			
		||||
type BluetoothState = {
 | 
			
		||||
  ready: boolean | null;
 | 
			
		||||
@@ -84,7 +85,6 @@ export const AvailablePeripherals = atom((get) => {
 | 
			
		||||
});
 | 
			
		||||
export const UnknownPeripherals = atom((get) => {
 | 
			
		||||
  const peripherals = get(Peripherals);
 | 
			
		||||
  console.debug('[origin peripherals]', peripherals);
 | 
			
		||||
  return peripherals.filter((i) => i.isUnknown);
 | 
			
		||||
});
 | 
			
		||||
const Channels: Record<Channels, PrimitiveAtom<ChannelState>> = {
 | 
			
		||||
@@ -158,7 +158,7 @@ const EstimWatchProvider: FC<{ children?: ReactNode }> = ({ children }) => {
 | 
			
		||||
      try {
 | 
			
		||||
        unlistenBle.current = await listen('central_state_updated', () => refreshBle());
 | 
			
		||||
        unlistenDevice.current = await listen('peripheral_connected', () => refreshDevice());
 | 
			
		||||
        unlistenPreipherals.current = await listen('peripheral_found', (event) => {
 | 
			
		||||
        unlistenPreipherals.current = await listen('peripheral_updated', (event) => {
 | 
			
		||||
          refreshPeripherals(event.payload);
 | 
			
		||||
        });
 | 
			
		||||
        unlistenChannelA.current = await listen('channel_a_updated', () => refreshChannelA());
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user