ble_ledly/device/
mod.rs

1use crate::error::BluetoothError;
2
3use btleplug::api::Characteristic;
4use btleplug::api::{Peripheral as _, WriteType};
5use btleplug::platform::Peripheral;
6
7use uuid::Uuid;
8
9use async_trait::async_trait;
10use enumflags2::{bitflags, BitFlags};
11use std::fmt;
12
13//----------//
14// Re-export//
15//----------//
16////////////////////////////////////
17pub use self::led_device::LedDevice;
18////////////////////////////////////
19
20pub mod led_device;
21
22const BT_BASE_UUID: u128 = 0x00000000_0000_1000_8000_00805f9b34fb;
23
24// Wrapper for native ble charprops
25/// Describe the _operation kind_ supported
26/// by each characteristic. Each `OpKind` can piped.
27///
28/// ## Examples
29/// ```
30/// use ble_ledly::device::OpKind;
31///
32/// let char_kind_filter = OpKind::Write | OpKind::WriteWithoutResponse;
33/// assert_eq!(format!("{:04b}", char_kind_filter), "1100");
34/// ```
35#[bitflags]
36#[repr(u8)]
37#[derive(Copy, Clone, Debug, PartialEq)]
38pub enum OpKind {
39    Broadcast = 0x01,
40    Read = 0x02,
41    WriteWithoutResponse = 0x04,
42    Write = 0x08,
43    Notify = 0x10,
44    Indicate = 0x20,
45    AuthenticatedSignedWrites = 0x40,
46    ExtendedProperties = 0x80,
47}
48
49/// Allows to provide and wrap the default Device
50/// characteristic in different types.
51pub enum UuidKind {
52    Uuid(Uuid),
53    Uuid16(u16),
54    Uuid32(u32),
55    Uuid128(u128),
56}
57
58// Defines the characteristic kind, Write or Read.
59pub enum CharKind {
60    Read,
61    Write,
62}
63
64pub trait Device: fmt::Display {
65    fn new(
66        name: &str,
67        alias: &str,
68        peripheral: Option<Peripheral>,
69        write_char: Option<Characteristic>,
70        read_char: Option<Characteristic>,
71    ) -> Self;
72    //--------//
73    // Getter //
74    //--------//
75    fn alias(&self) -> &str;
76    fn name(&self) -> &str;
77    fn address(&self) -> Option<String>;
78    fn peripheral(&self) -> Option<&Peripheral>;
79    fn write_char(&self) -> Option<&Characteristic>;
80    fn read_char(&self) -> Option<&Characteristic>;
81    fn default_write_characteristic_uuid(&self) -> Uuid;
82
83    /// Return all the discovered device characteristic.
84    ///
85    /// ## Examples
86    /// ```compile_fail
87    ///   for characteristic in light.characteristics().unwrap().iter() {
88    ///        println!(
89    ///            "\tUuid: {:?}, Type: {:?}",
90    ///            characteristic.uuid, characteristic.properties
91    ///        );
92    ///    }
93    /// ```
94    fn characteristics(&self) -> Option<Vec<Characteristic>> {
95        if let Some(peripheral) = self.peripheral().as_ref() {
96            return Some(
97                peripheral
98                    .characteristics()
99                    .into_iter()
100                    .collect::<Vec<Characteristic>>(),
101            );
102        }
103        None
104    }
105
106    /// Return all the discovered device characteristic and allows
107    /// to filter them by type
108    ///
109    /// ## Examples
110    /// ```compile_fail
111    ///       let char_kind_filter = OpKind::Write | OpKind::WriteWithoutResponse;
112
113    ///       for characteristic in light
114    ///           .characteristics_by_type(char_kind_filter)
115    ///           .unwrap()
116    ///           .iter()
117    ///       {
118    ///           println!(
119    ///               "\tUuid: {:?}, Type: {:?}",
120    ///               characteristic.uuid, characteristic.properties
121    ///           );
122    ///       }
123    /// ```
124    fn characteristics_by_type(&self, kinds: BitFlags<OpKind>) -> Option<Vec<Characteristic>> {
125        if let Some(chars) = self.characteristics() {
126            return Some(
127                chars
128                    .into_iter()
129                    .filter(|c| c.properties.bits() == kinds.bits())
130                    .collect(),
131            );
132        }
133        None
134    }
135
136    //--------//
137    // Setter //
138    //--------//
139    fn set_alias(&mut self, alias: &str);
140    fn set_name(&mut self, name: &str);
141    fn set_peripheral(&mut self, peripheral: Peripheral);
142
143    /// Allows to set the default characteristic (Write or Read),
144    /// per-device by providing the `Characteristic`.
145    ///
146    /// ## Examples
147    /// ```compile_fail
148    ///    // Set it with an Uuid, an u32, or u16
149    ///    light.set_char(&CharKind::Write, &UuidKind::Uuid16(0xFFD9))?;
150    /// ```
151    fn set_write_char(&mut self, characteristic: &Characteristic);
152
153    /// Allows to set the default characteristic (Write or Read),
154    /// per-device by providing the `UuidKind` of the characteristic.
155    ///
156    /// ## Examples
157    /// ```compile_fail
158    ///    // Set it with an Uuid, an u32, or u16
159    ///    light.set_char(&CharKind::Write, &UuidKind::Uuid16(0xFFD9))?;
160    /// ```
161    fn set_char(
162        &mut self,
163        char_kind: &CharKind,
164        uuid_kind: &UuidKind,
165    ) -> Result<(), BluetoothError> {
166        match char_kind {
167            CharKind::Write => match uuid_kind {
168                UuidKind::Uuid(uuid) => self.set_char_with_uuid(char_kind, &uuid),
169                UuidKind::Uuid128(uuid) => {
170                    self.set_char_with_uuid(char_kind, &Uuid::from_u128(*uuid))
171                }
172                UuidKind::Uuid32(uuid) => self.set_char_with_u32(char_kind, *uuid),
173                UuidKind::Uuid16(uuid) => self.set_char_with_u16(char_kind, *uuid),
174            },
175            CharKind::Read => unimplemented!(),
176        }
177    }
178    fn set_char_with_uuid(
179        &mut self,
180        char_kind: &CharKind,
181        uuid: &Uuid,
182    ) -> Result<(), BluetoothError> {
183        let char = self
184            .peripheral()
185            .as_ref()
186            .ok_or(BluetoothError::InvalidPeripheralReference)?
187            .characteristics()
188            .into_iter()
189            .find(|c| c.uuid.as_u128() == uuid.as_u128())
190            .ok_or(BluetoothError::NotFoundTargetCharacteristic)?;
191        match char_kind {
192            CharKind::Write => self.set_write_char(&char),
193            CharKind::Read => unimplemented!(),
194        }
195        Ok(())
196    }
197    fn set_char_with_u16(&mut self, char_kind: &CharKind, u16: u16) -> Result<(), BluetoothError> {
198        self.set_char_with_u32(char_kind, u16 as u32) // extend it to 32 bits
199    }
200    fn set_char_with_u32(&mut self, char_kind: &CharKind, u32: u32) -> Result<(), BluetoothError> {
201        let uuid = Uuid::from_u128(BT_BASE_UUID | ((u32 as u128) << 96));
202        self.set_char_with_uuid(char_kind, &uuid)
203    }
204}
205
206#[async_trait]
207pub trait Disconnect {
208    async fn leave(&self) -> Result<(), BluetoothError>;
209}
210pub trait Connect {}
211#[async_trait]
212pub trait Write {
213    async fn push(&self, raw_bytes: &[u8]) -> Result<(), BluetoothError>;
214}
215
216//-------------------------//
217// Blanket implementations //
218//-------------------------//
219#[async_trait]
220impl<D: Device + std::marker::Sync> Disconnect for D {
221    async fn leave(&self) -> Result<(), BluetoothError> {
222        self.peripheral()
223            .as_ref()
224            .ok_or(BluetoothError::InvalidPeripheralReference)?
225            .disconnect()
226            .await?;
227        Ok(())
228    }
229}
230
231#[async_trait]
232impl<D: Device + std::marker::Sync> Write for D {
233    async fn push(&self, raw_bytes: &[u8]) -> Result<(), BluetoothError> {
234        //TODO: Implement different WriteType(s)
235        self.peripheral()
236            .as_ref()
237            .ok_or(BluetoothError::InvalidPeripheralReference)?
238            .write(
239                self.write_char()
240                    .ok_or(BluetoothError::InvalidCharacteristic)?,
241                raw_bytes,
242                WriteType::WithoutResponse,
243            )
244            .await?;
245
246        Ok(())
247    }
248}