btleplug/api/
mod.rs

1// btleplug Source Code File
2//
3// Copyright 2020 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7//
8// Some portions of this file are taken and/or modified from Rumble
9// (https://github.com/mwylde/rumble), using a dual MIT/Apache License under the
10// following copyright:
11//
12// Copyright (c) 2014 The Rust Project Developers
13
14//! The `api` module contains the traits and types which make up btleplug's API. These traits have a
15//! different implementation for each supported platform, but only one implementation can be found
16//! on any given platform. These implementations are in the [`platform`](crate::platform) module.
17//!
18//! You will may want to import both the traits and their implementations, like:
19//! ```
20//! use btleplug::api::{Central, Manager as _, Peripheral as _};
21//! use btleplug::platform::{Adapter, Manager, Peripheral};
22//! ```
23
24pub(crate) mod bdaddr;
25pub mod bleuuid;
26
27use crate::Result;
28use async_trait::async_trait;
29use bitflags::bitflags;
30use futures::stream::Stream;
31#[cfg(feature = "serde")]
32use serde::{Deserialize, Serialize};
33#[cfg(feature = "serde")]
34use serde_cr as serde;
35use std::{
36    collections::{BTreeSet, HashMap},
37    fmt::{self, Debug, Display, Formatter},
38    pin::Pin,
39};
40use uuid::Uuid;
41
42pub use self::bdaddr::{BDAddr, ParseBDAddrError};
43
44use crate::platform::PeripheralId;
45
46#[cfg_attr(
47    feature = "serde",
48    derive(Serialize, Deserialize),
49    serde(crate = "serde_cr")
50)]
51#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
52pub enum AddressType {
53    Random,
54    #[default]
55    Public,
56}
57
58impl AddressType {
59    #[allow(clippy::should_implement_trait)]
60    pub fn from_str(v: &str) -> Option<AddressType> {
61        match v {
62            "public" => Some(AddressType::Public),
63            "random" => Some(AddressType::Random),
64            _ => None,
65        }
66    }
67
68    pub fn from_u8(v: u8) -> Option<AddressType> {
69        match v {
70            1 => Some(AddressType::Public),
71            2 => Some(AddressType::Random),
72            _ => None,
73        }
74    }
75
76    pub fn num(&self) -> u8 {
77        match *self {
78            AddressType::Public => 1,
79            AddressType::Random => 2,
80        }
81    }
82}
83
84/// A notification sent from a peripheral due to a change in a value.
85#[derive(Clone, Debug, Eq, PartialEq)]
86pub struct ValueNotification {
87    /// UUID of the characteristic that fired the notification.
88    pub uuid: Uuid,
89    /// The new value of the characteristic.
90    pub value: Vec<u8>,
91}
92
93bitflags! {
94    /// A set of properties that indicate what operations are supported by a Characteristic.
95    #[derive(Default, Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
96    pub struct CharPropFlags: u8 {
97        const BROADCAST = 0x01;
98        const READ = 0x02;
99        const WRITE_WITHOUT_RESPONSE = 0x04;
100        const WRITE = 0x08;
101        const NOTIFY = 0x10;
102        const INDICATE = 0x20;
103        const AUTHENTICATED_SIGNED_WRITES = 0x40;
104        const EXTENDED_PROPERTIES = 0x80;
105    }
106}
107
108/// A GATT service. Services are groups of characteristics, which may be standard or
109/// device-specific.
110#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
111pub struct Service {
112    /// The UUID for this service.
113    pub uuid: Uuid,
114    /// Whether this is a primary service.
115    pub primary: bool,
116    /// The characteristics of this service.
117    pub characteristics: BTreeSet<Characteristic>,
118}
119
120/// A Bluetooth characteristic. Characteristics are the main way you will interact with other
121/// bluetooth devices. Characteristics are identified by a UUID which may be standardized
122/// (like 0x2803, which identifies a characteristic for reading heart rate measurements) but more
123/// often are specific to a particular device. The standard set of characteristics can be found
124/// [here](https://www.bluetooth.com/specifications/gatt/characteristics).
125///
126/// A characteristic may be interacted with in various ways depending on its properties. You may be
127/// able to write to it, read from it, set its notify or indicate status, or send a command to it.
128#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
129pub struct Characteristic {
130    /// The UUID for this characteristic. This uniquely identifies its behavior.
131    pub uuid: Uuid,
132    /// The UUID of the service this characteristic belongs to.
133    pub service_uuid: Uuid,
134    /// The set of properties for this characteristic, which indicate what functionality it
135    /// supports. If you attempt an operation that is not supported by the characteristics (for
136    /// example setting notify on one without the NOTIFY flag), that operation will fail.
137    pub properties: CharPropFlags,
138    /// The descriptors of this characteristic.
139    pub descriptors: BTreeSet<Descriptor>,
140}
141
142impl Display for Characteristic {
143    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
144        write!(
145            f,
146            "uuid: {:?}, char properties: {:?}",
147            self.uuid, self.properties
148        )
149    }
150}
151
152/// Add doc
153#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
154pub struct Descriptor {
155    /// The UUID for this descriptor. This uniquely identifies its behavior.
156    pub uuid: Uuid,
157    /// The UUID of the service this descriptor belongs to.
158    pub service_uuid: Uuid,
159    /// The UUID of the characteristic this descriptor belongs to.
160    pub characteristic_uuid: Uuid,
161}
162
163impl Display for Descriptor {
164    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
165        write!(f, "uuid: {:?}", self.uuid)
166    }
167}
168
169/// The properties of this peripheral, as determined by the advertising reports we've received for
170/// it.
171#[cfg_attr(
172    feature = "serde",
173    derive(Serialize, Deserialize),
174    serde(crate = "serde_cr")
175)]
176#[derive(Debug, Default, Clone)]
177pub struct PeripheralProperties {
178    /// The address of this peripheral
179    pub address: BDAddr,
180    /// The type of address (either random or public)
181    pub address_type: Option<AddressType>,
182    /// The local name. This is generally a human-readable string that identifies the type of device.
183    pub local_name: Option<String>,
184    /// The transmission power level for the device
185    pub tx_power_level: Option<i16>,
186    /// The most recent Received Signal Strength Indicator for the device
187    pub rssi: Option<i16>,
188    /// Advertisement data specific to the device manufacturer. The keys of this map are
189    /// 'manufacturer IDs', while the values are arbitrary data.
190    pub manufacturer_data: HashMap<u16, Vec<u8>>,
191    /// Advertisement data specific to a service. The keys of this map are
192    /// 'Service UUIDs', while the values are arbitrary data.
193    pub service_data: HashMap<Uuid, Vec<u8>>,
194    /// Advertised services for this device
195    pub services: Vec<Uuid>,
196    pub class: Option<u32>,
197}
198
199#[cfg_attr(
200    feature = "serde",
201    derive(Serialize, Deserialize),
202    serde(crate = "serde_cr")
203)]
204/// The filter used when scanning for BLE devices.
205#[derive(Clone, Debug, Default, Eq, PartialEq)]
206pub struct ScanFilter {
207    /// If the filter contains at least one service UUID, only devices supporting at least one of
208    /// the given services will be available.
209    pub services: Vec<Uuid>,
210}
211
212/// The type of write operation to use.
213#[derive(Clone, Copy, Debug, Eq, PartialEq)]
214pub enum WriteType {
215    /// A write operation where the device is expected to respond with a confirmation or error. Also
216    /// known as a request.
217    WithResponse,
218    /// A write-without-response, also known as a command.
219    WithoutResponse,
220}
221
222/// Peripheral is the device that you would like to communicate with (the "server" of BLE). This
223/// struct contains both the current state of the device (its properties, characteristics, etc.)
224/// as well as functions for communication.
225#[async_trait]
226pub trait Peripheral: Send + Sync + Clone + Debug {
227    /// Returns the unique identifier of the peripheral.
228    fn id(&self) -> PeripheralId;
229
230    /// Returns the MAC address of the peripheral.
231    fn address(&self) -> BDAddr;
232
233    /// Returns the set of properties associated with the peripheral. These may be updated over time
234    /// as additional advertising reports are received.
235    async fn properties(&self) -> Result<Option<PeripheralProperties>>;
236
237    /// The set of services we've discovered for this device. This will be empty until
238    /// `discover_services` is called.
239    fn services(&self) -> BTreeSet<Service>;
240
241    /// The set of characteristics we've discovered for this device. This will be empty until
242    /// `discover_services` is called.
243    fn characteristics(&self) -> BTreeSet<Characteristic> {
244        self.services()
245            .iter()
246            .flat_map(|service| service.characteristics.clone().into_iter())
247            .collect()
248    }
249
250    /// Returns true iff we are currently connected to the device.
251    async fn is_connected(&self) -> Result<bool>;
252
253    /// Creates a connection to the device. If this method returns Ok there has been successful
254    /// connection. Note that peripherals allow only one connection at a time. Operations that
255    /// attempt to communicate with a device will fail until it is connected.
256    async fn connect(&self) -> Result<()>;
257
258    /// Terminates a connection to the device.
259    async fn disconnect(&self) -> Result<()>;
260
261    /// Discovers all services for the device, including their characteristics.
262    async fn discover_services(&self) -> Result<()>;
263
264    /// Write some data to the characteristic. Returns an error if the write couldn't be sent or (in
265    /// the case of a write-with-response) if the device returns an error.
266    async fn write(
267        &self,
268        characteristic: &Characteristic,
269        data: &[u8],
270        write_type: WriteType,
271    ) -> Result<()>;
272
273    /// Sends a read request to the device. Returns either an error if the request was not accepted
274    /// or the response from the device.
275    async fn read(&self, characteristic: &Characteristic) -> Result<Vec<u8>>;
276
277    /// Enables either notify or indicate (depending on support) for the specified characteristic.
278    async fn subscribe(&self, characteristic: &Characteristic) -> Result<()>;
279
280    /// Disables either notify or indicate (depending on support) for the specified characteristic.
281    async fn unsubscribe(&self, characteristic: &Characteristic) -> Result<()>;
282
283    /// Returns a stream of notifications for characteristic value updates. The stream will receive
284    /// a notification when a value notification or indication is received from the device.
285    /// The stream will remain valid across connections and can be queried before any connection
286    /// is made.
287    async fn notifications(&self) -> Result<Pin<Box<dyn Stream<Item = ValueNotification> + Send>>>;
288
289    /// Write some data to the descriptor. Returns an error if the write couldn't be sent or (in
290    /// the case of a write-with-response) if the device returns an error.
291    async fn write_descriptor(&self, descriptor: &Descriptor, data: &[u8]) -> Result<()>;
292
293    /// Sends a read descriptor request to the device. Returns either an error if the request
294    /// was not accepted or the response from the device.
295    async fn read_descriptor(&self, descriptor: &Descriptor) -> Result<Vec<u8>>;
296}
297
298#[cfg_attr(
299    feature = "serde",
300    derive(Serialize, Deserialize),
301    serde(crate = "serde_cr")
302)]
303#[derive(Debug, Clone)]
304pub enum CentralEvent {
305    DeviceDiscovered(PeripheralId),
306    DeviceUpdated(PeripheralId),
307    DeviceConnected(PeripheralId),
308    DeviceDisconnected(PeripheralId),
309    /// Emitted when a Manufacturer Data advertisement has been received from a device
310    ManufacturerDataAdvertisement {
311        id: PeripheralId,
312        manufacturer_data: HashMap<u16, Vec<u8>>,
313    },
314    /// Emitted when a Service Data advertisement has been received from a device
315    ServiceDataAdvertisement {
316        id: PeripheralId,
317        service_data: HashMap<Uuid, Vec<u8>>,
318    },
319    /// Emitted when the advertised services for a device has been updated
320    ServicesAdvertisement {
321        id: PeripheralId,
322        services: Vec<Uuid>,
323    },
324}
325
326/// Central is the "client" of BLE. It's able to scan for and establish connections to peripherals.
327/// A Central can be obtained from [`Manager::adapters()`].
328#[async_trait]
329pub trait Central: Send + Sync + Clone {
330    type Peripheral: Peripheral;
331
332    /// Retrieve a stream of `CentralEvent`s. This stream will receive notifications when events
333    /// occur for this Central module. See [`CentralEvent`] for the full set of possible events.
334    async fn events(&self) -> Result<Pin<Box<dyn Stream<Item = CentralEvent> + Send>>>;
335
336    /// Starts a scan for BLE devices. This scan will generally continue until explicitly stopped,
337    /// although this may depend on your Bluetooth adapter. Discovered devices will be announced
338    /// to subscribers of `events` and will be available via `peripherals()`.
339    /// The filter can be used to scan only for specific devices. While some implementations might
340    /// ignore (parts of) the filter and make additional devices available, other implementations
341    /// might require at least one filter for security reasons. Cross-platform code should provide
342    /// a filter, but must be able to handle devices, which do not fit into the filter.
343    async fn start_scan(&self, filter: ScanFilter) -> Result<()>;
344
345    /// Stops scanning for BLE devices.
346    async fn stop_scan(&self) -> Result<()>;
347
348    /// Returns the list of [`Peripheral`]s that have been discovered so far. Note that this list
349    /// may contain peripherals that are no longer available.
350    async fn peripherals(&self) -> Result<Vec<Self::Peripheral>>;
351
352    /// Returns a particular [`Peripheral`] by its address if it has been discovered.
353    async fn peripheral(&self, id: &PeripheralId) -> Result<Self::Peripheral>;
354
355    /// Add a [`Peripheral`] from a MAC address without a scan result. Not supported on all Bluetooth systems.
356    async fn add_peripheral(&self, address: &PeripheralId) -> Result<Self::Peripheral>;
357
358    /// Get information about the Bluetooth adapter being used, such as the model or type.
359    ///
360    /// The details of this are platform-specific andyou should not attempt to parse it, but it may
361    /// be useful for debug logs.
362    async fn adapter_info(&self) -> Result<String>;
363}
364
365/// The Manager is the entry point to the library, providing access to all the Bluetooth adapters on
366/// the system. You can obtain an instance from [`platform::Manager::new()`](crate::platform::Manager::new).
367///
368/// ## Usage
369/// ```
370/// use btleplug::api::Manager as _;
371/// use btleplug::platform::Manager;
372/// # use std::error::Error;
373///
374/// # async fn example() -> Result<(), Box<dyn Error>> {
375/// let manager = Manager::new().await?;
376/// let adapter_list = manager.adapters().await?;
377/// if adapter_list.is_empty() {
378///    eprintln!("No Bluetooth adapters");
379/// }
380/// # Ok(())
381/// # }
382/// ```
383#[async_trait]
384pub trait Manager {
385    /// The concrete type of the [`Central`] implementation.
386    type Adapter: Central;
387
388    /// Get a list of all Bluetooth adapters on the system. Each adapter implements [`Central`].
389    async fn adapters(&self) -> Result<Vec<Self::Adapter>>;
390}