mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
core: battery limit management
This commit is contained in:
parent
78490f7c08
commit
09a38dd136
8 changed files with 276 additions and 18 deletions
|
@ -13,7 +13,7 @@ pub fn run_debug(config: &AppConfig) -> Result<(), Box<dyn Error>> {
|
||||||
println!("Version: {}", env!("CARGO_PKG_VERSION"));
|
println!("Version: {}", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
// Current date and time
|
// Current date and time
|
||||||
let now = SystemTime::now();
|
let _now = SystemTime::now(); // Prefix with underscore to indicate intentionally unused
|
||||||
let formatted_time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
|
let formatted_time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
|
||||||
println!("Timestamp: {formatted_time}");
|
println!("Timestamp: {formatted_time}");
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ pub fn load_config_from_path(specific_path: Option<&str>) -> Result<AppConfig, C
|
||||||
Ok(AppConfig {
|
Ok(AppConfig {
|
||||||
charger: ProfileConfig::from(default_toml_config.charger),
|
charger: ProfileConfig::from(default_toml_config.charger),
|
||||||
battery: ProfileConfig::from(default_toml_config.battery),
|
battery: ProfileConfig::from(default_toml_config.battery),
|
||||||
battery_charge_thresholds: default_toml_config.battery_charge_thresholds,
|
global_battery_charge_thresholds: default_toml_config.battery_charge_thresholds,
|
||||||
ignored_power_supplies: default_toml_config.ignored_power_supplies,
|
ignored_power_supplies: default_toml_config.ignored_power_supplies,
|
||||||
poll_interval_sec: default_toml_config.poll_interval_sec,
|
poll_interval_sec: default_toml_config.poll_interval_sec,
|
||||||
daemon: DaemonConfig::default(),
|
daemon: DaemonConfig::default(),
|
||||||
|
@ -82,11 +82,24 @@ fn load_and_parse_config(path: &Path) -> Result<AppConfig, ConfigError> {
|
||||||
let toml_app_config =
|
let toml_app_config =
|
||||||
toml::from_str::<AppConfigToml>(&contents).map_err(ConfigError::TomlError)?;
|
toml::from_str::<AppConfigToml>(&contents).map_err(ConfigError::TomlError)?;
|
||||||
|
|
||||||
|
// Handle inheritance of values from global to profile configs
|
||||||
|
let mut charger_profile = toml_app_config.charger.clone();
|
||||||
|
let mut battery_profile = toml_app_config.battery.clone();
|
||||||
|
|
||||||
|
// If profile-specific battery thresholds are not set, inherit from global config
|
||||||
|
if charger_profile.battery_charge_thresholds.is_none() {
|
||||||
|
charger_profile.battery_charge_thresholds = toml_app_config.battery_charge_thresholds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if battery_profile.battery_charge_thresholds.is_none() {
|
||||||
|
battery_profile.battery_charge_thresholds = toml_app_config.battery_charge_thresholds;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert AppConfigToml to AppConfig
|
// Convert AppConfigToml to AppConfig
|
||||||
Ok(AppConfig {
|
Ok(AppConfig {
|
||||||
charger: ProfileConfig::from(toml_app_config.charger),
|
charger: ProfileConfig::from(charger_profile),
|
||||||
battery: ProfileConfig::from(toml_app_config.battery),
|
battery: ProfileConfig::from(battery_profile),
|
||||||
battery_charge_thresholds: toml_app_config.battery_charge_thresholds,
|
global_battery_charge_thresholds: toml_app_config.battery_charge_thresholds,
|
||||||
ignored_power_supplies: toml_app_config.ignored_power_supplies,
|
ignored_power_supplies: toml_app_config.ignored_power_supplies,
|
||||||
poll_interval_sec: toml_app_config.poll_interval_sec,
|
poll_interval_sec: toml_app_config.poll_interval_sec,
|
||||||
daemon: DaemonConfig {
|
daemon: DaemonConfig {
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub struct ProfileConfig {
|
||||||
pub max_freq_mhz: Option<u32>,
|
pub max_freq_mhz: Option<u32>,
|
||||||
pub platform_profile: Option<String>,
|
pub platform_profile: Option<String>,
|
||||||
pub turbo_auto_settings: Option<TurboAutoSettings>,
|
pub turbo_auto_settings: Option<TurboAutoSettings>,
|
||||||
|
pub battery_charge_thresholds: Option<(u8, u8)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProfileConfig {
|
impl Default for ProfileConfig {
|
||||||
|
@ -26,6 +27,7 @@ impl Default for ProfileConfig {
|
||||||
max_freq_mhz: None, // no override
|
max_freq_mhz: None, // no override
|
||||||
platform_profile: None, // no override
|
platform_profile: None, // no override
|
||||||
turbo_auto_settings: Some(TurboAutoSettings::default()),
|
turbo_auto_settings: Some(TurboAutoSettings::default()),
|
||||||
|
battery_charge_thresholds: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +38,8 @@ pub struct AppConfig {
|
||||||
pub charger: ProfileConfig,
|
pub charger: ProfileConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub battery: ProfileConfig,
|
pub battery: ProfileConfig,
|
||||||
pub battery_charge_thresholds: Option<(u8, u8)>, // (start_threshold, stop_threshold)
|
#[serde(rename = "battery_charge_thresholds")]
|
||||||
|
pub global_battery_charge_thresholds: Option<(u8, u8)>, // (start_threshold, stop_threshold)
|
||||||
pub ignored_power_supplies: Option<Vec<String>>,
|
pub ignored_power_supplies: Option<Vec<String>>,
|
||||||
#[serde(default = "default_poll_interval_sec")]
|
#[serde(default = "default_poll_interval_sec")]
|
||||||
pub poll_interval_sec: u64,
|
pub poll_interval_sec: u64,
|
||||||
|
@ -92,6 +95,7 @@ pub struct ProfileConfigToml {
|
||||||
pub min_freq_mhz: Option<u32>,
|
pub min_freq_mhz: Option<u32>,
|
||||||
pub max_freq_mhz: Option<u32>,
|
pub max_freq_mhz: Option<u32>,
|
||||||
pub platform_profile: Option<String>,
|
pub platform_profile: Option<String>,
|
||||||
|
pub battery_charge_thresholds: Option<(u8, u8)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Default)]
|
#[derive(Deserialize, Debug, Clone, Default)]
|
||||||
|
@ -118,6 +122,7 @@ impl Default for ProfileConfigToml {
|
||||||
min_freq_mhz: None,
|
min_freq_mhz: None,
|
||||||
max_freq_mhz: None,
|
max_freq_mhz: None,
|
||||||
platform_profile: None,
|
platform_profile: None,
|
||||||
|
battery_charge_thresholds: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,6 +180,7 @@ impl From<ProfileConfigToml> for ProfileConfig {
|
||||||
max_freq_mhz: toml_config.max_freq_mhz,
|
max_freq_mhz: toml_config.max_freq_mhz,
|
||||||
platform_profile: toml_config.platform_profile,
|
platform_profile: toml_config.platform_profile,
|
||||||
turbo_auto_settings: Some(TurboAutoSettings::default()),
|
turbo_auto_settings: Some(TurboAutoSettings::default()),
|
||||||
|
battery_charge_thresholds: toml_config.battery_charge_thresholds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
189
src/cpu.rs
189
src/cpu.rs
|
@ -1,7 +1,8 @@
|
||||||
use crate::core::{GovernorOverrideMode, TurboSetting};
|
use crate::core::{GovernorOverrideMode, TurboSetting};
|
||||||
use crate::util::error::ControlError;
|
use crate::util::error::ControlError;
|
||||||
use core::str;
|
use core::str;
|
||||||
use std::{fs, io, path::Path, string::ToString};
|
use log::debug;
|
||||||
|
use std::{fs, io, path::{Path, PathBuf}, string::ToString};
|
||||||
|
|
||||||
pub type Result<T, E = ControlError> = std::result::Result<T, E>;
|
pub type Result<T, E = ControlError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
@ -342,3 +343,189 @@ pub fn get_governor_override() -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set battery charge thresholds to protect battery health
|
||||||
|
///
|
||||||
|
/// This sets the start and stop charging thresholds for batteries that support this feature.
|
||||||
|
/// Different laptop vendors implement battery thresholds in different ways, so this function
|
||||||
|
/// attempts to handle multiple implementations (Lenovo, ASUS, etc.).
|
||||||
|
///
|
||||||
|
/// The thresholds determine at what percentage the battery starts charging (when below start_threshold)
|
||||||
|
/// and at what percentage it stops (when it reaches stop_threshold).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `start_threshold` - The battery percentage at which charging should start (typically 0-99)
|
||||||
|
/// * `stop_threshold` - The battery percentage at which charging should stop (typically 1-100)
|
||||||
|
///
|
||||||
|
pub fn set_battery_charge_thresholds(start_threshold: u8, stop_threshold: u8) -> Result<()> {
|
||||||
|
// Validate threshold values
|
||||||
|
if start_threshold >= stop_threshold {
|
||||||
|
return Err(ControlError::InvalidValueError(format!(
|
||||||
|
"Start threshold ({}) must be less than stop threshold ({})",
|
||||||
|
start_threshold, stop_threshold
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if stop_threshold > 100 {
|
||||||
|
return Err(ControlError::InvalidValueError(format!(
|
||||||
|
"Stop threshold ({}) cannot exceed 100%",
|
||||||
|
stop_threshold
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Known sysfs paths for battery threshold control by vendor
|
||||||
|
let threshold_paths = vec![
|
||||||
|
// Standard sysfs paths (used by Lenovo and some others)
|
||||||
|
ThresholdPathPattern {
|
||||||
|
description: "Standard",
|
||||||
|
start_path: "charge_control_start_threshold",
|
||||||
|
stop_path: "charge_control_end_threshold",
|
||||||
|
},
|
||||||
|
// ASUS-specific paths
|
||||||
|
ThresholdPathPattern {
|
||||||
|
description: "ASUS",
|
||||||
|
start_path: "charge_control_start_percentage",
|
||||||
|
stop_path: "charge_control_end_percentage",
|
||||||
|
},
|
||||||
|
// Huawei-specific paths
|
||||||
|
ThresholdPathPattern {
|
||||||
|
description: "Huawei",
|
||||||
|
start_path: "charge_start_threshold",
|
||||||
|
stop_path: "charge_stop_threshold",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let power_supply_path = Path::new("/sys/class/power_supply");
|
||||||
|
if !power_supply_path.exists() {
|
||||||
|
return Err(ControlError::NotSupported(
|
||||||
|
"Power supply path not found, battery threshold control not supported".to_string()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let entries = fs::read_dir(power_supply_path).map_err(|e| {
|
||||||
|
if e.kind() == io::ErrorKind::PermissionDenied {
|
||||||
|
ControlError::PermissionDenied(format!(
|
||||||
|
"Permission denied accessing power supply directory: {}",
|
||||||
|
power_supply_path.display()
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
ControlError::Io(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut supported_batteries = Vec::new();
|
||||||
|
|
||||||
|
// Scan all power supplies for battery threshold support
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let ps_path = entry.path();
|
||||||
|
let name = entry.file_name().into_string().unwrap_or_default();
|
||||||
|
|
||||||
|
// Skip non-battery devices
|
||||||
|
if !is_battery(&ps_path)? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try each threshold path pattern for this battery
|
||||||
|
for pattern in &threshold_paths {
|
||||||
|
let start_threshold_path = ps_path.join(&pattern.start_path);
|
||||||
|
let stop_threshold_path = ps_path.join(&pattern.stop_path);
|
||||||
|
|
||||||
|
if start_threshold_path.exists() && stop_threshold_path.exists() {
|
||||||
|
// Found a battery with threshold support
|
||||||
|
supported_batteries.push(SupportedBattery {
|
||||||
|
name: name.clone(),
|
||||||
|
pattern: pattern.clone(),
|
||||||
|
path: ps_path.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Found a supported pattern, no need to check others for this battery
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if supported_batteries.is_empty() {
|
||||||
|
return Err(ControlError::NotSupported(
|
||||||
|
"No batteries with charge threshold control support found".to_string()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply thresholds to all supported batteries
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let mut success_count = 0;
|
||||||
|
|
||||||
|
for battery in supported_batteries {
|
||||||
|
let start_path = battery.path.join(&battery.pattern.start_path);
|
||||||
|
let stop_path = battery.path.join(&battery.pattern.stop_path);
|
||||||
|
|
||||||
|
// Attempt to set both thresholds
|
||||||
|
match (
|
||||||
|
write_sysfs_value(&start_path, &start_threshold.to_string()),
|
||||||
|
write_sysfs_value(&stop_path, &stop_threshold.to_string())
|
||||||
|
) {
|
||||||
|
(Ok(_), Ok(_)) => {
|
||||||
|
debug!("Set {}-{}% charge thresholds for {} battery '{}'",
|
||||||
|
start_threshold, stop_threshold, battery.pattern.description, battery.name);
|
||||||
|
success_count += 1;
|
||||||
|
},
|
||||||
|
(start_result, stop_result) => {
|
||||||
|
let mut error_msg = format!("Failed to set thresholds for {} battery '{}'",
|
||||||
|
battery.pattern.description, battery.name);
|
||||||
|
|
||||||
|
if let Err(e) = start_result {
|
||||||
|
error_msg.push_str(&format!(": start threshold error: {}", e));
|
||||||
|
}
|
||||||
|
if let Err(e) = stop_result {
|
||||||
|
error_msg.push_str(&format!(": stop threshold error: {}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.push(error_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if success_count > 0 {
|
||||||
|
// As long as we successfully set thresholds on at least one battery, consider it a success
|
||||||
|
if !errors.is_empty() {
|
||||||
|
debug!("Partial success setting battery thresholds: {}", errors.join("; "));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ControlError::WriteError(format!(
|
||||||
|
"Failed to set charge thresholds on any battery: {}",
|
||||||
|
errors.join("; ")
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper struct for battery charge threshold path patterns
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct ThresholdPathPattern {
|
||||||
|
description: &'static str,
|
||||||
|
start_path: &'static str,
|
||||||
|
stop_path: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper struct for batteries with threshold support
|
||||||
|
struct SupportedBattery {
|
||||||
|
name: String,
|
||||||
|
pattern: ThresholdPathPattern,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a power supply entry is a battery
|
||||||
|
fn is_battery(path: &Path) -> Result<bool> {
|
||||||
|
let type_path = path.join("type");
|
||||||
|
|
||||||
|
if !type_path.exists() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ps_type = fs::read_to_string(&type_path)
|
||||||
|
.map_err(|_| ControlError::ReadError(format!("Failed to read {}", type_path.display())))?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
Ok(ps_type == "Battery")
|
||||||
|
}
|
||||||
|
|
|
@ -202,16 +202,6 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box<dyn st
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the logger with the appropriate level
|
|
||||||
const fn get_log_level_filter(log_level: LogLevel) -> LevelFilter {
|
|
||||||
match log_level {
|
|
||||||
LogLevel::Error => LevelFilter::Error,
|
|
||||||
LogLevel::Warning => LevelFilter::Warn,
|
|
||||||
LogLevel::Info => LevelFilter::Info,
|
|
||||||
LogLevel::Debug => LevelFilter::Debug,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write current system stats to a file for --stats to read
|
/// Write current system stats to a file for --stats to read
|
||||||
fn write_stats_file(path: &str, report: &SystemReport) -> Result<(), std::io::Error> {
|
fn write_stats_file(path: &str, report: &SystemReport) -> Result<(), std::io::Error> {
|
||||||
let mut file = File::create(path)?;
|
let mut file = File::create(path)?;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::config::{AppConfig, ProfileConfig};
|
use crate::config::{AppConfig, ProfileConfig};
|
||||||
use crate::core::{OperationalMode, SystemReport, TurboSetting};
|
use crate::core::{OperationalMode, SystemReport, TurboSetting};
|
||||||
use crate::cpu::{self};
|
use crate::cpu::{self};
|
||||||
use crate::util::error::EngineError;
|
use crate::util::error::{ControlError, EngineError};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
|
|
||||||
/// Determines the appropriate CPU profile based on power status or forced mode,
|
/// Determines the appropriate CPU profile based on power status or forced mode,
|
||||||
|
@ -92,6 +92,12 @@ pub fn determine_and_apply_settings(
|
||||||
cpu::set_platform_profile(profile)?;
|
cpu::set_platform_profile(profile)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply battery charge thresholds if configured
|
||||||
|
apply_battery_charge_thresholds(
|
||||||
|
selected_profile_config.battery_charge_thresholds,
|
||||||
|
config.global_battery_charge_thresholds,
|
||||||
|
)?;
|
||||||
|
|
||||||
debug!("Profile settings applied successfully.");
|
debug!("Profile settings applied successfully.");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -186,3 +192,39 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<()
|
||||||
Err(e) => Err(EngineError::ControlError(e)),
|
Err(e) => Err(EngineError::ControlError(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply battery charge thresholds from configuration
|
||||||
|
fn apply_battery_charge_thresholds(
|
||||||
|
profile_thresholds: Option<(u8, u8)>,
|
||||||
|
global_thresholds: Option<(u8, u8)>,
|
||||||
|
) -> Result<(), EngineError> {
|
||||||
|
// Try profile-specific thresholds first, fall back to global thresholds
|
||||||
|
let thresholds = profile_thresholds.or(global_thresholds);
|
||||||
|
|
||||||
|
if let Some((start_threshold, stop_threshold)) = thresholds {
|
||||||
|
info!("Setting battery charge thresholds: {start_threshold}-{stop_threshold}%");
|
||||||
|
match cpu::set_battery_charge_thresholds(start_threshold, stop_threshold) {
|
||||||
|
Ok(()) => {
|
||||||
|
debug!("Successfully set battery charge thresholds");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// If the battery doesn't support thresholds, log but don't fail
|
||||||
|
if matches!(e, ControlError::NotSupported(_)) {
|
||||||
|
debug!("Battery charge thresholds not supported: {e}");
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// For permission errors, provide more helpful message
|
||||||
|
if matches!(e, ControlError::PermissionDenied(_)) {
|
||||||
|
debug!("Permission denied setting battery thresholds - requires root privileges");
|
||||||
|
}
|
||||||
|
Err(EngineError::ControlError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No thresholds configured, this is not an error
|
||||||
|
debug!("No battery charge thresholds configured");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -77,6 +77,13 @@ enum Commands {
|
||||||
},
|
},
|
||||||
/// Set ACPI platform profile
|
/// Set ACPI platform profile
|
||||||
SetPlatformProfile { profile: String },
|
SetPlatformProfile { profile: String },
|
||||||
|
/// Set battery charge thresholds to extend battery lifespan
|
||||||
|
SetBatteryThresholds {
|
||||||
|
/// Percentage at which charging starts (when below this value)
|
||||||
|
start_threshold: u8,
|
||||||
|
/// Percentage at which charging stops (when it reaches this value)
|
||||||
|
stop_threshold: u8,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -358,6 +365,17 @@ fn main() {
|
||||||
}
|
}
|
||||||
Some(Commands::SetPlatformProfile { profile }) => cpu::set_platform_profile(&profile)
|
Some(Commands::SetPlatformProfile { profile }) => cpu::set_platform_profile(&profile)
|
||||||
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>),
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>),
|
||||||
|
Some(Commands::SetBatteryThresholds {
|
||||||
|
start_threshold,
|
||||||
|
stop_threshold,
|
||||||
|
}) => {
|
||||||
|
info!(
|
||||||
|
"Setting battery thresholds: start at {}%, stop at {}%",
|
||||||
|
start_threshold, stop_threshold
|
||||||
|
);
|
||||||
|
cpu::set_battery_charge_thresholds(start_threshold, stop_threshold)
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
|
||||||
|
}
|
||||||
Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose),
|
Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose),
|
||||||
Some(Commands::Debug) => cli::debug::run_debug(&config),
|
Some(Commands::Debug) => cli::debug::run_debug(&config),
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::io;
|
||||||
pub enum ControlError {
|
pub enum ControlError {
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
WriteError(String),
|
WriteError(String),
|
||||||
|
ReadError(String),
|
||||||
InvalidValueError(String),
|
InvalidValueError(String),
|
||||||
NotSupported(String),
|
NotSupported(String),
|
||||||
PermissionDenied(String),
|
PermissionDenied(String),
|
||||||
|
@ -24,6 +25,7 @@ impl std::fmt::Display for ControlError {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => write!(f, "I/O error: {e}"),
|
Self::Io(e) => write!(f, "I/O error: {e}"),
|
||||||
Self::WriteError(s) => write!(f, "Failed to write to sysfs path: {s}"),
|
Self::WriteError(s) => write!(f, "Failed to write to sysfs path: {s}"),
|
||||||
|
Self::ReadError(s) => write!(f, "Failed to read sysfs path: {s}"),
|
||||||
Self::InvalidValueError(s) => write!(f, "Invalid value for setting: {s}"),
|
Self::InvalidValueError(s) => write!(f, "Invalid value for setting: {s}"),
|
||||||
Self::NotSupported(s) => write!(f, "Control action not supported: {s}"),
|
Self::NotSupported(s) => write!(f, "Control action not supported: {s}"),
|
||||||
Self::PermissionDenied(s) => {
|
Self::PermissionDenied(s) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue