diff --git a/src/commands/battery.rs b/src/commands/battery.rs index e9485b6..99c5165 100644 --- a/src/commands/battery.rs +++ b/src/commands/battery.rs @@ -1,12 +1,117 @@ use crate::cli::BatteryCommands; +use crate::utils::system::assert_root; +use log::{debug, error, info}; +use std::fs; +use std::path::Path; pub fn execute(command: &BatteryCommands) { match command { BatteryCommands::Limit { percent } => { - println!("Setting battery limit to {}%", percent); + assert_root(); + + if percent < &1 || percent > &100 { + error!("Invalid percentage. Please provide a value between 1 and 100."); + std::process::exit(1); + } + + println!("Setting battery charge limit to {}%", percent); + + let mut success_count = 0; + let sys_class_power = Path::new("/sys/class/power_supply"); + + if let Ok(entries) = fs::read_dir(sys_class_power) { + for entry in entries.flatten() { + let name = entry.file_name(); + let name_str = name.to_string_lossy(); + + if name_str.starts_with("BAT") { + let threshold_path = entry.path().join("charge_control_end_threshold"); + if threshold_path.exists() { + match fs::write(&threshold_path, percent.to_string()) { + Ok(_) => { + info!("Successfully set limit for {}", name_str); + success_count += 1; + } + Err(e) => debug!("Failed to write to {}: {}", threshold_path.display(), e), + } + } else { + // Ideapad fallback + let mut ideapad_found = false; + if let Ok(ideapad_entries) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") { + for ideapad_entry in ideapad_entries.flatten() { + let ideapad_path = ideapad_entry.path(); + let conservation_path = ideapad_path.join("conservation_mode"); + if conservation_path.exists() { + ideapad_found = true; + let val = if *percent < 100 { "1" } else { "0" }; + match fs::write(&conservation_path, val) { + Ok(_) => { + if *percent < 100 { + println!("WARNING: Your laptop (Lenovo Ideapad) does NOT support custom charge limits (like {}%).", percent); + println!("Instead, lapctl has enabled Lenovo's built-in 'Conservation Mode' via the Ideapad ACPI."); + println!("This mode is hard-coded into your laptop's firmware to stop charging at ~60%."); + } else { + println!("Disabled Conservation Mode (charging to 100%)."); + } + success_count += 1; + } + Err(e) => debug!("Failed to write to conservation_mode: {}", e), + } + } + } + } + if !ideapad_found { + debug!("limit not supported for {} (missing thresholds)", name_str); + } + } + } + } + } + + if success_count > 0 { + println!("Operation completed successfully."); + } else { + error!("Hardware does not support dynamic charge thresholds via sysfs or no batteries were found."); + } } BatteryCommands::Status => { - println!("Battery status placeholder"); + // Re-use logic from `lapctl status` to show battery-specific details: + let sys_class_power = Path::new("/sys/class/power_supply"); + if let Ok(entries) = fs::read_dir(sys_class_power) { + for entry in entries.flatten() { + let name = entry.file_name(); + let name_str = name.to_string_lossy(); + if name_str.starts_with("BAT") { + let bat_path = entry.path(); + let capacity = fs::read_to_string(bat_path.join("capacity")).unwrap_or_else(|_| "Unknown".into()); + let status = fs::read_to_string(bat_path.join("status")).unwrap_or_else(|_| "Unknown".into()); + + println!("{}:", name_str); + println!(" Capacity: {}%", capacity.trim()); + println!(" Status: {}", status.trim()); + + if let Ok(limit) = fs::read_to_string(bat_path.join("charge_control_end_threshold")) { + println!(" Charge Limit: {}%", limit.trim()); + } else { + // Ideapad fallback display + if let Ok(ideapad_entries) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") { + for ideapad_entry in ideapad_entries.flatten() { + let conservation_path = ideapad_entry.path().join("conservation_mode"); + if conservation_path.exists() { + if let Ok(mode) = fs::read_to_string(&conservation_path) { + if mode.trim() == "1" { + println!(" Charge Limit: Conservation Mode (~60%)"); + } else { + println!(" Charge Limit: 100%"); + } + } + } + } + } + } + } + } + } } } } diff --git a/src/commands/status.rs b/src/commands/status.rs index 3014111..2ab6f4b 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -26,9 +26,23 @@ pub fn execute() { println!(" Capacity: {}%", capacity.trim()); println!(" Status: {}", status.trim()); - // Print charing limit if exists (e.g., charge_control_end_threshold) if let Ok(limit) = fs::read_to_string(bat_path.join("charge_control_end_threshold")) { println!(" Charge Limit: {}%", limit.trim()); + } else { + if let Ok(ideapad_entries) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") { + for ideapad_entry in ideapad_entries.flatten() { + let conservation_path = ideapad_entry.path().join("conservation_mode"); + if conservation_path.exists() { + if let Ok(mode) = fs::read_to_string(&conservation_path) { + if mode.trim() == "1" { + println!(" Charge Limit: Conservation Mode (~60%)"); + } else { + println!(" Charge Limit: 100%"); + } + } + } + } + } } } }