ble_ledly/controller.rs
1use crate::device::{CharKind, Device, UuidKind};
2use crate::error::BluetoothError;
3use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter};
4use btleplug::platform::{Adapter, Manager};
5use std::time::Duration;
6use tokio::time;
7
8pub struct Controller<D: Device> {
9 prefix: Option<String>,
10
11 ble_manager: Manager,
12 ble_adapter: Adapter,
13
14 //TODO: provide key-like access - hashmap
15 devices: Vec<D>,
16}
17
18impl<D: Device> Controller<D> {
19 /// Creates a new `Device` controller
20 ///
21 /// # Examples
22 ///
23 ///
24 /// ```no_run
25 /// use ble_ledly::device::LedDevice;
26 /// use ble_ledly::Controller;
27 /// use std::error::Error;
28 ///
29 /// async fn test() -> Result<(), Box<dyn Error>> {
30 /// let mut controller = Controller::<LedDevice>::new().await?;
31 /// Ok(())
32 /// }
33 /// ```
34 pub async fn new() -> Result<Controller<D>, BluetoothError> {
35 let ble_manager = Manager::new().await?;
36
37 let ble_adapter = ble_manager.adapters().await?;
38 let client = ble_adapter
39 .into_iter()
40 .nth(0) // take first
41 .ok_or(BluetoothError::InvalidBluetoothAdapter)?;
42
43 Ok(Self {
44 prefix: None,
45 ble_manager,
46 ble_adapter: client,
47 devices: Vec::<D>::new(),
48 })
49 }
50
51 /// Creates a new `Device` controller with `Prefix`.
52 /// The `prefix` is used to automatically filter the
53 /// devices found during `device_discovery()`; The filter looks
54 /// if the `prefix` __id contained__ in the device name.
55 ///
56 /// # Examples
57 ///
58 /// ```no_run
59 /// use ble_ledly::device::LedDevice;
60 /// use ble_ledly::Controller;
61 /// use std::error::Error;
62 ///
63 /// async fn test() -> Result<(), Box<dyn Error>> {
64 /// let mut controller = Controller::<LedDevice>::new_with_prefix("QHM-").await?;
65 /// Ok(())
66 /// }
67 /// ```
68 pub async fn new_with_prefix(prefix: &str) -> Result<Controller<D>, BluetoothError> {
69 let ble_manager = Manager::new().await?;
70
71 let ble_adapter = ble_manager.adapters().await?;
72 let client = ble_adapter
73 .into_iter()
74 .nth(0) // take first
75 .ok_or(BluetoothError::InvalidBluetoothAdapter)?;
76
77 Ok(Self {
78 prefix: Some(prefix.to_string()),
79 ble_manager,
80 ble_adapter: client,
81 devices: Vec::<D>::new(),
82 })
83 }
84
85 /// Sets all the devices default _Characteristic_ (Write or Read)
86 /// to the provided value. Global shortcut, instead of setting, per-device _Characteristic_
87 /// configuration
88 ///
89 /// # Examples
90 ///
91 /// ```compile_fail
92 /// controller.set_all_char(&CharKind::Write, &UuidKind::Uuid16(0xFFD9))?;
93 /// ````
94 pub fn set_all_char(
95 &mut self,
96 char_kind: &CharKind,
97 uuid_kind: &UuidKind,
98 ) -> Result<(), BluetoothError> {
99 self.devices
100 .iter_mut()
101 .map(|device| device.set_char(char_kind, uuid_kind))
102 .collect::<Result<(), BluetoothError>>()?;
103 Ok(())
104 }
105
106 //---------//
107 // Getters //
108 //---------//
109 pub fn ble_manager(&self) -> &Manager {
110 &self.ble_manager
111 }
112
113 /// Return a list (Vec<D>) of all the connected devices.
114 /// The list is empty until devices are connected to the controller.
115 ///
116 /// # Examples
117 ///
118 /// ```no_run
119 /// use ble_ledly::device::LedDevice;
120 /// use ble_ledly::Controller;
121 /// use std::error::Error;
122 ///
123 /// async fn test() -> Result<(), Box<dyn Error>> {
124 /// let mut controller = Controller::<LedDevice>::new_with_prefix("QHM-").await?;
125 /// let connected_lights = controller.list();
126 /// Ok(())
127 /// }
128 pub fn list(&mut self) -> &mut Vec<D> {
129 &mut self.devices
130 }
131
132 //------------------//
133 // Device Discovery //
134 //------------------//
135 /// Discover _ble devices_ by running a scan op. on the default adapter
136 /// and returns the found _devices__.
137 ///
138 /// # Examples
139 ///
140 /// ```no_run
141 /// use ble_ledly::device::LedDevice;
142 /// use ble_ledly::Controller;
143 /// use std::error::Error;
144 ///
145 /// async fn test() -> Result<(), Box<dyn Error>> {
146 /// let mut controller = Controller::<LedDevice>::new_with_prefix("QHM-").await?;
147 /// let led_devices = controller.device_discovery().await?;
148 /// Ok(())
149 /// }
150 pub async fn device_discovery(&self) -> Result<Vec<D>, BluetoothError> {
151 self.ble_adapter.start_scan(ScanFilter::default()).await?;
152 time::sleep(Duration::from_secs(2)).await;
153
154 let mut devices: Vec<D> = Vec::new();
155
156 for p in self.ble_adapter.peripherals().await? {
157 let name = &p
158 .properties()
159 .await?
160 .ok_or(BluetoothError::InvalidPeriperipheralProperty)?
161 .local_name
162 .unwrap_or(String::from("Unknown"));
163
164 if name.contains(self.prefix.as_ref().unwrap_or(&"".to_string())) {
165 devices.push(D::new(&name, &name, Some(p), None, None));
166 }
167 }
168 Ok(devices)
169 }
170 //---------//
171 // Connect //
172 //---------//
173 /// Standalone `connect()` that can be used in conjunction with
174 /// `Controller::new_with_prefix(prfix)`; it automatically runs
175 /// a `device_discovery()` and connects to all devices that match `prefix`.
176 /// If no `prefix` is provided it attemps to connect to all available devices.
177 ///
178 /// # Examples
179 ///
180 /// ```no_run
181 /// use ble_ledly::device::LedDevice;
182 /// use ble_ledly::Controller;
183 /// use std::error::Error;
184 ///
185 /// async fn test() -> Result<(), Box<dyn Error>> {
186 /// let mut controller = Controller::<LedDevice>::new_with_prefix("QHM-").await?;
187 /// controller.connect().await?;
188 /// Ok(())
189 /// }
190 pub async fn connect(&mut self) -> Result<(), BluetoothError> {
191 // Discover devices //
192 let devices = self.device_discovery().await?;
193 self._connect(devices).await?;
194 Ok(())
195 }
196
197 /// Connect to the devices passed as function's argument.
198 ///
199 /// # Examples
200 ///
201 /// ```compile_fail
202 /// let led_devices = controller.device_discovery().await?;
203 /// filter devices
204 /// let lights: Vec<LedDevice> = led_devices
205 /// .into_iter()
206 /// .filter(|device| device.name.contains("QHM-"))
207 /// .collect();
208 /// controller.connect_with_devices(lights).await?;
209 ///
210 /// ```
211 pub async fn connect_with_devices(&mut self, devices: Vec<D>) -> Result<(), BluetoothError> {
212 self._connect(devices).await?;
213 Ok(())
214 }
215 async fn _connect(&mut self, devices: Vec<D>) -> Result<(), BluetoothError> {
216 self.devices = devices;
217
218 // Connect devices //
219 for device in self.devices.iter_mut() {
220 // Connect //
221 device
222 .peripheral()
223 .as_ref()
224 .ok_or(BluetoothError::InvalidPeripheralReference)?
225 .connect()
226 .await?;
227
228 // Service discovry //
229 device
230 .peripheral()
231 .as_ref()
232 .ok_or(BluetoothError::InvalidPeripheralReference)?
233 .discover_services()
234 .await?;
235 }
236 Ok(())
237 }
238}