Skip to main content

btle/le/
report.rs

1use crate::le::advertisement::{RawAdvertisement, StaticAdvBuffer};
2use crate::ConversionError;
3use crate::{BTAddress, BT_ADDRESS_LEN, RSSI};
4use core::convert::TryFrom;
5use core::fmt::Formatter;
6
7pub struct NumReports(u8);
8impl NumReports {
9    pub const NUM_REPORTS_MIN: u8 = 0x01;
10    pub const NUM_REPORTS_MAX: u8 = 0x19;
11    pub fn new(num_reports: u8) -> NumReports {
12        if let Ok(r) = NumReports::try_from(num_reports) {
13            r
14        } else {
15            panic!("invalid number of reports: {}", num_reports);
16        }
17    }
18}
19impl From<NumReports> for u8 {
20    fn from(n: NumReports) -> Self {
21        n.0
22    }
23}
24impl TryFrom<u8> for NumReports {
25    type Error = ConversionError;
26
27    fn try_from(value: u8) -> Result<Self, Self::Error> {
28        if value <= NumReports::NUM_REPORTS_MAX && value >= NumReports::NUM_REPORTS_MIN {
29            Ok(NumReports(value))
30        } else {
31            Err(ConversionError(()))
32        }
33    }
34}
35impl TryFrom<usize> for NumReports {
36    type Error = ConversionError;
37
38    fn try_from(value: usize) -> Result<Self, Self::Error> {
39        match u8::try_from(value).map(NumReports::try_from) {
40            Ok(Ok(v)) => Ok(v),
41            _ => Err(ConversionError(())),
42        }
43    }
44}
45#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)]
46pub enum EventType {
47    AdvInd = 0x00,
48    AdvDirectInd = 0x01,
49    AdvScanInd = 0x02,
50    AdvNonconnInd = 0x03,
51    ScanRsp = 0x04,
52}
53impl EventType {
54    pub fn as_str(self) -> &'static str {
55        match self {
56            EventType::AdvInd => "ADV_IND",
57            EventType::AdvDirectInd => "ADV_DIRECT_IND",
58            EventType::AdvScanInd => "ADV_SCAN_IND",
59            EventType::AdvNonconnInd => "ADV_NONNCONN_IND",
60            EventType::ScanRsp => "SCAN_RSP",
61        }
62    }
63}
64impl core::fmt::Display for EventType {
65    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
66        f.write_str(self.as_str())
67    }
68}
69impl TryFrom<u8> for EventType {
70    type Error = ConversionError;
71    fn try_from(v: u8) -> Result<Self, Self::Error> {
72        match v {
73            0x00 => Ok(EventType::AdvInd),
74            0x01 => Ok(EventType::AdvDirectInd),
75            0x02 => Ok(EventType::AdvScanInd),
76            0x03 => Ok(EventType::AdvNonconnInd),
77            0x04 => Ok(EventType::ScanRsp),
78            _ => Err(ConversionError(())),
79        }
80    }
81}
82impl From<EventType> for u8 {
83    fn from(e: EventType) -> Self {
84        e as u8
85    }
86}
87#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)]
88pub enum AddressType {
89    PublicDevice = 0x00,
90    RandomDevice = 0x01,
91    PublicIdentity = 0x02,
92    RandomIdentity = 0x03,
93}
94impl TryFrom<u8> for AddressType {
95    type Error = ConversionError;
96
97    fn try_from(value: u8) -> Result<Self, Self::Error> {
98        match value {
99            0x00 => Ok(AddressType::PublicDevice),
100            0x01 => Ok(AddressType::RandomDevice),
101            0x02 => Ok(AddressType::PublicIdentity),
102            0x03 => Ok(AddressType::RandomIdentity),
103            _ => Err(ConversionError(())),
104        }
105    }
106}
107impl From<AddressType> for u8 {
108    fn from(a: AddressType) -> Self {
109        a as u8
110    }
111}
112/// BLE Advertising report from scanning for advertisements that contains advertisement type [`EventType`],
113/// address type [`AddressType`], bluetooth address [`BTAddress`], data (0-31 bytes) and
114/// maybe (`Option`) RSSI [`RSSI`].
115///
116/// # Important
117/// `T` is the byte buffer that stores the advertisement data (0-31 bytes) which means `T` should
118/// always be able to hold 31 bytes if you are using `unpack_from`.
119#[derive(Copy, Clone)]
120pub struct ReportInfo<T = StaticAdvBuffer> {
121    /// Advertisement Type.
122    pub event_type: EventType,
123    /// Bluetooth Address type.
124    pub address_type: AddressType,
125    /// Bluetooth Address associated with the Advertisement.
126    pub address: BTAddress,
127    /// Advertisement data (0-31 bytes).
128    pub data: RawAdvertisement<T>,
129    /// RSSI (-127dBm to +20dBm) or `None` if RSSI readings are unsupported by the adapter.
130    pub rssi: Option<RSSI>,
131}
132impl<T: AsRef<[u8]>> core::fmt::Debug for ReportInfo<T> {
133    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134        f.debug_struct("ReportInfo")
135            .field("event_type", &self.event_type)
136            .field("address_type", &self.address_type)
137            .field("address", &self.address)
138            .field("rssi", &self.rssi)
139            .field("data", &self.data.as_ref())
140            .finish()
141    }
142}
143impl<T: AsRef<[u8]> + Default> Default for ReportInfo<T> {
144    fn default() -> Self {
145        Self {
146            event_type: EventType::AdvInd,
147            address_type: AddressType::PublicDevice,
148            address: BTAddress::ZEROED,
149            data: RawAdvertisement(T::default()),
150            rssi: None,
151        }
152    }
153}
154impl<T: AsRef<[u8]>> ReportInfo<T> {
155    pub fn byte_len(&self) -> usize {
156        // event_type (1) + address_type (1) + address (6) + data (data.len()) + rssi (1)
157        1 + 1 + BT_ADDRESS_LEN + self.data.as_ref().len() + 1
158    }
159    pub fn as_ref(&self) -> ReportInfo<&[u8]> {
160        ReportInfo {
161            event_type: self.event_type,
162            address_type: self.address_type,
163            address: self.address,
164            data: RawAdvertisement(self.data.as_ref()),
165            rssi: self.rssi,
166        }
167    }
168}