1use crate::prelude::*;
2use btleplug::api::{
3 Central, Characteristic, Manager as _, Peripheral, PeripheralProperties, ScanFilter,
4};
5use btleplug::platform::Manager;
6use std::error::Error;
7use std::io;
8use std::sync::{Arc, Mutex};
9use std::time::Duration;
10use tokio::time;
11use uuid::Uuid;
12
13#[derive(Clone)]
18pub struct Device {
19 pub name: String,
20 pub address: String,
21 peripheral: btleplug::platform::Peripheral,
22 characteristics: Option<Characteristics>,
23}
24
25#[derive(Clone)]
26pub struct Characteristics {
27 _channel_count: Characteristic,
28 sensor: Characteristic,
29}
30
31impl Device {
32 pub async fn find_characteristics(&mut self) {
33 let uuid_sensor = Uuid::parse_str(firmware::SENSOR_CHARACTERISTICS_UUID).unwrap();
34 let uuid_channel_count =
35 Uuid::parse_str(firmware::CHANNEL_COUNT_CHARACTERISTICS_UUID).unwrap();
36
37 let _ = self.peripheral.connect().await;
38 let _ = self.peripheral.discover_services().await;
39 let characteristics = self.peripheral.characteristics();
40 let chr_sensor = characteristics
41 .iter()
42 .find(|c| c.uuid == uuid_sensor)
43 .unwrap();
44 let chr_channel_count = characteristics
45 .iter()
46 .find(|c| c.uuid == uuid_channel_count)
47 .unwrap();
48
49 self.characteristics = Some(Characteristics {
50 _channel_count: chr_channel_count.clone(),
51 sensor: chr_sensor.clone(),
52 });
53 }
54
55 pub async fn read(&self) -> Result<Vec<u8>, Box<dyn Error>> {
56 if let Some(chr) = &self.characteristics {
57 Ok(self.peripheral.read(&chr.sensor).await?)
58 } else {
59 Err(Box::new(std::io::Error::new(
60 std::io::ErrorKind::Other,
61 "Must load characteristics before calling read()",
62 )))
63 }
64 }
65
66 pub async fn disconnect(&mut self) -> Result<(), Box<dyn Error>> {
67 self.peripheral.disconnect().await?;
68 Ok(())
69 }
70}
71
72pub async fn scan(app: App) -> Result<(), Box<dyn Error>> {
73 println!("Scanning Bluetooth for PsyLink device...");
74
75 let manager = Manager::new().await?;
76 let adapter_list = manager.adapters().await?;
77 if adapter_list.is_empty() {
78 eprintln!("No Bluetooth adapters found");
79 }
80
81 for adapter in adapter_list.iter() {
82 if app.verbose > 0 {
83 println!(
84 "Trying bluetooth adapter {}...",
85 adapter.adapter_info().await?
86 );
87 }
88 adapter
89 .start_scan(ScanFilter::default())
90 .await
91 .expect("Can't scan BLE adapter for connected devices...");
92 time::sleep(Duration::from_secs_f32(app.scantime)).await;
93
94 let peripherals = adapter.peripherals().await?;
95 if peripherals.is_empty() {
96 eprintln!("No BLE peripheral devices found.");
97 } else {
98 for peripheral in peripherals.iter() {
99 let properties = peripheral.properties().await?;
100 if app.verbose > 1 {
101 dbg!(&properties);
102 }
103 if let Some(PeripheralProperties {
104 address,
105 local_name: Some(name),
106 ..
107 }) = &properties
108 {
109 if name == "PsyLink" {
110 println!("Found PsyLink device with address {address}");
111 }
112 }
113 }
114 }
115 }
116 Ok(())
117}
118
119pub async fn stream(app: App) -> Result<(), Box<dyn Error>> {
120 println!("Scanning Bluetooth for PsyLink device...");
121 let manager = Manager::new().await?;
122 let adapter_list = manager.adapters().await?;
123 if adapter_list.is_empty() {
124 eprintln!("No Bluetooth adapters found");
125 }
126 let sensor_uuid = Uuid::parse_str(firmware::SENSOR_CHARACTERISTICS_UUID).unwrap();
127
128 let psylink = find_peripheral(app, None).await?;
129
130 let _ = psylink.peripheral.connect().await;
131 let _ = psylink.peripheral.discover_services().await;
132 let characteristics = psylink.peripheral.characteristics();
133
134 let sensor_characteristic = characteristics
135 .iter()
136 .find(|c| c.uuid == sensor_uuid)
137 .unwrap();
138 loop {
139 let data = psylink.peripheral.read(sensor_characteristic).await?;
140 dbg!(data);
141 }
142}
143
144pub async fn find_peripheral(
145 app: App,
146 mutex_quit: Option<Arc<Mutex<bool>>>,
147) -> Result<Device, Box<dyn Error>> {
148 println!("Scanning Bluetooth for PsyLink device...");
149
150 let manager = Manager::new().await?;
151 let adapter_list = manager.adapters().await?;
152 if adapter_list.is_empty() {
153 eprintln!("No Bluetooth adapters found");
154 }
155
156 loop {
157 for adapter in adapter_list.iter() {
158 if app.verbose > 0 {
159 println!(
160 "Trying Bluetooth adapter {}...",
161 adapter.adapter_info().await?
162 );
163 }
164 let _ = adapter.start_scan(ScanFilter::default()).await;
165 time::sleep(Duration::from_secs_f32(0.1)).await;
167
168 let peripherals = adapter.peripherals().await?;
169 if peripherals.is_empty() {
170 eprintln!("No BLE peripheral devices found.");
171 } else {
172 for peripheral in peripherals.iter() {
173 let properties = peripheral.properties().await?;
174 if app.verbose > 1 {
175 dbg!(&properties);
176 }
177 if let Some(PeripheralProperties {
178 address,
179 local_name: Some(name),
180 ..
181 }) = &properties
182 {
183 if name == "PsyLink" {
184 println!("Found PsyLink device with address {address}");
185 return Ok(Device {
186 name: name.to_string(),
187 address: address.to_string(),
188 peripheral: peripheral.clone(),
189 characteristics: None,
190 });
191 }
192 }
193 }
194 }
195 }
196 if let Some(mutex_quit) = &mutex_quit {
197 if *(mutex_quit.lock().unwrap()) {
198 println!("Quitting bluetooth::Device::find_peripheral");
199 return Err(Box::new(io::Error::new(io::ErrorKind::Other, "The bluetooth::Device::find_peripheral function received a quit command while scanning for peripherals")));
200 }
201 }
202 }
203}