use crate::device::traits::{Device, Light};
use crate::errors::BluetoothError;
use std::error::Error;
use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter};
use btleplug::platform::{Adapter, Manager};
use uuid::Uuid;
use std::time::Duration;
use tokio::time;
pub struct Controller<T: Light> {
prefix: String,
ble_manager: Manager,
ble_adapter: Adapter,
led_devices: Vec<T>,
}
impl<T> Controller<T>
where
T: Light + Device,
{
pub async fn new(prefix: &str) -> Result<Self, BluetoothError> {
let ble_manager = Manager::new().await?;
let ble_adapter = ble_manager.adapters().await?;
let client = ble_adapter
.into_iter()
.nth(0) .ok_or(BluetoothError::InvalidBluetoothAdapter)?;
Ok(Self {
prefix: prefix.to_string(),
ble_manager,
ble_adapter: client,
led_devices: Vec::<T>::new(),
})
}
pub fn ble_manager(&self) -> &Manager {
&self.ble_manager
}
pub fn list(&mut self) -> &mut Vec<T> {
&mut self.led_devices
}
pub async fn device_discovery(&self) -> Result<Vec<T>, Box<dyn Error>> {
self.ble_adapter.start_scan(ScanFilter::default()).await?;
time::sleep(Duration::from_secs(2)).await;
let mut led_devices: Vec<T> = Vec::new();
for p in self.ble_adapter.peripherals().await? {
let name = &p
.properties()
.await?
.ok_or(BluetoothError::InvalidPeriperipheralProperty)?
.local_name
.unwrap_or(String::from("Unknown"));
if name.contains(&self.prefix) {
led_devices.push(T::new(&name, &name, p, None, None));
}
}
Ok(led_devices)
}
pub async fn connect(
&mut self,
led_devices: Option<Vec<T>>,
characteristics_uuid: Option<Uuid>,
) -> Result<(), Box<dyn Error>> {
if let Some(l_devices) = led_devices {
self.led_devices = l_devices;
} else {
self.led_devices = self.device_discovery().await?;
}
for led_device in self.led_devices.iter_mut() {
led_device
.peripheral()
.as_ref()
.ok_or(BluetoothError::InvalidPeripheralReference)?
.connect()
.await?;
led_device
.peripheral()
.as_ref()
.ok_or(BluetoothError::InvalidPeripheralReference)?
.discover_services()
.await?;
let characteric = led_device
.peripheral()
.as_ref()
.ok_or(BluetoothError::InvalidPeripheralReference)?
.characteristics()
.into_iter()
.find(|c| {
c.uuid
== characteristics_uuid
.unwrap_or(*led_device.default_write_characteristic_uuid())
})
.ok_or(BluetoothError::NotFoundTargetCharacteristic)?;
led_device.add_write_characteristic(characteric);
}
Ok(())
}
pub async fn disconnect(&self, led_devices: &Vec<T>) -> Result<(), BluetoothError> {
for led_device in led_devices.iter() {
led_device
.peripheral()
.as_ref()
.ok_or(BluetoothError::InvalidPeripheralReference)?
.disconnect()
.await?;
}
Ok(())
}
}