Skip to content

Commit

Permalink
Allow searching by MAC address too
Browse files Browse the repository at this point in the history
This is enabled by adding the BleId enum. It has one variant for name
and one for MAC address.
  • Loading branch information
lukipuki committed Jan 21, 2025
1 parent 6f8abbd commit c662253
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 12 deletions.
51 changes: 41 additions & 10 deletions src/connections/ble_handler.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use btleplug::api::{
Central, CentralEvent, Characteristic, Manager as _, Peripheral as _, ScanFilter,
BDAddr, Central, CentralEvent, Characteristic, Manager as _, Peripheral as _, ScanFilter,
ValueNotification, WriteType,
};
use btleplug::platform::{Adapter, Manager, Peripheral};
use futures::stream::StreamExt;
use futures_util::stream::BoxStream;
use log::error;
use std::fmt::Display;
use std::future;
use std::str::FromStr;
use uuid::Uuid;

use crate::errors_internal::{BleConnectionError, Error, InternalStreamError};
Expand Down Expand Up @@ -36,13 +38,37 @@ pub enum RadioMessage {
Packet(EncodedToRadioPacketWithHeader),
}

pub enum BleId {
Name(String),
MacAddress(BDAddr),
}

impl BleId {
pub fn from_mac_address(mac: &str) -> Result<BleId, Error> {
let bdaddr = BDAddr::from_str(mac).map_err(|e| Error::InvalidParameter {
source: Box::new(e),
description: "Error while parsing a MAC address".to_owned(),
})?;
Ok(BleId::MacAddress(bdaddr))
}
}

impl Display for BleId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BleId::Name(name) => write!(f, "name={name}"),
BleId::MacAddress(mac) => write!(f, "MAC={mac}"),
}
}
}

#[allow(dead_code)]
impl BleHandler {
pub async fn new(name: String) -> Result<Self, Error> {
let (radio, adapter) = Self::find_ble_radio(&name).await?;
pub async fn new(ble_id: &BleId) -> Result<Self, Error> {
let (radio, adapter) = Self::find_ble_radio(ble_id).await?;
radio.connect().await.map_err(|e| Error::StreamBuildError {
source: Box::new(e),
description: format!("Failed to connect to the device {name}"),
description: format!("Failed to connect to the device {ble_id}"),
})?;
let [toradio_char, fromnum_char, fromradio_char] =
Self::find_characteristics(&radio).await?;
Expand All @@ -68,7 +94,7 @@ impl BleHandler {
/// It searches for the 'MSH_SERVICE' running on the device.
///
/// It also returns the associated adapter that can reach this radio.
async fn find_ble_radio(name: &str) -> Result<(Peripheral, Adapter), Error> {
async fn find_ble_radio(ble_id: &BleId) -> Result<(Peripheral, Adapter), Error> {
//TODO: support searching both by a name and by a MAC address
let scan_error_fn = |e: btleplug::Error| Error::StreamBuildError {
source: Box::new(e),
Expand All @@ -86,10 +112,15 @@ impl BleHandler {
continue;
}
Ok(peripherals) => {
let needle = Some(name.to_owned());
for peripheral in peripherals {
if let Ok(Some(peripheral_properties)) = peripheral.properties().await {
if peripheral_properties.local_name == needle {
let matches = match ble_id {
BleId::Name(name) => {
peripheral_properties.local_name.as_ref() == Some(name)
}
BleId::MacAddress(mac) => peripheral_properties.address == *mac,
};
if matches {
return Ok((peripheral, adapter.clone()));
}
}
Expand All @@ -100,8 +131,8 @@ impl BleHandler {
Err(Error::StreamBuildError {
source: Box::new(BleConnectionError()),
description: format!(
"Failed to find {name}, or meshtastic is not running on the device"
) + ", or it's already connected.",
"Failed to find {ble_id}, or meshtastic is not running on the device"
) + ", or it's already connected to a client.",
})
}

Expand Down Expand Up @@ -160,7 +191,7 @@ impl BleHandler {
if data.is_empty() {
Ok(RadioMessage::Eof)
} else {
format_data_packet(data.into()).map(|packet| RadioMessage::Packet(packet))
format_data_packet(data.into()).map(RadioMessage::Packet)
}
})
}
Expand Down
6 changes: 6 additions & 0 deletions src/errors_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ pub enum Error {
packet: EncodedToRadioPacketWithHeader,
},

#[error("Invalid function parameter: {source:?}")]
InvalidParameter {
source: Box<dyn std::error::Error + Send + Sync + 'static>,
description: String,
},

/// An error indicating that the library failed when performing an operation on an internal data stream.
#[error(transparent)]
InternalStreamError(#[from] InternalStreamError),
Expand Down
6 changes: 4 additions & 2 deletions src/utils_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,14 @@ pub async fn build_tcp_stream(
}

#[cfg(feature = "bluetooth-le")]
pub async fn build_ble_stream(name: String) -> Result<StreamHandle<DuplexStream>, Error> {
pub async fn build_ble_stream(
ble_id: &crate::connections::ble_handler::BleId,
) -> Result<StreamHandle<DuplexStream>, Error> {
use crate::{
connections::ble_handler::{AdapterEvent, RadioMessage},
errors_internal::InternalStreamError,
};
let ble_handler = BleHandler::new(name).await?;
let ble_handler = BleHandler::new(ble_id).await?;
// `client` will be returned to the user, server is the opposite end of the channel and it's
// directly connected to a `BleHandler`.
let (client, mut server) = tokio::io::duplex(1024);
Expand Down

0 comments on commit c662253

Please sign in to comment.