first commit
This commit is contained in:
commit
710522ba3d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1290
Cargo.lock
generated
Normal file
1290
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "bleeper"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.82"
|
||||||
|
btleplug = "0.11.5"
|
||||||
|
crossterm = "0.27.0"
|
||||||
|
futures = "0.3.30"
|
||||||
|
ratatui = "0.26.1"
|
||||||
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
147
src/app.rs
Normal file
147
src/app.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
|
use crossterm::event::{self, Event, KeyCode};
|
||||||
|
use ratatui::prelude::*;
|
||||||
|
use ratatui::widgets::{Block, Borders, List, ListItem, ListState};
|
||||||
|
use std::io::Stdout;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
|
||||||
|
pub enum AppEvent {
|
||||||
|
Term(Event),
|
||||||
|
Log(String),
|
||||||
|
}
|
||||||
|
pub struct App {
|
||||||
|
events: Receiver<AppEvent>,
|
||||||
|
logs: Vec<String>,
|
||||||
|
log_state: ListState,
|
||||||
|
}
|
||||||
|
impl App {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (send, recv) = channel::<AppEvent>();
|
||||||
|
let ble_sender = send.clone();
|
||||||
|
std::thread::spawn(move || term_event_sender(send));
|
||||||
|
|
||||||
|
let async_runtime = tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
async_runtime.spawn(async move {
|
||||||
|
let e = ble_event_sender(&ble_sender)
|
||||||
|
.await
|
||||||
|
.expect_err("Bluetooth thread closed");
|
||||||
|
ble_sender
|
||||||
|
.send(AppEvent::Log(format!("Unrecoverable Error: {e}")))
|
||||||
|
.expect("mpsc fail :(");
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let e = ble_event_sender(&ble_sender)
|
||||||
|
.block_on()
|
||||||
|
.expect_err("Bluetooth thread closed");
|
||||||
|
ble_sender
|
||||||
|
.send(AppEvent::Log(format!("Unrecoverable Error: {}", e)))
|
||||||
|
.expect("mpsc fail :(");
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
Self {
|
||||||
|
events: recv,
|
||||||
|
logs: vec![format!("Hello World!")],
|
||||||
|
log_state: ListState::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn ui(&mut self, f: &mut Frame) {
|
||||||
|
//let greeting = widgets::Paragraph::new("Hello World!");
|
||||||
|
//f.render_widget(greeting, f.size());
|
||||||
|
|
||||||
|
let logs: Vec<ListItem> = self
|
||||||
|
.logs
|
||||||
|
.iter()
|
||||||
|
.map(|s| ListItem::new(vec![text::Line::from(Span::raw(s))]))
|
||||||
|
.collect();
|
||||||
|
let logs = List::new(logs).block(Block::default().borders(Borders::ALL).title("Logs"));
|
||||||
|
f.render_stateful_widget(logs, f.size(), &mut self.log_state);
|
||||||
|
}
|
||||||
|
pub fn run(&mut self, term: &mut Terminal<CrosstermBackend<Stdout>>) -> anyhow::Result<()> {
|
||||||
|
loop {
|
||||||
|
term.draw(|f| self.ui(f))?;
|
||||||
|
|
||||||
|
match self.events.recv()? {
|
||||||
|
AppEvent::Term(Event::Key(key)) if key.code == KeyCode::Char('q') => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
AppEvent::Term(Event::Key(key)) if key.code == KeyCode::Char('w') => {
|
||||||
|
self.logs.push(format!("w was pressed!"));
|
||||||
|
}
|
||||||
|
AppEvent::Term(_) => {}
|
||||||
|
AppEvent::Log(message) => self.logs.push(message),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn term_event_sender(tx: Sender<AppEvent>) {
|
||||||
|
while let Ok(e) = event::read() {
|
||||||
|
if tx.send(AppEvent::Term(e)).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ble_event_sender(tx: &Sender<AppEvent>) -> anyhow::Result<()> {
|
||||||
|
use btleplug::api::{Central, CentralEvent, Manager as _, ScanFilter};
|
||||||
|
use btleplug::platform::Manager;
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
tx.send(AppEvent::Log(format!("Starting Bluetooth thread")))?;
|
||||||
|
|
||||||
|
let manager = Manager::new().await?;
|
||||||
|
tx.send(AppEvent::Log(format!("Got bluetooth manager!")))?;
|
||||||
|
let mut adapter_list = manager.adapters().await?;
|
||||||
|
tx.send(AppEvent::Log(format!(
|
||||||
|
"Found {} bluetooth adapters",
|
||||||
|
adapter_list.len()
|
||||||
|
)))?;
|
||||||
|
for adapter in &adapter_list {
|
||||||
|
tx.send(AppEvent::Log(format!(
|
||||||
|
"\t{}",
|
||||||
|
adapter.adapter_info().await?
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
|
let adapter = adapter_list
|
||||||
|
.pop()
|
||||||
|
.ok_or(anyhow!("Error: no adapters found"))?;
|
||||||
|
let mut events = adapter.events().await?;
|
||||||
|
adapter.start_scan(ScanFilter::default()).await?;
|
||||||
|
|
||||||
|
while let Some(event) = events.next().await {
|
||||||
|
match event {
|
||||||
|
CentralEvent::DeviceDiscovered(id) => {
|
||||||
|
tx.send(AppEvent::Log(format!("Discov: {:?}", id)))?;
|
||||||
|
}
|
||||||
|
CentralEvent::DeviceUpdated(id) => {
|
||||||
|
tx.send(AppEvent::Log(format!("Update: {:?}", id)))?;
|
||||||
|
}
|
||||||
|
CentralEvent::DeviceConnected(id) => {
|
||||||
|
tx.send(AppEvent::Log(format!("Connec: {:?}", id)))?;
|
||||||
|
}
|
||||||
|
CentralEvent::DeviceDisconnected(id) => {
|
||||||
|
tx.send(AppEvent::Log(format!("Discon: {:?}", id)))?;
|
||||||
|
}
|
||||||
|
CentralEvent::ManufacturerDataAdvertisement {
|
||||||
|
id,
|
||||||
|
manufacturer_data,
|
||||||
|
} => {
|
||||||
|
tx.send(AppEvent::Log(format!("ManAdv: {:?}", id)))?;
|
||||||
|
let _ = manufacturer_data;
|
||||||
|
}
|
||||||
|
CentralEvent::ServiceDataAdvertisement { id, service_data } => {
|
||||||
|
tx.send(AppEvent::Log(format!("DatAdv: {:?}", id)))?;
|
||||||
|
let _ = service_data;
|
||||||
|
}
|
||||||
|
CentralEvent::ServicesAdvertisement { id, services } => {
|
||||||
|
tx.send(AppEvent::Log(format!("SrvAdv: {:?}", id)))?;
|
||||||
|
let _ = services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(anyhow!("Error: Bluetooth closed"))
|
||||||
|
}
|
||||||
89
src/main.rs
Normal file
89
src/main.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//use anyhow::{bail, Result};
|
||||||
|
//use btleplug::api::{bleuuid::BleUuid, Central, CentralEvent, Manager as _, ScanFilter};
|
||||||
|
//use btleplug::platform::{Adapter, Manager};
|
||||||
|
//use futures::stream::{Stream, StreamExt};
|
||||||
|
//
|
||||||
|
//#[tokio::main]
|
||||||
|
//async fn main() -> Result<()> {
|
||||||
|
// println!("Hello, world!");
|
||||||
|
//
|
||||||
|
// let manager = Manager::new().await?;
|
||||||
|
// let mut adapter_list = manager.adapters().await?;
|
||||||
|
// if adapter_list.is_empty() {
|
||||||
|
// bail!("Error: no bluetooth adapters found.");
|
||||||
|
// }
|
||||||
|
// println!("Found {} bluetooth adapters:", adapter_list.len());
|
||||||
|
// for adapter in &adapter_list {
|
||||||
|
// println!("\t{}", adapter.adapter_info().await?);
|
||||||
|
// }
|
||||||
|
// let adapter: Adapter = adapter_list.pop().expect("No adapters after check!?");
|
||||||
|
// drop(adapter_list);
|
||||||
|
//
|
||||||
|
// let mut events = adapter.events().await?;
|
||||||
|
// adapter.start_scan(ScanFilter::default()).await?;
|
||||||
|
//
|
||||||
|
// while let Some(event) = events.next().await {
|
||||||
|
// match event {
|
||||||
|
// CentralEvent::DeviceDiscovered(id) => {
|
||||||
|
// println!("Discov: {:?}", id);
|
||||||
|
// }
|
||||||
|
// CentralEvent::DeviceUpdated(id) => println!("Update: {:?}", id),
|
||||||
|
// CentralEvent::DeviceConnected(id) => println!("Connec: {:?}", id),
|
||||||
|
// CentralEvent::DeviceDisconnected(id) => println!("Discon: {:?}", id),
|
||||||
|
// CentralEvent::ManufacturerDataAdvertisement {
|
||||||
|
// id,
|
||||||
|
// manufacturer_data,
|
||||||
|
// } => println!("ManAdv: {:?}", id),
|
||||||
|
// CentralEvent::ServiceDataAdvertisement { id, service_data } => {
|
||||||
|
// println!("DatAdv: {:?}", id)
|
||||||
|
// }
|
||||||
|
// CentralEvent::ServicesAdvertisement { id, services } => println!("SrvAdv: {:?}", id),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
//}
|
||||||
|
|
||||||
|
use std::io::{self, Stdout};
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use crossterm::{
|
||||||
|
execute,
|
||||||
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
};
|
||||||
|
use ratatui::prelude::*;
|
||||||
|
|
||||||
|
mod app;
|
||||||
|
use app::App;
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let mut a = App::new();
|
||||||
|
let mut term = setup_term()?;
|
||||||
|
let res = a.run(&mut term);
|
||||||
|
restore_term(&mut term)?;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
fn render_app(frame: &mut Frame) {
|
||||||
|
let greeting = widgets::Paragraph::new("Hello World!");
|
||||||
|
frame.render_widget(greeting, frame.size());
|
||||||
|
}
|
||||||
|
fn run(term: &mut Terminal<CrosstermBackend<Stdout>>, mut app: App) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
fn setup_term() -> Result<Terminal<CrosstermBackend<Stdout>>> {
|
||||||
|
let mut stdout = io::stdout();
|
||||||
|
enable_raw_mode().context("Failed to enable raw mode")?;
|
||||||
|
execute!(stdout, EnterAlternateScreen).context("Failed to enter alternate screen")?;
|
||||||
|
Terminal::new(CrosstermBackend::new(stdout)).context("Failed to create terminal")
|
||||||
|
}
|
||||||
|
// TODO restore term shouldn't fail early and should continue to attempt to restore the terminal even in
|
||||||
|
// the event of one or two errors
|
||||||
|
fn restore_term(term: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
||||||
|
disable_raw_mode().context("Failed to disable raw mode")?;
|
||||||
|
execute!(term.backend_mut(), LeaveAlternateScreen)
|
||||||
|
.context("Failed to leave alternate screen")?;
|
||||||
|
term.show_cursor().context("Failed to show cursor")
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user