88 lines
2.2 KiB
Rust
88 lines
2.2 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use anyhow::{anyhow, bail, Result};
|
|
use easee::api::Triphase;
|
|
use ureq::serde::Deserialize;
|
|
|
|
use tracing::warn;
|
|
|
|
const PROM_QUERY: &str = "/api/v1/query?query=ac_power_watts%7Bmeter%3D%22mains%22%2Cpower%3D%22active%22%2Cphase%21%3D%22total%22%7D";
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[serde(tag = "status")]
|
|
enum PromReply {
|
|
Success {
|
|
data: PromData,
|
|
},
|
|
Error {
|
|
data: PromData,
|
|
error_type: String,
|
|
error: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[serde(tag = "resultType", content = "result")]
|
|
enum PromData {
|
|
Vector(Vec<VecEntry>),
|
|
Matrix(Vec<MatrixEntry>),
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct VecEntry {
|
|
metric: HashMap<String, String>,
|
|
value: (f64, String),
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct MatrixEntry {
|
|
metric: HashMap<String, String>,
|
|
values: Vec<(f64, String)>,
|
|
}
|
|
|
|
pub struct PromClient {
|
|
base: String,
|
|
power_query_url: String,
|
|
}
|
|
|
|
impl PromClient {
|
|
pub fn new(base: String) -> Self {
|
|
let power_query_url = format!("{}{}", &base, PROM_QUERY);
|
|
PromClient {
|
|
base,
|
|
power_query_url,
|
|
}
|
|
}
|
|
|
|
pub fn current_power(&self) -> Result<Triphase> {
|
|
let reply: PromReply = ureq::get(&self.power_query_url).call()?.into_json()?;
|
|
|
|
let PromReply::Success {
|
|
data: PromData::Vector(v),
|
|
} = reply
|
|
else {
|
|
bail!("Could not understand Prometheus reply: {:?}", reply)
|
|
};
|
|
|
|
let mut r = (None, None, None);
|
|
|
|
for entry in &v {
|
|
let val: f64 = entry.value.1.parse()?;
|
|
match entry.metric.get("phase").map(|s| &**s) {
|
|
Some("a") => r.0 = Some(val),
|
|
Some("b") => r.1 = Some(val),
|
|
Some("c") => r.2 = Some(val),
|
|
_ => warn!("Metric with unexpected phase"),
|
|
}
|
|
}
|
|
|
|
Ok(Triphase {
|
|
phase1: r.0.ok_or_else(|| anyhow!("Missing phase a"))?,
|
|
phase2: r.1.ok_or_else(|| anyhow!("Missing phase b"))?,
|
|
phase3: r.2.ok_or_else(|| anyhow!("Missing phase c"))?,
|
|
})
|
|
}
|
|
}
|