Compare commits

...

13 commits
v2.0.2 ... main

21 changed files with 224 additions and 50 deletions

View file

@ -20,7 +20,7 @@ body:
id: reproduction id: reproduction
attributes: attributes:
label: Steps To Reproduce label: Steps To Reproduce
description: Steps to reproduce the behavior description: Steps to reproduce the behaviour.
placeholder: | placeholder: |
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
@ -33,12 +33,12 @@ body:
attributes: attributes:
label: Anything else? label: Anything else?
description: | description: |
Screenshots? Logs? pt_oc.json? limits_overrides.json? Anything that will give more context about the problem! Screenshots? Logs? limits_overrides.ron? limits_cache.ron? Anything that will give more context about the problem!
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
Please include the log (located at `/tmp/powertools.log`) if possible. Please include the log (located at `/tmp/powertools.log`) if possible.
Note: the log is deleted when the device is restarted. Note: the log is deleted when the device is restarted.
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. Tip: You can attach files by clicking this area to highlight it and then dragging them in.
validations: validations:
required: false required: false
- type: input - type: input

View file

@ -1,5 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: NGnius - name: NGnius
url: https://github.com/NGnius url: https://git.ngni.us/sys/website/wiki#contact-information
about: Repository owner about: Repository owner

View file

@ -3,7 +3,7 @@ description: Suggest functionality to add
labels: "enhancement" labels: "enhancement"
body: body:
- type: textarea - type: textarea
id: described id: feature-description
attributes: attributes:
label: Describe what you'd like to be able to do label: Describe what you'd like to be able to do
description: A clear and concise description of what you want. description: A clear and concise description of what you want.
@ -22,7 +22,11 @@ body:
id: extras id: extras
attributes: attributes:
label: Anything else? label: Anything else?
description: Description of how this can be achieved, or other additional context description: |
Description of how this can be achieved, or other additional context.
If this is related to the UI, consider adding a picture.
Tip: You can attach files by clicking this area to highlight it and then dragging them in.
placeholder: This can be accomplished by doing ... placeholder: This can be accomplished by doing ...
validations: validations:
required: false required: false

23
.github/ISSUE_TEMPLATE/question.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: "Question"
description: "Ask for more information about PowerTools"
labels: "question"
body:
- type: textarea
id: question-elaboration
attributes:
label: Question
description: |
A clear and concise description of what you'd like to know.
Please check the wiki and closed issues to avoid waiting for an answer when you didn't need to.
validations:
required: true
- type: textarea
id: extras
attributes:
label: Extra Info
description: |
Additional context or information which may be helpful when answering your question.
Tip: You can attach files by clicking this area to highlight it and then dragging them in.
validations:
required: false

13
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,13 @@
## Description
A short description of what the PR changes.
If this is a minor change and the commit message(s) explains it well, feel free to delete this section.
## Motivation
Why did you write this PR and/or why should it be accepted.
## Fixes
Please indicate issues that this fixes or addresses here.
If this is a bugfix with no pre-existing issue, please describe the bug here instead.

View file

@ -1,12 +1,12 @@
# PowerTools # PowerTools
<!-- TODO Update badges for new git repo location --> <!-- TODO Update badges for new git repo location -->
[![Decky store](https://img.shields.io/badge/dynamic/json?color=blue&label=release&query=%24%5B%3F%28%40.name%3D%3D%27PowerTools%27%29%5D.versions%5B0%5D.name&url=https%3A%2F%2Fplugins.deckbrew.xyz%2Fplugins&style=flat-square)](https://plugins.deckbrew.xyz/) [![Decky store](https://img.shields.io/badge/dynamic/json?color=blue&label=release&query=%24%5B%3F%28%40.name%3D%3D%27PowerTools%27%29%5D.versions%5B0%5D.name&url=https%3A%2F%2Fplugins.deckbrew.xyz%2Fplugins&style=flat-square)](https://plugins.deckbrew.xyz/)
[![Custom store](https://img.shields.io/badge/dynamic/json?color=blue&label=preview&query=%24%5B%3F%28%40.name%3D%3D%27PowerTools%27%29%5D.versions%5B0%5D.name&url=https%3A%2F%2Fnot-decky-alpha.ngni.us%2Fplugins&style=flat-square)](https://github.com/NGnius/PowerTools/wiki) [![Custom store](https://img.shields.io/badge/dynamic/json?color=blue&label=preview&query=%24%5B%3F%28%40.name%3D%3D%27PowerTools%27%29%5D.versions%5B0%5D.name&url=https%3A%2F%2Fnot-decky-alpha.ngni.us%2Fplugins&style=flat-square)](https://git.ngni.us/NG-SD-Plugins/PowerTools/wiki)
[![GitHub package.json version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.ngni.us%2FNG-SD-Plugins%2FPowerTools%2Fraw%2Fbranch%2Fmain%2Fpackage.json&query=%24.version&style=flat-square&label=local&cacheSeconds=600)](https://git.ngni.us/NG-SD-Plugins/PowerTools/src/branch/main/package.json) [![GitHub package.json version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.ngni.us%2FNG-SD-Plugins%2FPowerTools%2Fraw%2Fbranch%2Fmain%2Fpackage.json&query=%24.version&style=flat-square&label=local&cacheSeconds=600)](https://git.ngni.us/NG-SD-Plugins/PowerTools/src/branch/main/package.json)
[![Liberapay](https://img.shields.io/liberapay/patrons/NGnius?style=flat-square)](https://liberapay.com/NGnius) [![Liberapay](https://img.shields.io/liberapay/patrons/NGnius?style=flat-square)](https://liberapay.com/NGnius)
[![GitHub](https://img.shields.io/badge/GPL--3.0-orange?style=flat-square&label=license&cacheSeconds=600)](https://github.com/NGnius/PowerTools/blob/main/LICENSE) [![GitHub](https://img.shields.io/badge/GPL--3.0-orange?style=flat-square&label=license&cacheSeconds=600)](https://git.ngni.us/NG-SD-Plugins/PowerTools/blob/main/LICENSE)
[![GitHub package.json dependency version (local)](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.ngni.us%2FNG-SD-Plugins%2FPowerTools%2Fraw%2Fbranch%2Fmain%2Fpackage.json&query=%24..%5B'decky-frontend-lib'%5D&style=flat-square&label=decky-frontend-lib&cacheSeconds=600)](https://github.com/NGnius/PowerTools/blob/main/pnpm-lock.yaml) [![GitHub package.json dependency version (local)](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.ngni.us%2FNG-SD-Plugins%2FPowerTools%2Fraw%2Fbranch%2Fmain%2Fpackage.json&query=%24..%5B'decky-frontend-lib'%5D&style=flat-square&label=decky-frontend-lib&cacheSeconds=600)](https://git.ngni.us/NG-SD-Plugins/PowerTools/blob/main/pnpm-lock.yaml)
![plugin_demo](./assets/ui.png) ![plugin_demo](./assets/ui.png)
@ -20,24 +20,38 @@ You will need that installed for this plugin to work.
- Enable & disable CPU threads & SMT - Enable & disable CPU threads & SMT
- Set CPU frequencies - Set CPU frequencies
- Set GPU frequencies and power (fastPPT & slowPPT) - Set GPU frequencies and power (fastPPT & slowPPT)
- Cap battery charge rate (when awake) - Cap battery charge level
- Display supplementary battery info - Display supplementary battery info
- Keep settings between restarts (stored in `~/.config/powertools/<gameId>.json`) - Keep settings between restarts (stored in `~/homebrew/settings/PowerTools/<appId>.ron`)
This plugin is tested on Steam Deck, but is designed to work on other Linux devices as well. Unfortunately I am currently unable to test on other devices. This plugin is tested on Steam Deck LCD/OLED, but is designed to work on other Linux devices as well. Unfortunately I am currently unable to test on most other devices.
## Install ## Install
Please use Decky's [built-in store](https://plugins.deckbrew.xyz/) to install official releases. Please use Decky's [built-in store](https://plugins.deckbrew.xyz/) to install official releases.
If you want to test unstable versions, use [my custom store](https://not-decky-alpha.ngni.us/plugins). If you would like to use an in-development version, feel free to build PowerTools yourself. If you want to test unstable versions, use [my custom store](https://not-decky-alpha.ngni.us/plugins). If you would like to use an in-development version, feel free to build PowerTools yourself.
## Build ## Build/Deploy
0. Requirements: a functioning Rust toolchain for x86_64-unknown-linux-gnu (or -musl), pnpm, and some tech literacy 0. Requirements: a functioning Rust toolchain for x86_64-unknown-linux-gnu (or -musl), pnpm, and some tech literacy
1. In a terminal, navigate to the backend directory of this project and run `./build.sh` 1. In a terminal, navigate to the backend directory of this project and run `./build.sh`
2. In the root of this project, run `pnpm run build` 2. In the root of this project, run `pnpm run build`
3. Transfer the project (especially dist/ and bin/) to a folder in your Steam Deck's `~/homebrew/plugins` directory 3. Transfer the project (especially dist/ and bin/) to a folder in your Steam Deck's `~/homebrew/plugins` directory
4. Restart Decky with `sudo systemctl restart plugin_loader.service`
## License ## License
This is licensed under GNU GPLv3. This is licensed under GNU GPLv3.
## Contributing
All contributions are welcome!
Anything from a comment on an issue to a new feature pull request will be appreciated by PowerTools's crack team of one (NGnius).
### Translations
Adding new languages and keeping existing language files up to date makes PowerTools more accessible to the majority of the world which doesn't speak English. Take a look at [this comment](https://git.ngni.us/NG-SD-Plugins/PowerTools/issues/9#issuecomment-345) (and the rest of that issue) to get started.
### Code
To prevent spam, this server does not allow regular users to create/fork repositories. Please open an issue [here](https://git.ngni.us/sys/website) to request permission. There's no pressure to actually do anything with that permission, though it may be revoked when the server is running low on space.

10
backend/Cargo.lock generated
View file

@ -1170,7 +1170,7 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "powertools" name = "powertools"
version = "2.0.2" version = "2.0.3"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
@ -1481,9 +1481,9 @@ dependencies = [
[[package]] [[package]]
name = "smokepatio" name = "smokepatio"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626ef8beee78bebc397d841469fa47bf7e370ddb8b8f3e628e69b03bf968d089" checksum = "3416e8c907d171c4334df3933873c32bff97ca5ad7ae0ee93e6268e04e2041ef"
dependencies = [ dependencies = [
"embedded-io", "embedded-io",
"log", "log",
@ -1540,9 +1540,9 @@ dependencies = [
[[package]] [[package]]
name = "sysfuss" name = "sysfuss"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fa4dd5879b3fd41aff63991a59970cdfeced6f0d5920c5da0937279904d9f45" checksum = "f33bae529511a671b5f2ed4cc46ae0b2ccdf8c03ccf7eebe95a5a886ff7914dc"
[[package]] [[package]]
name = "termcolor" name = "termcolor"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "powertools" name = "powertools"
version = "2.0.2" version = "2.0.3"
edition = "2021" edition = "2021"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"] authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
description = "Backend (superuser) functionality for PowerTools" description = "Backend (superuser) functionality for PowerTools"
@ -16,7 +16,7 @@ usdpl-back = { version = "0.10.1", features = ["blocking", "decky"] }#, path = "
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
ron = "0.8" ron = "0.8"
sysfuss = { version = "0.2", features = ["derive"] }#,path = "../../sysfs-nav"} sysfuss = { version = "0.3", features = ["derive"] }#,path = "../../sysfs-nav"}
# async # async
tokio = { version = "*", features = ["time"] } tokio = { version = "*", features = ["time"] }
@ -31,7 +31,7 @@ limits_core = { version = "3", path = "./limits_core" }
regex = "1" regex = "1"
# steam deck libs # steam deck libs
smokepatio = { version = "0.1", features = [ "std" ] } smokepatio = { version = "0.2", default-features = false }
libc = "0.2" libc = "0.2"
# online settings # online settings

View file

@ -34,8 +34,8 @@ impl GenericBatteryLimit {
fn default_steam_deck() -> Self { fn default_steam_deck() -> Self {
Self { Self {
charge_rate: Some(RangeLimit { charge_rate: Some(RangeLimit {
min: Some(250), min: Some(0),
max: Some(2500), max: Some(100),
}), }),
charge_modes: vec![ charge_modes: vec![
"normal".to_owned(), "normal".to_owned(),

View file

@ -612,8 +612,8 @@
"provider": "GabeBoy", "provider": "GabeBoy",
"limits": { "limits": {
"charge_rate": { "charge_rate": {
"min": 250, "min": 0,
"max": 2500 "max": 100
}, },
"charge_modes": [ "charge_modes": [
"normal", "normal",
@ -878,8 +878,8 @@
"provider": "GabeBoySP", "provider": "GabeBoySP",
"limits": { "limits": {
"charge_rate": { "charge_rate": {
"min": 250, "min": 0,
"max": 2500 "max": 100
}, },
"charge_modes": [ "charge_modes": [
"normal", "normal",

View file

@ -32,6 +32,30 @@ pub enum ApiMessage {
UploadCurrentVariant(String, String), // SteamID, Steam username UploadCurrentVariant(String, String), // SteamID, Steam username
} }
impl core::fmt::Display for ApiMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Battery(x) => write!(f, "Battery;{}", x),
Self::Cpu(x) => write!(f, "Cpu;{}", x),
Self::Gpu(x) => write!(f, "Gpu;{}", x),
Self::General(x) => write!(f, "General;{}", x),
Self::OnResume => write!(f, "OnResume"),
Self::OnPluggedIn => write!(f, "OnPluggedIn"),
Self::OnUnplugged => write!(f, "OnUnplugged"),
Self::OnChargeChange(x) => write!(f, "OnChargeChange({:?})", x),
Self::PowerVibeCheck => write!(f, "PowerVibeCheck"),
Self::WaitForEmptyQueue(_) => write!(f, "WaitForEmptyQueue"),
Self::LoadSettings(path, name, variant, variant_name) => write!(f, "LoadSettings({}, {}, {}, {})", path, name, variant, variant_name),
Self::LoadVariant(variant, variant_name) => write!(f, "LoadVariant({}, {})", variant, variant_name),
Self::LoadMainSettings => write!(f, "LoadMainSettings"),
Self::LoadSystemSettings => write!(f, "LoadSystemSettings"),
Self::GetLimits(_) => write!(f, "GetLimits"),
Self::GetProvider(s, _) => write!(f, "GetProvider({})", s),
Self::UploadCurrentVariant(id, user) => write!(f, "UploadCurrentVariant(id: {}, user: {})", id, user),
}
}
}
pub enum BatteryMessage { pub enum BatteryMessage {
SetChargeRate(Option<u64>), SetChargeRate(Option<u64>),
GetChargeRate(Callback<Option<u64>>), GetChargeRate(Callback<Option<u64>>),
@ -46,6 +70,24 @@ pub enum BatteryMessage {
GetChargeLimit(Callback<Option<f64>>), GetChargeLimit(Callback<Option<f64>>),
} }
impl core::fmt::Display for BatteryMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SetChargeRate(x) => write!(f, "SetChargeRate({:?})", x),
Self::GetChargeRate(_) => write!(f, "GetChargeRate"),
Self::SetChargeMode(x) => write!(f, "SetChargeMode({:?})", x),
Self::GetChargeMode(_) => write!(f, "GetChargeMode"),
Self::ReadChargeFull(_) => write!(f, "ReadChargeFull"),
Self::ReadChargeNow(_) => write!(f, "ReadChargeNow"),
Self::ReadChargeDesign(_) => write!(f, "ReadChargeDesign"),
Self::ReadCurrentNow(_) => write!(f, "ReadCurrentNow"),
Self::ReadChargePower(_) => write!(f, "ReadChargePower"),
Self::SetChargeLimit(x) => write!(f, "SetChargeLimit({:?})", x),
Self::GetChargeLimit(_) => write!(f, "GetChargeLimit"),
}
}
}
impl BatteryMessage { impl BatteryMessage {
fn process(self, settings: &mut dyn TBattery) -> bool { fn process(self, settings: &mut dyn TBattery) -> bool {
let dirty = self.is_modify(); let dirty = self.is_modify();
@ -87,6 +129,23 @@ pub enum CpuMessage {
GetCpusGovernor(Callback<Vec<String>>), GetCpusGovernor(Callback<Vec<String>>),
} }
impl core::fmt::Display for CpuMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SetCpuOnline(i, x) => write!(f, "SetCpuOnline({}, {})", i, x),
Self::SetCpusOnline(x) => write!(f, "SetCpusOnline({:?})", x),
Self::SetSmt(x, _) => write!(f, "SetChargeMode({})", x),
Self::GetSmt(_) => write!(f, "GetSmt"),
Self::GetCpusOnline(_) => write!(f, "GetCpusOnline"),
Self::SetClockLimits(x, y) => write!(f, "SetClockLimits({}, {:?})", x, y),
Self::GetClockLimits(x, _) => write!(f, "GetClockLimits({})", x),
Self::SetCpuGovernor(i, x) => write!(f, "SetCpuGovernor({}, {})", i, x),
Self::SetCpusGovernor(x) => write!(f, "SetCpusGovernor({:?})", x),
Self::GetCpusGovernor(_) => write!(f, "GetCpusGovernor"),
}
}
}
impl CpuMessage { impl CpuMessage {
fn process(self, settings: &mut dyn TCpus) -> bool { fn process(self, settings: &mut dyn TCpus) -> bool {
let dirty = self.is_modify(); let dirty = self.is_modify();
@ -206,6 +265,19 @@ pub enum GpuMessage {
GetMemoryClock(Callback<Option<u64>>), GetMemoryClock(Callback<Option<u64>>),
} }
impl core::fmt::Display for GpuMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SetPpt(x, y) => write!(f, "SetPpt(fast {:?}, slow {:?})", x, y),
Self::GetPpt(_) => write!(f, "GetPpt"),
Self::SetClockLimits(x) => write!(f, "SetClockLimits({:?})", x),
Self::GetClockLimits(_) => write!(f, "GetClockLimits"),
Self::SetMemoryClock(x) => write!(f, "SetMemoryClock({:?})", x),
Self::GetMemoryClock(_) => write!(f, "GetMemoryClock"),
}
}
}
impl GpuMessage { impl GpuMessage {
fn process(self, settings: &mut dyn TGpu) -> bool { fn process(self, settings: &mut dyn TGpu) -> bool {
let dirty = self.is_modify(); let dirty = self.is_modify();
@ -242,6 +314,21 @@ pub enum GeneralMessage {
ApplyNow, ApplyNow,
} }
impl core::fmt::Display for GeneralMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SetPersistent(x) => write!(f, "SetPersistent({})", x),
Self::GetPersistent(_) => write!(f, "GetPersistent"),
Self::GetCurrentProfileName(_) => write!(f, "GetCurrentProfileName"),
Self::GetPath(_) => write!(f, "GetPath"),
Self::GetCurrentVariant(_) => write!(f, "GetCurrentVariant"),
Self::GetAllVariants(_) => write!(f, "GetAllVariants"),
Self::AddVariant(variant, _) => write!(f, "AddVariant(name: `{}` [...])", variant.name),
Self::ApplyNow => write!(f, "ApplyNow"),
}
}
}
impl GeneralMessage { impl GeneralMessage {
fn process(self, settings: &mut dyn TGeneral) -> bool { fn process(self, settings: &mut dyn TGeneral) -> bool {
let dirty = self.is_modify(); let dirty = self.is_modify();
@ -285,20 +372,31 @@ fn print_errors(call_name: &str, errors: Vec<crate::settings::SettingError>) {
log::error!("Settings {}() err:\n{}", call_name, err_list); log::error!("Settings {}() err:\n{}", call_name, err_list);
} }
fn print_messages(msgs: &Vec<String>) {
let mut log_msg = String::new();
for msg in msgs.iter() {
//use core::fmt::Write;
write!(log_msg, "{}, ", msg).unwrap();
}
log::info!("Processed messages: [{}]", log_msg);
}
impl ApiMessageHandler { impl ApiMessageHandler {
pub fn process_forever(&mut self, settings: &mut Settings) { pub fn process_forever(&mut self, settings: &mut Settings) {
crate::utility::ioperm_power_ec(); crate::utility::ioperm_power_ec();
//let mut dirty_echo = true; // set everything twice, to make sure PowerTools wins on race conditions let mut dirty_echo = true; // set everything twice, to make sure PowerTools wins on race conditions
while let Ok(msg) = self.intake.recv() { while let Ok(msg) = self.intake.recv() {
let mut messages = vec![msg.to_string()]; // keep messages for logging
let mut dirty = self.process(settings, msg); let mut dirty = self.process(settings, msg);
while let Ok(msg) = self.intake.try_recv() { while let Ok(msg) = self.intake.try_recv() {
messages.push(msg.to_string());
dirty |= self.process(settings, msg); dirty |= self.process(settings, msg);
} }
if dirty if dirty
/*|| dirty_echo */ || dirty_echo
{ {
//dirty_echo = dirty; // echo only once dirty_echo = dirty; // echo only once
print_messages(&messages);
// run on_set // run on_set
if let Err(e) = settings.on_set() { if let Err(e) = settings.on_set() {
print_errors("on_set", e); print_errors("on_set", e);

View file

@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
use sysfuss::capability::attributes; use sysfuss::capability::attributes;
use sysfuss::{ use sysfuss::{
HwMonAttribute, HwMonAttributeItem, HwMonAttributeType, HwMonPath, PowerSupplyAttribute, HwMonAttribute, HwMonAttributeItem, HwMonAttributeType, HwMonPath, PowerSupplyAttribute,
PowerSupplyPath, SysAttribute, SysEntity, SysEntityAttributesExt, PowerSupplyPath, SysEntity, SysEntityAttributesExt, SysAttributeExt,
}; };
use limits_core::json_v2::GenericBatteryLimit; use limits_core::json_v2::GenericBatteryLimit;
@ -230,12 +230,12 @@ const HWMON_NEEDS: &[HwMonAttribute] = &[
]; ];
const MAX_BATTERY_CHARGE_RATE_ATTR: HwMonAttribute = const MAX_BATTERY_CHARGE_RATE_ATTR: HwMonAttribute =
HwMonAttribute::custom("maximum_battery_charge_rate"); HwMonAttribute::custom("max_battery_charge_rate");
const MAX_BATTERY_CHARGE_LEVEL_ATTR: HwMonAttribute = const MAX_BATTERY_CHARGE_LEVEL_ATTR: HwMonAttribute =
HwMonAttribute::custom("max_battery_charge_level"); HwMonAttribute::custom("max_battery_charge_level");
const MAX_CHARGE_RATE: u64 = 2500; const MAX_CHARGE_RATE: u64 = 100;
const MIN_CHARGE_RATE: u64 = 250; const MIN_CHARGE_RATE: u64 = 0;
impl Battery { impl Battery {
fn find_battery_sysfs(root: Option<impl AsRef<std::path::Path>>) -> PowerSupplyPath { fn find_battery_sysfs(root: Option<impl AsRef<std::path::Path>>) -> PowerSupplyPath {
@ -325,7 +325,7 @@ impl Battery {
self.state.charge_rate_set = true; self.state.charge_rate_set = true;
let path = MAX_BATTERY_CHARGE_RATE_ATTR.path(&*self.sysfs_hwmon); let path = MAX_BATTERY_CHARGE_RATE_ATTR.path(&*self.sysfs_hwmon);
self.sysfs_hwmon self.sysfs_hwmon
.set(MAX_BATTERY_CHARGE_RATE_ATTR, charge_rate) .set(MAX_BATTERY_CHARGE_RATE_ATTR, format!("{}\n", charge_rate))
.map_err(|e| SettingError { .map_err(|e| SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e), msg: format!("Failed to write to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Battery, setting: crate::settings::SettingVariant::Battery,
@ -336,10 +336,10 @@ impl Battery {
self.sysfs_hwmon self.sysfs_hwmon
.set( .set(
MAX_BATTERY_CHARGE_RATE_ATTR, MAX_BATTERY_CHARGE_RATE_ATTR,
self.limits format!("{}\n", self.limits
.charge_rate .charge_rate
.and_then(|lim| lim.max) .and_then(|lim| lim.max)
.unwrap_or(2500), .unwrap_or(100)),
) )
.map_err(|e| SettingError { .map_err(|e| SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e), msg: format!("Failed to write to `{}`: {}", path.display(), e),

View file

@ -1,8 +1,7 @@
use std::convert::Into; use std::convert::Into;
use sysfuss::{ use sysfuss::{
capability::attributes, BasicEntityPath, HwMonPath, SysAttribute, SysEntity, capability::attributes, BasicEntityPath, HwMonPath, SysEntity, SysEntityAttributesExt, SysAttributeExt,
SysEntityAttributes, SysEntityAttributesExt,
}; };
use limits_core::json_v2::GenericGpuLimit; use limits_core::json_v2::GenericGpuLimit;
@ -151,7 +150,7 @@ impl Gpu {
if let super::Model::OLED = self.variant { if let super::Model::OLED = self.variant {
if let Ok(f) = self if let Ok(f) = self
.sysfs_card .sysfs_card
.read_value(GPU_CLOCK_READOUT_ATTRIBUTE.to_owned()) .read_value(&GPU_CLOCK_READOUT_ATTRIBUTE.to_owned())
{ {
let options = parse_pp_dpm_sclk(&String::from_utf8_lossy(&f)); let options = parse_pp_dpm_sclk(&String::from_utf8_lossy(&f));
return options return options
@ -192,7 +191,7 @@ impl Gpu {
fn quantize_memory_clock(&self, clock: u64) -> u64 { fn quantize_memory_clock(&self, clock: u64) -> u64 {
if let Ok(f) = self if let Ok(f) = self
.sysfs_card .sysfs_card
.read_value(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned()) .read_value(&GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned())
{ {
let options = parse_pp_dpm_fclk(&String::from_utf8_lossy(&f)); let options = parse_pp_dpm_fclk(&String::from_utf8_lossy(&f));
// round (and find) nearest valid clock step // round (and find) nearest valid clock step
@ -238,7 +237,7 @@ impl Gpu {
{ {
let options_count = self let options_count = self
.sysfs_card .sysfs_card
.read_value(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned()) .read_value(&GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned())
.map(|b| parse_pp_dpm_fclk(&String::from_utf8_lossy(&b)).len()) .map(|b| parse_pp_dpm_fclk(&String::from_utf8_lossy(&b)).len())
.unwrap_or_else(|_| if is_oled { 4 } else { 2 }); .unwrap_or_else(|_| if is_oled { 4 } else { 2 });
let modifier = (options_count - 1) as u64; let modifier = (options_count - 1) as u64;

View file

@ -5,7 +5,7 @@
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use sysfuss::{BasicEntityPath, SysAttribute, SysEntityAttributesExt}; use sysfuss::{BasicEntityPath, SysEntityAttributesExt, SysAttributeExt};
use crate::settings::SettingError; use crate::settings::SettingError;

View file

@ -1,9 +1,13 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::sync::Mutex;
pub const JUPITER_HWMON_NAME: &'static str = "jupiter"; pub const JUPITER_HWMON_NAME: &'static str = "jupiter";
pub const STEAMDECK_HWMON_NAME: &'static str = "steamdeck_hwmon"; pub const STEAMDECK_HWMON_NAME: &'static str = "steamdeck_hwmon";
pub const GPU_HWMON_NAME: &'static str = "amdgpu"; pub const GPU_HWMON_NAME: &'static str = "amdgpu";
pub static THING_EC: Mutex<smokepatio::ec::unnamed_power::UnnamedPowerEC> = Mutex::new(smokepatio::ec::unnamed_power::UnnamedPowerEC::new());
pub fn range_min_or_fallback<I: Copy>( pub fn range_min_or_fallback<I: Copy>(
range: &Option<limits_core::json_v2::RangeLimit<I>>, range: &Option<limits_core::json_v2::RangeLimit<I>>,
fallback: I, fallback: I,
@ -25,6 +29,7 @@ pub fn card_also_has(card: &dyn sysfuss::SysEntity, extensions: &'static [&'stat
} }
const THINGS: &[u8] = &[ const THINGS: &[u8] = &[
0, 0, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1,
1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
@ -35,13 +40,14 @@ const TIME_UNIT: std::time::Duration = std::time::Duration::from_millis(250);
pub fn flash_led() { pub fn flash_led() {
use smokepatio::ec::ControllerSet; use smokepatio::ec::ControllerSet;
let mut ec = smokepatio::ec::unnamed_power::UnnamedPowerEC::new();
let mut ec = THING_EC.lock().unwrap();
for &code in THINGS { for &code in THINGS {
let on = code != 0; let on = code != 0;
let colour = if on { let colour = if on {
smokepatio::ec::unnamed_power::StaticColour::Red smokepatio::ec::unnamed_power::StaticColour::Red
} else { } else {
smokepatio::ec::unnamed_power::StaticColour::Off smokepatio::ec::unnamed_power::StaticColour::Disabled
}; };
if let Err(e) = ec.set(colour) { if let Err(e) = ec.set(colour) {
log::error!("Thing err: {}", e); log::error!("Thing err: {}", e);

View file

@ -69,7 +69,8 @@ pub fn chown_settings_dir() -> std::io::Result<()> {
.parse() .parse()
.unwrap_or(1000); .unwrap_or(1000);
log::info!( log::info!(
"chmod/chown ~/.config/powertools for user `{}` ({})", "chmod/chown {} for user `{}` ({})",
dir.display(),
deck_user, deck_user,
uid uid
); );

View file

@ -1,6 +1,6 @@
{ {
"name": "PowerTools", "name": "PowerTools",
"version": "2.0.2", "version": "2.0.3",
"description": "Power tweaks for power users", "description": "Power tweaks for power users",
"scripts": { "scripts": {
"build": "shx rm -rf dist && rollup -c", "build": "shx rm -rf dist && rollup -c",

View file

@ -85,7 +85,7 @@ export class Battery extends Component<backend.IdcProps> {
}} }}
/> />
{ get_value(CHARGE_RATE_BATT) != null && <SliderField { get_value(CHARGE_RATE_BATT) != null && <SliderField
label={tr("Maximum (mA)")} label={tr("Maximum (%)")}
value={get_value(CHARGE_RATE_BATT)} value={get_value(CHARGE_RATE_BATT)}
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.max} max={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.max}
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.min} min={(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current!.min}

BIN
translations/en-US.mo Normal file

Binary file not shown.

16
translations/en-US.po Normal file
View file

@ -0,0 +1,16 @@
# TEMPLATE TITLE.
# Copyright (C) 2024 NGnius
# This file is distributed under the same license as the PowerTools package.
# NGnius (Graham) <ngniusness@gmail.com>, 2024.
msgid ""
msgstr ""
"Project-Id-Version: v1.1\n"
"Report-Msgid-Bugs-To: https://git.ngni.us/NG-SD-Plugins/PowerTools/PowerTools/issues\n"
"POT-Creation-Date: 2023-01-09 19:52-0500\n"
"PO-Revision-Date: 2024-05-07 18:42-0500\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: en-US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

View file

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: v1.1\n" "Project-Id-Version: v1.1\n"
"Report-Msgid-Bugs-To: https://github.com/NGnius/PowerTools/issues\n" "Report-Msgid-Bugs-To: https://git.ngni.us/NG-SD-Plugins/PowerTools/issues\n"
"POT-Creation-Date: 2023-01-09 19:52-0500\n" "POT-Creation-Date: 2023-01-09 19:52-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"