Compare commits
4 Commits
e166866bb0
...
cc63f34e9e
Author | SHA1 | Date | |
---|---|---|---|
cc63f34e9e | |||
eedf62e304 | |||
556599609a | |||
649af90de8 |
125
Cargo.lock
generated
125
Cargo.lock
generated
@ -17,6 +17,54 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.82"
|
version = "1.0.82"
|
||||||
@ -135,6 +183,54 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim 0.11.1",
|
||||||
|
"unicase",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@ -197,7 +293,7 @@ dependencies = [
|
|||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim 0.10.0",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -473,6 +569,12 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex"
|
name = "hex"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -815,6 +917,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"bluer",
|
"bluer",
|
||||||
"bluetooth-hci",
|
"bluetooth-hci",
|
||||||
|
"clap",
|
||||||
"futures",
|
"futures",
|
||||||
"tokio",
|
"tokio",
|
||||||
"warp",
|
"warp",
|
||||||
@ -905,6 +1008,12 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.26.2"
|
version = "0.26.2"
|
||||||
@ -920,7 +1029,7 @@ version = "0.26.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
@ -1054,6 +1163,18 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -9,6 +9,7 @@ edition = "2021"
|
|||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
bluer = { version = "0.17.1", features = ["bluetoothd"] }
|
bluer = { version = "0.17.1", features = ["bluetoothd"] }
|
||||||
bluetooth-hci = { version = "0.1.0", features = ["version-4-2", "version-5-0"] }
|
bluetooth-hci = { version = "0.1.0", features = ["version-4-2", "version-5-0"] }
|
||||||
|
clap = { version = "4.5.4", features = ["derive", "unicode"] }
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
tokio = { version = "1.37.0", features = ["rt", "macros"] }
|
tokio = { version = "1.37.0", features = ["rt", "macros"] }
|
||||||
warp = { version = "0.3.7", default-features = false, features = ["multer", "multipart"] }
|
warp = { version = "0.3.7", default-features = false, features = ["multer", "multipart"] }
|
||||||
|
56
src/main.rs
56
src/main.rs
@ -1,8 +1,9 @@
|
|||||||
use std::{collections::HashMap, convert::Infallible, sync::Arc, time::{SystemTime, UNIX_EPOCH}};
|
use std::{collections::HashMap, convert::Infallible, sync::Arc, time::{SystemTime, UNIX_EPOCH}};
|
||||||
|
|
||||||
use bluer::{self, Adapter, AdapterEvent, Address};
|
use bluer::{self, Adapter, AdapterEvent, Address, DiscoveryFilter, DiscoveryTransport};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Result, anyhow};
|
||||||
|
use clap::Parser;
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use tokio;
|
use tokio;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
@ -28,11 +29,13 @@ struct Data {
|
|||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
fn from_pkt(addr: Address, b: &[u8]) -> Data {
|
fn from_pkt(addr: Address, b: &[u8]) -> Data {
|
||||||
eprintln!("Packet {:?}", b);
|
|
||||||
|
let round = |v: f32| (v*100.0).round() / 100.0;
|
||||||
|
|
||||||
Data {
|
Data {
|
||||||
addr,
|
addr,
|
||||||
temperature: (u16::from_le_bytes([b[2], b[3]]) as f32) * 175.0 / 65535.0 - 45.0,
|
temperature: round((u16::from_le_bytes([b[2], b[3]]) as f32) * 175.0 / 65535.0 - 45.0),
|
||||||
humidity: (u16::from_le_bytes([b[4], b[5]]) as f32) * 100.0 / 65535.0,
|
humidity: round((u16::from_le_bytes([b[4], b[5]]) as f32) * 100.0 / 65535.0),
|
||||||
co2: u16::from_le_bytes([b[6], b[7]]),
|
co2: u16::from_le_bytes([b[6], b[7]]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,9 +63,9 @@ impl DataStore {
|
|||||||
for (_, (data, point)) in self.devices.iter() {
|
for (_, (data, point)) in self.devices.iter() {
|
||||||
let Data { addr, temperature, humidity, co2 } = data;
|
let Data { addr, temperature, humidity, co2 } = data;
|
||||||
let time = point.duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis();
|
let time = point.duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis();
|
||||||
scrape += &format!("sensirion_temperature_celsius{{id=\"{addr}\"}} {temperature} {time}\n");
|
scrape += &format!("sensirion_temperature_celsius{{id=\"{addr}\"}} {temperature} {time}\n");
|
||||||
scrape += &format!("sensirion_humidity_percent{{id=\"{addr}\"}} {humidity} {time}\n");
|
scrape += &format!("sensirion_humidity_percent{{id=\"{addr}\"}} {humidity} {time}\n");
|
||||||
scrape += &format!("sensirion_co2_ppm{{id=\"{addr}\"}} {co2} {time}\n");
|
scrape += &format!("sensirion_co2_ppm{{id=\"{addr}\"}} {co2} {time}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
scrape
|
scrape
|
||||||
@ -70,15 +73,34 @@ impl DataStore {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,clap::Parser)]
|
||||||
|
struct CLI {
|
||||||
|
/// Address to bind
|
||||||
|
#[arg(short, long, default_value = "127.0.0.1:9174")]
|
||||||
|
bind: String,
|
||||||
|
|
||||||
|
/// BLE HCI adapter to use
|
||||||
|
#[arg(short, long)]
|
||||||
|
interface: Option<String>,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor="current_thread")]
|
#[tokio::main(flavor="current_thread")]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|
||||||
|
let args = CLI::parse();
|
||||||
|
|
||||||
let session = bluer::Session::new().await?;
|
let session = bluer::Session::new().await?;
|
||||||
let adapter = Arc::new(session.default_adapter().await?);
|
let adapter = match &args.interface {
|
||||||
|
Some(hci) => session.adapter(&*hci),
|
||||||
|
None => session.default_adapter().await,
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let adapter = Arc::new(adapter);
|
||||||
let store = Arc::new(Mutex::new(DataStore::default()));
|
let store = Arc::new(Mutex::new(DataStore::default()));
|
||||||
|
|
||||||
let mut stream = Box::pin(sensor_reports(adapter).await?);
|
let mut stream = Box::pin(sensor_reports(adapter).await?);
|
||||||
|
|
||||||
let sniffer_store = Arc::clone(&store);
|
let sniffer_store = Arc::clone(&store);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Some(data) = stream.next().await {
|
while let Some(data) = stream.next().await {
|
||||||
@ -86,14 +108,17 @@ async fn main() -> Result<()> {
|
|||||||
sniffer_store.lock().await.insert(data, SystemTime::now());
|
sniffer_store.lock().await.insert(data, SystemTime::now());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let filter =
|
let filter =
|
||||||
warp::path!("metrics")
|
warp::path!("metrics")
|
||||||
.map(move || Arc::clone(&store))
|
.map(move || Arc::clone(&store))
|
||||||
.and_then(|store: Arc<Mutex<DataStore>>| async move { Ok::<_,Infallible>(store.lock().await.scrape()) })
|
.and_then(|store: Arc<Mutex<DataStore>>| async move { Ok::<_,Infallible>(store.lock().await.scrape()) })
|
||||||
.or(warp::path!().map(|| { warp::reply::html("<h1>Sensirion BLE exporter</h1>") }));
|
.or(warp::path!().map(|| { warp::reply::html("<h1>Sensirion BLE exporter</h1>") }));
|
||||||
|
|
||||||
Ok(warp::serve(filter).run(([127,0,0,1],9177)).await)
|
let host = tokio::net::lookup_host(&args.bind).await?
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow!("Cannot resolve host to bind {}", &args.bind))?;
|
||||||
|
Ok(warp::serve(filter).run(host).await)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +133,13 @@ async fn extract(adapter: Arc<Adapter>, evt: AdapterEvent) -> Option<Data> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn sensor_reports<'a>(adapter: Arc<Adapter>) -> Result<impl Stream<Item=Data> + 'a> {
|
async fn sensor_reports<'a>(adapter: Arc<Adapter>) -> Result<impl Stream<Item=Data> + 'a> {
|
||||||
|
let filter = DiscoveryFilter {
|
||||||
|
transport: DiscoveryTransport::Le,
|
||||||
|
duplicate_data: true,
|
||||||
|
..Default::default() };
|
||||||
|
|
||||||
|
adapter.set_discovery_filter(filter).await?;
|
||||||
|
|
||||||
Ok(adapter.discover_devices_with_changes().await?
|
Ok(adapter.discover_devices_with_changes().await?
|
||||||
.filter_map(move |evt| { extract(Arc::clone(&adapter), evt) }))
|
.filter_map(move |evt| { extract(Arc::clone(&adapter), evt) }))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user