mirror of
https://codeberg.org/icewind/mitemp-prometheus.git
synced 2026-06-03 17:14:08 +02:00
updates
This commit is contained in:
parent
a501b3c4c3
commit
7c3d4a7867
10 changed files with 1435 additions and 653 deletions
117
src/config.rs
Normal file
117
src/config.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
use btleplug::api::BDAddr;
|
||||
use main_error::MainError;
|
||||
use serde::Deserialize;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fs::read_to_string;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
pub listen: ListenConfig,
|
||||
pub names: BTreeMap<BDAddr, String>,
|
||||
#[allow(dead_code)]
|
||||
pub mqtt: Option<MqttConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ListenConfig {
|
||||
Ip {
|
||||
#[serde(default = "default_address")]
|
||||
address: IpAddr,
|
||||
port: u16,
|
||||
},
|
||||
Unix {
|
||||
socket: String,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_address() -> IpAddr {
|
||||
Ipv4Addr::UNSPECIFIED.into()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MqttConfig {
|
||||
#[serde(rename = "hostname")]
|
||||
host: String,
|
||||
#[serde(default = "default_mqtt_port")]
|
||||
port: u16,
|
||||
#[serde(flatten)]
|
||||
credentials: Option<Credentials>,
|
||||
}
|
||||
|
||||
fn default_mqtt_port() -> u16 {
|
||||
1883
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Credentials {
|
||||
Raw {
|
||||
username: String,
|
||||
password: String,
|
||||
},
|
||||
File {
|
||||
username: String,
|
||||
password_file: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Credentials {
|
||||
pub fn username(&self) -> String {
|
||||
match self {
|
||||
Credentials::Raw { username, .. } => username.clone(),
|
||||
Credentials::File { username, .. } => username.clone(),
|
||||
}
|
||||
}
|
||||
pub fn password(&self) -> String {
|
||||
match self {
|
||||
Credentials::Raw { password, .. } => password.clone(),
|
||||
Credentials::File { password_file, .. } => secretfile::load(password_file).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_env() -> Result<Config, MainError> {
|
||||
let mut env: HashMap<String, String> = dotenvy::vars().collect();
|
||||
let port = env
|
||||
.get("PORT")
|
||||
.and_then(|s| u16::from_str(s).ok())
|
||||
.unwrap_or(80);
|
||||
|
||||
let names = env.remove("NAMES").unwrap_or_default();
|
||||
let names = names
|
||||
.split(',')
|
||||
.map(|pair| {
|
||||
let mut parts = pair.split('=');
|
||||
if let (Some(Ok(mac)), Some(name)) =
|
||||
(parts.next().map(BDAddr::from_str), parts.next())
|
||||
{
|
||||
Ok((mac, name.to_string()))
|
||||
} else {
|
||||
Err(MainError::from("Invalid NAMES"))
|
||||
}
|
||||
})
|
||||
.collect::<Result<BTreeMap<BDAddr, String>, MainError>>()?;
|
||||
|
||||
Ok(Config {
|
||||
listen: ListenConfig::Ip {
|
||||
port,
|
||||
address: default_address(),
|
||||
},
|
||||
names,
|
||||
mqtt: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config, MainError> {
|
||||
let raw = read_to_string(path)?;
|
||||
Ok(toml::from_str(&raw)?)
|
||||
}
|
||||
}
|
||||
62
src/main.rs
62
src/main.rs
|
|
@ -1,41 +1,41 @@
|
|||
mod config;
|
||||
|
||||
use btleplug::api::{Central, Manager as _};
|
||||
use btleplug::platform::Manager;
|
||||
use log::info;
|
||||
use tracing::info;
|
||||
use main_error::MainError;
|
||||
use mitemp::{listen, BDAddr, Sensor};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt::Write;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::{pin, spawn};
|
||||
use tokio_stream::StreamExt;
|
||||
use warp::Filter;
|
||||
use clap::Parser;
|
||||
use tokio::net::UnixListener;
|
||||
use tokio_stream::wrappers::UnixListenerStream;
|
||||
use crate::config::{Config, ListenConfig};
|
||||
|
||||
type Cache = Arc<Mutex<HashMap<BDAddr, Sensor>>>;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Config file to use, if omitted the config will be loaded from environment variables
|
||||
config: Option<String>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), MainError> {
|
||||
env_logger::init();
|
||||
tracing_subscriber::fmt::init();
|
||||
let cache: Cache = Arc::default();
|
||||
|
||||
let mut env: HashMap<String, String> = dotenv::vars().collect();
|
||||
let port = env
|
||||
.get("PORT")
|
||||
.and_then(|s| u16::from_str(s).ok())
|
||||
.unwrap_or(80);
|
||||
let names = env.remove("NAMES").unwrap_or_default();
|
||||
let names = names
|
||||
.split(',')
|
||||
.map(|pair| {
|
||||
let mut parts = pair.split('=');
|
||||
if let (Some(Ok(mac)), Some(name)) = (parts.next().map(BDAddr::from_str), parts.next())
|
||||
{
|
||||
Ok((mac, name.to_string()))
|
||||
} else {
|
||||
Err(MainError::from("Invalid NAMES"))
|
||||
}
|
||||
})
|
||||
.collect::<Result<HashMap<BDAddr, String>, MainError>>()?;
|
||||
let args = Args::parse();
|
||||
let config = match args.config {
|
||||
Some(path) => Config::from_file(path)?,
|
||||
_ => Config::from_env()?,
|
||||
};
|
||||
info!("{} devices configured", config.names.len());
|
||||
|
||||
let manager = Manager::new().await?;
|
||||
for adapter in manager.adapters().await? {
|
||||
|
|
@ -60,6 +60,7 @@ async fn main() -> Result<(), MainError> {
|
|||
});
|
||||
}
|
||||
|
||||
let names = config.names;
|
||||
let metrics = warp::path!("metrics").map(move || {
|
||||
let mut result = String::new();
|
||||
|
||||
|
|
@ -70,7 +71,16 @@ async fn main() -> Result<(), MainError> {
|
|||
result
|
||||
});
|
||||
|
||||
warp::serve(metrics).run(([0, 0, 0, 0], port)).await;
|
||||
match config.listen {
|
||||
ListenConfig::Ip { address, port } => {
|
||||
warp::serve(metrics).run((address, port)).await;
|
||||
}
|
||||
ListenConfig::Unix { socket: path } => {
|
||||
let listener = UnixListener::bind(path).unwrap();
|
||||
let incoming = UnixListenerStream::new(listener);
|
||||
warp::serve(metrics).run_incoming(incoming).await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -78,7 +88,7 @@ async fn main() -> Result<(), MainError> {
|
|||
fn format<W: Write>(
|
||||
mut writer: W,
|
||||
sensor: &Sensor,
|
||||
names: &HashMap<BDAddr, String>,
|
||||
names: &BTreeMap<BDAddr, String>,
|
||||
) -> std::fmt::Result {
|
||||
if sensor.data.temperature == 0.0 || sensor.data.humidity == 0.0 {
|
||||
return Ok(());
|
||||
|
|
@ -92,7 +102,7 @@ fn format<W: Write>(
|
|||
name, sensor.mac, sensor.data.battery
|
||||
)?;
|
||||
} else {
|
||||
info!("Skipping unnamed censor {}", sensor.mac);
|
||||
info!("Skipping unnamed sensor {}", sensor.mac);
|
||||
}
|
||||
}
|
||||
if let Some(name) = name {
|
||||
|
|
@ -107,7 +117,7 @@ fn format<W: Write>(
|
|||
name, sensor.mac, sensor.data.humidity
|
||||
)?;
|
||||
} else {
|
||||
info!("Skipping unnamed censor {}", sensor.mac);
|
||||
info!("Skipping unnamed sensor {}", sensor.mac);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue