1use btleplug::api::{Central, Manager as _, Peripheral as _};
3use btleplug::platform::{Adapter, Manager};
4use futures::stream::StreamExt;
5use std::sync::Arc;
6use std::time::Duration;
7use tokio::sync::mpsc;
8use tokio::time;
9use uuid::Uuid;
10
11const UUID_STR: &str = "00002a37-0000-1000-8000-00805f9b34fb";
13const SUPPORT_DEVICES: &[&str] = &["Xiaomi Smart Band 9 082F"];
14
15#[derive(thiserror::Error, Debug)]
17pub enum Error {
18 #[error("Bluetooth adapters not found")]
19 BluetoothAdaptersNotFound,
20 #[error("Device not found")]
21 DeviceNotFound,
22 #[error("Heart rate characteristic not found")]
23 CharacteristicNotFound,
24 #[error("HeartRateMonitor not found")]
25 HeartRateMonitorNotFound,
26 #[error("Bluetooth error: {0}")]
27 Bluetooth(#[from] btleplug::Error),
28}
29
30pub struct HeartRateMonitor {
32 adapter: Adapter,
33 device_address: String,
34}
35
36impl HeartRateMonitor {
37 pub async fn new(adapter: Adapter, device_address: String) -> Self {
38 Self {
39 adapter,
40 device_address,
41 }
42 }
43
44 pub async fn start_monitoring(&self) -> mpsc::Receiver<u8> {
45 let (sender, receiver) = mpsc::channel(100);
46 let adapter = self.adapter.clone();
47 let device_address = self.device_address.clone();
48 let adapter_arc = Arc::new(adapter);
49 tokio::spawn({
50 let adapter_arc = Arc::clone(&adapter_arc);
51 async move {
52 loop {
53 match Self::connect_and_monitor(&adapter_arc, &device_address, &sender).await {
54 Ok(_) => break,
55 Err(e) => {
56 eprintln!("Monitoring error: {e}");
57 time::sleep(Duration::from_secs(5)).await;
58 }
59 }
60 }
61 }
62 });
63
64 receiver
65 }
66
67 async fn connect_and_monitor(
68 adapter: &Adapter,
69 device_address: &str,
70 sender: &mpsc::Sender<u8>,
71 ) -> Result<(), Error> {
72 let heart_rate_uuid: Uuid = Uuid::parse_str(UUID_STR).expect("UUID_STR");
73 adapter.start_scan(Default::default()).await?;
74 time::sleep(Duration::from_secs(5)).await;
75
76 let peripherals = adapter.peripherals().await?;
77 let device = peripherals
78 .into_iter()
79 .find(|p| p.address().to_string() == device_address)
80 .ok_or(Error::DeviceNotFound)?;
81
82 device.connect().await?;
83 device.discover_services().await?;
84
85 let characteristics = device.characteristics();
86 let hr_char = characteristics
87 .into_iter()
88 .find(|c| c.uuid == heart_rate_uuid)
89 .ok_or(Error::CharacteristicNotFound)?;
90
91 device.subscribe(&hr_char).await?;
92 let mut notification_stream = device.notifications().await?;
93
94 while let Some(data) = notification_stream.next().await {
95 if data.uuid == heart_rate_uuid {
96 let value = parse_heart_rate(&data.value);
97 if sender.send(value).await.is_err() {
98 break; }
100 }
101 }
102
103 device.disconnect().await?;
104 Ok(())
105 }
106}
107
108fn parse_heart_rate(data: &[u8]) -> u8 {
109 if data.len() >= 2 {
110 if data[0] & 0x01 == 0 {
111 data[1]
112 } else {
113 u16::from_le_bytes([data[1], data[2]]) as u8
114 }
115 } else {
116 0
117 }
118}
119
120async fn get_device_address(device_name: &str) -> Result<String, Error> {
121 let manager = Manager::new().await?;
122 let adapters = manager.adapters().await?;
123 let adapter = adapters
124 .into_iter()
125 .next()
126 .ok_or(Error::BluetoothAdaptersNotFound)?;
127
128 adapter.start_scan(Default::default()).await?;
129 time::sleep(Duration::from_secs(5)).await;
130
131 let peripherals = adapter.peripherals().await?;
132 for peripheral in peripherals {
133 let properties = peripheral.properties().await?;
134 if let Some(props) = properties {
135 if let Some(name) = props.local_name {
136 if name == device_name {
137 let address = props.address.to_string();
138 adapter.stop_scan().await?;
139 return Ok(address);
140 }
141 }
142 }
143 }
144
145 Err(Error::DeviceNotFound)
146}
147
148pub async fn detect_monitor() -> Result<HeartRateMonitor, Error> {
149 for device_name in SUPPORT_DEVICES {
150 match get_device_address(device_name).await {
151 Ok(device_address) => {
152 let manager = Manager::new().await?;
153 let adapters = manager.adapters().await?;
154 let adapter = adapters
155 .into_iter()
156 .next()
157 .ok_or(Error::BluetoothAdaptersNotFound)?;
158 let monitor = HeartRateMonitor::new(adapter, device_address).await;
159 return Ok(monitor);
160 }
161 Err(e) => eprintln!("Device not found: {device_name} - Error: {e}"),
162 }
163 }
164 Err(Error::HeartRateMonitorNotFound)
165}