use std::{collections::HashMap, convert::Infallible, sync::Arc, time::{SystemTime, UNIX_EPOCH}}; use bluer::{self, Adapter, AdapterEvent, Address, DiscoveryFilter, DiscoveryTransport}; use anyhow::{Result, anyhow}; use clap::Parser; use futures::{Stream, StreamExt}; use tokio; use warp::Filter; use tokio::sync::Mutex; const VENDOR_ID_SENSIRION: u16 = 0x06d5; #[derive(Debug)] struct Data { /// ID addr: Address, /// Temperature in °C temperature: f32, /// Humidity in % humidity: f32, /// CO2 in parts per million co2: u16, } impl Data { fn from_pkt(addr: Address, b: &[u8]) -> Data { let round = |v: f32| (v*100.0).round() / 100.0; Data { addr, temperature: round((u16::from_le_bytes([b[2], b[3]]) as f32) * 175.0 / 65535.0 - 45.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]]), } } } #[derive(Debug,Default)] struct DataStore { devices: HashMap
, } impl DataStore { pub fn insert(&mut self, data: Data, timestamp: SystemTime) { self.devices.insert(data.addr, (data, timestamp)); } pub fn scrape(&mut self) -> String { let mut scrape = format!("# HELP sensirion_temperature_celsius Temperature reported by a Sensirion sensor, in °C\n"); scrape += "# TYPE sensirion_temperature_celsius gauge\n"; scrape += "# HELP sensirion_humidity_percent Humidity reported by a Sensirion sensor, in %H\n"; scrape += "# TYPE sensirion_humidity_percent gauge\n"; scrape += "# HELP sensirion_co2_ppm CO2 detected by a Sensirion sensor, in parts per million\n"; scrape += "# TYPE sensirion_co2_ppm gauge\n"; for (_, (data, point)) in self.devices.iter() { let Data { addr, temperature, humidity, co2 } = data; 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_humidity_percent{{id=\"{addr}\"}} {humidity} {time}\n"); scrape += &format!("sensirion_co2_ppm{{id=\"{addr}\"}} {co2} {time}\n"); } scrape } } #[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