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:
parent
b2e39d8893
commit
4c4b2e0093
3 changed files with 63 additions and 45 deletions
|
|
@ -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"] }
|
||||||
|
|
@ -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(())
|
||||||
|
|
|
||||||
77
src/lib.rs
77
src/lib.rs
|
|
@ -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))
|
.filter_map(move |(id, service_data)| async move {
|
||||||
.flat_map(|peripheral| {
|
let addr = id_to_addr(adapter, id).await?;
|
||||||
peripheral
|
Some((addr, service_data))
|
||||||
.properties()
|
})
|
||||||
.service_data
|
.filter_map(
|
||||||
.into_iter()
|
|(bd_addr, mut service_data): (BDAddr, HashMap<Uuid, Vec<u8>>)| {
|
||||||
.map(move |(_, data)| (peripheral.address(), data))
|
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,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.filter_map(|(bd_addr, data)| Some((bd_addr, parse_advertising_data(&data).ok()?)))
|
|
||||||
.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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue