use super::{TemperatureResult, is_wmi_not_found_error};
use once_cell::sync::OnceCell;
use serde::Deserialize;
use wmi::WMIConnection;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct LhmSensor {
name: Option<String>,
#[allow(dead_code)]
sensor_type: Option<String>,
value: Option<f32>,
#[serde(default)]
parent: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum LhmNamespace {
Libre,
Open,
}
pub struct LibreHardwareMonitorSource {
available_namespace: OnceCell<Option<LhmNamespace>>,
}
impl Default for LibreHardwareMonitorSource {
fn default() -> Self {
Self::new()
}
}
impl LibreHardwareMonitorSource {
pub fn new() -> Self {
Self {
available_namespace: OnceCell::new(),
}
}
fn get_available_namespace(&self) -> Option<LhmNamespace> {
*self.available_namespace.get_or_init(|| {
if WMIConnection::with_namespace_path("root\\LibreHardwareMonitor").is_ok() {
Some(LhmNamespace::Libre)
} else if WMIConnection::with_namespace_path("root\\OpenHardwareMonitor").is_ok() {
Some(LhmNamespace::Open)
} else {
None
}
})
}
fn create_connection(&self) -> Option<WMIConnection> {
match self.get_available_namespace()? {
LhmNamespace::Libre => {
WMIConnection::with_namespace_path("root\\LibreHardwareMonitor").ok()
}
LhmNamespace::Open => {
WMIConnection::with_namespace_path("root\\OpenHardwareMonitor").ok()
}
}
}
pub fn get_temperature(&self) -> TemperatureResult {
if self.get_available_namespace().is_none() {
return TemperatureResult::NotFound;
}
let connection = match self.create_connection() {
Some(conn) => conn,
None => return TemperatureResult::Error,
};
let query =
"SELECT Name, SensorType, Value, Parent FROM Sensor WHERE SensorType='Temperature'";
let results: Result<Vec<LhmSensor>, _> = connection.raw_query(query);
match results {
Ok(sensors) => {
if sensors.is_empty() {
return TemperatureResult::NoValidReading;
}
let priority_names = ["CPU Package", "CPU CCD", "CPU Core"];
for priority in priority_names {
for sensor in &sensors {
if let (Some(name), Some(value)) = (&sensor.name, sensor.value) {
let is_cpu = sensor
.parent
.as_ref()
.map(|p| p.to_lowercase().contains("cpu"))
.unwrap_or(false)
|| name.to_lowercase().contains("cpu");
if is_cpu && name.contains(priority) {
let temp = value as f64;
if temp > 0.0 && temp < 150.0 {
return TemperatureResult::Success(temp.round() as u32);
}
}
}
}
}
for sensor in &sensors {
if let (Some(name), Some(value)) = (&sensor.name, sensor.value) {
let is_cpu = sensor
.parent
.as_ref()
.map(|p| p.to_lowercase().contains("cpu"))
.unwrap_or(false)
|| name.to_lowercase().contains("cpu");
if is_cpu {
let temp = value as f64;
if temp > 0.0 && temp < 150.0 {
return TemperatureResult::Success(temp.round() as u32);
}
}
}
}
TemperatureResult::NoValidReading
}
Err(e) => {
if is_wmi_not_found_error(&e) {
TemperatureResult::NotFound
} else {
TemperatureResult::Error
}
}
}
}
}