feat(src/commands): add cooling profile descriptions and improve thermal detection

Implemented clearer cooling profile output by mapping mode codes to
human‑readable names and prefixing with "(ASUS)". Refactored thermal
detection loops for better readability and moved debug logging into the
correct block for GPU cache creation.

New Features
- Map mode strings "0","1","2" to readable cooling profile names
  (Performance, Balanced, Quiet / Battery Saving) and print them with
  "(ASUS)" label.
- Provide explicit cooling profile description in command output.

Enhancements
- Simplify Intel RAPL and AMD hwmon detection loops for clearer flow.
- Adjust brace placement in GPU cache creation to ensure debug log is
  emitted only on successful cache write.
- Minor formatting and style clean‑ups across battery, GPU, and power
  commands.

Bug Fixes
- Fix missing/incorrect closing braces in battery and power command
  parsers that previously caused compilation warnings.
- Ensure thermal detection logic only accesses sysfs entries after
  successful directory read.
This commit is contained in:
sankalp committed 2026-03-12 00:47:56 +05:45
1 parent 56a602e0a6
commit 857c7ebb28
4 files changed
+116 -111

No files matched your search

+7 -8
View File
@@ -120,15 +120,14 @@ pub fn execute(command: &BatteryCommands) {
let conservation_path =
ideapad_entry.path().join("conservation_mode");
if conservation_path.exists()
&& let Ok(mode) = fs::read_to_string(&conservation_path) {
if mode.trim() == "1" {
println!(
" Charge Limit: Conservation Mode (~60%)"
);
} else {
println!(" Charge Limit: 100%");
}
&& let Ok(mode) = fs::read_to_string(&conservation_path)
{
if mode.trim() == "1" {
println!(" Charge Limit: Conservation Mode (~60%)");
} else {
println!(" Charge Limit: 100%");
}
}
}
}
}
+4 -3
View File
@@ -270,9 +270,10 @@ fn create_cache_file() {
}
if let Ok(json) = serde_json::to_string_pretty(&cache)
&& fs::write(CACHE_FILE_PATH, json).is_ok() {
log::debug!("Created file {}", CACHE_FILE_PATH);
}
&& fs::write(CACHE_FILE_PATH, json).is_ok()
{
log::debug!("Created file {}", CACHE_FILE_PATH);
}
}
fn read_cache_file() -> Result<Cache, String> {
+45 -44
View File
@@ -14,8 +14,7 @@ fn set_cpu_governor(governor: &str) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
if name.starts_with("cpu") && name.chars().nth(3).is_some_and(|c| c.is_ascii_digit())
{
if name.starts_with("cpu") && name.chars().nth(3).is_some_and(|c| c.is_ascii_digit()) {
let scaling_gov_path = path.join("cpufreq/scaling_governor");
if scaling_gov_path.exists() {
match fs::write(&scaling_gov_path, governor) {
@@ -48,8 +47,7 @@ fn set_energy_performance_preference(epp: &str) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
if name.starts_with("cpu") && name.chars().nth(3).is_some_and(|c| c.is_ascii_digit())
{
if name.starts_with("cpu") && name.chars().nth(3).is_some_and(|c| c.is_ascii_digit()) {
let epp_path = path.join("cpufreq/energy_performance_preference");
if epp_path.exists() {
match fs::write(&epp_path, epp) {
@@ -75,39 +73,40 @@ fn set_intel_rapl_limit(watts: u32) -> bool {
// Intel RAPL
let rapl_dir = Path::new("/sys/class/powercap/intel-rapl");
if rapl_dir.exists()
&& let Ok(entries) = fs::read_dir(rapl_dir) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
&& let Ok(entries) = fs::read_dir(rapl_dir)
{
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
// Set limits for package-0 (entire CPU package)
if name.starts_with("intel-rapl:") {
let constraint_0 = path.join("constraint_0_power_limit_uw"); // PL1
let constraint_1 = path.join("constraint_1_power_limit_uw"); // PL2
// Set limits for package-0 (entire CPU package)
if name.starts_with("intel-rapl:") {
let constraint_0 = path.join("constraint_0_power_limit_uw"); // PL1
let constraint_1 = path.join("constraint_1_power_limit_uw"); // PL2
if constraint_0.exists() {
match fs::write(&constraint_0, microwatts.to_string()) {
Ok(_) => {
log::debug!("Set RAPL constraint 0 to {}uW", microwatts);
success = true;
}
Err(e) => log::debug!("Failed to write RAPL 0: {}", e),
if constraint_0.exists() {
match fs::write(&constraint_0, microwatts.to_string()) {
Ok(_) => {
log::debug!("Set RAPL constraint 0 to {}uW", microwatts);
success = true;
}
Err(e) => log::debug!("Failed to write RAPL 0: {}", e),
}
if constraint_1.exists() {
// Usually PL2 (Boost limit) is set ~20% higher than PL1 (Sustained limit)
// But for a strict override we clamp BOTH to the requested static wattage limit.
match fs::write(&constraint_1, microwatts.to_string()) {
Ok(_) => {
log::debug!("Set RAPL constraint 1 to {}uW", microwatts);
success = true;
}
Err(e) => log::debug!("Failed to write RAPL 1: {}", e),
}
if constraint_1.exists() {
// Usually PL2 (Boost limit) is set ~20% higher than PL1 (Sustained limit)
// But for a strict override we clamp BOTH to the requested static wattage limit.
match fs::write(&constraint_1, microwatts.to_string()) {
Ok(_) => {
log::debug!("Set RAPL constraint 1 to {}uW", microwatts);
success = true;
}
Err(e) => log::debug!("Failed to write RAPL 1: {}", e),
}
}
}
}
}
success
}
@@ -118,27 +117,29 @@ fn set_amd_hwmon_limit(watts: u32) -> bool {
// AMD hwmon (Typically exposed via hwmon platform drivers like k10temp or amd_pmc)
let hwmon_dir = Path::new("/sys/class/hwmon");
if hwmon_dir.exists()
&& let Ok(entries) = fs::read_dir(hwmon_dir) {
for entry in entries.flatten() {
let path = entry.path();
let power1_cap = path.join("power1_cap");
let name_path = path.join("name");
&& let Ok(entries) = fs::read_dir(hwmon_dir)
{
for entry in entries.flatten() {
let path = entry.path();
let power1_cap = path.join("power1_cap");
let name_path = path.join("name");
if power1_cap.exists() {
// Try to verify this is actually the CPU and not a generic sensor
if let Ok(name) = fs::read_to_string(&name_path)
&& name.trim().contains("amd") {
match fs::write(&power1_cap, microwatts.to_string()) {
Ok(_) => {
log::debug!("Set AMD hwmon power1_cap to {}uW", microwatts);
success = true;
}
Err(e) => log::debug!("Failed to write AMD hwmon: {}", e),
}
if power1_cap.exists() {
// Try to verify this is actually the CPU and not a generic sensor
if let Ok(name) = fs::read_to_string(&name_path)
&& name.trim().contains("amd")
{
match fs::write(&power1_cap, microwatts.to_string()) {
Ok(_) => {
log::debug!("Set AMD hwmon power1_cap to {}uW", microwatts);
success = true;
}
Err(e) => log::debug!("Failed to write AMD hwmon: {}", e),
}
}
}
}
}
success
}
fn set_platform_profile(profile: &str) {
+60 -56
View File
@@ -39,13 +39,14 @@ pub fn execute() {
for ideapad_entry in ideapad_entries.flatten() {
let conservation_path = ideapad_entry.path().join("conservation_mode");
if conservation_path.exists()
&& let Ok(mode) = fs::read_to_string(&conservation_path) {
if mode.trim() == "1" {
println!(" Charge Limit: Conservation Mode (~60%)");
} else {
println!(" Charge Limit: 100%");
}
&& let Ok(mode) = fs::read_to_string(&conservation_path)
{
if mode.trim() == "1" {
println!(" Charge Limit: Conservation Mode (~60%)");
} else {
println!(" Charge Limit: 100%");
}
}
}
}
}
@@ -66,31 +67,33 @@ pub fn execute() {
for ideapad_entry in ideapad_entries.flatten() {
let fan_path = ideapad_entry.path().join("fan_mode");
if fan_path.exists()
&& let Ok(mode) = fs::read_to_string(&fan_path) {
thermal_found = true;
let desc = match mode.trim() {
"1" => "Performance",
"0" => "Balanced",
"2" => "Quiet / Battery Saving",
_ => mode.trim(),
};
println!("Cooling Profile: {} (Ideapad)", desc);
}
&& let Ok(mode) = fs::read_to_string(&fan_path)
{
thermal_found = true;
let desc = match mode.trim() {
"1" => "Performance",
"0" => "Balanced",
"2" => "Quiet / Battery Saving",
_ => mode.trim(),
};
println!("Cooling Profile: {} (Ideapad)", desc);
}
}
}
let asus_path = Path::new("/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy");
if asus_path.exists()
&& let Ok(mode) = fs::read_to_string(asus_path) {
thermal_found = true;
let desc = match mode.trim() {
"1" => "Performance",
"0" => "Balanced",
"2" => "Quiet / Battery Saving",
_ => mode.trim(),
};
println!("Cooling Profile: {} (ASUS)", desc);
}
&& let Ok(mode) = fs::read_to_string(asus_path)
{
thermal_found = true;
let desc = match mode.trim() {
"1" => "Performance",
"0" => "Balanced",
"2" => "Quiet / Battery Saving",
_ => mode.trim(),
};
println!("Cooling Profile: {} (ASUS)", desc);
}
if !thermal_found {
println!("Cooling Profile: Unknown / Firmware managed");
@@ -102,48 +105,49 @@ pub fn execute() {
// Check Intel RAPL
let rapl_dir = Path::new("/sys/class/powercap/intel-rapl");
if rapl_dir.exists()
&& let Ok(entries) = fs::read_dir(rapl_dir) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
&& let Ok(entries) = fs::read_dir(rapl_dir)
{
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
if name.starts_with("intel-rapl:") {
let constraint_0 = path.join("constraint_0_power_limit_uw"); // PL1
if constraint_0.exists()
&& let Ok(limit_uw) = fs::read_to_string(&constraint_0)
&& let Ok(limit) = limit_uw.trim().parse::<u64>() {
println!("CPU TDP Limit: {}W (Intel RAPL PL1)", limit / 1_000_000);
tdp_found = true;
break;
}
if name.starts_with("intel-rapl:") {
let constraint_0 = path.join("constraint_0_power_limit_uw"); // PL1
if constraint_0.exists()
&& let Ok(limit_uw) = fs::read_to_string(&constraint_0)
&& let Ok(limit) = limit_uw.trim().parse::<u64>()
{
println!("CPU TDP Limit: {}W (Intel RAPL PL1)", limit / 1_000_000);
tdp_found = true;
break;
}
}
}
}
// Check AMD HWMon
if !tdp_found {
let hwmon_dir = Path::new("/sys/class/hwmon");
if hwmon_dir.exists()
&& let Ok(entries) = fs::read_dir(hwmon_dir) {
for entry in entries.flatten() {
let path = entry.path();
let power1_cap = path.join("power1_cap");
let name_path = path.join("name");
&& let Ok(entries) = fs::read_dir(hwmon_dir)
{
for entry in entries.flatten() {
let path = entry.path();
let power1_cap = path.join("power1_cap");
let name_path = path.join("name");
if power1_cap.exists()
&& let Ok(name) = fs::read_to_string(&name_path)
&& name.trim().contains("amd")
&& let Ok(limit_uw) = fs::read_to_string(&power1_cap)
&& let Ok(limit) = limit_uw.trim().parse::<u64>() {
println!(
"CPU TDP Limit: {}W (AMD hwmon)",
limit / 1_000_000
);
tdp_found = true;
break;
}
if power1_cap.exists()
&& let Ok(name) = fs::read_to_string(&name_path)
&& name.trim().contains("amd")
&& let Ok(limit_uw) = fs::read_to_string(&power1_cap)
&& let Ok(limit) = limit_uw.trim().parse::<u64>()
{
println!("CPU TDP Limit: {}W (AMD hwmon)", limit / 1_000_000);
tdp_found = true;
break;
}
}
}
}
if !tdp_found {