1
0
Fork 0
mirror of https://codeberg.org/icewind/mitemp-rs.git synced 2026-06-03 17:24:08 +02:00

btleplug 0.9

This commit is contained in:
Robin Appelman 2021-10-31 17:37:56 +01:00
commit 4c4b2e0093
3 changed files with 63 additions and 45 deletions

View file

@ -7,9 +7,13 @@ description = "Read Xiaomi MI Temperature and Humidity Sensor over BLE"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
[dependencies] [dependencies]
btleplug = { version = "0.7", git = "https://github.com/icewind1991/btleplug", branch = "unify-platform-api" } btleplug = "0.9"
num_enum = "0.4" num_enum = "0.5"
tokio-stream = "0.1"
futures-util = "0.3"
uuid = "0.8"
[dev-dependencies] [dev-dependencies]
main_error = "0.1" main_error = "0.1"
env_logger = "0.7" env_logger = "0.7"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

View file

@ -1,13 +1,22 @@
use mitemp::{adapter_by_mac, listen, BDAddr}; use btleplug::api::Manager as _;
use std::str::FromStr; use btleplug::platform::Manager;
use futures_util::StreamExt;
use main_error::MainError;
use mitemp::{listen};
fn main() -> Result<(), btleplug::Error> { use tokio::pin;
#[tokio::main]
async fn main() -> Result<(), MainError> {
env_logger::init(); env_logger::init();
let addr = BDAddr::from_str("00:1A:7D:DA:71:08").unwrap(); let manager = Manager::new().await?;
let adapter = adapter_by_mac(addr)?; let adapter = manager.adapters().await?.pop().unwrap();
for sensor in listen(adapter)? { let stream = listen(&adapter).await?;
pin!(stream);
while let Some(sensor) = stream.next().await {
println!("{}: {:?}", sensor.mac, sensor.data); println!("{}: {:?}", sensor.mac, sensor.data);
} }
Ok(()) Ok(())

View file

@ -1,10 +1,14 @@
pub use btleplug::api::BDAddr; pub use btleplug::api::BDAddr;
use btleplug::api::{Central, CentralEvent, Peripheral}; use btleplug::api::{Central, CentralEvent, Peripheral, ScanFilter};
use btleplug::platform::{Adapter, Manager}; use btleplug::platform::PeripheralId;
use futures_util::{future::ready, StreamExt};
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use tokio_stream::Stream;
use uuid::Uuid;
/// Detected mitemp sensor and the data read from it /// Detected mitemp sensor and the data read from it
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Sensor { pub struct Sensor {
@ -33,36 +37,47 @@ impl SensorRawData {
} }
} }
const UUID: Uuid = Uuid::from_bytes([
0, 0, 254, 149, 0, 0, 16, 0, 128, 0, 0, 128, 95, 155, 52, 251,
]);
/// Listen for sensor data /// Listen for sensor data
/// ///
/// Returns an iterator that will block waiting for new sensor data /// Returns an iterator that will block waiting for new sensor data
pub fn listen<A: Central + 'static>( pub async fn listen<'a, A: Central + 'static>(
adapter: A, adapter: &'a A,
) -> Result<impl Iterator<Item = Sensor>, btleplug::Error> { ) -> Result<impl Stream<Item = Sensor> + 'a, btleplug::Error> {
let mut sensors: HashMap<BDAddr, SensorRawData> = HashMap::new(); let mut sensors: HashMap<BDAddr, SensorRawData> = HashMap::new();
let event_receiver = adapter.event_receiver().unwrap(); let event_receiver = adapter.events().await?;
// start scanning for devices // start scanning for devices
adapter.start_scan()?; adapter.start_scan(ScanFilter::default()).await?;
Ok(event_receiver Ok(event_receiver
.into_iter() .filter_map(|event| {
.filter_map(|event| match event { ready(match event {
CentralEvent::DeviceDiscovered(bd_addr) | CentralEvent::DeviceUpdated(bd_addr) => { CentralEvent::ServiceDataAdvertisement { id, service_data } => {
Some(bd_addr) Some((id, service_data))
} }
_ => None, _ => None,
}) })
.filter_map(move |bd_addr| adapter.peripheral(bd_addr))
.flat_map(|peripheral| {
peripheral
.properties()
.service_data
.into_iter()
.map(move |(_, data)| (peripheral.address(), data))
}) })
.filter_map(|(bd_addr, data)| Some((bd_addr, parse_advertising_data(&data).ok()?))) .filter_map(move |(id, service_data)| async move {
let addr = id_to_addr(adapter, id).await?;
Some((addr, service_data))
})
.filter_map(
|(bd_addr, mut service_data): (BDAddr, HashMap<Uuid, Vec<u8>>)| {
ready(service_data.remove(&UUID).map(move |data| (bd_addr, data)))
},
)
.filter_map(|(bd_addr, data)| {
ready(match parse_advertising_data(&data) {
Ok(update) => Some((bd_addr, update)),
_ => None,
})
})
.map(move |(bd_addr, update)| { .map(move |(bd_addr, update)| {
let sensor_data = sensors.entry(bd_addr).or_default(); let sensor_data = sensors.entry(bd_addr).or_default();
sensor_data.update(update); sensor_data.update(update);
@ -73,6 +88,11 @@ pub fn listen<A: Central + 'static>(
})) }))
} }
async fn id_to_addr<A: Central + 'static>(adapter: &A, id: PeripheralId) -> Option<BDAddr> {
let peripheral = adapter.peripheral(&id).await.ok()?;
Some(peripheral.address())
}
/// Collected data from a sensor /// Collected data from a sensor
/// ///
/// Because not all data is emitted at the same time, some fields might not be populated yet /// Because not all data is emitted at the same time, some fields might not be populated yet
@ -97,21 +117,6 @@ impl From<SensorRawData> for SensorData {
} }
} }
pub fn adapter_by_mac(addr: BDAddr) -> Result<Adapter, btleplug::Error> {
let manager = Manager::new()?;
manager
.adapters()?
.into_iter()
.find(|adapter| {
adapter
.address()
.ok()
.filter(|adapter_addr| *adapter_addr == addr)
.is_some()
})
.ok_or(btleplug::Error::DeviceNotFound)
}
#[derive(Debug, Eq, PartialEq, TryFromPrimitive, Clone, Copy)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive, Clone, Copy)]
#[repr(u8)] #[repr(u8)]
enum SensorType { enum SensorType {