redbear-info: Add thermal, fan, and C-state health dashboard items

Reads from /scheme/acpi/thermal/, /scheme/acpi/fan/, and /scheme/acpi/cstates/
plus /scheme/sys/cstate_policy to populate the --health dashboard with
hardware thermal status, fan activity, and CPU power-management state.
This commit is contained in:
2026-05-20 22:20:47 +03:00
parent 0046c76e43
commit 17791421c4
@@ -2859,6 +2859,76 @@ fn collect_health_items(runtime: &Runtime, report: &Report<'_>) -> Vec<HealthIte
},
});
let thermal_zones = runtime.read_dir_names("/scheme/acpi/thermal").unwrap_or_default();
if !thermal_zones.is_empty() {
let temps: Vec<String> = thermal_zones
.iter()
.filter_map(|zone| {
read_trimmed(runtime, &format!("/scheme/acpi/thermal/{zone}/temperature"))
})
.collect();
let avg_temp = temps.iter().filter_map(|t| t.parse::<f64>().ok()).sum::<f64>()
/ temps.len().max(1) as f64;
let state = if avg_temp > 85.0 {
HealthState::Critical
} else if avg_temp > 70.0 {
HealthState::Warning
} else {
HealthState::Healthy
};
items.push(HealthItem {
label: "Thermal",
state,
detail: format!("{} zone(s), avg {:.1}°C", thermal_zones.len(), avg_temp),
});
} else {
items.push(HealthItem {
label: "Thermal",
state: HealthState::Warning,
detail: "no thermal zones".to_string(),
});
}
let fans = runtime.read_dir_names("/scheme/acpi/fan").unwrap_or_default();
if !fans.is_empty() {
let active = fans
.iter()
.filter(|fan| {
read_trimmed(runtime, &format!("/scheme/acpi/fan/{fan}/status"))
.map(|s| s == "on")
.unwrap_or(false)
})
.count();
items.push(HealthItem {
label: "Fans",
state: HealthState::Healthy,
detail: format!("{} fan(s), {} active", fans.len(), active),
});
} else {
items.push(HealthItem {
label: "Fans",
state: HealthState::Warning,
detail: "no fan devices".to_string(),
});
}
let cstate_policy = read_trimmed(runtime, "/scheme/sys/cstate_policy");
let cstates = runtime.read_dir_names("/scheme/acpi/cstates").unwrap_or_default();
if !cstates.is_empty() {
let max_policy = cstate_policy.as_deref().unwrap_or("unlimited");
items.push(HealthItem {
label: "C-states",
state: HealthState::Healthy,
detail: format!("{} processor(s), policy={}", cstates.len(), max_policy),
});
} else {
items.push(HealthItem {
label: "C-states",
state: HealthState::Warning,
detail: "no C-state surface".to_string(),
});
}
items
}