Add battery charge limit functionality
This commit is contained in:
parent
32ae1a7eeb
commit
f42efab0b0
33 changed files with 799 additions and 63 deletions
2
backend/Cargo.lock
generated
2
backend/Cargo.lock
generated
|
@ -1095,7 +1095,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "powertools"
|
||||
version = "1.3.0-alpha"
|
||||
version = "1.3.0-beta1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"libryzenadj",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "powertools"
|
||||
version = "1.3.0-alpha"
|
||||
version = "1.3.0-beta1"
|
||||
edition = "2021"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
description = "Backend (superuser) functionality for PowerTools"
|
||||
|
|
|
@ -26,6 +26,8 @@ pub struct BatteryLimits {
|
|||
pub charge_current: Option<RangeLimit<u64>>,
|
||||
pub charge_current_step: u64,
|
||||
pub charge_modes: Vec<String>,
|
||||
pub charge_limit: Option<RangeLimit<f64>>,
|
||||
pub charge_limit_step: f64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
|
|
@ -193,3 +193,83 @@ pub fn unset_charge_mode(
|
|||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate unplugged event receiver web method
|
||||
pub fn on_unplugged(
|
||||
sender: Sender<ApiMessage>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||
move |_| {
|
||||
sender.lock().unwrap().send(ApiMessage::OnUnplugged).expect("on_unplugged send failed");
|
||||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate plugged in event receiver web method
|
||||
pub fn on_plugged(
|
||||
sender: Sender<ApiMessage>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||
move |_| {
|
||||
sender.lock().unwrap().send(ApiMessage::OnPluggedIn).expect("on_plugged send failed");
|
||||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate set battery charge limit web method
|
||||
pub fn set_charge_limit(
|
||||
sender: Sender<ApiMessage>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||
let setter = move |limit: f64|
|
||||
sender.lock()
|
||||
.unwrap()
|
||||
.send(ApiMessage::Battery(BatteryMessage::SetChargeLimit(Some(limit))))
|
||||
.expect("set_charge_limit send failed");
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(&Primitive::F64(new_val)) = params_in.get(0) {
|
||||
setter(new_val);
|
||||
vec![new_val.into()]
|
||||
} else {
|
||||
vec!["set_charge_limit missing parameter".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate unset battery charge limit web method
|
||||
pub fn unset_charge_limit(
|
||||
sender: Sender<ApiMessage>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||
let unsetter = move ||
|
||||
sender.lock()
|
||||
.unwrap()
|
||||
.send(ApiMessage::Battery(BatteryMessage::SetChargeLimit(None)))
|
||||
.expect("unset_charge_limit send failed");
|
||||
move |_: super::ApiParameterType| {
|
||||
unsetter();
|
||||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Charge design web method
|
||||
pub fn get_charge_limit(
|
||||
sender: Sender<ApiMessage>,
|
||||
) -> impl AsyncCallable {
|
||||
let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety
|
||||
let getter = move || {
|
||||
let sender2 = sender.clone();
|
||||
move || {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let callback = move |val: Option<f64>| tx.send(val).expect("get_charge_limit callback send failed");
|
||||
sender2.lock().unwrap().send(ApiMessage::Battery(BatteryMessage::GetChargeLimit(Box::new(callback)))).expect("get_charge_limit send failed");
|
||||
rx.recv().expect("get_charge_limit callback recv failed")
|
||||
}
|
||||
};
|
||||
super::async_utils::AsyncIshGetter {
|
||||
set_get: getter,
|
||||
trans_getter: |result| {
|
||||
super::utility::map_optional_result(Ok(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,14 +47,14 @@ pub fn load_settings(
|
|||
sender: Sender<ApiMessage>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||
let setter = move |path: String, name: String|
|
||||
let setter = move |path: i64, name: String|
|
||||
sender.lock()
|
||||
.unwrap()
|
||||
.send(ApiMessage::LoadSettings(path, name)).expect("load_settings send failed");
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::String(path)) = params_in.get(0) {
|
||||
if let Some(Primitive::F64(id)) = params_in.get(0) {
|
||||
if let Some(Primitive::String(name)) = params_in.get(1) {
|
||||
setter(path.to_owned(), name.to_owned());
|
||||
setter(*id as i64, name.to_owned());
|
||||
vec![true.into()]
|
||||
} else {
|
||||
vec!["load_settings missing name parameter".into()]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::settings::{Settings, TCpus, TGpu, TBattery, TGeneral, OnSet, OnResume, MinMax};
|
||||
use crate::settings::{Settings, TCpus, TGpu, TBattery, TGeneral, OnSet, OnResume, MinMax, OnPowerEvent, PowerMode};
|
||||
use crate::persist::SettingsJson;
|
||||
use crate::utility::unwrap_maybe_fatal;
|
||||
|
||||
|
@ -13,8 +13,15 @@ pub enum ApiMessage {
|
|||
Gpu(GpuMessage),
|
||||
General(GeneralMessage),
|
||||
OnResume,
|
||||
#[allow(dead_code)]
|
||||
OnPluggedIn,
|
||||
#[allow(dead_code)]
|
||||
OnUnplugged,
|
||||
#[allow(dead_code)]
|
||||
OnChargeChange(f64), // battery fill amount: 0 = empty, 1 = full
|
||||
PowerVibeCheck,
|
||||
WaitForEmptyQueue(Callback<()>),
|
||||
LoadSettings(String, String), // (path, name)
|
||||
LoadSettings(i64, String), // (path, name)
|
||||
LoadMainSettings,
|
||||
LoadSystemSettings,
|
||||
GetLimits(Callback<super::SettingsLimits>),
|
||||
|
@ -30,6 +37,8 @@ pub enum BatteryMessage {
|
|||
ReadChargeNow(Callback<Option<f64>>),
|
||||
ReadChargeDesign(Callback<Option<f64>>),
|
||||
ReadCurrentNow(Callback<Option<f64>>),
|
||||
SetChargeLimit(Option<f64>),
|
||||
GetChargeLimit(Callback<Option<f64>>),
|
||||
}
|
||||
|
||||
impl BatteryMessage {
|
||||
|
@ -44,6 +53,8 @@ impl BatteryMessage {
|
|||
Self::ReadChargeNow(cb) => cb(settings.read_charge_now()),
|
||||
Self::ReadChargeDesign(cb) => cb(settings.read_charge_design()),
|
||||
Self::ReadCurrentNow(cb) => cb(settings.read_current_now()),
|
||||
Self::SetChargeLimit(limit) => settings.charge_limit(limit),
|
||||
Self::GetChargeLimit(cb) => cb(settings.get_charge_limit()),
|
||||
}
|
||||
dirty
|
||||
}
|
||||
|
@ -287,11 +298,44 @@ impl ApiMessageHandler {
|
|||
}
|
||||
false
|
||||
}
|
||||
ApiMessage::OnPluggedIn => {
|
||||
if let Err(e) = settings.on_power_event(PowerMode::PluggedIn) {
|
||||
print_errors("on_power_event(PluggedIn)", e);
|
||||
}
|
||||
true
|
||||
}
|
||||
ApiMessage::OnUnplugged => {
|
||||
if let Err(e) = settings.on_power_event(PowerMode::PluggedOut) {
|
||||
print_errors("on_power_event(PluggedOut)", e);
|
||||
}
|
||||
true
|
||||
}
|
||||
ApiMessage::OnChargeChange(charge) => {
|
||||
if let Err(e) = settings.on_power_event(PowerMode::BatteryCharge(charge)) {
|
||||
print_errors(&format!("on_power_event(BatteryCharge={:#0.5})", charge), e);
|
||||
}
|
||||
true
|
||||
}
|
||||
ApiMessage::PowerVibeCheck => {
|
||||
match settings.battery.check_power() {
|
||||
Err(e) => print_errors("check_power()", e),
|
||||
Ok(events) => {
|
||||
for ev in events {
|
||||
let name = format!("on_power_event([vibe]{:?})", ev);
|
||||
if let Err(e) = settings.on_power_event(ev) {
|
||||
print_errors(&name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
ApiMessage::WaitForEmptyQueue(callback) => {
|
||||
self.on_empty.push(callback);
|
||||
false
|
||||
},
|
||||
ApiMessage::LoadSettings(path, name) => {
|
||||
ApiMessage::LoadSettings(id, name) => {
|
||||
let path = format!("{}.json", id);
|
||||
match settings.load_file(path.into(), name, false) {
|
||||
Ok(success) => log::info!("Loaded settings file? {}", success),
|
||||
Err(e) => log::warn!("Load file err: {}", e),
|
||||
|
|
|
@ -5,6 +5,7 @@ mod state;
|
|||
|
||||
mod consts;
|
||||
use consts::*;
|
||||
mod power_worker;
|
||||
mod resume_worker;
|
||||
//mod save_worker;
|
||||
mod api_worker;
|
||||
|
@ -70,6 +71,7 @@ fn main() -> Result<(), ()> {
|
|||
|
||||
//let (_save_handle, save_sender) = save_worker::spawn(loaded_settings.clone());
|
||||
let _resume_handle = resume_worker::spawn(api_sender.clone());
|
||||
let _power_handle = power_worker::spawn(api_sender.clone());
|
||||
|
||||
let instance = Instance::new(PORT)
|
||||
.register("V_INFO", |_: Vec<Primitive>| {
|
||||
|
@ -111,6 +113,18 @@ fn main() -> Result<(), ()> {
|
|||
"BATTERY_unset_charge_mode",
|
||||
api::battery::unset_charge_mode(api_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_set_charge_limit",
|
||||
api::battery::set_charge_limit(api_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_unset_charge_limit",
|
||||
api::battery::unset_charge_limit(api_sender.clone()),
|
||||
)
|
||||
.register_async(
|
||||
"BATTERY_get_charge_limit",
|
||||
api::battery::get_charge_limit(api_sender.clone()),
|
||||
)
|
||||
// cpu API functions
|
||||
.register("CPU_count", api::cpu::max_cpus)
|
||||
.register(
|
||||
|
@ -232,10 +246,17 @@ fn main() -> Result<(), ()> {
|
|||
api::general::get_provider(api_sender.clone())
|
||||
)
|
||||
.register("GENERAL_idk", api::general::gunter)
|
||||
// general API functions
|
||||
.register(
|
||||
"GENERAL_apply_now",
|
||||
api::general::force_apply(api_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_on_pluggedin",
|
||||
api::battery::on_plugged(api_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_on_unplugged",
|
||||
api::battery::on_unplugged(api_sender.clone())
|
||||
);
|
||||
|
||||
if let Err(e) = loaded_settings.on_set() {
|
||||
|
|
|
@ -7,6 +7,15 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct BatteryJson {
|
||||
pub charge_rate: Option<u64>,
|
||||
pub charge_mode: Option<String>,
|
||||
#[serde(default)]
|
||||
pub events: Vec<BatteryEventJson>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct BatteryEventJson {
|
||||
pub trigger: String,
|
||||
pub charge_rate: Option<u64>,
|
||||
pub charge_mode: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for BatteryJson {
|
||||
|
@ -14,6 +23,7 @@ impl Default for BatteryJson {
|
|||
Self {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
events: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ mod error;
|
|||
mod general;
|
||||
mod gpu;
|
||||
|
||||
pub use battery::BatteryJson;
|
||||
pub use battery::{BatteryJson, BatteryEventJson};
|
||||
pub use cpu::CpuJson;
|
||||
pub use driver::DriverJson;
|
||||
pub use general::{MinMaxJson, SettingsJson};
|
||||
|
|
19
backend/src/power_worker.rs
Normal file
19
backend/src/power_worker.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::thread::{self, JoinHandle};
|
||||
use std::time::Duration;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::api::handler::ApiMessage;
|
||||
//use crate::utility::unwrap_maybe_fatal;
|
||||
|
||||
const PERIOD: Duration = Duration::from_secs(5);
|
||||
|
||||
pub fn spawn(sender: Sender<ApiMessage>) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
log::info!("power_worker starting...");
|
||||
loop {
|
||||
sender.send(ApiMessage::PowerVibeCheck).expect("power_worker send failed");
|
||||
thread::sleep(PERIOD);
|
||||
}
|
||||
//log::warn!("resume_worker completed!");
|
||||
})
|
||||
}
|
|
@ -48,6 +48,8 @@ impl OnResume for General {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for General {}
|
||||
|
||||
impl TGeneral for General {
|
||||
fn limits(&self) -> crate::api::GeneralLimits {
|
||||
crate::api::GeneralLimits { }
|
||||
|
@ -92,11 +94,23 @@ pub struct Settings {
|
|||
|
||||
impl OnSet for Settings {
|
||||
fn on_set(&mut self) -> Result<(), Vec<SettingError>> {
|
||||
self.battery.on_set()?;
|
||||
self.cpus.on_set()?;
|
||||
self.gpu.on_set()?;
|
||||
self.general.on_set()?;
|
||||
let mut errors = Vec::new();
|
||||
|
||||
log::debug!("Applying settings for on_resume");
|
||||
self.general.on_set().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed general");
|
||||
self.battery.on_set().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed battery");
|
||||
self.cpus.on_set().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed CPUs");
|
||||
self.gpu.on_set().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed GPU");
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,14 +240,40 @@ impl Settings {
|
|||
|
||||
impl OnResume for Settings {
|
||||
fn on_resume(&self) -> Result<(), Vec<SettingError>> {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
log::debug!("Applying settings for on_resume");
|
||||
self.battery.on_resume()?;
|
||||
self.general.on_resume().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed general");
|
||||
self.battery.on_resume().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed battery");
|
||||
self.cpus.on_resume()?;
|
||||
self.cpus.on_resume().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed CPUs");
|
||||
self.gpu.on_resume()?;
|
||||
self.gpu.on_resume().unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
log::debug!("Resumed GPU");
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Settings {
|
||||
fn on_power_event(&mut self, new_mode: super::PowerMode) -> Result<(), Vec<SettingError>> {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
self.general.on_power_event(new_mode).unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
self.battery.on_power_event(new_mode).unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
self.cpus.on_power_event(new_mode).unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
self.gpu.on_power_event(new_mode).unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ impl Into<BatteryJson> for Battery {
|
|||
BatteryJson {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
events: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,12 +66,16 @@ impl OnResume for Battery {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Battery {}
|
||||
|
||||
impl TBattery for Battery {
|
||||
fn limits(&self) -> crate::api::BatteryLimits {
|
||||
crate::api::BatteryLimits {
|
||||
charge_current: None,
|
||||
charge_current_step: 50,
|
||||
charge_modes: vec![],
|
||||
charge_limit: None,
|
||||
charge_limit_step: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +131,10 @@ impl TBattery for Battery {
|
|||
None
|
||||
}
|
||||
|
||||
fn charge_limit(&mut self, _limit: Option<f64>) {}
|
||||
|
||||
fn get_charge_limit(&self) -> Option<f64> { None }
|
||||
|
||||
fn provider(&self) -> crate::persist::DriverJson {
|
||||
crate::persist::DriverJson::Generic
|
||||
}
|
||||
|
|
|
@ -138,7 +138,21 @@ impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + FromGenericCpuInfo> Cpus<C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + OnResume + OnSet> TCpus for Cpus<C> {
|
||||
impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + crate::settings::OnPowerEvent> crate::settings::OnPowerEvent for Cpus<C> {
|
||||
fn on_power_event(&mut self, new_mode: crate::settings::PowerMode) -> Result<(), Vec<SettingError>> {
|
||||
let mut errors = Vec::new();
|
||||
for cpu in &mut self.cpus {
|
||||
cpu.on_power_event(new_mode).unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: AsMut<Cpu> + AsRef<Cpu> + TCpu + OnResume + OnSet + crate::settings::OnPowerEvent> TCpus for Cpus<C> {
|
||||
fn limits(&self) -> crate::api::CpusLimits {
|
||||
crate::api::CpusLimits {
|
||||
cpus: self.cpus.iter().map(|x| x.as_ref().limits()).collect(),
|
||||
|
@ -333,6 +347,8 @@ impl OnResume for Cpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Cpu {}
|
||||
|
||||
impl TCpu for Cpu {
|
||||
fn online(&mut self) -> &mut bool {
|
||||
&mut self.online
|
||||
|
|
|
@ -80,6 +80,8 @@ impl OnResume for Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Gpu {}
|
||||
|
||||
impl TGpu for Gpu {
|
||||
fn limits(&self) -> crate::api::GpuLimits {
|
||||
crate::api::GpuLimits {
|
||||
|
|
|
@ -37,6 +37,8 @@ impl OnSet for Cpus {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Cpus {}
|
||||
|
||||
impl TCpus for Cpus {
|
||||
fn limits(&self) -> crate::api::CpusLimits {
|
||||
self.generic.limits()
|
||||
|
@ -110,6 +112,8 @@ impl OnSet for Cpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Cpu {}
|
||||
|
||||
impl TCpu for Cpu {
|
||||
fn online(&mut self) -> &mut bool {
|
||||
self.generic.online()
|
||||
|
|
|
@ -213,6 +213,8 @@ impl OnSet for Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Gpu {}
|
||||
|
||||
impl TGpu for Gpu {
|
||||
fn limits(&self) -> crate::api::GpuLimits {
|
||||
self.generic.limits()
|
||||
|
|
|
@ -17,7 +17,7 @@ pub use general::{SettingVariant, Settings, General};
|
|||
pub use min_max::{MinMax, min_max_from_json};
|
||||
|
||||
pub use error::SettingError;
|
||||
pub use traits::{OnResume, OnSet, SettingsRange, TGeneral, TGpu, TCpus, TBattery, TCpu};
|
||||
pub use traits::{OnResume, OnSet, TGeneral, TGpu, TCpus, TBattery, TCpu, OnPowerEvent, PowerMode};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::{OnResume, OnSet, SettingError};
|
||||
use crate::settings::{OnResume, OnSet, SettingError, OnPowerEvent, PowerMode};
|
||||
use crate::settings::TBattery;
|
||||
use crate::persist::BatteryJson;
|
||||
use crate::persist::{BatteryJson, BatteryEventJson};
|
||||
use super::util::ChargeMode;
|
||||
use super::oc_limits::{BatteryLimits, OverclockLimits};
|
||||
|
||||
|
@ -11,11 +11,155 @@ use super::oc_limits::{BatteryLimits, OverclockLimits};
|
|||
pub struct Battery {
|
||||
pub charge_rate: Option<u64>,
|
||||
pub charge_mode: Option<ChargeMode>,
|
||||
events: Vec<EventInstruction>,
|
||||
limits: BatteryLimits,
|
||||
state: crate::state::steam_deck::Battery,
|
||||
driver_mode: crate::persist::DriverJson,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum EventTrigger {
|
||||
PluggedIn,
|
||||
PluggedOut,
|
||||
BatteryAbove(f64),
|
||||
BatteryBelow(f64),
|
||||
Ignored,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct EventInstruction {
|
||||
trigger: EventTrigger,
|
||||
charge_rate: Option<u64>,
|
||||
charge_mode: Option<ChargeMode>,
|
||||
is_triggered: bool,
|
||||
}
|
||||
|
||||
impl OnPowerEvent for EventInstruction {
|
||||
fn on_power_event(&mut self, new_mode: PowerMode) -> Result<(), Vec<SettingError>> {
|
||||
match (&self.trigger, new_mode) {
|
||||
(EventTrigger::PluggedIn, PowerMode::PluggedIn) => {
|
||||
log::info!("Steam Deck plugged in event handled");
|
||||
self.set_all()
|
||||
},
|
||||
(EventTrigger::PluggedOut, PowerMode::PluggedOut) => {
|
||||
log::info!("Steam Deck plugged out event handled");
|
||||
self.set_all()
|
||||
},
|
||||
(EventTrigger::BatteryAbove(exp), PowerMode::BatteryCharge(act)) => {
|
||||
if act > *exp {
|
||||
if self.is_triggered {
|
||||
Ok(())
|
||||
} else {
|
||||
self.is_triggered = true;
|
||||
log::info!("Steam Deck battery above {} event handled", exp);
|
||||
self.set_all()
|
||||
}
|
||||
} else {
|
||||
self.is_triggered = false;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
(EventTrigger::BatteryBelow(exp), PowerMode::BatteryCharge(act)) => {
|
||||
if act < *exp {
|
||||
if self.is_triggered {
|
||||
Ok(())
|
||||
} else {
|
||||
self.is_triggered = true;
|
||||
log::info!("Steam Deck battery below {} event handled", exp);
|
||||
self.set_all()
|
||||
}
|
||||
} else {
|
||||
self.is_triggered = false;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventInstruction {
|
||||
#[inline]
|
||||
fn trigger_to_str(mode: EventTrigger) -> String {
|
||||
match mode {
|
||||
EventTrigger::PluggedIn => "plug-in".to_owned(),
|
||||
EventTrigger::PluggedOut => "plug-out".to_owned(),
|
||||
EventTrigger::BatteryAbove(x) => format!(">{:#0.2}", x * 100.0),
|
||||
EventTrigger::BatteryBelow(x) => format!("<{:#0.2}", x * 100.0),
|
||||
EventTrigger::Ignored => "/shrug".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn str_to_trigger(s: &str) -> Option<EventTrigger> {
|
||||
match s {
|
||||
"normal" => Some(EventTrigger::PluggedIn),
|
||||
"idle" => Some(EventTrigger::PluggedOut),
|
||||
s if s.starts_with('>') => s.trim_start_matches('>').parse::<f64>().ok().map(|x| EventTrigger::BatteryAbove(x)),
|
||||
s if s.starts_with('<') => s.trim_start_matches('<').parse::<f64>().ok().map(|x| EventTrigger::BatteryBelow(x)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_json(other: BatteryEventJson, _version: u64) -> Self {
|
||||
Self {
|
||||
trigger: Self::str_to_trigger(&other.trigger).unwrap_or(EventTrigger::Ignored),
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Battery::str_to_charge_mode(&x)).flatten(),
|
||||
is_triggered: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_charge_mode(&self) -> Result<(), SettingError> {
|
||||
if let Some(charge_mode) = self.charge_mode {
|
||||
super::util::set(super::util::Setting::ChargeMode, charge_mode as _).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to set charge mode: {}", e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
).map(|_| ())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn set_charge_rate(&self) -> Result<(), SettingError> {
|
||||
if let Some(charge_rate) = self.charge_rate {
|
||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, charge_rate).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
).map(|_| ())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&self) -> Result<(), Vec<SettingError>> {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
self.set_charge_rate().unwrap_or_else(|e| errors.push(e));
|
||||
self.set_charge_mode().unwrap_or_else(|e| errors.push(e));
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BatteryEventJson> for EventInstruction {
|
||||
fn into(self) -> BatteryEventJson {
|
||||
BatteryEventJson {
|
||||
trigger: Self::trigger_to_str(self.trigger),
|
||||
charge_rate: self.charge_rate,
|
||||
charge_mode: self.charge_mode.map(|c| Battery::charge_mode_to_str(c)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const BATTERY_VOLTAGE: f64 = 7.7;
|
||||
|
||||
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
||||
|
@ -23,6 +167,7 @@ const BATTERY_CURRENT_NOW_PATH: &str = "/sys/class/power_supply/BAT1/current_now
|
|||
const BATTERY_CHARGE_NOW_PATH: &str = "/sys/class/power_supply/BAT1/charge_now"; // read-only
|
||||
const BATTERY_CHARGE_FULL_PATH: &str = "/sys/class/power_supply/BAT1/charge_full"; // read-only
|
||||
const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/power_supply/BAT1/charge_full_design"; // read-only
|
||||
const USB_PD_IN_MVOLTAGE_PATH: &str = "/sys/class/hwmon/hwmon5/in0_input"; // read-only
|
||||
|
||||
impl Battery {
|
||||
#[inline]
|
||||
|
@ -34,6 +179,7 @@ impl Battery {
|
|||
0 => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
|
||||
events: other.events.into_iter().map(|x| EventInstruction::from_json(x, version)).collect(),
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
driver_mode: driver,
|
||||
|
@ -41,6 +187,7 @@ impl Battery {
|
|||
_ => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
|
||||
events: other.events.into_iter().map(|x| EventInstruction::from_json(x, version)).collect(),
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
driver_mode: driver,
|
||||
|
@ -62,11 +209,33 @@ impl Battery {
|
|||
match s {
|
||||
"normal" => Some(ChargeMode::Normal),
|
||||
"idle" => Some(ChargeMode::Idle),
|
||||
"discharge" | "disacharge" => Some(ChargeMode::Discharge),
|
||||
"discharge" => Some(ChargeMode::Discharge),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_charge_mode(&mut self) -> Result<(), SettingError> {
|
||||
if let Some(charge_mode) = self.charge_mode {
|
||||
self.state.charge_mode_set = true;
|
||||
super::util::set(super::util::Setting::ChargeMode, charge_mode as _).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to set charge mode: {}", e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
).map(|_| ())
|
||||
} else if self.state.charge_mode_set {
|
||||
self.state.charge_mode_set = false;
|
||||
super::util::set(super::util::Setting::ChargeMode, ChargeMode::Normal as _).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to set charge mode: {}", e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
).map(|_| ())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&mut self) -> Result<(), Vec<SettingError>> {
|
||||
let mut errors = Vec::new();
|
||||
if let Some(charge_rate) = self.charge_rate {
|
||||
|
@ -86,23 +255,7 @@ impl Battery {
|
|||
},
|
||||
).unwrap_or_else(|e| errors.push(e));
|
||||
}
|
||||
if let Some(charge_mode) = self.charge_mode {
|
||||
self.state.charge_mode_set = true;
|
||||
super::util::set(super::util::Setting::ChargeMode, charge_mode as _).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to set charge mode: {}", e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
).unwrap_or_else(|e| {errors.push(e); 0});
|
||||
} else if self.state.charge_mode_set {
|
||||
self.state.charge_mode_set = false;
|
||||
super::util::set(super::util::Setting::ChargeMode, ChargeMode::Normal as _).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to set charge mode: {}", e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
).unwrap_or_else(|e| {errors.push(e); 0});
|
||||
}
|
||||
self.set_charge_mode().unwrap_or_else(|e| errors.push(e));
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -161,6 +314,17 @@ impl Battery {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_usb_voltage() -> Result<f64, SettingError> {
|
||||
match usdpl_back::api::files::read_single::<_, u64, _>(USB_PD_IN_MVOLTAGE_PATH) {
|
||||
Err(e) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", USB_PD_IN_MVOLTAGE_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
// convert to V (from mV)
|
||||
Ok(val) => Ok((val as f64)/1000.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
let (oc_limits, is_default) = OverclockLimits::load_or_default();
|
||||
let oc_limits = oc_limits.battery;
|
||||
|
@ -168,11 +332,40 @@ impl Battery {
|
|||
Self {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
events: Vec::new(),
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
driver_mode: driver,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_limit_event(&self) -> Option<usize> {
|
||||
for (i, event) in self.events.iter().enumerate() {
|
||||
match event.trigger {
|
||||
EventTrigger::BatteryAbove(_) => {
|
||||
if event.charge_mode.is_some() {
|
||||
return Some(i);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_unlimit_event(&self) -> Option<usize> {
|
||||
for (i, event) in self.events.iter().enumerate() {
|
||||
match event.trigger {
|
||||
EventTrigger::BatteryBelow(_) => {
|
||||
if event.charge_mode.is_some() {
|
||||
return Some(i);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BatteryJson> for Battery {
|
||||
|
@ -181,6 +374,7 @@ impl Into<BatteryJson> for Battery {
|
|||
BatteryJson {
|
||||
charge_rate: self.charge_rate,
|
||||
charge_mode: self.charge_mode.map(Self::charge_mode_to_str),
|
||||
events: self.events.into_iter().map(|x| x.into()).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,6 +392,35 @@ impl OnResume for Battery {
|
|||
}
|
||||
}
|
||||
|
||||
impl OnPowerEvent for Battery {
|
||||
fn on_power_event(&mut self, new_mode: PowerMode) -> Result<(), Vec<SettingError>> {
|
||||
let mut errors = Vec::new();
|
||||
match new_mode {
|
||||
PowerMode::PluggedIn => {
|
||||
// plug event resets battery settings
|
||||
self.events.iter_mut().for_each(|ev| ev.is_triggered = false);
|
||||
self.set_charge_mode()
|
||||
.map_err(|e| vec![e])
|
||||
},
|
||||
PowerMode::PluggedOut => {
|
||||
// plug event resets battery settings
|
||||
self.events.iter_mut().for_each(|ev| ev.is_triggered = false);
|
||||
self.set_charge_mode()
|
||||
.map_err(|e| vec![e])
|
||||
},
|
||||
PowerMode::BatteryCharge(_) => Ok(())
|
||||
}.unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
for ev in &mut self.events {
|
||||
ev.on_power_event(new_mode).unwrap_or_else(|mut e| errors.append(&mut e));
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TBattery for Battery {
|
||||
fn limits(&self) -> crate::api::BatteryLimits {
|
||||
crate::api::BatteryLimits {
|
||||
|
@ -207,6 +430,11 @@ impl TBattery for Battery {
|
|||
}),
|
||||
charge_current_step: 50,
|
||||
charge_modes: vec!["normal".to_owned(), "discharge".to_owned(), "idle".to_owned()],
|
||||
charge_limit: Some(RangeLimit {
|
||||
min: 10.0,
|
||||
max: 90.0,
|
||||
}),
|
||||
charge_limit_step: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,6 +498,107 @@ impl TBattery for Battery {
|
|||
}
|
||||
}
|
||||
|
||||
fn charge_limit(&mut self, limit: Option<f64>) {
|
||||
// upper limit
|
||||
let index = self.find_limit_event();
|
||||
if let Some(index) = index {
|
||||
if let Some(limit) = limit {
|
||||
log::info!("Updating Steam Deck charge limit event instruction to >{}", limit);
|
||||
self.events[index] = EventInstruction {
|
||||
trigger: EventTrigger::BatteryAbove(limit/100.0),
|
||||
charge_rate: None,
|
||||
charge_mode: Some(ChargeMode::Idle),
|
||||
is_triggered: false,
|
||||
};
|
||||
} else {
|
||||
self.events.remove(index);
|
||||
}
|
||||
} else if let Some(limit) = limit {
|
||||
log::info!("Creating Steam Deck charge limit event instruction of >{}", limit);
|
||||
self.events.push(
|
||||
EventInstruction {
|
||||
trigger: EventTrigger::BatteryAbove(limit/100.0),
|
||||
charge_rate: None,
|
||||
charge_mode: Some(ChargeMode::Idle),
|
||||
is_triggered: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
// lower limit
|
||||
let index = self.find_unlimit_event();
|
||||
if let Some(index) = index {
|
||||
if let Some(limit) = limit {
|
||||
let limit = (limit - 10.0).clamp(0.0, 100.0);
|
||||
log::info!("Updating Steam Deck charge limit event instruction to <{}", limit);
|
||||
self.events[index] = EventInstruction {
|
||||
trigger: EventTrigger::BatteryBelow(limit/100.0),
|
||||
charge_rate: None,
|
||||
charge_mode: Some(ChargeMode::Normal),
|
||||
is_triggered: false,
|
||||
};
|
||||
} else {
|
||||
self.events.remove(index);
|
||||
}
|
||||
} else if let Some(limit) = limit {
|
||||
let limit = (limit - 10.0).clamp(0.0, 100.0);
|
||||
log::info!("Creating Steam Deck charge limit event instruction of <{}", limit);
|
||||
self.events.push(
|
||||
EventInstruction {
|
||||
trigger: EventTrigger::BatteryBelow(limit/100.0),
|
||||
charge_rate: None,
|
||||
charge_mode: Some(ChargeMode::Normal),
|
||||
is_triggered: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_charge_limit(&self) -> Option<f64> {
|
||||
let index = self.find_limit_event();
|
||||
if let Some(index) = index {
|
||||
if let EventTrigger::BatteryAbove(limit) = self.events[index].trigger {
|
||||
Some(limit * 100.0)
|
||||
} else {
|
||||
log::error!("Got index {} for battery charge limit which does not have expected event trigger: {:?}", index, &self.events);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_power(&mut self) -> Result<Vec<PowerMode>, Vec<SettingError>> {
|
||||
log::debug!("Steam Deck power vibe check");
|
||||
let mut errors = Vec::new();
|
||||
let mut events = Vec::new();
|
||||
match (Self::read_charge_full(), Self::read_charge_now()) {
|
||||
(Ok(full), Ok(now)) => events.push(PowerMode::BatteryCharge(now/full)),
|
||||
(Err(e1), Err(e2)) => {
|
||||
errors.push(e1);
|
||||
errors.push(e2);
|
||||
},
|
||||
(Err(e), _) => errors.push(e),
|
||||
(_, Err(e)) => errors.push(e),
|
||||
}
|
||||
match Self::read_usb_voltage() {
|
||||
Ok(voltage) => {
|
||||
if voltage > 0.0 && self.state.charger_state != crate::state::steam_deck::ChargeState::PluggedIn {
|
||||
events.push(PowerMode::PluggedIn);
|
||||
self.state.charger_state = crate::state::steam_deck::ChargeState::PluggedIn;
|
||||
} else if voltage == 0.0 && self.state.charger_state != crate::state::steam_deck::ChargeState::Unplugged {
|
||||
events.push(PowerMode::PluggedOut);
|
||||
self.state.charger_state = crate::state::steam_deck::ChargeState::Unplugged;
|
||||
}
|
||||
},
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(events)
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
|
||||
fn provider(&self) -> crate::persist::DriverJson {
|
||||
self.driver_mode.clone()
|
||||
}
|
||||
|
|
|
@ -158,6 +158,8 @@ impl Cpus {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Cpus {}
|
||||
|
||||
impl TCpus for Cpus {
|
||||
fn limits(&self) -> crate::api::CpusLimits {
|
||||
crate::api::CpusLimits {
|
||||
|
|
|
@ -249,6 +249,8 @@ impl OnResume for Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Gpu {}
|
||||
|
||||
impl TGpu for Gpu {
|
||||
fn limits(&self) -> crate::api::GpuLimits {
|
||||
crate::api::GpuLimits {
|
||||
|
|
|
@ -10,12 +10,37 @@ pub trait OnResume {
|
|||
fn on_resume(&self) -> Result<(), Vec<SettingError>>;
|
||||
}
|
||||
|
||||
pub trait SettingsRange {
|
||||
fn max() -> Self;
|
||||
fn min() -> Self;
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PowerMode {
|
||||
PluggedIn,
|
||||
PluggedOut, // unplugged
|
||||
BatteryCharge(f64), // battery fill amount: 0 = empty, 1 = full
|
||||
}
|
||||
|
||||
pub trait TGpu: OnResume + OnSet + Debug + Send {
|
||||
pub trait OnPowerEvent {
|
||||
fn on_plugged_in(&mut self) -> Result<(), Vec<SettingError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_plugged_out(&mut self) -> Result<(), Vec<SettingError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_charge_amount(&mut self, _amount: f64) -> Result<(), Vec<SettingError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_power_event(&mut self, new_mode: PowerMode) -> Result<(), Vec<SettingError>> {
|
||||
match new_mode {
|
||||
PowerMode::PluggedIn => self.on_plugged_in(),
|
||||
PowerMode::PluggedOut => self.on_plugged_out(),
|
||||
PowerMode::BatteryCharge(now) => self.on_charge_amount(now),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TGpu: OnSet + OnResume + OnPowerEvent + Debug + Send {
|
||||
fn limits(&self) -> crate::api::GpuLimits;
|
||||
|
||||
fn json(&self) -> crate::persist::GpuJson;
|
||||
|
@ -35,7 +60,7 @@ pub trait TGpu: OnResume + OnSet + Debug + Send {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait TCpus: OnResume + OnSet + Debug + Send {
|
||||
pub trait TCpus: OnSet + OnResume + OnPowerEvent + Debug + Send {
|
||||
fn limits(&self) -> crate::api::CpusLimits;
|
||||
|
||||
fn json(&self) -> Vec<crate::persist::CpuJson>;
|
||||
|
@ -63,7 +88,7 @@ pub trait TCpu: Debug + Send {
|
|||
fn get_clock_limits(&self) -> Option<&MinMax<u64>>;
|
||||
}
|
||||
|
||||
pub trait TGeneral: OnResume + OnSet + Debug + Send {
|
||||
pub trait TGeneral: OnSet + OnResume + OnPowerEvent + Debug + Send {
|
||||
fn limits(&self) -> crate::api::GeneralLimits;
|
||||
|
||||
fn get_persistent(&self) -> bool;
|
||||
|
@ -81,7 +106,7 @@ pub trait TGeneral: OnResume + OnSet + Debug + Send {
|
|||
fn provider(&self) -> crate::persist::DriverJson;
|
||||
}
|
||||
|
||||
pub trait TBattery: OnResume + OnSet + Debug + Send {
|
||||
pub trait TBattery: OnSet + OnResume + OnPowerEvent + Debug + Send {
|
||||
fn limits(&self) -> crate::api::BatteryLimits;
|
||||
|
||||
fn json(&self) -> crate::persist::BatteryJson;
|
||||
|
@ -102,6 +127,19 @@ pub trait TBattery: OnResume + OnSet + Debug + Send {
|
|||
|
||||
fn read_current_now(&self) -> Option<f64>;
|
||||
|
||||
fn charge_limit(&mut self, limit: Option<f64>);
|
||||
|
||||
fn get_charge_limit(&self) -> Option<f64>;
|
||||
|
||||
fn check_power(&mut self) -> Result<Vec<PowerMode>, Vec<SettingError>> {
|
||||
log::warn!("Power event check using default trait implementation");
|
||||
let mut events = Vec::new();
|
||||
if let (Some(full), Some(now)) = (self.read_charge_full(), self.read_charge_now()) {
|
||||
events.push(PowerMode::BatteryCharge(now/full));
|
||||
}
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
fn provider(&self) -> crate::persist::DriverJson {
|
||||
crate::persist::DriverJson::AutoDetect
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ impl Into<BatteryJson> for Battery {
|
|||
BatteryJson {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
events: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +30,16 @@ impl OnResume for Battery {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Battery {}
|
||||
|
||||
impl TBattery for Battery {
|
||||
fn limits(&self) -> crate::api::BatteryLimits {
|
||||
crate::api::BatteryLimits {
|
||||
charge_current: None,
|
||||
charge_current_step: 50,
|
||||
charge_modes: vec![],
|
||||
charge_limit: None,
|
||||
charge_limit_step: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +69,10 @@ impl TBattery for Battery {
|
|||
|
||||
fn read_current_now(&self) -> Option<f64> { None }
|
||||
|
||||
fn charge_limit(&mut self, _limit: Option<f64>) {}
|
||||
|
||||
fn get_charge_limit(&self) -> Option<f64> { None }
|
||||
|
||||
fn provider(&self) -> crate::persist::DriverJson {
|
||||
crate::persist::DriverJson::Unknown
|
||||
}
|
||||
|
|
|
@ -68,6 +68,8 @@ impl OnResume for Cpus {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Cpus {}
|
||||
|
||||
impl Cpus {
|
||||
pub fn cpu_count() -> Option<usize> {
|
||||
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
|
||||
|
|
|
@ -49,6 +49,8 @@ impl OnResume for Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::settings::OnPowerEvent for Gpu {}
|
||||
|
||||
impl TGpu for Gpu {
|
||||
fn limits(&self) -> crate::api::GpuLimits {
|
||||
crate::api::GpuLimits {
|
||||
|
|
|
@ -2,13 +2,22 @@
|
|||
pub struct Battery {
|
||||
pub charge_rate_set: bool,
|
||||
pub charge_mode_set: bool,
|
||||
pub charger_state: ChargeState,
|
||||
}
|
||||
|
||||
impl std::default::Default for Battery {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
charge_rate_set: false,
|
||||
charge_mode_set: false,
|
||||
charge_rate_set: true,
|
||||
charge_mode_set: true,
|
||||
charger_state: ChargeState::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ChargeState {
|
||||
PluggedIn,
|
||||
Unplugged,
|
||||
Unknown,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@ mod battery;
|
|||
mod cpu;
|
||||
mod gpu;
|
||||
|
||||
pub use battery::Battery;
|
||||
pub use battery::{Battery, ChargeState};
|
||||
pub use cpu::Cpu;
|
||||
pub use gpu::Gpu;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "PowerTools",
|
||||
"version": "1.3.0-alpha",
|
||||
"version": "1.3.0-beta1",
|
||||
"description": "Power tweaks for power users",
|
||||
"scripts": {
|
||||
"build": "shx rm -rf dist && rollup -c",
|
||||
|
|
|
@ -63,6 +63,8 @@ export type BatteryLimits = {
|
|||
charge_current: RangeLimit | null;
|
||||
charge_current_step: number;
|
||||
charge_modes: string[];
|
||||
charge_limit: RangeLimit | null;
|
||||
charge_limit_step: number;
|
||||
};
|
||||
|
||||
export type CpuLimits = {
|
||||
|
@ -139,6 +141,18 @@ export async function unsetBatteryChargeMode(): Promise<any[]> {
|
|||
return await call_backend("BATTERY_unset_charge_mode", []);
|
||||
}
|
||||
|
||||
export async function getBatteryChargeLimit(): Promise<number | null> {
|
||||
return (await call_backend("BATTERY_get_charge_limit", []))[0];
|
||||
}
|
||||
|
||||
export async function setBatteryChargeLimit(val: number): Promise<number> {
|
||||
return (await call_backend("BATTERY_set_charge_limit", [val]))[0];
|
||||
}
|
||||
|
||||
export async function unsetBatteryChargeLimit(): Promise<any[]> {
|
||||
return await call_backend("BATTERY_unset_charge_limit", []);
|
||||
}
|
||||
|
||||
// CPU
|
||||
|
||||
export async function setCpuSmt(status: boolean): Promise<boolean[]> {
|
||||
|
@ -229,8 +243,8 @@ export async function getGeneralPersistent(): Promise<boolean> {
|
|||
return (await call_backend("GENERAL_get_persistent", []))[0];
|
||||
}
|
||||
|
||||
export async function loadGeneralSettings(path: string, name: string): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_load_settings", [path, name]))[0];
|
||||
export async function loadGeneralSettings(id: number, name: string): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_load_settings", [id, name]))[0];
|
||||
}
|
||||
|
||||
export async function loadGeneralDefaultSettings(): Promise<boolean> {
|
||||
|
@ -280,3 +294,11 @@ export async function idk(): Promise<boolean> {
|
|||
export async function forceApplySettings(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_apply_now", []))[0];
|
||||
}
|
||||
|
||||
export async function onPluggedIn(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_on_pluggedin", []))[0];
|
||||
}
|
||||
|
||||
export async function onUnplugged(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_on_unplugged", []))[0];
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
CHARGE_RATE_BATT,
|
||||
CHARGE_MODE_BATT,
|
||||
CURRENT_BATT,
|
||||
CHARGE_LIMIT_BATT,
|
||||
} from "../consts";
|
||||
import { set_value, get_value} from "usdpl-front";
|
||||
|
||||
|
@ -53,6 +54,12 @@ export class Battery extends Component<backend.IdcProps> {
|
|||
{get_value(CHARGE_FULL_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_FULL_BATT) / get_value(CHARGE_DESIGN_BATT)).toFixed(1)}%)
|
||||
</Field>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
<Field
|
||||
label={tr("Current")}>
|
||||
{get_value(CURRENT_BATT)} mA
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current != null && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(CHARGE_RATE_BATT) != null}
|
||||
|
@ -60,7 +67,7 @@ export class Battery extends Component<backend.IdcProps> {
|
|||
description={tr("Control battery charge rate when awake")}
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CHARGE_RATE_BATT, 2500);
|
||||
set_value(CHARGE_RATE_BATT, (get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.max);
|
||||
reloadGUI("BATTChargeRateToggle");
|
||||
} else {
|
||||
set_value(CHARGE_RATE_BATT, null);
|
||||
|
@ -128,12 +135,44 @@ export class Battery extends Component<backend.IdcProps> {
|
|||
/>
|
||||
</Field>}
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
<Field
|
||||
label={tr("Current")}>
|
||||
{get_value(CURRENT_BATT)} mA
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_limit != null && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(CHARGE_LIMIT_BATT) != null}
|
||||
label={tr("Charge Limit")}
|
||||
description={tr("Limit battery charge when awake")}
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CHARGE_LIMIT_BATT, (get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_limit!.max);
|
||||
reloadGUI("BATTChargeLimitToggle");
|
||||
} else {
|
||||
set_value(CHARGE_LIMIT_BATT, null);
|
||||
backend.resolve(backend.unsetBatteryChargeLimit(), (_: any[]) => {
|
||||
reloadGUI("BATTUnsetChargeRate");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{ get_value(CHARGE_LIMIT_BATT) != null && <SliderField
|
||||
label={tr("Maximum (%)")}
|
||||
value={get_value(CHARGE_LIMIT_BATT)}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_limit!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_limit!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_limit_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CHARGE_LIMIT_BATT) == null}
|
||||
onChange={(val: number) => {
|
||||
backend.log(backend.LogLevel.Debug, "Charge limit is now " + val.toString());
|
||||
const rateNow = get_value(CHARGE_LIMIT_BATT);
|
||||
if (val != rateNow) {
|
||||
backend.resolve(backend.setBatteryChargeLimit(val),
|
||||
(rate: number) => {
|
||||
set_value(CHARGE_LIMIT_BATT, rate);
|
||||
reloadGUI("BATTChargeLimit");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>}
|
||||
</Fragment>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export const LIMITS_INFO = "LIMITS_all";
|
|||
export const CURRENT_BATT = "BATTERY_current_now";
|
||||
export const CHARGE_RATE_BATT = "BATTERY_charge_rate";
|
||||
export const CHARGE_MODE_BATT = "BATTERY_charge_mode";
|
||||
export const CHARGE_LIMIT_BATT = "BATTERY_charge_limit";
|
||||
export const CHARGE_NOW_BATT = "BATTERY_charge_now";
|
||||
export const CHARGE_FULL_BATT = "BATTERY_charge_full";
|
||||
export const CHARGE_DESIGN_BATT = "BATTERY_charge_design";
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
CURRENT_BATT,
|
||||
CHARGE_RATE_BATT,
|
||||
CHARGE_MODE_BATT,
|
||||
CHARGE_LIMIT_BATT,
|
||||
CHARGE_NOW_BATT,
|
||||
CHARGE_FULL_BATT,
|
||||
CHARGE_DESIGN_BATT,
|
||||
|
@ -108,6 +109,7 @@ const reload = function() {
|
|||
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
|
||||
backend.resolve_nullable(backend.getBatteryChargeRate(), (rate: number | null) => { set_value(CHARGE_RATE_BATT, rate) });
|
||||
backend.resolve_nullable(backend.getBatteryChargeMode(), (mode: string | null) => { set_value(CHARGE_MODE_BATT, mode) });
|
||||
backend.resolve_nullable(backend.getBatteryChargeLimit(), (limit: number | null) => { set_value(CHARGE_LIMIT_BATT, limit) });
|
||||
backend.resolve(backend.getBatteryChargeNow(), (rate: number) => { set_value(CHARGE_NOW_BATT, rate) });
|
||||
backend.resolve(backend.getBatteryChargeFull(), (rate: number) => { set_value(CHARGE_FULL_BATT, rate) });
|
||||
backend.resolve(backend.getBatteryChargeDesign(), (rate: number) => { set_value(CHARGE_DESIGN_BATT, rate) });
|
||||
|
@ -175,7 +177,7 @@ const reload = function() {
|
|||
let gameInfo: any = appStore.GetAppOverviewByGameID(id);
|
||||
// don't use gameInfo.appid, haha
|
||||
backend.resolve(
|
||||
backend.loadGeneralSettings(id.toString() + ".json", gameInfo.display_name),
|
||||
backend.loadGeneralSettings(id, gameInfo.display_name),
|
||||
(ok: boolean) => {backend.log(backend.LogLevel.Debug, "Loading settings ok? " + ok)}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -83,7 +83,7 @@ msgstr "Maximum (mA)"
|
|||
# (Battery charge mode override toggle)
|
||||
#: components/battery.tsx:97,115
|
||||
msgid "Charge Mode"
|
||||
msgstr "Mode de Charge"
|
||||
msgstr "Mode de charge"
|
||||
|
||||
# (Battery charge mode override toggle description)
|
||||
#: components/battery.tsx:98
|
||||
|
@ -100,6 +100,21 @@ msgstr "Mode"
|
|||
msgid "Current"
|
||||
msgstr "Courant"
|
||||
|
||||
#: components/battery.tsx:141
|
||||
# (Battery charging maximum)
|
||||
msgid "Charge Limit"
|
||||
msgstr "Limite de charge"
|
||||
|
||||
#: components/battery.tsx:142
|
||||
# (Battery charging maximum description)
|
||||
msgid "Limit battery charge when awake"
|
||||
msgstr "Limiter la charge de la batterie quand éveillé"
|
||||
|
||||
#: components/battery.tsx:156
|
||||
# (Battery charging maximum slider)
|
||||
msgid "Maximum (%)"
|
||||
msgstr "Maximum (%)"
|
||||
|
||||
# -- components/cpus.tsx --
|
||||
# (CPU section title)
|
||||
#: components/cpus.tsx:64
|
||||
|
|
|
@ -99,6 +99,21 @@ msgstr ""
|
|||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
#: components/battery.tsx:141
|
||||
# (Battery charging maximum)
|
||||
msgid "Charge Limit"
|
||||
msgstr ""
|
||||
|
||||
#: components/battery.tsx:142
|
||||
# (Battery charging maximum description)
|
||||
msgid "Limit battery charge when awake"
|
||||
msgstr ""
|
||||
|
||||
#: components/battery.tsx:156
|
||||
# (Battery charging maximum slider)
|
||||
msgid "Maximum (%)"
|
||||
msgstr ""
|
||||
|
||||
# -- components/cpus.tsx --
|
||||
|
||||
#: components/cpus.tsx:64
|
||||
|
|
Loading…
Reference in a new issue