use crate::config::Config;
use crate::devices::{DeviceInfo, DeviceMemory};
use crate::data::{IoTData, BloodPressureData, HeartRateData, BatteryLevelData, DataType, DataStore, TemperatureData, PressureData, AlertData};
use crate::pairing::BluetoothPairing;
use crate::{Error, Result};
use bluer::{Address, AdapterEvent, Device, Session, Adapter};
use futures::stream::StreamExt;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::{sleep, interval, timeout};
use uuid::Uuid;
use chrono::Utc;
use tracing::{info, warn, error, debug};
use std::str::FromStr;
pub struct BleManager {
session: Session,
adapter: Adapter,
config: Config,
device_memory: Arc<DeviceMemory>,
bluetooth_pairing: Arc<BluetoothPairing>,
data_store: Arc<DataStore>,
}
impl BleManager {
pub async fn new(config: Config, device_memory: Arc<DeviceMemory>, data_store: Arc<DataStore>) -> Result<Self> {
info!("Initializing BLE Manager...");
let arch = std::env::consts::ARCH;
let os = std::env::consts::OS;
info!("Platform: {} on {}", arch, os);
let session = Session::new().await
.map_err(|e| {
error!("Failed to create Bluetooth session on {}: {}", arch, e);
error!("This might indicate: 1) Bluetooth not available, 2) Permission issues, 3) Bluetooth daemon not running");
Error::General(format!("Failed to create Bluetooth session: {}", e))
})?;
let adapter = session.default_adapter().await
.map_err(|e| {
error!("No Bluetooth adapter found on {}: {}", arch, e);
error!("Check: 1) Bluetooth hardware present, 2) Driver loaded, 3) Adapter powered on");
Error::General(format!("No Bluetooth adapter found: {}", e))
})?;
info!("Using Bluetooth adapter");
if let Ok(address) = adapter.address().await {
info!("Adapter address: {}", address);
}
if let Ok(powered) = adapter.is_powered().await {
info!("Adapter powered: {}", powered);
if !powered {
warn!("Bluetooth adapter is not powered on - BLE operations may fail");
}
}
let bluetooth_pairing = Arc::new(BluetoothPairing::new().await?);
info!("BLE Manager initialized successfully on {}", arch);
Ok(Self {
session,
adapter,
config,
device_memory,
bluetooth_pairing,
data_store,
})
}
pub async fn start_discovery_loop(&self) -> Result<()> {
let mut discovery_interval = interval(self.config.bluetooth_scan_duration());
loop {
discovery_interval.tick().await;
if let Err(e) = self.scan_for_devices().await {
error!("Error during device scan: {}", e);
}
}
}
pub async fn scan_for_devices(&self) -> Result<Vec<DeviceInfo>> {
info!("Starting BLE scan with advertisement monitoring...");
debug!("BLE scan parameters: timeout=5s, max_events=20");
debug!("Bluetooth scan interval: {:?}", self.config.bluetooth_scan_duration());
let mut discovered_devices = Vec::new();
if let Ok(powered) = self.adapter.is_powered().await {
if !powered {
error!("Cannot scan: Bluetooth adapter is not powered");
return Err(Error::General("Bluetooth adapter not powered".to_string()));
}
}
debug!("Starting device discovery...");
let discover = self.adapter.discover_devices().await
.map_err(|e| {
error!("Failed to start device discovery: {}", e);
error!("This may indicate: 1) Adapter not ready, 2) Permission issues, 3) Resource busy");
Error::General(format!("Discovery failed: {}", e))
})?;
let mut events = discover.take(20);
debug!("Waiting for BLE advertisement events...");
let scan_result = timeout(Duration::from_secs(5), async {
while let Some(event) = events.next().await {
match event {
AdapterEvent::DeviceAdded(addr) => {
let device = self.adapter.device(addr)?;
debug!("Processing discovered device: {}", addr);
if let Ok(name) = device.name().await {
if let Some(name) = name {
let address = addr.to_string();
let now = Utc::now().timestamp();
debug!("Found named device: {} at {}", name, address);
let device_class = match device.class().await {
Ok(Some(class)) => {
debug!("Device {} class: 0x{:06X}", address, class);
class
}
Ok(None) => {
debug!("Device {} has no class information", address);
0
}
Err(e) => {
debug!("Failed to get class for device {}: {}", address, e);
0
}
};
let appearance = match device.appearance().await {
Ok(Some(app)) => {
debug!("Device {} appearance: 0x{:04X}", address, app);
app
}
Ok(None) => {
debug!("Device {} has no appearance information", address);
0
}
Err(e) => {
debug!("Failed to get appearance for device {}: {}", address, e);
0
}
};
if let Err(e) = self.device_memory.update_device_from_discovery(
address.clone(),
name.clone(),
now,
device_class,
appearance,
).await {
warn!("Failed to update device from discovery: {}", e);
} else {
let mut device_info = DeviceInfo::new(address.clone(), name.clone());
device_info.device_class = device_class;
device_info.appearance = appearance;
discovered_devices.push(device_info);
info!("Discovered device: {} ({}) class=0x{:06X} appearance=0x{:04X}",
name, address, device_class, appearance);
}
debug!("Processing advertisement data for device: {}", address);
if let Err(e) = self.process_device_advertisements(&device, &address).await {
debug!("Failed to process advertisement data for {}: {}", address, e);
} else {
debug!("Successfully processed advertisement data for {}", address);
}
} else {
debug!("Device {} has no name, skipping", addr);
}
} else {
debug!("Failed to get name for device {}", addr);
}
}
_ => {} }
}
Ok::<Vec<DeviceInfo>, Error>(discovered_devices.clone())
}).await;
match scan_result {
Ok(result) => {
let device_count = result?.len();
info!("BLE scan completed successfully. Found {} devices", device_count);
if device_count == 0 {
warn!("No BLE devices discovered. This may indicate:");
warn!("1. No BLE devices in range");
warn!("2. Bluetooth adapter issues");
warn!("3. Permission/privilege problems");
warn!("4. Platform-specific BLE driver issues");
}
Ok(discovered_devices)
}
Err(_) => {
warn!("BLE scan timed out after 5 seconds. Found {} devices", discovered_devices.len());
debug!("Timeout is normal - continuing with discovered devices");
Ok(discovered_devices)
}
}
}
async fn process_device_advertisements(&self, device: &Device, device_address: &str) -> Result<()> {
if !self.should_process_device_data(device_address).await {
debug!("Device {} not approved for data processing", device_address);
return Ok(());
}
let mut service_data = HashMap::new();
if let Ok(Some(service_data_prop)) = device.service_data().await {
debug!("Found {} service data entries for device {}", service_data_prop.len(), device_address);
for (service_uuid, data) in service_data_prop {
let service_str = service_uuid.to_string();
if self.is_monitored_service_startswith(&service_str) {
let data_len = data.len();
service_data.insert(service_uuid, data);
info!("Found monitored service data: {} on device {} ({} bytes)", service_str, device_address, data_len);
} else {
debug!("Ignoring non-monitored service: {} on device {}", service_str, device_address);
}
}
}
if let Ok(Some(manufacturer_data)) = device.manufacturer_data().await {
for (manufacturer_id, data) in manufacturer_data {
debug!("Found manufacturer data {} on device {}: {} bytes", manufacturer_id, device_address, data.len());
}
}
if !service_data.is_empty() {
debug!("Processing {} service data entries for device {}", service_data.len(), device_address);
let iot_data = self.parse_advertisement_data(device_address, &service_data).await;
if !iot_data.is_empty() {
info!("Storing {} IoT data points from advertisement for device {}", iot_data.len(), device_address);
if let Err(e) = self.data_store.add_data_batch(iot_data).await {
warn!("Failed to store advertisement data for {}: {}", device_address, e);
}
} else {
debug!("No IoT data extracted from service data for device {}", device_address);
}
} else {
debug!("No monitored service data found for device {}", device_address);
}
Ok(())
}
fn is_monitored_service_startswith(&self, service_uuid: &str) -> bool {
let monitored_uuids = [
"00002a37-0000-1000-8000-00805f9b34fb", "00002a6d-0000-1000-8000-00805f9b34fb", "00002a6e-0000-1000-8000-00805f9b34fb", "0000180f-0000-1000-8000-00805f9b34fb", "00002a46-0000-1000-8000-00805f9b34fb", ];
for monitored_uuid in &monitored_uuids {
if service_uuid.starts_with(monitored_uuid) || monitored_uuid.starts_with(service_uuid) {
return true;
}
}
false
}
async fn process_manufacturer_data(&self, device_address: &str, manufacturer_id: u16, data: &[u8]) {
debug!("Processing manufacturer data from {}: ID={}, {} bytes", device_address, manufacturer_id, data.len());
if !data.is_empty() {
debug!("Manufacturer data: {}", hex::encode(data));
}
}
async fn should_process_device_data(&self, device_address: &str) -> bool {
if let Some(device_info) = self.device_memory.get_device(device_address).await {
device_info.approved
} else {
false
}
}
fn is_supported_device(&self, name: &str) -> bool {
self.config.supported_devices.iter()
.any(|pattern| name.contains(pattern))
}
pub async fn connect_and_collect_data(&self, address: &str) -> Result<Vec<IoTData>> {
let addr = Address::from_str(address)
.map_err(|e| Error::General(format!("Invalid address {}: {}", address, e)))?;
let device = self.adapter.device(addr)
.map_err(|e| Error::General(format!("Failed to get device {}: {}", address, e)))?;
let device_name = device.name().await.unwrap_or_default().unwrap_or_default();
if !device_name.is_empty() && !self.is_supported_device(&device_name) {
warn!("Device {} ({}) is not supported for data collection", device_name, address);
return Ok(Vec::new()); }
if device_name.is_empty() {
warn!("Device {} has no name, skipping data collection", address);
return Ok(Vec::new());
}
debug!("Attempting to connect to supported device: {} ({})", device_name, address);
for attempt in 1..=3 {
if device.is_connected().await.unwrap_or(false) {
break;
}
match device.connect().await {
Ok(()) => {
info!("Connected to device {} on attempt {}", address, attempt);
break;
}
Err(e) if attempt < 3 => {
warn!("Connection attempt {} failed for {}: {}", attempt, address, e);
sleep(Duration::from_millis(500)).await;
}
Err(e) => {
error!("Failed to connect to device {} after 3 attempts: {}", address, e);
return Err(Error::General(format!("Connection failed: {}", e)));
}
}
}
let data = self.collect_device_data(&device, address, &device_name).await?;
if let Err(e) = device.disconnect().await {
warn!("Failed to disconnect from device {}: {}", address, e);
}
Ok(data)
}
async fn collect_device_data(&self, device: &Device, address: &str, device_name: &str) -> Result<Vec<IoTData>> {
let mut collected_data = Vec::new();
let timestamp = Utc::now().timestamp();
info!("Starting GATT data collection from device: {} ({})", device_name, address);
let battery_level_uuid = "00002a19-0000-1000-8000-00805f9b34fb"; let serial_number_uuid = "00002a25-0000-1000-8000-00805f9b34fb"; let date_time_uuid = "00002a08-0000-1000-8000-00805f9b34fb"; let bp_measurement_uuid = "00002a35-0000-1000-8000-00805f9b34fb";
debug!("Enumerating all GATT services and characteristics for device: {}", address);
debug!("Attempting to read battery level characteristic");
if let Ok(battery_level) = self.try_read_characteristic(device, battery_level_uuid).await {
if let Some(battery_data) = self.parse_battery_data(address, &battery_level, timestamp).await {
collected_data.push(battery_data);
info!("Read battery level from GATT: {} bytes", battery_level.len());
}
}
debug!("Attempting to read serial number characteristic");
if let Ok(serial_data) = self.try_read_characteristic(device, serial_number_uuid).await {
if let Ok(serial) = String::from_utf8(serial_data) {
info!("Device serial number: {}", serial);
}
}
debug!("Attempting to read/sync device time");
if let Ok(time_data) = self.try_read_characteristic(device, date_time_uuid).await {
if let Some(device_time) = self.parse_device_time(&time_data) {
let system_time = Utc::now().timestamp();
let time_diff = (system_time - device_time).abs();
info!("Device time vs system time difference: {} seconds", time_diff);
if time_diff > 60 {
warn!("Device time drift detected: {} seconds, update needed", time_diff);
}
}
}
if device_name.contains("A&D_UA-651BLE_") || device_name.contains("BLESmart_") {
info!("Detected blood pressure device: {}", device_name);
debug!("Attempting to subscribe to blood pressure notifications");
if let Ok(bp_data) = self.subscribe_to_blood_pressure_notifications(device, address, timestamp).await {
let bp_count = bp_data.len();
collected_data.extend(bp_data);
info!("Collected {} blood pressure measurements via notifications", bp_count);
} else {
debug!("Blood pressure notification subscription not available");
}
}
if collected_data.is_empty() {
debug!("No GATT data collected, falling back to advertisement data");
if let Err(e) = self.process_device_advertisements(device, address).await {
debug!("Advertisement data fallback failed: {}", e);
}
}
info!("GATT data collection completed: {} data points from device {}", collected_data.len(), address);
Ok(collected_data)
}
async fn parse_characteristic_data(
&self,
address: &str,
device_name: &str,
characteristic_uuid: &Uuid,
data: &[u8],
timestamp: i64,
) -> Option<IoTData> {
if device_name.contains("A&D_UA-651BLE_") || device_name.contains("BLESmart_") {
return self.parse_blood_pressure_data(address, data, timestamp).await;
}
if device_name.contains("X4 Smart") {
return self.parse_heart_rate_data(address, data, timestamp).await;
}
let battery_uuid = Uuid::parse_str("00002a19-0000-1000-8000-00805f9b34fb").unwrap();
if characteristic_uuid == &battery_uuid {
return self.parse_battery_data(address, data, timestamp).await;
}
self.parse_generic_data(address, data, timestamp).await
}
async fn parse_blood_pressure_data(&self, address: &str, data: &[u8], timestamp: i64) -> Option<IoTData> {
if data.len() >= 4 {
let systolic = u16::from_le_bytes([data[1], data[2]]) as i32;
let diastolic = u16::from_le_bytes([data[3], data[4]]) as i32;
let mut data_map = HashMap::new();
data_map.insert("sys".to_string(), serde_json::Value::Number(systolic.into()));
data_map.insert("dias".to_string(), serde_json::Value::Number(diastolic.into()));
let bp_data = BloodPressureData::new(data_map, address.to_string(), timestamp);
Some(IoTData::BloodPressure(bp_data))
} else {
None
}
}
async fn parse_heart_rate_data(&self, address: &str, data: &[u8], timestamp: i64) -> Option<IoTData> {
if !data.is_empty() {
let heart_rate = if data[0] & 0x01 == 0 {
data[1] as i32
} else {
u16::from_le_bytes([data[1], data[2]]) as i32
};
let mut data_map = HashMap::new();
data_map.insert("rate".to_string(), serde_json::Value::Number(heart_rate.into()));
let hr_data = HeartRateData::new(data_map, address.to_string(), timestamp);
Some(IoTData::HeartRate(hr_data))
} else {
None
}
}
async fn parse_battery_data(&self, address: &str, data: &[u8], timestamp: i64) -> Option<IoTData> {
if !data.is_empty() {
let battery_level = data[0] as i32;
let mut data_map = HashMap::new();
data_map.insert("level".to_string(), serde_json::Value::Number(battery_level.into()));
let battery_data = BatteryLevelData::new(data_map, address.to_string(), timestamp);
Some(IoTData::BatteryLevel(battery_data))
} else {
None
}
}
async fn parse_generic_data(&self, address: &str, data: &[u8], timestamp: i64) -> Option<IoTData> {
if !data.is_empty() {
let mut data_map = HashMap::new();
data_map.insert("raw_data".to_string(), serde_json::Value::String(hex::encode(data)));
data_map.insert("length".to_string(), serde_json::Value::Number(data.len().into()));
let generic_data = DataType::new(data_map, address.to_string(), timestamp, "generic".to_string());
Some(IoTData::Generic(generic_data))
} else {
None
}
}
pub async fn parse_advertisement_data(&self, device_address: &str, service_data: &HashMap<uuid::Uuid, Vec<u8>>) -> Vec<IoTData> {
let mut collected_data = Vec::new();
let timestamp = chrono::Utc::now().timestamp();
debug!("Parsing advertisement data for device {} with {} service entries", device_address, service_data.len());
let type_key_dict = [
("HEART_RATE", "00002a37-0000-1000-8000-00805f9b34fb"),
("PRESSURE", "00002a6d-0000-1000-8000-00805f9b34fb"),
("TEMPERATURE", "00002a6e-0000-1000-8000-00805f9b34fb"),
("BATTERY_LEVEL", "0000180f-0000-1000-8000-00805f9b34fb"),
("ALERT", "00002a46-0000-1000-8000-00805f9b34fb"),
];
for (data_type, uuid_str) in type_key_dict {
debug!("Checking for {} data type with UUID {}", data_type, uuid_str);
let mut matched_data: Option<&Vec<u8>> = None;
for (service_uuid, data) in service_data {
let service_str = service_uuid.to_string();
if service_str.starts_with(uuid_str) {
matched_data = Some(data);
debug!("Found matching service UUID {} for {}", service_str, data_type);
break;
}
}
if let Some(data) = matched_data {
if data.is_empty() {
debug!("Empty data for {} - skipping", data_type);
continue;
}
debug!("Processing {} bytes of {} data: {:?}", data.len(), data_type, data);
let iot_data = match data_type {
"HEART_RATE" => {
let data_value = self.parse_signed_le_bytes(data);
info!("Advertisement: Heart rate = {} BPM from device {}", data_value, device_address);
let mut data_map = HashMap::new();
data_map.insert("rate".to_string(), serde_json::Value::Number(data_value.into()));
Some(IoTData::HeartRate(HeartRateData::new(data_map, device_address.to_string(), timestamp)))
}
"PRESSURE" => {
let raw_value = self.parse_unsigned_le_bytes(data);
let data_value = (raw_value as f64 * 4.16).round() as u32; info!("Advertisement: Pressure = {} hPa (raw: {}) from device {}", data_value, raw_value, device_address);
let mut data_map = HashMap::new();
data_map.insert("level".to_string(), serde_json::json!(data_value as f64));
Some(IoTData::Pressure(PressureData::new(data_map, device_address.to_string(), timestamp)))
}
"TEMPERATURE" => {
let data_value = self.parse_signed_le_bytes(data);
info!("Advertisement: Temperature = {}C from device {}", data_value, device_address);
let mut data_map = HashMap::new();
data_map.insert("level".to_string(), serde_json::json!(data_value as f64));
Some(IoTData::Temperature(TemperatureData::new(data_map, device_address.to_string(), timestamp)))
}
"BATTERY_LEVEL" => {
let data_value = self.parse_signed_le_bytes(data);
info!("Advertisement: Battery level = {}% from device {}", data_value, device_address);
let mut data_map = HashMap::new();
data_map.insert("level".to_string(), serde_json::Value::Number(data_value.into()));
Some(IoTData::BatteryLevel(BatteryLevelData::new(data_map, device_address.to_string(), timestamp)))
}
"ALERT" => {
let data_value = self.parse_bytes_to_utf8(data);
info!("Advertisement: Alert = {:?} from device {}", data_value, device_address);
if let Some(alert_text) = data_value {
let mut data_map = HashMap::new();
data_map.insert("id".to_string(), serde_json::Value::String(alert_text));
Some(IoTData::Alert(AlertData::new(data_map, device_address.to_string(), timestamp)))
} else {
warn!("Failed to decode alert data as UTF-8 for device {}", device_address);
None
}
}
_ => None,
};
if let Some(data) = iot_data {
collected_data.push(data);
} else {
debug!("Failed to parse {} data for device {}", data_type, device_address);
}
} else {
debug!("No {} data found in advertisements for device {}", data_type, device_address);
}
}
if collected_data.is_empty() {
debug!("No IoT data extracted from advertisements for device {}", device_address);
}
collected_data
}
fn parse_signed_le_bytes(&self, data: &[u8]) -> i32 {
match data.len() {
1 => data[0] as i8 as i32,
2 => i16::from_le_bytes([data[0], data[1]]) as i32,
4 => i32::from_le_bytes([data[0], data[1], data[2], data[3]]),
_ => {
if data.is_empty() { 0 } else { data[0] as i8 as i32 }
}
}
}
fn parse_unsigned_le_bytes(&self, data: &[u8]) -> u32 {
match data.len() {
1 => data[0] as u32,
2 => u16::from_le_bytes([data[0], data[1]]) as u32,
4 => u32::from_le_bytes([data[0], data[1], data[2], data[3]]),
_ => {
if data.is_empty() { 0 } else { data[0] as u32 }
}
}
}
fn parse_bytes_to_utf8(&self, data: &[u8]) -> Option<String> {
match String::from_utf8(data.to_vec()) {
Ok(text) => Some(text),
Err(e) => {
warn!("Failed to decode bytes to UTF-8: {}", e);
None
}
}
}
fn decode_sfloat(&self, data: u16) -> f64 {
let exponent = ((data >> 12) & 0x0F) as i8;
let mantissa = (data & 0x0FFF) as i16;
let signed_mantissa = if mantissa & 0x0800 != 0 {
mantissa | (0xF000u16 as i16)
} else {
mantissa
} as f64;
if data == 0x07FF {
return f64::NAN; } else if data == 0x0800 {
return f64::NAN; } else if data == 0x0801 {
return f64::NAN; } else if data == 0x0802 {
return f64::NAN; }
signed_mantissa * 10.0_f64.powi(exponent as i32)
}
fn parse_blood_pressure_measurement(&self, data: &[u8]) -> Option<(f64, f64, Option<f64>, Option<i64>)> {
if data.len() < 3 {
return None;
}
let flags = data[0];
let mut offset = 1;
if offset + 2 > data.len() {
return None;
}
let systolic_raw = u16::from_le_bytes([data[offset], data[offset + 1]]);
let systolic = self.decode_sfloat(systolic_raw);
offset += 2;
if offset + 2 > data.len() {
return None;
}
let diastolic_raw = u16::from_le_bytes([data[offset], data[offset + 1]]);
let diastolic = self.decode_sfloat(diastolic_raw);
offset += 2;
if offset + 2 > data.len() {
return None;
}
let _mean_raw = u16::from_le_bytes([data[offset], data[offset + 1]]);
offset += 2;
let mut timestamp = None;
if flags & 0x02 != 0 {
if offset + 7 <= data.len() {
let year = u16::from_le_bytes([data[offset], data[offset + 1]]);
let month = data[offset + 2];
let day = data[offset + 3];
let hour = data[offset + 4];
let minute = data[offset + 5];
let second = data[offset + 6];
if let Some(datetime) = chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32)
.and_then(|date| date.and_hms_opt(hour as u32, minute as u32, second as u32)) {
timestamp = Some(datetime.and_utc().timestamp());
}
offset += 7;
}
}
let mut pulse_rate = None;
if flags & 0x04 != 0 {
if offset + 2 <= data.len() {
let pulse_raw = u16::from_le_bytes([data[offset], data[offset + 1]]);
pulse_rate = Some(self.decode_sfloat(pulse_raw));
}
}
Some((systolic, diastolic, pulse_rate, timestamp))
}
fn parse_device_time(&self, data: &[u8]) -> Option<i64> {
if data.len() >= 7 {
let year = u16::from_le_bytes([data[0], data[1]]);
let month = data[2];
let day = data[3];
let hour = data[4];
let minute = data[5];
let second = data[6];
if let Some(datetime) = chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32)
.and_then(|date| date.and_hms_opt(hour as u32, minute as u32, second as u32)) {
Some(datetime.and_utc().timestamp())
} else {
None
}
} else {
None
}
}
async fn try_read_characteristic(&self, device: &Device, characteristic_uuid: &str) -> Result<Vec<u8>> {
debug!("Attempting to read characteristic {} from device", characteristic_uuid);
Err(Error::General("GATT operations not yet implemented with current bluer API".to_string()))
}
async fn subscribe_to_blood_pressure_notifications(
&self,
device: &Device,
address: &str,
base_timestamp: i64
) -> Result<Vec<IoTData>> {
info!("Blood pressure notification subscription not yet implemented for device: {}", address);
debug!("Will rely on advertisement data for blood pressure measurements");
Ok(Vec::new())
}
pub async fn pair_device(&self, address: &str) -> Result<bool> {
info!("Attempting to pair with device: {}", address);
let paired = self.bluetooth_pairing.pair_device(address).await?;
if paired {
self.device_memory.set_device_paired(address, true).await?;
info!("Successfully paired with device: {}", address);
} else {
warn!("Failed to pair with device: {}", address);
}
Ok(paired)
}
pub async fn unpair_device(&self, address: &str) -> Result<bool> {
info!("Unpairing device: {}", address);
let unpaired = self.bluetooth_pairing.unpair_device(address).await?;
if unpaired {
self.device_memory.set_device_paired(address, false).await?;
info!("Successfully unpaired device: {}", address);
} else {
warn!("Failed to unpair device: {}", address);
}
Ok(unpaired)
}
pub async fn is_device_paired(&self, address: &str) -> Result<bool> {
self.bluetooth_pairing.is_device_paired(address).await
}
pub async fn is_device_connected(&self, address: &str) -> Result<bool> {
let addr = Address::from_str(address)
.map_err(|e| Error::General(format!("Invalid address {}: {}", address, e)))?;
let device = self.adapter.device(addr)
.map_err(|e| Error::General(format!("Failed to get device {}: {}", address, e)))?;
device.is_connected().await
.map_err(|e| Error::General(format!("Failed to check connection status for {}: {}", address, e)))
}
pub async fn connect_device(&self, address: &str) -> Result<bool> {
info!("Attempting to connect to device: {}", address);
let addr = Address::from_str(address)
.map_err(|e| Error::General(format!("Invalid address {}: {}", address, e)))?;
let device = self.adapter.device(addr)
.map_err(|e| Error::General(format!("Failed to get device {}: {}", address, e)))?;
if device.is_connected().await.unwrap_or(false) {
info!("Device {} is already connected", address);
self.device_memory.set_device_connected(address, true).await?;
return Ok(true);
}
for attempt in 1..=3 {
match device.connect().await {
Ok(()) => {
info!("Successfully connected to device {} on attempt {}", address, attempt);
self.device_memory.set_device_connected(address, true).await?;
return Ok(true);
}
Err(e) if attempt < 3 => {
warn!("Connection attempt {} failed for {}: {}", attempt, address, e);
sleep(Duration::from_millis(500)).await;
}
Err(e) => {
error!("Failed to connect to device {} after 3 attempts: {}", address, e);
return Err(Error::General(format!("Connection failed: {}", e)));
}
}
}
Ok(false)
}
pub async fn disconnect_device(&self, address: &str) -> Result<bool> {
info!("Attempting to disconnect from device: {}", address);
let addr = Address::from_str(address)
.map_err(|e| Error::General(format!("Invalid address {}: {}", address, e)))?;
let device = self.adapter.device(addr)
.map_err(|e| Error::General(format!("Failed to get device {}: {}", address, e)))?;
if !device.is_connected().await.unwrap_or(true) {
info!("Device {} is already disconnected", address);
self.device_memory.set_device_connected(address, false).await?;
return Ok(true);
}
match device.disconnect().await {
Ok(()) => {
info!("Successfully disconnected from device {}", address);
self.device_memory.set_device_connected(address, false).await?;
Ok(true)
}
Err(e) => {
error!("Failed to disconnect from device {}: {}", address, e);
Err(Error::General(format!("Disconnect failed: {}", e)))
}
}
}
pub async fn get_device_class(&self, address: &str) -> Result<u32> {
let addr = Address::from_str(address)
.map_err(|e| Error::General(format!("Invalid address {}: {}", address, e)))?;
let device = self.adapter.device(addr)
.map_err(|e| Error::General(format!("Failed to get device {}: {}", address, e)))?;
match device.class().await {
Ok(Some(class)) => Ok(class),
Ok(None) => Ok(0),
Err(e) => {
debug!("Failed to get class for device {}: {}", address, e);
Ok(0)
}
}
}
pub async fn get_device_appearance(&self, address: &str) -> Result<u16> {
let addr = Address::from_str(address)
.map_err(|e| Error::General(format!("Invalid address {}: {}", address, e)))?;
let device = self.adapter.device(addr)
.map_err(|e| Error::General(format!("Failed to get device {}: {}", address, e)))?;
match device.appearance().await {
Ok(Some(appearance)) => Ok(appearance),
Ok(None) => Ok(0),
Err(e) => {
debug!("Failed to get appearance for device {}: {}", address, e);
Ok(0)
}
}
}
pub async fn start_advertisement_monitoring_loop(&self) -> Result<()> {
info!("Starting continuous advertisement monitoring loop...");
let mut monitoring_interval = interval(Duration::from_secs(2));
loop {
monitoring_interval.tick().await;
let all_devices = self.device_memory.get_all_devices().await;
for device_info in all_devices {
if device_info.approved {
if let Ok(addr) = Address::from_str(&device_info.address) {
let device = self.adapter.device(addr).unwrap_or_else(|_| self.adapter.device(addr).unwrap());
if let Err(e) = self.process_device_advertisements(&device, &device_info.address).await {
debug!("Failed to process advertisement data for {}: {}", device_info.address, e);
}
}
}
}
}
}
}