forked from NG-SD-Plugins/PowerTools
Add battery charge mode back-end support (untested)
This commit is contained in:
parent
31c72de11e
commit
1466c4647b
18 changed files with 319 additions and 244 deletions
|
@ -16,8 +16,9 @@ pub struct SettingsLimits {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BatteryLimits {
|
||||
pub charge_rate: Option<RangeLimit<u64>>,
|
||||
pub charge_step: u64,
|
||||
pub charge_current: Option<RangeLimit<u64>>,
|
||||
pub charge_current_step: u64,
|
||||
pub charge_modes: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
|
|
@ -71,3 +71,51 @@ pub fn unset_charge_rate(
|
|||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate set battery charge mode web method
|
||||
pub fn set_charge_mode(
|
||||
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 |mode: String|
|
||||
sender.lock()
|
||||
.unwrap()
|
||||
.send(ApiMessage::Battery(BatteryMessage::SetChargeMode(Some(mode))))
|
||||
.expect("set_charge_mode send failed");
|
||||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::String(new_val)) = params_in.get(0) {
|
||||
setter(new_val.to_owned());
|
||||
vec![new_val.to_owned().into()]
|
||||
} else {
|
||||
vec!["set_charge_rate missing parameter".into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate get battery charge mode web method
|
||||
pub fn get_charge_mode(
|
||||
sender: Sender<ApiMessage>,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety
|
||||
let getter = move || {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let callback = move |mode: Option<String>| tx.send(mode).expect("get_charge_mode callback send failed");
|
||||
sender.lock().unwrap().send(ApiMessage::Battery(BatteryMessage::GetChargeMode(Box::new(callback)))).expect("get_charge_mode send failed");
|
||||
rx.recv().expect("get_charge_mode callback recv failed")
|
||||
};
|
||||
move |_: super::ApiParameterType| {
|
||||
vec![getter().map(|x| x.into()).unwrap_or(Primitive::Empty)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate unset battery charge mode web method
|
||||
pub fn unset_charge_mode(
|
||||
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 || sender.lock().unwrap().send(ApiMessage::Battery(BatteryMessage::SetChargeMode(None))).expect("unset_charge_mode send failed");
|
||||
move |_params_in: super::ApiParameterType| {
|
||||
setter();
|
||||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,3 +177,12 @@ pub fn get_limits(
|
|||
vec![Primitive::Json(serde_json::to_string(&getter()).unwrap())]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gunter(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||
std::thread::spawn(|| {
|
||||
log::info!("Zhu Li, do the thing!");
|
||||
crate::settings::driver::maybe_do_button();
|
||||
log::info!("Thing done.")
|
||||
});
|
||||
vec![true.into()]
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ pub enum ApiMessage {
|
|||
pub enum BatteryMessage {
|
||||
SetChargeRate(Option<u64>),
|
||||
GetChargeRate(Callback<Option<u64>>),
|
||||
SetChargeMode(Option<String>),
|
||||
GetChargeMode(Callback<Option<String>>),
|
||||
}
|
||||
|
||||
impl BatteryMessage {
|
||||
|
@ -29,6 +31,8 @@ impl BatteryMessage {
|
|||
match self {
|
||||
Self::SetChargeRate(rate) => settings.charge_rate(rate),
|
||||
Self::GetChargeRate(cb) => cb(settings.get_charge_rate()),
|
||||
Self::SetChargeMode(mode) => settings.charge_mode(mode),
|
||||
Self::GetChargeMode(cb) => cb(settings.get_charge_mode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,18 @@ fn main() -> Result<(), ()> {
|
|||
"BATTERY_unset_charge_rate",
|
||||
api::battery::unset_charge_rate(api_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_set_charge_mode",
|
||||
api::battery::set_charge_mode(api_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_get_charge_mode",
|
||||
api::battery::get_charge_mode(api_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_unset_charge_mode",
|
||||
api::battery::unset_charge_mode(api_sender.clone()),
|
||||
)
|
||||
// cpu API functions
|
||||
.register("CPU_count", api::cpu::max_cpus)
|
||||
.register(
|
||||
|
@ -186,10 +198,11 @@ fn main() -> Result<(), ()> {
|
|||
"GENERAL_wait_for_unlocks",
|
||||
api::general::lock_unlock_all(api_sender.clone())
|
||||
)
|
||||
.register(
|
||||
.register_blocking(
|
||||
"GENERAL_get_limits",
|
||||
api::general::get_limits(api_sender.clone())
|
||||
);
|
||||
)
|
||||
.register("GENERAL_idk", api::general::gunter);
|
||||
|
||||
api_worker::spawn(loaded_settings, api_handler);
|
||||
|
||||
|
|
|
@ -6,10 +6,14 @@ use serde::{Deserialize, Serialize};
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BatteryJson {
|
||||
pub charge_rate: Option<u64>,
|
||||
pub charge_mode: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for BatteryJson {
|
||||
fn default() -> Self {
|
||||
Self { charge_rate: None }
|
||||
Self {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ fn auto_detect() -> DriverJson {
|
|||
log::debug!("Read from /etc/os-release:\n{}", os_info);
|
||||
if let Some(_) = lscpu.find("model name\t: AMD Custom APU 0405\n") {
|
||||
// definitely a Steam Deck, check if it's overclocked
|
||||
// TODO: this auto-detect doesn't work
|
||||
// look for a file instead?
|
||||
let max_freq: u64 = match usdpl_back::api::files::read_single("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") {
|
||||
Ok(u) => u,
|
||||
Err(_) => return DriverJson::SteamDeck,
|
||||
|
@ -84,7 +86,7 @@ impl Driver {
|
|||
}),
|
||||
cpus: Box::new(super::steam_deck_adv::Cpus::from_json(settings.cpus, settings.version)),
|
||||
gpu: Box::new(super::steam_deck_adv::Gpu::from_json(settings.gpu, settings.version)),
|
||||
battery: Box::new(super::steam_deck_adv::Battery::from_json(settings.battery, settings.version)),
|
||||
battery: Box::new(super::steam_deck::Battery::from_json(settings.battery, settings.version)),
|
||||
}),
|
||||
DriverJson::Unknown => Ok(Self {
|
||||
general: Box::new(General {
|
||||
|
@ -123,7 +125,7 @@ impl Driver {
|
|||
}),
|
||||
cpus: Box::new(super::steam_deck_adv::Cpus::system_default()),
|
||||
gpu: Box::new(super::steam_deck_adv::Gpu::system_default()),
|
||||
battery: Box::new(super::steam_deck_adv::Battery::system_default()),
|
||||
battery: Box::new(super::steam_deck::Battery::system_default()),
|
||||
},
|
||||
DriverJson::Unknown => Self {
|
||||
general: Box::new(General {
|
||||
|
@ -146,7 +148,7 @@ impl Driver {
|
|||
pub fn read_current_now() -> Result<Option<u64>, SettingError> {
|
||||
match auto_detect() {
|
||||
DriverJson::SteamDeck => super::steam_deck::Battery::read_current_now().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_current_now().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck::Battery::read_current_now().map(|x| Some(x)),
|
||||
DriverJson::Unknown => Ok(None),
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +157,7 @@ pub fn read_current_now() -> Result<Option<u64>, SettingError> {
|
|||
pub fn read_charge_now() -> Result<Option<f64>, SettingError> {
|
||||
match auto_detect() {
|
||||
DriverJson::SteamDeck => super::steam_deck::Battery::read_charge_now().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_charge_now().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck::Battery::read_charge_now().map(|x| Some(x)),
|
||||
DriverJson::Unknown => Ok(None),
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ pub fn read_charge_now() -> Result<Option<f64>, SettingError> {
|
|||
pub fn read_charge_full() -> Result<Option<f64>, SettingError> {
|
||||
match auto_detect() {
|
||||
DriverJson::SteamDeck => super::steam_deck::Battery::read_charge_full().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_charge_full().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck::Battery::read_charge_full().map(|x| Some(x)),
|
||||
DriverJson::Unknown => Ok(None),
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +175,27 @@ pub fn read_charge_full() -> Result<Option<f64>, SettingError> {
|
|||
pub fn read_charge_design() -> Result<Option<f64>, SettingError> {
|
||||
match auto_detect() {
|
||||
DriverJson::SteamDeck => super::steam_deck::Battery::read_charge_design().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck_adv::Battery::read_charge_design().map(|x| Some(x)),
|
||||
DriverJson::SteamDeckAdvance => super::steam_deck::Battery::read_charge_design().map(|x| Some(x)),
|
||||
DriverJson::Unknown => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn maybe_do_button() {
|
||||
match auto_detect() {
|
||||
DriverJson::SteamDeck | DriverJson::SteamDeckAdvance => {
|
||||
let period = std::time::Duration::from_millis(500);
|
||||
for _ in 0..10 {
|
||||
if let Err(e) = crate::settings::steam_deck::set_led(false, true, false) {
|
||||
log::error!("Thing err: {}", e);
|
||||
}
|
||||
std::thread::sleep(period);
|
||||
if let Err(e) = crate::settings::steam_deck::set_led(false, false, false) {
|
||||
log::error!("Thing err: {}", e);
|
||||
};
|
||||
std::thread::sleep(period);
|
||||
}
|
||||
},
|
||||
DriverJson::Unknown => log::warn!("Can't do button activities on unknown platform"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,12 @@ use crate::api::RangeLimit;
|
|||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::TBattery;
|
||||
use crate::persist::BatteryJson;
|
||||
use super::util::ChargeMode;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub charge_rate: Option<u64>,
|
||||
pub charge_mode: Option<ChargeMode>,
|
||||
state: crate::state::steam_deck::Battery,
|
||||
}
|
||||
|
||||
|
@ -25,15 +27,36 @@ impl Battery {
|
|||
match version {
|
||||
0 => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
_ => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn charge_mode_to_str(mode: ChargeMode) -> String {
|
||||
match mode {
|
||||
ChargeMode::Normal => "normal",
|
||||
ChargeMode::Idle => "idle",
|
||||
ChargeMode::Discharge => "discharge",
|
||||
}.to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn str_to_charge_mode(s: &str) -> Option<ChargeMode> {
|
||||
match s {
|
||||
"normal" => Some(ChargeMode::Normal),
|
||||
"idle" => Some(ChargeMode::Idle),
|
||||
"discharge" | "disacharge" => Some(ChargeMode::Discharge),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||
if let Some(charge_rate) = self.charge_rate {
|
||||
self.state.charge_rate_set = true;
|
||||
|
@ -42,7 +65,7 @@ impl Battery {
|
|||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
)
|
||||
)?;
|
||||
} else if self.state.charge_rate_set {
|
||||
self.state.charge_rate_set = false;
|
||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).map_err(
|
||||
|
@ -50,10 +73,26 @@ impl Battery {
|
|||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
)?;
|
||||
}
|
||||
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,
|
||||
},
|
||||
)?;
|
||||
} 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,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
|
@ -144,6 +183,7 @@ impl Battery {
|
|||
pub fn system_default() -> Self {
|
||||
Self {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +194,7 @@ impl Into<BatteryJson> for Battery {
|
|||
fn into(self) -> BatteryJson {
|
||||
BatteryJson {
|
||||
charge_rate: self.charge_rate,
|
||||
charge_mode: self.charge_mode.map(Self::charge_mode_to_str),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,6 +217,7 @@ impl SettingsRange for Battery {
|
|||
fn max() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(2500),
|
||||
charge_mode: None,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +226,7 @@ impl SettingsRange for Battery {
|
|||
fn min() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(250),
|
||||
charge_mode: None,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
|
@ -194,11 +237,12 @@ impl TBattery for Battery {
|
|||
let max = Self::max();
|
||||
let min = Self::min();
|
||||
crate::api::BatteryLimits {
|
||||
charge_rate: Some(RangeLimit{
|
||||
charge_current: Some(RangeLimit{
|
||||
min: min.charge_rate.unwrap(),
|
||||
max: max.charge_rate.unwrap(),
|
||||
}),
|
||||
charge_step: 50,
|
||||
charge_current_step: 50,
|
||||
charge_modes: vec!["discharge".to_owned(), "idle".to_owned(), "normal".to_owned()],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,4 +257,12 @@ impl TBattery for Battery {
|
|||
fn get_charge_rate(&self) -> Option<u64> {
|
||||
self.charge_rate
|
||||
}
|
||||
|
||||
fn charge_mode(&mut self, mode: Option<String>) {
|
||||
self.charge_mode = mode.map(|s| Self::str_to_charge_mode(&s)).flatten()
|
||||
}
|
||||
|
||||
fn get_charge_mode(&self) -> Option<String> {
|
||||
self.charge_mode.map(Self::charge_mode_to_str)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
mod battery;
|
||||
mod cpu;
|
||||
mod gpu;
|
||||
mod util;
|
||||
|
||||
pub use battery::Battery;
|
||||
pub use cpu::{Cpu, Cpus};
|
||||
pub use gpu::Gpu;
|
||||
|
||||
pub use util::set_led;
|
||||
|
|
93
backend/src/settings/steam_deck/util.rs
Normal file
93
backend/src/settings/steam_deck/util.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
//! Rough Rust port of some BatCtrl functionality
|
||||
//! I do not have access to the source code, so this is my own interpretation of what it does.
|
||||
//!
|
||||
//! But also Quanta is based in a place with some questionable copyright practices, so...
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Error, Seek, SeekFrom, Read, Write};
|
||||
|
||||
#[inline]
|
||||
fn write2(p0: u8, p1: u8) -> Result<usize, Error> {
|
||||
write_to(0x6c, 0x81)?;
|
||||
wait_ready_for_write()?;
|
||||
let count0 = write_to(0x68, p0)?;
|
||||
wait_ready_for_write()?;
|
||||
let count1 = write_to(0x68, p1)?;
|
||||
Ok(count0 + count1)
|
||||
}
|
||||
|
||||
fn write_read(p0: u8) -> Result<u8, Error> {
|
||||
write_to(0x6c, 0x81)?;
|
||||
wait_ready_for_write()?;
|
||||
write_to(0x68, p0)?;
|
||||
wait_ready_for_read()?;
|
||||
read_from(0x68)
|
||||
}
|
||||
|
||||
fn write_to(location: u64, value: u8) -> Result<usize, Error> {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open("/dev/port")?;
|
||||
file.seek(SeekFrom::Start(location))?;
|
||||
file.write(&[value])
|
||||
}
|
||||
|
||||
fn read_from(location: u64) -> Result<u8, Error> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open("/dev/port")?;
|
||||
file.seek(SeekFrom::Start(location))?;
|
||||
let mut buffer = [0];
|
||||
file.read(&mut buffer)?;
|
||||
Ok(buffer[0])
|
||||
}
|
||||
|
||||
fn wait_ready_for_write() -> Result<(), Error> {
|
||||
let mut count = 0;
|
||||
while count < 0x1ffff && (read_from(0x6c)? & 2) != 0 {
|
||||
count += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_ready_for_read() -> Result<(), Error> {
|
||||
let mut count = 0;
|
||||
while count < 0x1ffff && (read_from(0x6c)? & 1) == 0 {
|
||||
count += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_led(red_unused: bool, green_aka_white: bool, blue_unused: bool) -> Result<usize, Error> {
|
||||
let payload: u8 = 0x80 + (red_unused as u8 & 1) + ((green_aka_white as u8 & 1) << 1) + ((blue_unused as u8 & 1) << 2);
|
||||
//log::info!("Payload: {:b}", payload);
|
||||
write2(Setting::LEDStatus as _, payload)
|
||||
}
|
||||
|
||||
pub fn set(setting: Setting, mode: u8) -> Result<usize, Error> {
|
||||
write2(setting as u8, mode)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum Setting {
|
||||
Charge = 0xA6,
|
||||
ChargeMode = 0x76,
|
||||
LEDStatus = 199,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum ChargeMode {
|
||||
Normal = 0,
|
||||
Discharge = 0x42,
|
||||
Idle = 0x45,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum Charge {
|
||||
Enable = 0,
|
||||
Disable = 4,
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::TBattery;
|
||||
use crate::persist::BatteryJson;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub charge_rate: Option<u64>,
|
||||
state: crate::state::steam_deck::Battery,
|
||||
}
|
||||
|
||||
const BATTERY_VOLTAGE: f64 = 7.7;
|
||||
|
||||
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
||||
const BATTERY_CURRENT_NOW_PATH: &str = "/sys/class/power_supply/BAT1/current_now"; // read-only
|
||||
const BATTERY_CHARGE_NOW_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_now"; // read-only
|
||||
const BATTERY_CHARGE_FULL_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_full"; // read-only
|
||||
const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_full_design"; // read-only
|
||||
|
||||
impl Battery {
|
||||
#[inline]
|
||||
pub fn from_json(other: BatteryJson, version: u64) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
_ => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||
if let Some(charge_rate) = self.charge_rate {
|
||||
self.state.charge_rate_set = true;
|
||||
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,
|
||||
},
|
||||
)
|
||||
} else if self.state.charge_rate_set {
|
||||
self.state.charge_rate_set = false;
|
||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(charge_rate) = &mut self.charge_rate {
|
||||
*charge_rate = (*charge_rate).clamp(min.charge_rate.unwrap(), max.charge_rate.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_current_now() -> Result<u64, SettingError> {
|
||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
||||
Err((Some(e), None)) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
BATTERY_CURRENT_NOW_PATH
|
||||
),
|
||||
// this value is in uA, while it's set in mA
|
||||
// so convert this to mA for consistency
|
||||
Ok(val) => Ok(val / 1000),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_charge_now() -> Result<f64, SettingError> {
|
||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_NOW_PATH) {
|
||||
Err((Some(e), None)) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
BATTERY_CHARGE_NOW_PATH
|
||||
),
|
||||
// convert to Wh
|
||||
Ok(val) => Ok((val as f64) / 1000000.0 * BATTERY_VOLTAGE),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_charge_full() -> Result<f64, SettingError> {
|
||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_FULL_PATH) {
|
||||
Err((Some(e), None)) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
BATTERY_CHARGE_NOW_PATH
|
||||
),
|
||||
// convert to Wh
|
||||
Ok(val) => Ok((val as f64) / 1000000.0 * BATTERY_VOLTAGE),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_charge_design() -> Result<f64, SettingError> {
|
||||
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_DESIGN_PATH) {
|
||||
Err((Some(e), None)) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err((None, Some(e))) => Err(SettingError {
|
||||
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_DESIGN_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
}),
|
||||
Err(_) => panic!(
|
||||
"Invalid error while reading from `{}`",
|
||||
BATTERY_CHARGE_NOW_PATH
|
||||
),
|
||||
// convert to Wh
|
||||
Ok(val) => Ok((val as f64) / 1000000.0 * BATTERY_VOLTAGE),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
Self {
|
||||
charge_rate: None,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BatteryJson> for Battery {
|
||||
#[inline]
|
||||
fn into(self) -> BatteryJson {
|
||||
BatteryJson {
|
||||
charge_rate: self.charge_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnSet for Battery {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
self.clamp_all();
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Battery {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
self.clone().set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Battery {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(2500),
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(250),
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TBattery for Battery {
|
||||
fn limits(&self) -> crate::api::BatteryLimits {
|
||||
let max = Self::max();
|
||||
let min = Self::min();
|
||||
crate::api::BatteryLimits {
|
||||
charge_rate: Some(RangeLimit{
|
||||
min: min.charge_rate.unwrap(),
|
||||
max: max.charge_rate.unwrap(),
|
||||
}),
|
||||
charge_step: 50,
|
||||
}
|
||||
}
|
||||
|
||||
fn json(&self) -> crate::persist::BatteryJson {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
fn charge_rate(&mut self, rate: Option<u64>) {
|
||||
self.charge_rate = rate;
|
||||
}
|
||||
|
||||
fn get_charge_rate(&self) -> Option<u64> {
|
||||
self.charge_rate
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
mod battery;
|
||||
//mod battery;
|
||||
mod cpu;
|
||||
mod gpu;
|
||||
|
||||
pub use battery::Battery;
|
||||
//pub use battery::Battery;
|
||||
pub use cpu::{Cpu, Cpus};
|
||||
pub use gpu::Gpu;
|
||||
|
|
|
@ -79,4 +79,8 @@ pub trait TBattery: OnResume + OnSet + Debug + Send {
|
|||
fn charge_rate(&mut self, rate: Option<u64>);
|
||||
|
||||
fn get_charge_rate(&self) -> Option<u64>;
|
||||
|
||||
fn charge_mode(&mut self, mode: Option<String>);
|
||||
|
||||
fn get_charge_mode(&self) -> Option<String>;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ impl Into<BatteryJson> for Battery {
|
|||
fn into(self) -> BatteryJson {
|
||||
BatteryJson {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +32,9 @@ impl OnResume for Battery {
|
|||
impl TBattery for Battery {
|
||||
fn limits(&self) -> crate::api::BatteryLimits {
|
||||
crate::api::BatteryLimits {
|
||||
charge_rate: None,
|
||||
charge_step: 50,
|
||||
charge_current: None,
|
||||
charge_current_step: 50,
|
||||
charge_modes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,4 +48,11 @@ impl TBattery for Battery {
|
|||
fn get_charge_rate(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
fn charge_mode(&mut self, _rate: Option<String>) {
|
||||
}
|
||||
|
||||
fn get_charge_mode(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub charge_rate_set: bool,
|
||||
pub charge_mode_set: bool,
|
||||
}
|
||||
|
||||
impl std::default::Default for Battery {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
charge_rate_set: false,
|
||||
charge_mode_set: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
main.py
2
main.py
|
@ -11,6 +11,6 @@ class Plugin:
|
|||
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
||||
async def _main(self):
|
||||
# startup
|
||||
self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"])
|
||||
#self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"])
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
|
|
@ -39,8 +39,9 @@ export type SettingsLimits = {
|
|||
};
|
||||
|
||||
export type BatteryLimits = {
|
||||
charge_rate: RangeLimit | null;
|
||||
charge_step: number;
|
||||
charge_current: RangeLimit | null;
|
||||
charge_current_step: number;
|
||||
charge_modes: number;
|
||||
};
|
||||
|
||||
export type CpuLimits = {
|
||||
|
@ -104,6 +105,18 @@ export async function unsetBatteryChargeRate(): Promise<any[]> {
|
|||
return await call_backend("BATTERY_unset_charge_rate", []);
|
||||
}
|
||||
|
||||
export async function getBatteryChargeMode(): Promise<number> {
|
||||
return (await call_backend("BATTERY_get_charge_mode", []))[0];
|
||||
}
|
||||
|
||||
export async function setBatteryChargeMode(val: number): Promise<number> {
|
||||
return (await call_backend("BATTERY_set_charge_mode", [val]))[0];
|
||||
}
|
||||
|
||||
export async function unsetBatteryChargeMode(): Promise<any[]> {
|
||||
return await call_backend("BATTERY_unset_charge_mode", []);
|
||||
}
|
||||
|
||||
// CPU
|
||||
|
||||
export async function setCpuSmt(status: boolean): Promise<boolean> {
|
||||
|
@ -213,3 +226,7 @@ export async function waitForComplete(): Promise<boolean> {
|
|||
export async function getLimits(): Promise<SettingsLimits> {
|
||||
return (await call_backend("GENERAL_get_limits", []))[0];
|
||||
}
|
||||
|
||||
export async function idk(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_idk", []))[0];
|
||||
}
|
||||
|
|
|
@ -701,7 +701,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
{get_value(CHARGE_FULL_BATT).toFixed(1)} Wh ({(100 * get_value(CHARGE_FULL_BATT) / get_value(CHARGE_DESIGN_BATT)).toFixed(1)}%)
|
||||
</Field>
|
||||
</PanelSectionRow>}
|
||||
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate != null && <PanelSectionRow>
|
||||
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current != null && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(CHARGE_RATE_BATT) != null}
|
||||
label="Charge Current Limits"
|
||||
|
@ -721,9 +721,9 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
{ get_value(CHARGE_RATE_BATT) != null && <SliderField
|
||||
label="Maximum (mA)"
|
||||
value={get_value(CHARGE_RATE_BATT)}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_rate!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_step}
|
||||
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.max}
|
||||
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.min}
|
||||
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current_step}
|
||||
showValue={true}
|
||||
disabled={get_value(CHARGE_RATE_BATT) == null}
|
||||
onChange={(val: number) => {
|
||||
|
@ -812,6 +812,16 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
|||
v{version_usdpl()}
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
{eggCount % 10 == 9 && <PanelSectionRow>
|
||||
<ButtonItem
|
||||
layout="below"
|
||||
onClick={(_: MouseEvent) => {
|
||||
backend.idk();
|
||||
}}
|
||||
>
|
||||
???
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
<ButtonItem
|
||||
layout="below"
|
||||
|
|
Loading…
Reference in a new issue