Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8947bd643a | ||
|
|
5ac2aed516 | ||
|
|
4020f65e28 | ||
|
|
d52b3992e3 |
No files matched your search
Generated
+1
-1
@@ -596,7 +596,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lapctl"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"env_logger",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lapctl"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
+72
-45
@@ -14,6 +14,10 @@ trait Lapctl {
|
||||
async fn set_battery_limit(&self, percent: u32) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
fn is_ideapad() -> bool {
|
||||
Path::new("/sys/bus/platform/drivers/ideapad_acpi").exists()
|
||||
}
|
||||
|
||||
fn try_call_daemon(command: &BatteryCommands) -> bool {
|
||||
let rt = match tokio::runtime::Runtime::new() {
|
||||
Ok(rt) => rt,
|
||||
@@ -56,7 +60,16 @@ pub fn execute(command: &BatteryCommands) {
|
||||
error!("Invalid percentage. Please provide a value between 1 and 100.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
println!("Setting battery charge limit to {}%", percent);
|
||||
|
||||
if is_ideapad() {
|
||||
if *percent < 100 {
|
||||
println!("Setting battery charge limit (Lenovo Conservation Mode ~60%)...");
|
||||
} else {
|
||||
println!("Disabling battery charge limit (charging to 100%)...");
|
||||
}
|
||||
} else {
|
||||
println!("Setting battery charge limit to {}%...", percent);
|
||||
}
|
||||
}
|
||||
BatteryCommands::Status => {}
|
||||
}
|
||||
@@ -72,6 +85,27 @@ pub fn execute_local(command: &BatteryCommands) {
|
||||
match command {
|
||||
BatteryCommands::Limit { percent } => {
|
||||
let mut success_count = 0;
|
||||
|
||||
// Ideapad fallback - search for conservation_mode in ideapad_acpi driver directories
|
||||
if let Ok(drivers) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") {
|
||||
for entry in drivers.flatten() {
|
||||
let conservation_path = entry.path().join("conservation_mode");
|
||||
if conservation_path.exists() {
|
||||
let val = if *percent < 100 { "1" } else { "0" };
|
||||
match fs::write(&conservation_path, val) {
|
||||
Ok(_) => {
|
||||
success_count += 1;
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to write to {}: {}", conservation_path.display(), e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only try standard thresholds if we haven't already succeeded with Ideapad fallback
|
||||
// or if we want to ensure all bases are covered. For now, let's do both but prioritize the message.
|
||||
let sys_class_power = Path::new("/sys/class/power_supply");
|
||||
|
||||
if let Ok(entries) = fs::read_dir(sys_class_power) {
|
||||
@@ -82,6 +116,10 @@ pub fn execute_local(command: &BatteryCommands) {
|
||||
if name_str.starts_with("BAT") {
|
||||
let threshold_path = entry.path().join("charge_control_end_threshold");
|
||||
if threshold_path.exists() {
|
||||
println!(
|
||||
"Setting battery charge limit for {} to {}%",
|
||||
name_str, percent
|
||||
);
|
||||
match fs::write(&threshold_path, percent.to_string()) {
|
||||
Ok(_) => {
|
||||
info!("Successfully set limit for {}", name_str);
|
||||
@@ -91,36 +129,6 @@ pub fn execute_local(command: &BatteryCommands) {
|
||||
debug!("Failed to write to {}: {}", threshold_path.display(), e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ideapad fallback
|
||||
let conservation_path = Path::new(
|
||||
"/sys/bus/platform/devices/ideapad_acpi/conservation_mode",
|
||||
);
|
||||
if conservation_path.exists() {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,22 +159,41 @@ pub fn execute_local(command: &BatteryCommands) {
|
||||
println!(" Capacity: {}%", capacity.trim());
|
||||
println!(" Status: {}", status.trim());
|
||||
|
||||
if let Ok(limit) =
|
||||
fs::read_to_string(bat_path.join("charge_control_end_threshold"))
|
||||
// Check for Ideapad conservation mode first using robust detection
|
||||
let mut limit_shown = false;
|
||||
if let Ok(drivers) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi")
|
||||
{
|
||||
println!(" Charge Limit: {}%", limit.trim());
|
||||
} else {
|
||||
// Ideapad fallback display
|
||||
let conservation_path = Path::new(
|
||||
"/sys/bus/platform/devices/ideapad_acpi/conservation_mode",
|
||||
);
|
||||
if conservation_path.exists()
|
||||
&& let Ok(mode) = fs::read_to_string(conservation_path)
|
||||
for entry in drivers.flatten() {
|
||||
let conservation_path = 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%)");
|
||||
limit_shown = true;
|
||||
} else if mode.trim() == "0" {
|
||||
// On Ideapads 0 means 100% or off.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !limit_shown {
|
||||
if let Ok(limit) =
|
||||
fs::read_to_string(bat_path.join("charge_control_end_threshold"))
|
||||
{
|
||||
if mode.trim() == "1" {
|
||||
println!(" Charge Limit: Conservation Mode (~60%)");
|
||||
} else {
|
||||
println!(" Charge Limit: 100%");
|
||||
println!(" Charge Limit: {}%", limit.trim());
|
||||
} else {
|
||||
// Re-check if it's an ideapad and we haven't shown a limit (likely 100%)
|
||||
if let Ok(drivers) =
|
||||
fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi")
|
||||
{
|
||||
for entry in drivers.flatten() {
|
||||
if entry.path().join("conservation_mode").exists() {
|
||||
println!(" Charge Limit: 100%");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user