diff --git a/src/device/dsmr.rs b/src/device/dsmr.rs new file mode 100644 index 0000000..3568700 --- /dev/null +++ b/src/device/dsmr.rs @@ -0,0 +1,98 @@ +use std::{ + fmt::{self, Display}, + time::Instant, +}; + +pub enum DsmrMessageType { + Water, + Gas, + Energy1, + Energy2, + Power, +} + +#[derive(Debug)] +pub struct DsmrState { + pub name: String, + pub power: Option, + pub power_total_tariff_1: Option, + pub power_total_tariff_2: Option, + pub gas_total: Option, + pub water_total: Option, + pub last_seen: Instant, +} + +impl DsmrState { + pub fn new(name: String) -> Self { + DsmrState { + name, + power: None, + power_total_tariff_1: None, + power_total_tariff_2: None, + gas_total: None, + water_total: None, + last_seen: Instant::now(), + } + } + + pub fn format_open_metrics(&self) -> impl Display + '_ { + DsmrFormatter { device: self } + } +} + +struct DsmrFormatter<'a> { + device: &'a DsmrState, +} + +impl Display for DsmrFormatter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let power_total = self.device.power_total_tariff_1.unwrap_or_default() + + self.device.power_total_tariff_2.unwrap_or_default(); + if power_total > 0.0 { + writeln!( + f, + "power_total_kwh{{name=\"{}\"}} {}", + self.device.name, power_total + )?; + } + + if let Some(power) = self.device.power_total_tariff_1 { + writeln!( + f, + "power_total_low_kwh{{name=\"{}\"}} {}", + self.device.name, power + )?; + } + + if let Some(power) = self.device.power_total_tariff_2 { + writeln!( + f, + "power_total_high_kwh{{name=\"{}\"}} {}", + self.device.name, power + )?; + } + + if let Some(power) = self.device.power { + writeln!( + f, + "power_watts{{name=\"{}\"}} {}", + self.device.name, + power * 1000.0 + )?; + } + + if let Some(gas) = self.device.gas_total { + writeln!(f, "gas_total_m3{{name=\"{}\"}} {}", self.device.name, gas)?; + } + + if let Some(water) = self.device.water_total { + writeln!( + f, + "water_total_m3{{name=\"{}\"}} {}", + self.device.name, water + )?; + } + + Ok(()) + } +} diff --git a/src/device/mitemp.rs b/src/device/mitemp.rs index 6ee237d..a7dd025 100644 --- a/src/device/mitemp.rs +++ b/src/device/mitemp.rs @@ -49,7 +49,7 @@ impl MiTempDevice { } } - pub fn format_prometheus(&self) -> impl Display + '_ { + pub fn format_open_metrics(&self) -> impl Display + '_ { MitempFormatter { device: self } } } diff --git a/src/device/mod.rs b/src/device/mod.rs index 2ee89f5..0a94600 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -1,7 +1,9 @@ +mod dsmr; mod mitemp; mod rf; use color_eyre::{Report, Result, eyre::WrapErr}; +pub use dsmr::{DsmrMessageType, DsmrState}; use jzon::JsonValue; pub use mitemp::MiTempDevice; pub use rf::{RfDeviceId, TempState}; @@ -75,7 +77,10 @@ impl DeviceStates { pub fn update_dsmr(&mut self, device: Device, ty: DsmrMessageType, payload: &str) { if let Ok(value) = payload.parse() { - let state = self.dsmr_devices.entry(device).or_default(); + let state = self + .dsmr_devices + .entry(device) + .or_insert_with_key(|device| DsmrState::new(device.hostname.clone())); match ty { DsmrMessageType::Water => state.water_total = Some(value), DsmrMessageType::Gas => state.gas_total = Some(value), @@ -230,37 +235,6 @@ impl Default for DeviceState { } } -pub enum DsmrMessageType { - Water, - Gas, - Energy1, - Energy2, - Power, -} - -#[derive(Debug)] -pub struct DsmrState { - pub power: Option, - pub power_total_tariff_1: Option, - pub power_total_tariff_2: Option, - pub gas_total: Option, - pub water_total: Option, - pub last_seen: Instant, -} - -impl Default for DsmrState { - fn default() -> Self { - DsmrState { - power: None, - power_total_tariff_1: None, - power_total_tariff_2: None, - gas_total: None, - water_total: None, - last_seen: Instant::now(), - } - } -} - impl DeviceState { pub fn update(&mut self, json: JsonValue) { self.last_seen = Instant::now(); @@ -422,52 +396,6 @@ pub fn format_device_state( Ok(()) } -pub fn format_dsmr_state(mut writer: W, device: &str, state: &DsmrState) -> fmt::Result { - let power_total = state.power_total_tariff_1.unwrap_or_default() - + state.power_total_tariff_2.unwrap_or_default(); - if power_total > 0.0 { - writeln!( - writer, - "power_total_kwh{{name=\"{}\"}} {}", - device, power_total - )?; - } - - if let Some(power) = state.power_total_tariff_1 { - writeln!( - writer, - "power_total_low_kwh{{name=\"{}\"}} {}", - device, power - )?; - } - - if let Some(power) = state.power_total_tariff_2 { - writeln!( - writer, - "power_total_high_kwh{{name=\"{}\"}} {}", - device, power - )?; - } - - if let Some(power) = state.power { - writeln!( - writer, - "power_watts{{name=\"{}\"}} {}", - device, - power * 1000.0 - )?; - } - - if let Some(gas) = state.gas_total { - writeln!(writer, "gas_total_m3{{name=\"{}\"}} {}", device, gas)?; - } - - if let Some(water) = state.water_total { - writeln!(writer, "water_total_m3{{name=\"{}\"}} {}", device, water)?; - } - Ok(()) -} - /// Stores the 6 byte address used to identify Bluetooth devices. #[derive(Copy, Clone, Hash, Eq, PartialEq, Default, Ord, PartialOrd)] #[repr(C)] diff --git a/src/device/rf.rs b/src/device/rf.rs index 97586ed..7077ef6 100644 --- a/src/device/rf.rs +++ b/src/device/rf.rs @@ -23,7 +23,7 @@ impl TempState { } } - pub fn format_prometheus(&self) -> impl Display + '_ { + pub fn format_open_metrics(&self) -> impl Display + '_ { TempFormatter { device: self } } } diff --git a/src/main.rs b/src/main.rs index 011e777..2fbd1e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ mod mqtt; mod topic; use crate::config::{Config, ListenConfig}; -use crate::device::{Device, DeviceStates, format_device_state, format_dsmr_state}; +use crate::device::{Device, DeviceStates, format_device_state}; use crate::mqtt::mqtt_stream; use crate::topic::Topic; use clap::Parser; @@ -81,14 +81,17 @@ async fn serve(device_states: Arc>, listen: ListenConfig) { for (device, state) in state.devices() { format_device_state(&mut response, device, state).unwrap(); } - for (device, state) in state.dsmr_devices() { - format_dsmr_state(&mut response, device.hostname.as_str(), state).unwrap(); + for (_, state) in state.dsmr_devices() { + write!(&mut response, "{}", state.format_open_metrics()) + .expect("formatting failed"); } for (_, state) in state.mi_temp() { - write!(&mut response, "{}", state.format_prometheus()).expect("formatting failed"); + write!(&mut response, "{}", state.format_open_metrics()) + .expect("formatting failed"); } for (_, state) in state.rf_temp() { - write!(&mut response, "{}", state.format_prometheus()).expect("formatting failed"); + write!(&mut response, "{}", state.format_open_metrics()) + .expect("formatting failed"); } response });