diff --git a/Cargo.lock b/Cargo.lock index 255eb26..d0ebc50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,18 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" -dependencies = [ - "cfg-if 1.0.0", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "0.6.10" @@ -119,12 +107,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "arc-swap" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" - [[package]] name = "async-stream" version = "0.3.5" @@ -494,17 +476,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-epoch" -version = "0.9.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.18" @@ -878,15 +849,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -1091,7 +1053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", "serde", ] @@ -1359,45 +1321,6 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -[[package]] -name = "metrics" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b9e10a211c839210fd7f99954bda26e5f8e26ec686ad68da6a32df7c80e782" -dependencies = [ - "ahash", - "portable-atomic", -] - -[[package]] -name = "metrics-prometheus" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e1316f9ef05b91f4d0e0a0da5b620ba919d336280b83b36be096b86c030fdd" -dependencies = [ - "arc-swap", - "metrics", - "metrics-util", - "once_cell", - "prometheus", - "sealed", - "smallvec", - "thiserror", -] - -[[package]] -name = "metrics-util" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2670b8badcc285d486261e2e9f1615b506baff91427b61bd336a472b65bbf5ed" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", - "hashbrown 0.13.1", - "metrics", - "num_cpus", -] - [[package]] name = "miette" version = "5.10.0" @@ -1731,12 +1654,6 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - [[package]] name = "powerfmt" version = "0.2.0" @@ -2280,18 +2197,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "sealed" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" -dependencies = [ - "heck", - "proc-macro2", - "quote 1.0.33", - "syn 2.0.43", -] - [[package]] name = "security-framework" version = "2.9.2" @@ -2633,17 +2538,16 @@ dependencies = [ [[package]] name = "tesla-charge-controller" -version = "1.0.31" +version = "1.1.0" dependencies = [ "chrono", "clap 4.4.11", "env_logger 0.10.1", "if_chain", "include_dir", + "lazy_static 1.4.0", "libmodbus-rs", "log 0.4.20", - "metrics", - "metrics-prometheus", "notify-debouncer-mini", "prometheus", "rocket", @@ -3463,26 +3367,6 @@ dependencies = [ "is-terminal", ] -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote 1.0.33", - "syn 2.0.43", -] - [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 2bb1ccd..7ff57f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tesla-charge-controller" -version = "1.0.31" +version = "1.1.0" edition = "2021" license = "MITNFA" description = "Controls Tesla charge rate based on solar charge data" @@ -23,8 +23,6 @@ thiserror = "1.0" rocket = { version = "0.5", features = ["json"] } include_dir = "0.7" chrono = "0.4" -metrics = "0.22" -metrics-prometheus = "0.6.0" prometheus = "0.13" env_logger = "0.10" log = "0.4" @@ -32,3 +30,4 @@ serialport = "4.3" libmodbus-rs = "0.8.3" if_chain = "1.0.2" notify-debouncer-mini = { version = "0.4.1", default-features = false } +lazy_static = "1.4" diff --git a/src/api_interface.rs b/src/api_interface.rs index 75a8dfa..45567e6 100644 --- a/src/api_interface.rs +++ b/src/api_interface.rs @@ -1,4 +1,8 @@ -use metrics::{describe_gauge, gauge, Gauge, Label, Unit}; +use lazy_static::lazy_static; +use prometheus::{ + core::{AtomicI64, GenericGauge}, + register_gauge, register_int_gauge, register_int_gauge_vec, Gauge, IntGauge, IntGaugeVec, +}; use serde::{Deserialize, Serialize}; use std::{ path::PathBuf, @@ -20,153 +24,82 @@ use crate::{ types::{CarState, FromDriveState}, }; -struct Metrics { - battery_level: Gauge, - charge_rate: Gauge, - charge_request: Gauge, - charge_enable_request: Gauge, - charger_connected: Gauge, - inside_temp: Gauge, - outside_temp: Gauge, - battery_heater: Gauge, - climate_on: Gauge, - preconditioning: Gauge, - remote_heater_control_enabled: Gauge, - is_auto_conditioning_on: Gauge, - driver_temp_setting: Gauge, - passenger_temp_setting: Gauge, - tesla_online: Gauge, - charging_state: ChargingStateGauges, - cabin_overheat_protection: CabinOverheatProtectionGauges, - hvac_auto: HvacAutoRequestGauges, - home: Gauge, - sentry_mode: Gauge, - sentry_mode_available: Gauge, - charger_actual_current: Gauge, - charger_phases: Gauge, - charger_pilot_current: Gauge, - charger_power: Gauge, - charger_voltage: Gauge, -} - -impl Metrics { - fn new() -> Self { - describe_gauge!("tesla_battery_level", Unit::Percent, "Battery level"); - let battery_level = gauge!("tesla_battery_level"); - describe_gauge!("tesla_charge_rate", "Charge rate"); - let charge_rate = gauge!("tesla_charge_rate"); - describe_gauge!("tesla_charge_request", "Requested charge rate"); - let charge_request = gauge!("tesla_charge_request"); - describe_gauge!("tesla_charge_enable_request", "Charge enable request"); - let charge_enable_request = gauge!("tesla_charge_enable_request"); - describe_gauge!("tesla_charger_connected", "Charger connected"); - let charger_connected = gauge!("tesla_charger_connected"); - describe_gauge!("tesla_inside_temp", "Inside temperature"); - let inside_temp = gauge!("tesla_inside_temp"); - describe_gauge!("tesla_outside_temp", "Outside temperature"); - let outside_temp = gauge!("tesla_outside_temp"); - describe_gauge!("tesla_battery_heater", "Battery heater"); - let battery_heater = gauge!("tesla_battery_heater"); - describe_gauge!("tesla_climate_on", "Climate control"); - let climate_on = gauge!("tesla_climate_on"); - describe_gauge!("tesla_preconditioning", "Preconditioning"); - let preconditioning = gauge!("tesla_preconditioning"); - describe_gauge!( - "tesla_remote_heater_control_enabled", - "Remote heater control enabled" - ); - let remote_heater_control_enabled = gauge!("tesla_remote_heater_control_enabled"); - describe_gauge!("tesla_is_auto_conditioning_on", "Auto conditioning on"); - let is_auto_conditioning_on = gauge!("tesla_is_auto_conditioning_on"); - describe_gauge!("tesla_driver_temp_setting", "Driver temp"); - let driver_temp_setting = gauge!("tesla_driver_temp_setting"); - describe_gauge!("tesla_passenger_temp_setting", "Passenger temp"); - let passenger_temp_setting = gauge!("tesla_passenger_temp_setting"); - describe_gauge!("tesla_online", "Tesla online"); - let tesla_online = gauge!("tesla_online"); - describe_gauge!("tesla_charging_state", "Tesla charging state"); - let charging_state = ChargingStateGauges::new(); - describe_gauge!( - "tesla_cabin_overheat_protection_state", - "Cabin overheat protection state" - ); - let cabin_overheat_protection = CabinOverheatProtectionGauges::new(); - describe_gauge!("tesla_hvac_auto_request", "HVAC auto"); - let hvac_auto = HvacAutoRequestGauges::new(); - describe_gauge!("tesla_home", "Is home"); - let home = gauge!("tesla_home"); - describe_gauge!("tesla_sentry_mode", "Sentry mode"); - let sentry_mode = gauge!("tesla_sentry_mode"); - describe_gauge!("tesla_sentry_mode_available", "Sentry mode available"); - let sentry_mode_available = gauge!("tesla_sentry_mode_available"); - - describe_gauge!("tesla_charger_actual_current", "Charger actual current"); - let charger_actual_current = gauge!("tesla_charger_actual_current"); - describe_gauge!("tesla_charger_phases", "Charger phases"); - let charger_phases = gauge!("tesla_charger_phases"); - describe_gauge!("tesla_charger_pilot_current", "Charger pilot current"); - let charger_pilot_current = gauge!("tesla_charger_pilot_current"); - describe_gauge!("tesla_charger_power", "Charger power"); - let charger_power = gauge!("tesla_charger_power"); - describe_gauge!("tesla_charger_voltage", "Charger voltage"); - let charger_voltage = gauge!("tesla_charger_voltage"); - - Self { - battery_level, - charge_rate, - charge_request, - charge_enable_request, - charger_connected, - inside_temp, - outside_temp, - battery_heater, - climate_on, - preconditioning, - remote_heater_control_enabled, - is_auto_conditioning_on, - driver_temp_setting, - passenger_temp_setting, - tesla_online, - charging_state, - cabin_overheat_protection, - hvac_auto, - home, - sentry_mode, - sentry_mode_available, - charger_actual_current, - charger_phases, - charger_pilot_current, - charger_power, - charger_voltage, - } - } +lazy_static! { + pub static ref BATTERY_LEVEL: IntGauge = + register_int_gauge!("tesla_battery_level", "Battery level",).unwrap(); + pub static ref CHARGE_RATE: Gauge = + register_gauge!("tesla_charge_rate", "Charge rate",).unwrap(); + pub static ref CHARGE_REQUEST: IntGauge = + register_int_gauge!("tesla_charge_request", "Requested charge rate",).unwrap(); + pub static ref CHARGE_ENABLE_REQUEST: IntGauge = + register_int_gauge!("tesla_charge_enable_request", "Charge enable request",).unwrap(); + pub static ref CHARGER_CONNECTED: IntGauge = + register_int_gauge!("tesla_charger_connected", "Charger connected",).unwrap(); + pub static ref INSIDE_TEMP: Gauge = + register_gauge!("tesla_inside_temp", "Inside temperature",).unwrap(); + pub static ref OUTSIDE_TEMP: Gauge = + register_gauge!("tesla_outside_temp", "Outside temperature",).unwrap(); + pub static ref BATTERY_HEATER: IntGauge = + register_int_gauge!("tesla_battery_heater", "Battery heater",).unwrap(); + pub static ref CLIMATE_ON: IntGauge = + register_int_gauge!("tesla_climate_on", "Climate control",).unwrap(); + pub static ref PRECONDITIONING: IntGauge = + register_int_gauge!("tesla_preconditioning", "Preconditioning",).unwrap(); + pub static ref REMOTE_HEATER_CONTROL_ENABLED: IntGauge = register_int_gauge!( + "tesla_remote_heater_control_enabled", + "Remote heater control enabled", + ) + .unwrap(); + pub static ref IS_AUTO_CONDITIONING_ON: IntGauge = + register_int_gauge!("tesla_is_auto_conditioning_on", "Auto conditioning on",).unwrap(); + pub static ref DRIVER_TEMP_SETTING: Gauge = + register_gauge!("tesla_driver_temp_setting", "Driver temp",).unwrap(); + pub static ref PASSENGER_TEMP_SETTING: Gauge = + register_gauge!("tesla_passenger_temp_setting", "Passenger temp",).unwrap(); + pub static ref TESLA_ONLINE: IntGauge = + register_int_gauge!("tesla_online", "Tesla online",).unwrap(); + pub static ref HOME: IntGauge = register_int_gauge!("tesla_home", "Is home",).unwrap(); + pub static ref SENTRY_MODE: IntGauge = + register_int_gauge!("tesla_sentry_mode", "Sentry mode",).unwrap(); + pub static ref SENTRY_MODE_AVAILABLE: IntGauge = + register_int_gauge!("tesla_sentry_mode_available", "Sentry mode available",).unwrap(); + pub static ref CHARGER_ACTUAL_CURRENT: IntGauge = + register_int_gauge!("tesla_charger_actual_current", "Charger actual current",).unwrap(); + pub static ref CHARGER_PHASES: IntGauge = + register_int_gauge!("tesla_charger_phases", "Charger phases",).unwrap(); + pub static ref CHARGER_PILOT_CURRENT: IntGauge = + register_int_gauge!("tesla_charger_pilot_current", "Charger pilot current",).unwrap(); + pub static ref CHARGER_POWER: IntGauge = + register_int_gauge!("tesla_charger_power", "Charger power",).unwrap(); + pub static ref CHARGER_VOLTAGE: IntGauge = + register_int_gauge!("tesla_charger_voltage", "Charger voltage",).unwrap(); + pub static ref CHARGING_STATE: IntGaugeVec = + register_int_gauge_vec!("tesla_charging_state", "Tesla charging state", &["state"]) + .unwrap(); + pub static ref CABIN_OVERHEAT_PROTECTION: IntGaugeVec = register_int_gauge_vec!( + "tesla_cabin_overheat_protection_state", + "Cabin overheat protection state", + &["state"] + ) + .unwrap(); + pub static ref HVAC_AUTO: IntGaugeVec = + register_int_gauge_vec!("tesla_hvac_auto_request", "HVAC auto", &["state"]).unwrap(); } struct ChargingStateGauges { - charging: Gauge, - stopped: Gauge, - disconnected: Gauge, - other: Gauge, + charging: GenericGauge, + stopped: GenericGauge, + disconnected: GenericGauge, + other: GenericGauge, } impl ChargingStateGauges { fn new() -> Self { - let charging = gauge!( - "tesla_charging_state", - vec![Label::new("state", String::from("charging"))] - ); - let stopped = gauge!( - "tesla_charging_state", - vec![Label::new("state", String::from("stopped"))] - ); - let disconnected = gauge!( - "tesla_charging_state", - vec![Label::new("state", String::from("disconnected"))] - ); - let other = gauge!( - "tesla_charging_state", - vec![Label::new("state", String::from("other"))] - ); + let charging = CHARGING_STATE.with_label_values(&["charging"]); + let stopped = CHARGING_STATE.with_label_values(&["stopped"]); + let disconnected = CHARGING_STATE.with_label_values(&["disconnected"]); + let other = CHARGING_STATE.with_label_values(&["other"]); + Self { charging, stopped, @@ -178,58 +111,47 @@ impl ChargingStateGauges { fn set(&mut self, state: ChargingState) { match state { ChargingState::Charging => { - self.charging.set(1.); - self.stopped.set(0.); - self.disconnected.set(0.); - self.other.set(0.); + self.charging.set(1); + self.stopped.set(0); + self.disconnected.set(0); + self.other.set(0); } ChargingState::Stopped => { - self.charging.set(0.); - self.stopped.set(1.); - self.disconnected.set(0.); - self.other.set(0.); + self.charging.set(0); + self.stopped.set(1); + self.disconnected.set(0); + self.other.set(0); } ChargingState::Disconnected => { - self.charging.set(0.); - self.stopped.set(0.); - self.disconnected.set(1.); - self.other.set(0.); + self.charging.set(0); + self.stopped.set(0); + self.disconnected.set(1); + self.other.set(0); } ChargingState::Other => { - self.charging.set(0.); - self.stopped.set(0.); - self.disconnected.set(0.); - self.other.set(1.); + self.charging.set(0); + self.stopped.set(0); + self.disconnected.set(0); + self.other.set(1); } } } } struct CabinOverheatProtectionGauges { - off: Gauge, - on: Gauge, - fan_only: Gauge, - unknown: Gauge, + off: GenericGauge, + on: GenericGauge, + fan_only: GenericGauge, + unknown: GenericGauge, } impl CabinOverheatProtectionGauges { fn new() -> Self { - let off = gauge!( - "tesla_cabin_overheat_protection_state", - vec![Label::new("state", String::from("off"))] - ); - let on = gauge!( - "tesla_cabin_overheat_protection_state", - vec![Label::new("state", String::from("on"))] - ); - let fan_only = gauge!( - "tesla_cabin_overheat_protection_state", - vec![Label::new("state", String::from("fan_only"))] - ); - let unknown = gauge!( - "tesla_cabin_overheat_protection_state", - vec![Label::new("state", String::from("unknown"))] - ); + let off = CABIN_OVERHEAT_PROTECTION.with_label_values(&["off"]); + let on = CABIN_OVERHEAT_PROTECTION.with_label_values(&["on"]); + let fan_only = CABIN_OVERHEAT_PROTECTION.with_label_values(&["fan_only"]); + let unknown = CABIN_OVERHEAT_PROTECTION.with_label_values(&["unknown"]); + Self { off, on, @@ -241,53 +163,45 @@ impl CabinOverheatProtectionGauges { fn set(&mut self, state: CabinOverheatProtection) { match state { CabinOverheatProtection::Off => { - self.off.set(1.); - self.on.set(0.); - self.fan_only.set(0.); - self.unknown.set(0.); + self.off.set(1); + self.on.set(0); + self.fan_only.set(0); + self.unknown.set(0); } CabinOverheatProtection::On => { - self.off.set(0.); - self.on.set(1.); - self.fan_only.set(0.); - self.unknown.set(0.); + self.off.set(0); + self.on.set(1); + self.fan_only.set(0); + self.unknown.set(0); } CabinOverheatProtection::FanOnly => { - self.off.set(0.); - self.on.set(0.); - self.fan_only.set(1.); - self.unknown.set(0.); + self.off.set(0); + self.on.set(0); + self.fan_only.set(1); + self.unknown.set(0); } CabinOverheatProtection::Unknown => { - self.off.set(0.); - self.on.set(0.); - self.fan_only.set(0.); - self.unknown.set(1.); + self.off.set(0); + self.on.set(0); + self.fan_only.set(0); + self.unknown.set(1); } } } } struct HvacAutoRequestGauges { - override_g: Gauge, - on: Gauge, - unknown: Gauge, + override_g: GenericGauge, + on: GenericGauge, + unknown: GenericGauge, } impl HvacAutoRequestGauges { fn new() -> Self { - let override_g = gauge!( - "tesla_hvac_auto_request", - vec![Label::new("state", String::from("override"))] - ); - let on = gauge!( - "tesla_hvac_auto_request", - vec![Label::new("state", String::from("on"))] - ); - let unknown = gauge!( - "tesla_hvac_auto_request", - vec![Label::new("state", String::from("unknown"))] - ); + let override_g = HVAC_AUTO.with_label_values(&["override"]); + let on = HVAC_AUTO.with_label_values(&["on"]); + let unknown = HVAC_AUTO.with_label_values(&["unknown"]); + Self { override_g, on, @@ -298,19 +212,19 @@ impl HvacAutoRequestGauges { fn set(&mut self, state: HvacAutoRequest) { match state { HvacAutoRequest::Override => { - self.override_g.set(1.); - self.on.set(0.); - self.unknown.set(0.); + self.override_g.set(1); + self.on.set(0); + self.unknown.set(0); } HvacAutoRequest::On => { - self.override_g.set(0.); - self.on.set(1.); - self.unknown.set(0.); + self.override_g.set(0); + self.on.set(1); + self.unknown.set(0); } HvacAutoRequest::Unknown => { - self.override_g.set(0.); - self.on.set(0.); - self.unknown.set(1.); + self.override_g.set(0); + self.on.set(0); + self.unknown.set(1); } } } @@ -322,8 +236,10 @@ pub struct TeslaInterface { vehicle: Box, last_refresh: Instant, auth_path: PathBuf, - metrics: Metrics, monitored_values: MonitoredValues, + charging_state: ChargingStateGauges, + cabin_overheat_protection: CabinOverheatProtectionGauges, + hvac_auto: HvacAutoRequestGauges, } #[derive(Default)] @@ -366,16 +282,16 @@ impl TeslaInterface { .next() .ok_or(AuthLoadError::NoVehicles)?; - let metrics = Metrics::new(); - let interface = Self { state: Arc::new(RwLock::new(Default::default())), api, last_refresh, auth_path, vehicle, - metrics, monitored_values: Default::default(), + charging_state: ChargingStateGauges::new(), + cabin_overheat_protection: CabinOverheatProtectionGauges::new(), + hvac_auto: HvacAutoRequestGauges::new(), }; interface.save_key()?; @@ -423,97 +339,65 @@ impl TeslaInterface { async fn refresh_state(&mut self) { match self.get_state().await { Ok(new_state) => { - self.metrics.tesla_online.set(1.); + TESLA_ONLINE.set(1); let mut state = self .state .write() .expect("Tesla API state handler panicked!!"); if let Some(new_charge_state) = new_state.charge_state { - self.metrics - .battery_level - .set(new_charge_state.battery_level as f64); - self.metrics.charge_rate.set(new_charge_state.charge_rate); - self.metrics - .charge_request - .set(new_charge_state.charge_current_request as f64); - self.metrics - .charge_enable_request - .set(bf(new_charge_state.charge_enable_request)); - self.metrics - .charger_connected - .set(bf(new_charge_state.charger_connected)); - self.metrics - .charging_state - .set(new_charge_state.charging_state); + BATTERY_LEVEL.set(new_charge_state.battery_level); + CHARGE_RATE.set(new_charge_state.charge_rate); + CHARGE_REQUEST.set(new_charge_state.charge_current_request); + CHARGE_ENABLE_REQUEST.set(bi(new_charge_state.charge_enable_request)); + CHARGER_CONNECTED.set(bi(new_charge_state.charger_connected)); + self.charging_state.set(new_charge_state.charging_state); if let Some(v) = new_charge_state.charger_actual_current { - self.metrics.charger_actual_current.set(v as f64); + CHARGER_ACTUAL_CURRENT.set(v); } if let Some(v) = new_charge_state.charger_phases { - self.metrics.charger_phases.set(v as f64); + CHARGER_PHASES.set(v); } if let Some(v) = new_charge_state.charger_pilot_current { - self.metrics.charger_pilot_current.set(v as f64); + CHARGER_PILOT_CURRENT.set(v); } if let Some(v) = new_charge_state.charger_power { - self.metrics.charger_power.set(v as f64); + CHARGER_POWER.set(v); } if let Some(v) = new_charge_state.charger_voltage { - self.metrics.charger_voltage.set(v as f64); + CHARGER_VOLTAGE.set(v); } state.charge_state = Some(new_charge_state); } if let Some(new_location_data) = new_state.location_data { - self.metrics.home.set(bf(new_location_data.home)); + HOME.set(bi(new_location_data.home)); state.location_data = Some(new_location_data); } if let Some(new_climate_state) = new_state.climate_state { - self.metrics.inside_temp.set(new_climate_state.inside_temp); - self.metrics - .outside_temp - .set(new_climate_state.outside_temp); - self.metrics - .battery_heater - .set(bf(new_climate_state.battery_heater)); - self.metrics - .climate_on - .set(bf(new_climate_state.climate_on)); - self.metrics - .preconditioning - .set(bf(new_climate_state.preconditioning)); - self.metrics - .remote_heater_control_enabled - .set(bf(new_climate_state.remote_heater_control_enabled)); - self.metrics - .is_auto_conditioning_on - .set(bf(new_climate_state.is_auto_conditioning_on)); - self.metrics - .driver_temp_setting - .set(new_climate_state.driver_temp_setting); - self.metrics - .passenger_temp_setting - .set(new_climate_state.passenger_temp_setting); - self.metrics - .cabin_overheat_protection + INSIDE_TEMP.set(new_climate_state.inside_temp); + OUTSIDE_TEMP.set(new_climate_state.outside_temp); + BATTERY_HEATER.set(bi(new_climate_state.battery_heater)); + CLIMATE_ON.set(bi(new_climate_state.climate_on)); + PRECONDITIONING.set(bi(new_climate_state.preconditioning)); + REMOTE_HEATER_CONTROL_ENABLED + .set(bi(new_climate_state.remote_heater_control_enabled)); + IS_AUTO_CONDITIONING_ON.set(bi(new_climate_state.is_auto_conditioning_on)); + DRIVER_TEMP_SETTING.set(new_climate_state.driver_temp_setting); + PASSENGER_TEMP_SETTING.set(new_climate_state.passenger_temp_setting); + self.cabin_overheat_protection .set(new_climate_state.cabin_overheat_protection); - self.metrics - .hvac_auto - .set(new_climate_state.hvac_auto_request); + self.hvac_auto.set(new_climate_state.hvac_auto_request); state.climate_state = Some(new_climate_state); } if let Some(new_vehicle_state) = new_state.vehicle_state { - self.metrics - .sentry_mode - .set(obf(new_vehicle_state.sentry_mode)); - self.metrics - .sentry_mode_available - .set(obf(new_vehicle_state.sentry_mode_available)); + SENTRY_MODE.set(obi(new_vehicle_state.sentry_mode)); + SENTRY_MODE_AVAILABLE.set(obi(new_vehicle_state.sentry_mode_available)); state.vehicle_state = Some(new_vehicle_state); } } Err(e) => { - self.metrics.tesla_online.set(0.); + TESLA_ONLINE.set(0); match e { teslatte::error::TeslatteError::DecodeJsonError { source, @@ -616,14 +500,14 @@ impl TeslaInterface { } } -fn bf(value: bool) -> f64 { +fn bi(value: bool) -> i64 { if value { - 1.0 + 1 } else { - 0.0 + 0 } } -fn obf(value: Option) -> f64 { - value.map_or(-1., bf) +fn obi(value: Option) -> i64 { + value.map_or(-1, bi) } diff --git a/src/charge_controllers/gauge_names.rs b/src/charge_controllers/gauge_names.rs deleted file mode 100644 index 9745062..0000000 --- a/src/charge_controllers/gauge_names.rs +++ /dev/null @@ -1,20 +0,0 @@ -pub const CHARGE_CONTROLLER_LABEL: &str = "charge_controller"; -pub const PL_LABEL: &str = "pl_device"; -pub const TRISTAR_LABEL: &str = "tristar_device"; - -pub const BATTERY_VOLTAGE: &str = "battery_voltage"; -pub const TARGET_VOLTAGE: &str = "target_voltage"; -pub const INPUT_CURRENT: &str = "input_current"; -pub const CHARGE_STATE: &str = "charge_state"; -pub const BATTERY_TEMP: &str = "battery_temp"; - -pub const PL_DUTY_CYCLE: &str = "pl_duty_cycle"; -pub const PL_LOAD_CURRENT: &str = "pl_internal_load_current"; - -pub const TRISTAR_INPUT_VOLTAGE: &str = "tristar_input_voltage"; -pub const TRISTAR_CHARGE_CURRENT: &str = "tristar_charge_current"; -pub const TRISTAR_POWER_OUT: &str = "tristar_power_out"; -pub const TRISTAR_POWER_IN: &str = "tristar_power_in"; -pub const TRISTAR_MAX_ARRAY_POWER: &str = "tristar_max_array_power"; -pub const TRISTAR_MAX_ARRAY_VOLTAGE: &str = "tristar_max_array_voltage"; -pub const TRISTAR_OPEN_CIRCUIT_VOLTAGE: &str = "tristar_open_circuit_voltage"; diff --git a/src/charge_controllers/gauges.rs b/src/charge_controllers/gauges.rs new file mode 100644 index 0000000..aa92623 --- /dev/null +++ b/src/charge_controllers/gauges.rs @@ -0,0 +1,70 @@ +use super::{CHARGE_CONTROLLER_LABEL, PL_LABEL, TRISTAR_LABEL}; +use lazy_static::lazy_static; +use prometheus::{register_gauge_vec, register_int_gauge_vec, GaugeVec, IntGaugeVec}; + +lazy_static! { + pub static ref BATTERY_VOLTAGE: GaugeVec = register_gauge_vec!( + "battery_voltage", + "Battery voltage", + &[CHARGE_CONTROLLER_LABEL] + ) + .unwrap(); + pub static ref TARGET_VOLTAGE: GaugeVec = register_gauge_vec!( + "target_voltage", + "Target voltage", + &[CHARGE_CONTROLLER_LABEL] + ) + .unwrap(); + pub static ref INPUT_CURRENT: GaugeVec = register_gauge_vec!( + "input_current", + "Internal charge current", + &[CHARGE_CONTROLLER_LABEL] + ) + .unwrap(); + pub static ref CHARGE_STATE: IntGaugeVec = register_int_gauge_vec!( + "charge_state", + "Regulator state", + &[CHARGE_CONTROLLER_LABEL, "state"] + ) + .unwrap(); + pub static ref BATTERY_TEMP: GaugeVec = register_gauge_vec!( + "battery_temp", + "Battery temperature", + &[CHARGE_CONTROLLER_LABEL] + ) + .unwrap(); + pub static ref PL_DUTY_CYCLE: GaugeVec = + register_gauge_vec!("pl_duty_cycle", "Duty cycle", &[PL_LABEL]).unwrap(); + pub static ref PL_LOAD_CURRENT: GaugeVec = register_gauge_vec!( + "pl_internal_load_current", + "Internal load current", + &[PL_LABEL] + ) + .unwrap(); + pub static ref TRISTAR_INPUT_VOLTAGE: GaugeVec = + register_gauge_vec!("tristar_input_voltage", "Input voltage", &[TRISTAR_LABEL]).unwrap(); + pub static ref TRISTAR_CHARGE_CURRENT: GaugeVec = + register_gauge_vec!("tristar_charge_current", "Charge current", &[TRISTAR_LABEL]).unwrap(); + pub static ref TRISTAR_POWER_OUT: GaugeVec = + register_gauge_vec!("tristar_power_out", "Power out", &[TRISTAR_LABEL]).unwrap(); + pub static ref TRISTAR_POWER_IN: GaugeVec = + register_gauge_vec!("tristar_power_in", "Power in", &[TRISTAR_LABEL]).unwrap(); + pub static ref TRISTAR_MAX_ARRAY_POWER: GaugeVec = register_gauge_vec!( + "tristar_max_array_power", + "Maximum array power", + &[TRISTAR_LABEL] + ) + .unwrap(); + pub static ref TRISTAR_MAX_ARRAY_VOLTAGE: GaugeVec = register_gauge_vec!( + "tristar_max_array_voltage", + "Maximum array voltage", + &[TRISTAR_LABEL] + ) + .unwrap(); + pub static ref TRISTAR_OPEN_CIRCUIT_VOLTAGE: GaugeVec = register_gauge_vec!( + "tristar_open_circuit_voltage", + "Open circuit voltage", + &[TRISTAR_LABEL] + ) + .unwrap(); +} diff --git a/src/charge_controllers/mod.rs b/src/charge_controllers/mod.rs index e6beb95..f319cdc 100644 --- a/src/charge_controllers/mod.rs +++ b/src/charge_controllers/mod.rs @@ -1,54 +1,7 @@ -use std::sync::OnceLock; - -use metrics::{describe_gauge, Unit}; - -mod gauge_names; +pub mod gauges; pub mod pl; pub mod tristar; -static HAS_REGISTERED_SHARED: OnceLock<()> = OnceLock::new(); -static HAS_REGISTERED_PL: OnceLock<()> = OnceLock::new(); -static HAS_REGISTERED_TRISTAR: OnceLock<()> = OnceLock::new(); - -fn register_metrics() { - if HAS_REGISTERED_SHARED.get().is_none() { - describe_gauge!(gauge_names::BATTERY_VOLTAGE, "Battery voltage"); - describe_gauge!(gauge_names::TARGET_VOLTAGE, "Target voltage"); - describe_gauge!(gauge_names::INPUT_CURRENT, "Internal charge current"); - describe_gauge!(gauge_names::CHARGE_STATE, "Regulator state"); - describe_gauge!(gauge_names::BATTERY_TEMP, "Battery temperature"); - - HAS_REGISTERED_SHARED.get_or_init(|| ()); - } -} - -pub fn register_pl_metrics() { - register_metrics(); - if HAS_REGISTERED_PL.get().is_none() { - describe_gauge!(gauge_names::PL_DUTY_CYCLE, Unit::Percent, "Duty cycle"); - describe_gauge!(gauge_names::PL_LOAD_CURRENT, "Internal load current"); - - HAS_REGISTERED_PL.get_or_init(|| ()); - } -} - -pub fn register_tristar_metrics() { - register_metrics(); - if HAS_REGISTERED_TRISTAR.get().is_none() { - describe_gauge!(gauge_names::TRISTAR_INPUT_VOLTAGE, "Input voltage"); - describe_gauge!(gauge_names::TRISTAR_CHARGE_CURRENT, "Charge current"); - describe_gauge!(gauge_names::TRISTAR_POWER_OUT, "Power out"); - describe_gauge!(gauge_names::TRISTAR_POWER_IN, "Power in"); - describe_gauge!(gauge_names::TRISTAR_MAX_ARRAY_POWER, "Maximum array power"); - describe_gauge!( - gauge_names::TRISTAR_MAX_ARRAY_VOLTAGE, - "Maximum array voltage" - ); - describe_gauge!( - gauge_names::TRISTAR_OPEN_CIRCUIT_VOLTAGE, - "Open circuit voltage" - ); - - HAS_REGISTERED_TRISTAR.get_or_init(|| ()); - } -} +pub const CHARGE_CONTROLLER_LABEL: &str = "charge_controller"; +pub const PL_LABEL: &str = "pl_device"; +pub const TRISTAR_LABEL: &str = "tristar_device"; diff --git a/src/charge_controllers/pl.rs b/src/charge_controllers/pl.rs index 1b5739d..0a4f63b 100644 --- a/src/charge_controllers/pl.rs +++ b/src/charge_controllers/pl.rs @@ -5,24 +5,18 @@ use std::{ }; use chrono::Timelike; -use metrics::{gauge, Gauge, Label}; use serde::{Deserialize, Serialize}; use serialport::SerialPort; -use crate::errors::{PliError, PrintErrors}; - -use super::{gauge_names, register_pl_metrics}; +use crate::{ + charge_controllers::gauges::*, + errors::{PliError, PrintErrors}, +}; pub struct Pli { pub state: Arc>, + port_name: String, port: Box, - battery_voltage: Gauge, - target_voltage: Gauge, - duty_cycle: Gauge, - internal_charge_current: Gauge, - internal_load_current: Gauge, - battery_temp: Gauge, - regulator_gauges: RegulatorGauges, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] @@ -62,77 +56,36 @@ impl Default for RegulatorState { } } -struct RegulatorGauges { - boost: Gauge, - equalise: Gauge, - absorption: Gauge, - float: Gauge, -} +fn set_regulator_gauges(state: &RegulatorState, label: &str) { + let boost = CHARGE_STATE.with_label_values(&[label, "boost"]); + let equalise = CHARGE_STATE.with_label_values(&[label, "equalise"]); + let absorption = CHARGE_STATE.with_label_values(&[label, "absorption"]); + let float = CHARGE_STATE.with_label_values(&[label, "float"]); -impl RegulatorGauges { - fn new(labels: Vec