project initiate.
7
src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Generated by Tauri
|
||||
# will have schema files for capabilities auto-completion
|
||||
/gen/schemas
|
40
src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,40 @@
|
||||
[package]
|
||||
name = "estim"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
# The `_lib` suffix may seem redundant but it is necessary
|
||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||
name = "estim_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
btleplug = { version = "0.11.6", features = ["serde"] }
|
||||
tokio = { version = "1.40.0", features = ["full"] }
|
||||
sled = "0.34.7"
|
||||
anyhow = "1.0.89"
|
||||
thiserror = "2.0.11"
|
||||
serde_repr = "0.1.19"
|
||||
bincode = "1.3.3"
|
||||
uuid = { version = "1.10.0", features = ["serde"] }
|
||||
tauri-plugin-dialog = "2"
|
||||
futures = "0.3.31"
|
||||
tauri-plugin-devtools = "2.0.0"
|
||||
tauri-plugin-os = "2"
|
||||
tauri-plugin-notification = "2"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-single-instance = "2"
|
3
src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
16
src-tauri/capabilities/default.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"core:window:allow-start-dragging",
|
||||
"os:default",
|
||||
"notification:default",
|
||||
"dialog:default"
|
||||
]
|
||||
}
|
BIN
src-tauri/icons/128x128.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
After Width: | Height: | Size: 974 B |
BIN
src-tauri/icons/Square107x107Logo.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
src-tauri/icons/Square142x142Logo.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
src-tauri/icons/Square150x150Logo.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
src-tauri/icons/Square284x284Logo.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
src-tauri/icons/Square30x30Logo.png
Normal file
After Width: | Height: | Size: 903 B |
BIN
src-tauri/icons/Square310x310Logo.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
src-tauri/icons/Square44x44Logo.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src-tauri/icons/Square71x71Logo.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src-tauri/icons/Square89x89Logo.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src-tauri/icons/StoreLogo.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
src-tauri/icons/icon.png
Normal file
After Width: | Height: | Size: 14 KiB |
134
src-tauri/src/bluetooth.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use btleplug::{
|
||||
api::{Central, CentralState, Manager as _, Peripheral as _, ScanFilter},
|
||||
platform::{Adapter, Manager, Peripheral},
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use tauri::{
|
||||
async_runtime::{self, JoinHandle, RwLock},
|
||||
AppHandle, Emitter, State,
|
||||
};
|
||||
|
||||
use crate::{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 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) => {
|
||||
app.emit("app_state_updated", ()).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("app_state_updated", ()).unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
btleplug::api::CentralEvent::DeviceDisconnected(_id) => {
|
||||
let state = app_state.write().await;
|
||||
state.clear_connected_peripheral().await;
|
||||
app.emit("app_state_updated", ()).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
app.emit("app_state_updated", ()).unwrap();
|
||||
Err(errors::AppError::BluetoothNotReady)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_central_adapter() -> Result<Adapter, errors::AppError> {
|
||||
let manager = Manager::new()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::BluetoothNotReady)?;
|
||||
let adapters = manager
|
||||
.adapters()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::BluetoothAdapterNotFound)?;
|
||||
|
||||
let mut found_adapter = None;
|
||||
for adpater in adapters {
|
||||
let state = adpater.adapter_state().await;
|
||||
if let Ok(state) = state {
|
||||
if state == CentralState::PoweredOn {
|
||||
found_adapter = Some(adpater);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if let Some(adapter) = adapter {
|
||||
adapter
|
||||
.start_scan(ScanFilter::default())
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToStartScan)?;
|
||||
app_handle.emit("app_state_updated", ()).unwrap();
|
||||
state.set_scanning(true).await;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors::AppError::NoAvailableBluetoothAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if let Some(adapter) = adapter {
|
||||
adapter
|
||||
.stop_scan()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToStopScan)?;
|
||||
app_handle.emit("app_state_updated", ()).unwrap();
|
||||
state.set_scanning(false).await;
|
||||
Ok(())
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
122
src-tauri/src/cmd/mod.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use btleplug::api::{Central as _, Peripheral as _};
|
||||
use state::{ApplicationState, CentralState, ChannelState, PeripheralItem};
|
||||
use tauri::{async_runtime::RwLock, AppHandle, Emitter, State};
|
||||
|
||||
use crate::{bluetooth, errors, state::AppState};
|
||||
|
||||
mod state;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn activate_central_adapter(
|
||||
app_handle: AppHandle,
|
||||
app_state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<(), errors::AppError> {
|
||||
let app = Arc::new(app_handle);
|
||||
let adapter = bluetooth::get_central_adapter().await?;
|
||||
{
|
||||
let state = app_state.read().await;
|
||||
state.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 state = app_state.write().await; // Changed from lock() to write()
|
||||
state.set_central_event_handler(handle).await;
|
||||
}
|
||||
app.emit("app_state_updated", ()).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn refresh_application_state(
|
||||
app_state: State<'_, Arc<RwLock<AppState>>>,
|
||||
) -> Result<ApplicationState, errors::AppError> {
|
||||
let state = app_state.read().await;
|
||||
let mut peripherals: Vec<PeripheralItem> = vec![];
|
||||
let mut connected_peripheral = None;
|
||||
|
||||
let central = state.get_central_adapter().await;
|
||||
let central_state = if let Some(central) = central {
|
||||
let central_device_state = central
|
||||
.adapter_state()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::BluetoothNotReady)?;
|
||||
|
||||
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,
|
||||
};
|
||||
if peripheral
|
||||
.is_connected()
|
||||
.await
|
||||
.map_err(|_| errors::AppError::UnableToRetrievePeripheralState)?
|
||||
{
|
||||
connected_peripheral = Some(item.clone());
|
||||
}
|
||||
peripherals.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
}
|
||||
} else {
|
||||
CentralState::default()
|
||||
};
|
||||
|
||||
Ok(ApplicationState {
|
||||
central: central_state,
|
||||
peripherals,
|
||||
connected_peripheral,
|
||||
channel_a: ChannelState::default(),
|
||||
channel_b: ChannelState::default(),
|
||||
})
|
||||
}
|
||||
|
||||
#[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?;
|
||||
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?;
|
||||
Ok(())
|
||||
}
|
41
src-tauri/src/cmd/state.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use btleplug::platform::PeripheralId;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::playlist::PlayMode;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
pub struct CentralState {
|
||||
pub is_ready: bool,
|
||||
pub is_scanning: bool,
|
||||
pub connected: Option<PeripheralId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PeripheralItem {
|
||||
pub id: PeripheralId,
|
||||
pub address: String,
|
||||
pub represent: String,
|
||||
pub is_connected: bool,
|
||||
pub rssi: Option<i16>,
|
||||
pub battery: Option<i16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
pub struct ChannelState {
|
||||
pub is_playing: bool,
|
||||
pub strength: u32,
|
||||
pub strength_limit: u32,
|
||||
pub is_boosting: bool,
|
||||
pub boost_level: u32,
|
||||
pub boost_limit: u32,
|
||||
pub play_mode: PlayMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ApplicationState {
|
||||
pub central: CentralState,
|
||||
pub peripherals: Vec<PeripheralItem>,
|
||||
pub connected_peripheral: Option<PeripheralItem>,
|
||||
pub channel_a: ChannelState,
|
||||
pub channel_b: ChannelState,
|
||||
}
|
23
src-tauri/src/config_db.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::{fs::DirBuilder, path::PathBuf};
|
||||
|
||||
use tauri::{path::PathResolver, AppHandle, Manager, Runtime};
|
||||
|
||||
fn ensure_dir<R: Runtime>(resolver: &PathResolver<R>) -> anyhow::Result<PathBuf> {
|
||||
let config_dir = resolver
|
||||
.local_data_dir()
|
||||
.map_err(|e| anyhow::anyhow!("Unable to get local data directory: {}", e))?;
|
||||
if !config_dir.exists() {
|
||||
let mut dir_creator = DirBuilder::new();
|
||||
dir_creator
|
||||
.recursive(true)
|
||||
.create(&config_dir)
|
||||
.map_err(|e| anyhow::anyhow!("Unable to create config directory: {}", e))?;
|
||||
}
|
||||
Ok(config_dir)
|
||||
}
|
||||
|
||||
pub fn open_config_db<R: Runtime>(app_handle: &AppHandle<R>) -> anyhow::Result<sled::Db> {
|
||||
let config_dir = ensure_dir(app_handle.path())?;
|
||||
let db_path = config_dir.join("conf.db");
|
||||
sled::open(&db_path).map_err(|e| anyhow::anyhow!("Unable to open config database: {}", e))
|
||||
}
|
22
src-tauri/src/errors.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, Serialize)]
|
||||
pub enum AppError {
|
||||
#[error("Bluetooth not ready")]
|
||||
BluetoothNotReady,
|
||||
#[error("No Bluetooth adapter found")]
|
||||
BluetoothAdapterNotFound,
|
||||
#[error("No available Bluetooth adapter, maybe not powered on")]
|
||||
NoAvailableBluetoothAdapter,
|
||||
#[error("Unable to start scan devices")]
|
||||
UnableToStartScan,
|
||||
#[error("Unable to stop scan devices")]
|
||||
UnableToStopScan,
|
||||
#[error("Unable to retrieve peripherals")]
|
||||
UnableToRetrievePeripherals,
|
||||
#[error("Unable to retrieve peripheral properties")]
|
||||
UnableToRetrievePeripheralProperties,
|
||||
#[error("Unable to retrieve peripheral state")]
|
||||
UnableToRetrievePeripheralState,
|
||||
}
|
63
src-tauri/src/lib.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
#![allow(dead_code)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use tauri::{
|
||||
async_runtime::{self, RwLock},
|
||||
generate_handler, Manager,
|
||||
};
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
|
||||
mod bluetooth;
|
||||
mod cmd;
|
||||
mod config_db;
|
||||
mod errors;
|
||||
mod playlist;
|
||||
mod state;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
#[cfg(debug_assertions)]
|
||||
let devtools = tauri_plugin_devtools::init();
|
||||
|
||||
let mut builder = tauri::Builder::default();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
builder = builder.plugin(devtools);
|
||||
|
||||
builder
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_single_instance::init(|_app, _args, _cwd| {}))
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.setup(|app| {
|
||||
if let Err(e) = async_runtime::block_on(async {
|
||||
let state = state::AppState::new(app.handle()).await?;
|
||||
app.manage(Arc::new(RwLock::new(state)));
|
||||
Ok::<(), anyhow::Error>(())
|
||||
}) {
|
||||
app.dialog()
|
||||
.message(e.to_string())
|
||||
.kind(tauri_plugin_dialog::MessageDialogKind::Error)
|
||||
.title("Initialization error")
|
||||
.blocking_show();
|
||||
return Err(e.into());
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
window.open_devtools();
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(generate_handler![
|
||||
cmd::refresh_application_state,
|
||||
cmd::activate_central_adapter,
|
||||
cmd::start_scan_devices,
|
||||
cmd::stop_scan_devices
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
6
src-tauri/src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
estim_lib::run()
|
||||
}
|
17
src-tauri/src/playlist.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PlayMode {
|
||||
#[serde(rename = "repeat")]
|
||||
Repeat,
|
||||
#[serde(rename = "repeat-one")]
|
||||
RepeatOne,
|
||||
#[serde(rename = "shuffle")]
|
||||
Shuffle,
|
||||
}
|
||||
|
||||
impl Default for PlayMode {
|
||||
fn default() -> Self {
|
||||
PlayMode::Repeat
|
||||
}
|
||||
}
|
86
src-tauri/src/state.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use btleplug::platform::{Adapter, Peripheral};
|
||||
use tauri::{
|
||||
async_runtime::{JoinHandle, Mutex, RwLock},
|
||||
AppHandle, Runtime,
|
||||
};
|
||||
|
||||
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 scanning: Arc<Mutex<bool>>,
|
||||
pub connected_peripheral: Arc<Mutex<Option<Arc<Peripheral>>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AppState {}
|
||||
unsafe impl Sync for AppState {}
|
||||
|
||||
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)),
|
||||
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);
|
||||
}
|
||||
|
||||
pub async fn clear_central_event_handler(&self) {
|
||||
let mut central_event_handler = self.central_event_handler.lock().await;
|
||||
if let Some(handler) = central_event_handler.take() {
|
||||
handler.abort();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
let mut connected_peripheral = self.connected_peripheral.lock().await;
|
||||
*connected_peripheral = Some(Arc::new(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<Arc<Peripheral>> {
|
||||
let connected_peripheral = self.connected_peripheral.lock().await;
|
||||
|
||||
if let Some(peripheral) = connected_peripheral.clone() {
|
||||
Some(Arc::clone(&peripheral))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
39
src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "estim",
|
||||
"version": "0.1.0",
|
||||
"identifier": "net.archgrid.app.estim",
|
||||
"build": {
|
||||
"beforeDevCommand": "deno task dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "deno task build",
|
||||
"frontendDist": "../dist"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "estim",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"resizable": false,
|
||||
"hiddenTitle": true,
|
||||
"titleBarStyle": "Overlay",
|
||||
"theme": "Dark"
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|