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 IntelThermalZone {
current_temperature: Option<u32>,
#[serde(default)]
temperature: Option<u32>,
}
pub struct IntelWmiSource {
namespace_available: OnceCell<bool>,
}
impl Default for IntelWmiSource {
fn default() -> Self {
Self::new()
}
}
impl IntelWmiSource {
pub fn new() -> Self {
Self {
namespace_available: OnceCell::new(),
}
}
fn is_namespace_available(&self) -> bool {
*self
.namespace_available
.get_or_init(|| WMIConnection::with_namespace_path("root\\Intel").is_ok())
}
fn create_connection(&self) -> Option<WMIConnection> {
WMIConnection::with_namespace_path("root\\Intel").ok()
}
pub fn get_temperature(&self) -> TemperatureResult {
if !self.is_namespace_available() {
return TemperatureResult::NotFound;
}
let connection = match self.create_connection() {
Some(conn) => conn,
None => return TemperatureResult::Error,
};
let queries = [
"SELECT CurrentTemperature, Temperature FROM ThermalZoneInformation",
"SELECT CurrentTemperature, Temperature FROM Intel_ThermalZone",
];
for query in queries {
let results: Result<Vec<IntelThermalZone>, _> = connection.raw_query(query);
match results {
Ok(zones) if !zones.is_empty() => {
for zone in zones {
let temp_value = zone.current_temperature.or(zone.temperature);
if let Some(temp) = temp_value {
let celsius = if temp > 200 {
(temp as f64 / 10.0) - 273.15
} else {
temp as f64
};
if celsius > 0.0 && celsius < 150.0 {
return TemperatureResult::Success(celsius.round() as u32);
}
}
}
}
Ok(_) => continue, Err(e) if is_wmi_not_found_error(&e) => continue,
Err(_) => continue,
}
}
TemperatureResult::NotFound
}
}