Merge branch 'dev'

This commit is contained in:
NGnius (Graham) 2023-08-28 17:13:28 -04:00
commit decc0cd73d
72 changed files with 2711 additions and 1424 deletions

View file

@ -1,12 +1,12 @@
# PowerTools
<!-- 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/)
[![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)
[![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)
[![GitHub](https://img.shields.io/github/license/NGnius/PowerTools?style=flat-square)](https://github.com/NGnius/PowerTools/blob/main/LICENSE)
[![GitHub package.json version](https://img.shields.io/github/package-json/v/NGnius/PowerTools?style=flat-square)](https://github.com/NGnius/PowerTools/blob/main/package.json)
[![GitHub package.json dependency version (prod)](https://img.shields.io/github/package-json/dependency-version/NGnius/PowerTools/decky-frontend-lib?style=flat-square)](https://github.com/NGnius/PowerTools/blob/main/pnpm-lock.yaml)
[![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 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)
![plugin_demo](./assets/ui.png)
@ -24,17 +24,19 @@ You will need that installed for this plugin to work.
- Display supplementary battery info
- Keep settings between restarts (stored in `~/.config/powertools/<gameId>.json`)
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.
## Install
Please use Decky's [built-in store](https://beta.deckbrew.xyz/) to install official releases.
If you're an advanced user, and/or would like to use an in-development version, feel free to build PowerTools yourself.
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.
## Build
0. Requirements: a functioning Rust toolchain for x86_64-unknown-linux-musl, npm, 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`
2. In the root of this project, run `npm run build`
3. Transfer the project (especially dist/ and bin/) to a folder in your Steam Deck's homebrew plugins directory
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
## License

View file

@ -7,9 +7,9 @@
viewBox="0 0 483.77954 483.77954"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="icon.svg"
inkscape:export-filename="/home/ngnius/Documents/git-repos/PowerTools/extras/icon.png"
inkscape:export-filename="logo.png"
inkscape:export-xdpi="203.2"
inkscape:export-ydpi="203.2"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
@ -30,16 +30,18 @@
width="241.88977px"
inkscape:snap-object-midpoints="false"
inkscape:zoom="1.4142136"
inkscape:cx="250.66935"
inkscape:cy="188.0904"
inkscape:window-width="1280"
inkscape:window-height="1007"
inkscape:window-x="1280"
inkscape:cx="199.05056"
inkscape:cy="246.07316"
inkscape:window-width="2560"
inkscape:window-height="998"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:snap-intersection-paths="true"
inkscape:snap-midpoints="true" />
inkscape:snap-midpoints="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1" />
<defs
id="defs2" />
<g
@ -47,18 +49,25 @@
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;fill-opacity:1;fill-rule:evenodd"
style="fill:#000000;fill-opacity:1"
id="rect1945"
width="100"
height="10"
x="349.94388"
y="108.70168" />
<rect
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:1.03075"
id="rect31"
width="200"
width="212.49023"
height="100"
x="71.855446"
x="59.365211"
y="63.752789" />
<circle
style="fill:#000000;fill-opacity:1"
style="fill:#000000;fill-opacity:1;stroke-width:0.5"
id="path1244"
cx="71.855446"
cy="113.75279"
r="50" />
cx="59.355446"
cy="88.752792"
r="25" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1"
@ -76,24 +85,24 @@
d="m 355.89305,113.75249 c 0,9.92465 -77.355,54.58558 -85.95,49.62325 -8.595,-4.96232 -8.595,-94.284182 0,-99.246508 8.595,-4.962325 85.95,39.698608 85.95,49.623258 z"
inkscape:transform-center-x="-16.053868"
inkscape:transform-center-y="-0.5295475" />
<ellipse
style="fill:#808080;stroke-width:1.4865"
id="path591"
cx="334.35544"
cy="113.75249"
rx="50"
ry="20" />
<path
style="stroke:#000000;stroke-width:1.10309px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 72,164 v 150 l 120.000004,-150 z"
style="stroke:none;stroke-width:0.530701px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill:#808080"
d="m 135.67646,163.69764 v 74.33643 l 56.04644,-74.33643 z"
id="path1655" />
<rect
style="fill:#000000;fill-opacity:1"
style="fill:#000000;fill-opacity:1;stroke-width:0.790569"
id="rect1759"
width="150"
height="100"
width="125"
height="75"
x="71.855446"
y="313.75278" />
<rect
style="fill:#000000;fill-opacity:1"
id="rect1945"
width="100"
height="10"
x="349.94388"
y="108.70168" />
y="339" />
<rect
style="fill:#000000;fill-opacity:1;stroke-width:1.77271"
id="rect2155"
@ -101,5 +110,198 @@
height="250.24721"
x="71.855446"
y="163.75279" />
<circle
style="fill:#000000;fill-opacity:1;stroke-width:0.5"
id="circle277"
cx="59.355446"
cy="138.75279"
r="25" />
<rect
style="fill:#000000"
id="rect398"
width="25"
height="50"
x="34.355446"
y="88.752792" />
<rect
style="fill:#000000;stroke-width:0.820266"
id="rect414"
width="56.144562"
height="25.567711"
x="135.85544"
y="163.4323" />
<ellipse
style="fill:#000000"
id="path416"
cx="71.855446"
cy="357.75"
rx="22.669472"
ry="18.75" />
<ellipse
style="fill:#000000"
id="path416-3"
cx="71.855446"
cy="395.25"
rx="22.669472"
ry="18.75" />
<rect
style="fill:#000000"
id="rect458"
width="22.669472"
height="37.5"
x="49.185974"
y="357.75" />
<ellipse
style="fill:#000000"
id="path416-6"
cx="196.85544"
cy="357.75"
rx="22.669472"
ry="18.75" />
<ellipse
style="fill:#000000"
id="path416-3-7"
cx="196.85544"
cy="395.25"
rx="22.669472"
ry="18.75" />
<rect
style="fill:#000000"
id="rect458-5"
width="22.669472"
height="37.5"
x="196.85544"
y="357.75" />
<rect
style="fill:#000000;stroke-width:0.987024"
id="rect901"
width="48.7108"
height="40"
x="284.37106"
y="93.752487" />
<circle
style="fill:#808080;stroke:none;stroke-width:1.49999"
id="path1656"
cx="64.860382"
cy="81.150871"
r="7.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:1.49999"
id="path1656-3"
cx="266.36029"
cy="81.150871"
r="7.5" />
<rect
style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke-width:0.84968"
id="rect31-5"
width="180.49001"
height="80"
x="75.365326"
y="73.752792" />
<rect
style="fill:#808080;stroke:#808080;stroke-width:3.44828;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect1732"
width="21.551723"
height="21.551723"
x="48.579582"
y="94.675819" />
<rect
style="fill:#808080;stroke:#808080;stroke-width:3.44828;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect1732-9"
width="21.551723"
height="21.551723"
x="261.08936"
y="94.675819" />
<g
id="g2413"
transform="translate(7.8603821,-16.500301)">
<rect
style="fill:#808080;stroke:none"
id="rect1798"
width="10"
height="5"
x="52"
y="138.75279" />
<circle
style="fill:#808080;stroke:none"
id="path1800"
cx="52"
cy="141.25279"
r="2.5" />
<circle
style="fill:#808080;stroke:none"
id="path1800-2"
cx="62"
cy="141.25279"
r="2.5" />
</g>
<g
id="g2413-1"
transform="translate(209.36029,-16.500301)">
<rect
style="fill:#808080;stroke:none"
id="rect1798-2"
width="10"
height="5"
x="52"
y="138.75279" />
<circle
style="fill:#808080;stroke:none"
id="path1800-7"
cx="52"
cy="141.25279"
r="2.5" />
<circle
style="fill:#808080;stroke:none"
id="path1800-2-0"
cx="62"
cy="141.25279"
r="2.5" />
</g>
<g
id="g2678">
<rect
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect2563"
width="15"
height="5"
x="39.355446"
y="75.747589" />
<rect
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect2601"
width="5"
height="15"
x="44.355446"
y="70.747589" />
</g>
<g
id="g2674"
transform="translate(-1.5377998e-5,1.6793823)">
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605"
cx="279.36523"
cy="76.568207"
r="2.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605-9"
cx="284.36523"
cy="71.574265"
r="2.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605-3"
cx="289.36523"
cy="76.568207"
r="2.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605-6"
cx="284.36523"
cy="81.562149"
r="2.5" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 18 KiB

384
assets/sticker.svg Normal file
View file

@ -0,0 +1,384 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="sticker.svg"
inkscape:export-filename="../../../../Pictures/Projects/DeckyLoader/powertools-sticker.png"
inkscape:export-xdpi="384"
inkscape:export-ydpi="384"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:document-units="px"
showgrid="false"
units="mm"
width="241.88977px"
inkscape:snap-object-midpoints="false"
inkscape:zoom="1.4142136"
inkscape:cx="78.135297"
inkscape:cy="238.29498"
inkscape:window-width="2560"
inkscape:window-height="998"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:snap-intersection-paths="true"
inkscape:snap-midpoints="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#ff00ff;stroke-width:1.06004"
id="path169-0"
cx="256.33051"
cy="257.39117"
r="256" />
<rect
style="fill:#ff00ff;stroke-width:1.4133"
id="rect4535-6"
width="512"
height="510.60883"
x="0"
y="0.69558716" />
<rect
style="fill:#ff00ff"
id="rect4535"
width="256.33051"
height="510.60883"
x="0"
y="1.3911743" />
<circle
style="fill:#ff00ff;stroke-width:0.621117"
id="path169-6-2"
cx="256"
cy="256"
r="150" />
<circle
style="fill:#ffffff;stroke-width:0.53002"
id="path169-6"
cx="256"
cy="256"
r="128" />
<circle
style="fill:#ffffff;stroke-width:1.06004"
id="path169"
cx="256.33051"
cy="257.39117"
r="256" />
<g
id="g4192"
transform="matrix(0.84217934,0,0,0.84217934,111.88641,61.863508)">
<rect
style="fill:#000000;fill-opacity:1"
id="rect1945"
width="100"
height="10"
x="278.91406"
y="100.3456" />
<rect
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:1.03075"
id="rect31"
width="212.49023"
height="100"
x="-11.664615"
y="55.396706" />
<circle
style="fill:#000000;fill-opacity:1;stroke-width:0.5"
id="path1244"
cx="-11.67438"
cy="80.396706"
r="25" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1"
id="path1353"
inkscape:flatsided="true"
sodipodi:sides="3"
sodipodi:cx="298.59305"
sodipodi:cy="113.75249"
sodipodi:r1="57.299999"
sodipodi:r2="60"
sodipodi:arg1="0"
sodipodi:arg2="0"
inkscape:rounded="0.1"
inkscape:randomized="0"
d="m 355.89305,113.75249 c 0,9.92465 -77.355,54.58558 -85.95,49.62325 -8.595,-4.96232 -8.595,-94.284182 0,-99.246508 8.595,-4.962325 85.95,39.698608 85.95,49.623258 z"
inkscape:transform-center-x="-16.053868"
inkscape:transform-center-y="-0.5295475"
transform="translate(-71.029826,-8.356084)" />
<ellipse
style="fill:#808080;stroke-width:1.4865"
id="path591"
cx="263.32562"
cy="105.3964"
rx="50"
ry="20" />
<path
style="fill:#808080;stroke:none;stroke-width:0.530701px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 64.646634,155.34156 v 74.33643 l 56.046436,-74.33643 z"
id="path1655" />
<rect
style="fill:#000000;fill-opacity:1;stroke-width:0.790569"
id="rect1759"
width="125"
height="75"
x="0.82561988"
y="330.64392" />
<rect
style="fill:#000000;fill-opacity:1;stroke-width:1.77271"
id="rect2155"
width="64"
height="250.24721"
x="0.82561988"
y="155.39671" />
<circle
style="fill:#000000;fill-opacity:1;stroke-width:0.5"
id="circle277"
cx="-11.67438"
cy="130.39671"
r="25" />
<rect
style="fill:#000000"
id="rect398"
width="25"
height="50"
x="-36.674381"
y="80.396706" />
<rect
style="fill:#000000;stroke-width:0.820266"
id="rect414"
width="56.144562"
height="25.567711"
x="64.825615"
y="155.07622" />
<ellipse
style="fill:#000000"
id="path416"
cx="0.82561988"
cy="349.39392"
rx="22.669472"
ry="18.75" />
<ellipse
style="fill:#000000"
id="path416-3"
cx="0.82561988"
cy="386.89392"
rx="22.669472"
ry="18.75" />
<rect
style="fill:#000000"
id="rect458"
width="22.669472"
height="37.5"
x="-21.843851"
y="349.39392" />
<ellipse
style="fill:#000000"
id="path416-6"
cx="125.82561"
cy="349.39392"
rx="22.669472"
ry="18.75" />
<ellipse
style="fill:#000000"
id="path416-3-7"
cx="125.82561"
cy="386.89392"
rx="22.669472"
ry="18.75" />
<rect
style="fill:#000000"
id="rect458-5"
width="22.669472"
height="37.5"
x="125.82561"
y="349.39392" />
<rect
style="fill:#000000;stroke-width:0.987024"
id="rect901"
width="48.7108"
height="40"
x="213.34123"
y="85.3964" />
<circle
style="fill:#808080;stroke:none;stroke-width:1.49999"
id="path1656"
cx="-6.1694441"
cy="72.794785"
r="7.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:1.49999"
id="path1656-3"
cx="195.33046"
cy="72.794785"
r="7.5" />
<rect
style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke-width:0.84968"
id="rect31-5"
width="180.49001"
height="80"
x="4.3354998"
y="65.396706" />
<rect
style="fill:#808080;stroke:#808080;stroke-width:3.44828;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect1732"
width="21.551723"
height="21.551723"
x="-22.450243"
y="86.319733" />
<rect
style="fill:#808080;stroke:#808080;stroke-width:3.44828;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect1732-9"
width="21.551723"
height="21.551723"
x="190.05952"
y="86.319733" />
<g
id="g2413"
transform="translate(-63.169444,-24.856385)">
<rect
style="fill:#808080;stroke:none"
id="rect1798"
width="10"
height="5"
x="52"
y="138.75279" />
<circle
style="fill:#808080;stroke:none"
id="path1800"
cx="52"
cy="141.25279"
r="2.5" />
<circle
style="fill:#808080;stroke:none"
id="path1800-2"
cx="62"
cy="141.25279"
r="2.5" />
</g>
<g
id="g2413-1"
transform="translate(138.33046,-24.856385)">
<rect
style="fill:#808080;stroke:none"
id="rect1798-2"
width="10"
height="5"
x="52"
y="138.75279" />
<circle
style="fill:#808080;stroke:none"
id="path1800-7"
cx="52"
cy="141.25279"
r="2.5" />
<circle
style="fill:#808080;stroke:none"
id="path1800-2-0"
cx="62"
cy="141.25279"
r="2.5" />
</g>
<g
id="g2678"
transform="translate(-71.029826,-8.356084)">
<rect
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect2563"
width="15"
height="5"
x="39.355446"
y="75.747589" />
<rect
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect2601"
width="5"
height="15"
x="44.355446"
y="70.747589" />
</g>
<g
id="g2674"
transform="translate(-71.029841,-6.6767017)">
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605"
cx="279.36523"
cy="76.568207"
r="2.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605-9"
cx="284.36523"
cy="71.574265"
r="2.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605-3"
cx="289.36523"
cy="76.568207"
r="2.5" />
<circle
style="fill:#808080;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path2605-6"
cx="284.36523"
cy="81.562149"
r="2.5" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;display:none;fill:#000000;fill-opacity:1;stroke:none"
x="75.370758"
y="473.16794"
id="text4777"><tspan
sodipodi:role="line"
id="tspan4775"
x="75.370758"
y="473.16794"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:64px;font-family:Hack;-inkscape-font-specification:'Hack, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal">PowerTools</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;fill:#ffffff;fill-opacity:1;stroke:none"
x="92.57341"
y="389.06296"
id="text4831"><tspan
sodipodi:role="line"
id="tspan4829"
x="92.57341"
y="389.06296"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;font-family:Hack;-inkscape-font-specification:'Hack, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff">Power</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
x="237.4406"
y="395.76608"
id="text4835"><tspan
sodipodi:role="line"
id="tspan4833"
x="237.4406"
y="395.76608"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:64px;font-family:Hack;-inkscape-font-specification:'Hack, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal">Tools</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

584
backend/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "powertools"
version = "1.3.1"
version = "1.4.0"
edition = "2021"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
description = "Backend (superuser) functionality for PowerTools"
@ -15,6 +15,7 @@ readme = "../README.md"
usdpl-back = { version = "0.10.1", features = ["blocking"] }#, path = "../../usdpl-rs/usdpl-back"}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sysfuss = { version = "0.2", path = "../../sysfs-nav", features = ["derive"] }
# async
tokio = { version = "*", features = ["time"] }
@ -25,11 +26,11 @@ log = "0.4"
simplelog = "0.12"
# limits & driver functionality
limits_core = { version = "1.0.0", path = "./limits_core" }
limits_core = { version = "2", path = "./limits_core" }
regex = "1"
libryzenadj = { version = "0.12" }
# ureq's tls feature does not like musl targets
ureq = { version = "2.5", features = ["json", "gzip", "brotli", "charset"], default-features = false, optional = true }
ureq = { version = "2", features = ["json", "gzip", "brotli", "charset"], default-features = false, optional = true }
[features]
default = ["online", "decky"]

View file

@ -6,7 +6,7 @@
cargo build
mkdir -p ../bin
#cp ./target/x86_64-unknown-linux-musl/release/powertools ../bin/backend
#cp ./target/x86_64-unknown-linux-musl/debug/powertools ../bin/backend
#cp ./target/debug/powertools ../bin/backend
cp ./target/debug/powertools ../bin/backend
#cp --preserve=mode ./target/x86_64-unknown-linux-musl/release/powertools ../bin/backend
#cp --preserve=mode ./target/x86_64-unknown-linux-musl/debug/powertools ../bin/backend
#cp --preserve=mode ./target/debug/powertools ../bin/backend
cp --preserve=mode ./target/debug/powertools ../bin/backend

View file

@ -4,13 +4,13 @@ version = 3
[[package]]
name = "itoa"
version = "1.0.6"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
[[package]]
name = "limits_core"
version = "1.0.0"
version = "2.0.1"
dependencies = [
"serde",
"serde_json",
@ -18,42 +18,42 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.54"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.13"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
[[package]]
name = "serde"
version = "1.0.159"
version = "1.0.166"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.159"
version = "1.0.166"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
dependencies = [
"proc-macro2",
"quote",
@ -62,9 +62,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.95"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [
"itoa",
"ryu",
@ -73,9 +73,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.12"
version = "2.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
dependencies = [
"proc-macro2",
"quote",
@ -84,6 +84,6 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"

View file

@ -1,6 +1,6 @@
[package]
name = "limits_core"
version = "1.0.0"
version = "2.0.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -57,16 +57,16 @@ impl Default for Base {
},
limits: vec![
super::Limits::Cpu(super::CpuLimit::GenericAMD(super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: 1000, max: 3700 }),
clock_max: Some(super::RangeLimit { min: 1000, max: 3700 }),
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(3700) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(3700) }),
clock_step: 100,
})),
super::Limits::Gpu(super::GpuLimit::GenericAMD(super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: 1_000_000, max: 25_000_000 }),
slow_ppt: Some(super::RangeLimit { min: 1_000_000, max: 25_000_000 }),
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: 400, max: 1100 }),
clock_max: Some(super::RangeLimit { min: 400, max: 1100 }),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(1100) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(1100) }),
clock_step: Some(100),
..Default::default()
})),
@ -84,16 +84,16 @@ impl Default for Base {
},
limits: vec![
super::Limits::Cpu(super::CpuLimit::GenericAMD(super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: 1000, max: 4000 }),
clock_max: Some(super::RangeLimit { min: 1000, max: 4000 }),
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(4000) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(4000) }),
clock_step: 100,
})),
super::Limits::Gpu(super::GpuLimit::GenericAMD(super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: 1_000_000, max: 25_000_000 }),
slow_ppt: Some(super::RangeLimit { min: 1_000_000, max: 25_000_000 }),
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: 400, max: 1600 }),
clock_max: Some(super::RangeLimit { min: 400, max: 1600 }),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(1600) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(1600) }),
clock_step: Some(100),
..Default::default()
})),
@ -111,16 +111,16 @@ impl Default for Base {
},
limits: vec![
super::Limits::Cpu(super::CpuLimit::GenericAMD(super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: 1000, max: 4500 }),
clock_max: Some(super::RangeLimit { min: 1000, max: 4500 }),
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(4500) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(4500) }),
clock_step: 100,
})),
super::Limits::Gpu(super::GpuLimit::GenericAMD(super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: 1_000_000, max: 25_000_000 }),
slow_ppt: Some(super::RangeLimit { min: 1_000_000, max: 25_000_000 }),
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(25_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: 400, max: 2000 }),
clock_max: Some(super::RangeLimit { min: 400, max: 2000 }),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(2000) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(2000) }),
clock_step: Some(100),
..Default::default()
})),
@ -138,16 +138,16 @@ impl Default for Base {
},
limits: vec![
super::Limits::Cpu(super::CpuLimit::Generic(super::GenericCpuLimit {
clock_min: Some(super::RangeLimit { min: 1000, max: 4700 }),
clock_max: Some(super::RangeLimit { min: 1000, max: 4700 }),
clock_min: Some(super::RangeLimit { min: Some(1000), max: Some(4700) }),
clock_max: Some(super::RangeLimit { min: Some(1000), max: Some(4700) }),
clock_step: 100,
})),
super::Limits::Gpu(super::GpuLimit::Generic(super::GenericGpuLimit {
fast_ppt: Some(super::RangeLimit { min: 1_000_000, max: 28_000_000 }),
slow_ppt: Some(super::RangeLimit { min: 1_000_000, max: 28_000_000 }),
fast_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(28_000_000) }),
slow_ppt: Some(super::RangeLimit { min: Some(1_000_000), max: Some(28_000_000) }),
ppt_step: Some(1_000_000),
clock_min: Some(super::RangeLimit { min: 400, max: 2200 }),
clock_max: Some(super::RangeLimit { min: 400, max: 2200 }),
clock_min: Some(super::RangeLimit { min: Some(400), max: Some(2200) }),
clock_max: Some(super::RangeLimit { min: Some(400), max: Some(2200) }),
clock_step: Some(100),
..Default::default()
})),
@ -170,7 +170,14 @@ impl Default for Base {
]
}
],
messages: Vec::new(),
messages: vec![
super::DeveloperMessage {
id: 1,
title: "Welcome".to_owned(),
body: "Thanks for installing PowerTools! For more information, please check the wiki. For bugs and requests, please create an issue on GitHub.".to_owned(),
url: Some("https://github.com/NGnius/PowerTools/wiki".to_owned()),
}
],
refresh: Some("http://limits.ngni.us:45000/powertools/v1".to_owned())
}
}

View file

@ -3,6 +3,6 @@ use serde::{Deserialize, Serialize};
/// Base JSON limits information
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RangeLimit<T> {
pub min: T,
pub max: T,
pub min: Option<T>,
pub max: Option<T>,
}

View file

@ -2,6 +2,27 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@ -17,6 +38,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.1"
@ -25,9 +61,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.0"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "bitflags"
@ -46,9 +82,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.12.0"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "byteorder"
@ -76,40 +112,30 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.24"
version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "cpufeatures"
version = "0.2.6"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c"
dependencies = [
"libc",
]
@ -124,60 +150,25 @@ dependencies = [
"typenum",
]
[[package]]
name = "cxx"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn 2.0.12",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
[[package]]
name = "cxxbridge-macro"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
]
[[package]]
name = "digest"
version = "0.10.6"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "encoding_rs"
version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -186,9 +177,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
dependencies = [
"percent-encoding",
]
@ -247,9 +238,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.8"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
@ -257,10 +248,16 @@ dependencies = [
]
[[package]]
name = "h2"
version = "0.3.16"
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "h2"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
dependencies = [
"bytes",
"fnv",
@ -308,12 +305,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.2.6"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "http"
@ -351,9 +345,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hyper"
version = "0.14.25"
version = "0.14.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
dependencies = [
"bytes",
"futures-channel",
@ -375,9 +369,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.54"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@ -389,19 +383,18 @@ dependencies = [
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cxx",
"cxx-build",
"cc",
]
[[package]]
name = "idna"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@ -419,28 +412,28 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.6"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
[[package]]
name = "js-sys"
version = "0.3.61"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.140"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "limits_core"
version = "1.0.0"
version = "2.0.1"
dependencies = [
"serde",
"serde_json",
@ -448,7 +441,7 @@ dependencies = [
[[package]]
name = "limits_srv"
version = "1.0.0"
version = "2.0.1"
dependencies = [
"chrono",
"limits_core",
@ -458,23 +451,11 @@ dependencies = [
"warp",
]
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "log"
version = "0.4.17"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "memchr"
@ -499,39 +480,41 @@ dependencies = [
]
[[package]]
name = "mio"
version = "0.8.6"
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
]
[[package]]
name = "multiparty"
version = "0.1.0"
name = "multer"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed1ec6589a6d4a1e0b33b4c0a3f6ee96dfba88ebdb3da51403fd7cf0a24a4b04"
checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
dependencies = [
"bytes",
"futures-core",
"encoding_rs",
"futures-util",
"http",
"httparse",
"log",
"memchr",
"pin-project-lite",
"try-lock",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
"mime",
"spin",
"version_check",
]
[[package]]
@ -545,51 +528,60 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.15.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.1"
name = "object"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "percent-encoding"
version = "2.2.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pin-project"
version = "1.0.12"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.12"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
[[package]]
name = "pin-utils"
@ -605,18 +597,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.54"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
@ -652,19 +644,25 @@ dependencies = [
]
[[package]]
name = "rustls-pemfile"
version = "1.0.2"
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustls-pemfile"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [
"base64 0.21.0",
"base64 0.21.2",
]
[[package]]
name = "ryu"
version = "1.0.13"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
[[package]]
name = "scoped-tls"
@ -672,37 +670,31 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scratch"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]]
name = "serde"
version = "1.0.159"
version = "1.0.166"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.159"
version = "1.0.166"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.95"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [
"itoa",
"ryu",
@ -752,36 +744,22 @@ dependencies = [
]
[[package]]
name = "syn"
version = "1.0.109"
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "syn"
version = "2.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.40"
@ -799,7 +777,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn",
]
[[package]]
@ -830,11 +808,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.27.0"
version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"libc",
"mio",
@ -847,20 +826,20 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.0.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn",
]
[[package]]
name = "tokio-stream"
version = "0.1.12"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
dependencies = [
"futures-core",
"pin-project-lite",
@ -881,9 +860,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.7"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
dependencies = [
"bytes",
"futures-core",
@ -913,9 +892,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.30"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
dependencies = [
"once_cell",
]
@ -968,9 +947,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
[[package]]
name = "unicode-normalization"
@ -981,17 +960,11 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "url"
version = "2.3.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
@ -1012,19 +985,18 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "want"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"log",
"try-lock",
]
[[package]]
name = "warp"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c"
checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69"
dependencies = [
"bytes",
"futures-channel",
@ -1035,7 +1007,7 @@ dependencies = [
"log",
"mime",
"mime_guess",
"multiparty",
"multer",
"percent-encoding",
"pin-project",
"rustls-pemfile",
@ -1065,9 +1037,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -1075,24 +1047,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 1.0.109",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1100,22 +1072,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "winapi"
@ -1133,15 +1105,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@ -1150,27 +1113,27 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.46.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -1183,42 +1146,42 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

View file

@ -1,12 +1,12 @@
[package]
name = "limits_srv"
version = "1.0.0"
version = "2.0.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
limits_core = { version = "1.0.0", path = "../limits_core" }
limits_core = { version = "2.0.1", path = "../limits_core" }
chrono = { version = "0.4" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View file

@ -289,6 +289,13 @@
]
}
],
"messages": [],
"messages": [
{
"id": 1,
"title": "Welcome",
"body": "Thanks for installing PowerTools! For more information, please check the wiki. For bugs and requests, please create an issue on GitHub.",
"url": "https://github.com/NGnius/PowerTools/wiki"
}
],
"refresh": "http://limits.ngni.us:45000/powertools/v1"
}

View file

@ -1,18 +1,14 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RangeLimit<T> {
pub min: T,
pub max: T,
}
impl<T> From<limits_core::json::RangeLimit<T>> for RangeLimit<T> {
#[inline]
fn from(other: limits_core::json::RangeLimit<T>) -> Self {
RangeLimit {
min: other.min,
max: other.max,
}
impl<T> RangeLimit<T> {
pub fn new(min: T, max: T) -> Self {
Self { min, max }
}
}

View file

@ -30,11 +30,6 @@ pub fn current_now(sender: Sender<ApiMessage>) -> impl AsyncCallable {
}
}
/// Current current (ha!) web method
/*pub fn current_now(_: super::ApiParameterType) -> super::ApiParameterType {
super::utility::map_optional_result(crate::settings::driver::read_current_now())
}*/
/// Charge now web method
pub fn charge_now(sender: Sender<ApiMessage>) -> impl AsyncCallable {
let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety
@ -110,6 +105,31 @@ pub fn charge_design(sender: Sender<ApiMessage>) -> impl AsyncCallable {
}
}
/// Charge wattage web method
pub fn charge_power(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("power_now callback send failed");
sender2
.lock()
.unwrap()
.send(ApiMessage::Battery(BatteryMessage::ReadChargePower(
Box::new(callback),
)))
.expect("power_now send failed");
rx.recv().expect("power_now callback recv failed")
}
};
super::async_utils::AsyncIshGetter {
set_get: getter,
trans_getter: |result| super::utility::map_optional_result(Ok(result)),
}
}
/// Generate set battery charge rate web method
pub fn set_charge_rate(
sender: Sender<ApiMessage>,

View file

@ -6,6 +6,7 @@ use usdpl_back::AsyncCallable;
use crate::settings::{MinMax, SettingError, SettingVariant};
//use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
use super::handler::{ApiMessage, CpuMessage};
use super::utility::map_optional;
/// Available CPUs web method
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
@ -205,8 +206,8 @@ pub fn set_clock_limits(
setter(
index as usize,
MinMax {
min: safe_min as u64,
max: safe_max as u64,
min: Some(safe_min as u64),
max: Some(safe_max as u64),
},
);
vec![safe_min.into(), safe_max.into()]
@ -220,6 +221,7 @@ pub fn set_clock_limits(
vec!["set_clock_limits missing parameter 0".into()]
}
}
// TODO allow param 0 and/or 1 to be Primitive::Empty
}
pub fn get_clock_limits(
@ -245,7 +247,7 @@ pub fn get_clock_limits(
move |params_in: super::ApiParameterType| {
if let Some(&Primitive::F64(index)) = params_in.get(0) {
if let Some(min_max) = getter(index as usize) {
vec![min_max.min.into(), min_max.max.into()]
vec![map_optional(min_max.min), map_optional(min_max.max)]
} else {
vec![Primitive::Empty, Primitive::Empty]
}

View file

@ -321,3 +321,57 @@ pub fn force_apply(
vec![true.into()]
}
}
/// Generate get periodicals aggregate method
pub fn get_periodicals(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 (rx_curr, callback_curr) = build_comms("battery current callback send failed");
let (rx_charge_now, callback_charge_now) = build_comms("battery charge now callback send failed");
let (rx_charge_full, callback_charge_full) = build_comms("battery charge full callback send failed");
let (rx_charge_power, callback_charge_power) = build_comms("battery charge power callback send failed");
let (rx_path, callback_path) = build_comms("general get path (periodical) send failed");
let sender_locked = sender2
.lock()
.unwrap();
let curr = wait_for_response(&*sender_locked, rx_curr,
ApiMessage::Battery(super::handler::BatteryMessage::ReadCurrentNow(callback_curr)), "battery current");
let charge_now = wait_for_response(&*sender_locked, rx_charge_now,
ApiMessage::Battery(super::handler::BatteryMessage::ReadChargeNow(callback_charge_now)), "battery charge now");
let charge_full = wait_for_response(&*sender_locked, rx_charge_full,
ApiMessage::Battery(super::handler::BatteryMessage::ReadChargeFull(callback_charge_full)), "battery charge full");
let charge_power = wait_for_response(&*sender_locked, rx_charge_power,
ApiMessage::Battery(super::handler::BatteryMessage::ReadChargePower(callback_charge_power)), "battery charge power");
let settings_path = wait_for_response(&*sender_locked, rx_path,
ApiMessage::General(GeneralMessage::GetPath(callback_path)), "general get path");
vec![
super::utility::map_optional(curr),
super::utility::map_optional(charge_now),
super::utility::map_optional(charge_full),
super::utility::map_optional(charge_power),
super::utility::map_optional(settings_path.to_str()),
]
}
};
super::async_utils::AsyncIshGetter {
set_get: getter,
trans_getter: |result| result,
}
}
fn build_comms<'a, T: Send + 'a>(msg: &'static str) -> (mpsc::Receiver<T>, Box<dyn FnOnce(T) + Send + 'a>) {
let (tx, rx) = mpsc::channel();
let callback = move |t: T| tx.send(t).expect(msg);
(rx, Box::new(callback))
}
fn wait_for_response<T>(sender: &Sender<ApiMessage>, rx: mpsc::Receiver<T>, api_msg: ApiMessage, op: &str) -> T {
sender.send(api_msg).expect(&format!("{} send failed", op));
rx.recv().expect(&format!("{} callback recv failed", op))
}

View file

@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex};
use usdpl_back::core::serdes::Primitive;
use usdpl_back::AsyncCallable;
use super::utility::map_optional;
use crate::settings::MinMax;
//use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
use super::handler::{ApiMessage, GpuMessage};
@ -94,8 +95,8 @@ pub fn set_clock_limits(
let safe_max = if max < min { min } else { max };
let safe_min = if min > max { max } else { min };
setter(MinMax {
min: safe_min as _,
max: safe_max as _,
min: Some(safe_min as _),
max: Some(safe_max as _),
});
vec![(safe_min as u64).into(), (safe_max as u64).into()]
} else {
@ -105,6 +106,7 @@ pub fn set_clock_limits(
vec!["set_clock_limits missing parameter 0".into()]
}
}
// TODO allow param 0 and/or 1 to be Primitive::Empty
}
pub fn get_clock_limits(sender: Sender<ApiMessage>) -> impl AsyncCallable {
@ -131,7 +133,7 @@ pub fn get_clock_limits(sender: Sender<ApiMessage>) -> impl AsyncCallable {
set_get: getter,
trans_getter: |clocks: Option<MinMax<u64>>| {
clocks
.map(|x| vec![x.min.into(), x.max.into()])
.map(|x| vec![map_optional(x.min), map_optional(x.max)])
.unwrap_or_else(|| vec![Primitive::Empty, Primitive::Empty])
},
}

View file

@ -39,6 +39,7 @@ pub enum BatteryMessage {
ReadChargeNow(Callback<Option<f64>>),
ReadChargeDesign(Callback<Option<f64>>),
ReadCurrentNow(Callback<Option<f64>>),
ReadChargePower(Callback<Option<f64>>),
SetChargeLimit(Option<f64>),
GetChargeLimit(Callback<Option<f64>>),
}
@ -55,6 +56,7 @@ 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::ReadChargePower(cb) => cb(settings.read_charge_power()),
Self::SetChargeLimit(limit) => settings.charge_limit(limit),
Self::GetChargeLimit(cb) => cb(settings.get_charge_limit()),
}
@ -268,7 +270,9 @@ impl ApiMessageHandler {
while let Ok(msg) = self.intake.try_recv() {
dirty |= self.process(settings, msg);
}
if dirty /*|| dirty_echo */ {
if dirty
/*|| dirty_echo */
{
//dirty_echo = dirty; // echo only once
// run on_set
@ -288,6 +292,16 @@ impl ApiMessageHandler {
let settings_clone = settings.json();
let save_json: SettingsJson = settings_clone.into();
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
if let Some(event) = &settings.general.on_event().on_save {
if !event.is_empty() {
unwrap_maybe_fatal(
std::process::Command::new("/bin/bash")
.args(&["-c", event])
.spawn(),
"Failed to start on_save event command",
);
}
}
log::debug!("Saved settings to {}", save_path.display());
if let Err(e) = crate::utility::chown_settings_dir() {
log::error!("Failed to change config dir permissions: {}", e);

141
backend/src/api/message.rs Normal file
View file

@ -0,0 +1,141 @@
use std::sync::{atomic::{AtomicU64, Ordering}, Arc};
use serde::{Deserialize, Serialize};
use usdpl_back::AsyncCallable;
use usdpl_back::core::serdes::Primitive;
use limits_core::json::DeveloperMessage;
use crate::MESSAGE_SEEN_ID_FILE;
use crate::utility::settings_dir;
#[derive(Serialize, Deserialize)]
pub struct ApiMessage {
/// Message identifier
pub id: Option<u64>,
/// Message title
pub title: String,
/// Message content
pub body: String,
/// Link for further information
pub url: Option<String>,
}
impl std::convert::From<DeveloperMessage> for ApiMessage {
fn from(other: DeveloperMessage) -> Self {
Self {
id: Some(other.id),
title: other.title,
body: other.body,
url: other.url,
}
}
}
fn get_dev_messages() -> Vec<ApiMessage> {
crate::settings::get_dev_messages().drain(..).map(|msg| ApiMessage::from(msg)).collect()
}
pub struct MessageHandler {
seen: Arc<AtomicU64>,
}
impl MessageHandler {
pub fn new() -> Self {
let last_seen_id = if let Ok(last_seen_id_bytes) = std::fs::read(settings_dir().join(MESSAGE_SEEN_ID_FILE)) {
if last_seen_id_bytes.len() >= 8 /* bytes in u64 */ {
u64::from_le_bytes([
last_seen_id_bytes[0],
last_seen_id_bytes[1],
last_seen_id_bytes[2],
last_seen_id_bytes[3],
last_seen_id_bytes[4],
last_seen_id_bytes[5],
last_seen_id_bytes[6],
last_seen_id_bytes[7],
])
} else {
u64::MAX
}
} else {
u64::MIN
};
Self {
seen: Arc::new(AtomicU64::new(last_seen_id)),
}
}
pub fn to_callables(self) -> (AsyncMessageGetter, AsyncMessageDismisser) {
(
AsyncMessageGetter {
seen: self.seen.clone(),
},
AsyncMessageDismisser {
seen: self.seen.clone(),
}
)
}
}
pub struct AsyncMessageGetter {
seen: Arc<AtomicU64>,
}
impl AsyncMessageGetter {
fn remove_before_id(id: u64, messages: impl Iterator<Item=ApiMessage>) -> impl Iterator<Item=ApiMessage> {
messages.skip_while(move |msg| if let Some(msg_id) = msg.id { msg_id <= id } else { true })
}
}
#[async_trait::async_trait]
impl AsyncCallable for AsyncMessageGetter {
async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType {
let since = if let Some(param0) = params.get(0) {
if let Primitive::Empty = param0 {
self.seen.load(Ordering::Relaxed)
} else if let Primitive::U64(since) = param0 {
*since
} else {
return vec!["get message invalid parameter 0".into()];
}
} else {
self.seen.load(Ordering::Relaxed)
};
let mut messages = get_dev_messages();
Self::remove_before_id(since, messages.drain(..))
.filter_map(|msg| serde_json::to_string(&msg).ok().map(|x| Primitive::Json(x)))
.collect()
}
}
pub struct AsyncMessageDismisser {
seen: Arc<AtomicU64>,
}
#[async_trait::async_trait]
impl AsyncCallable for AsyncMessageDismisser {
async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType {
let id = if let Some(param0) = params.get(0) {
if let Primitive::Empty = param0 {
None
} else if let Primitive::F64(since) = param0 {
Some(*since as u64)
} else {
return vec!["dismiss message invalid parameter 0".into()];
}
} else {
None
};
if let Some(id) = id {
self.seen.store(id, Ordering::Relaxed);
let filename = settings_dir().join(MESSAGE_SEEN_ID_FILE);
if let Err(e) = std::fs::write(&filename, id.to_le_bytes()) {
log::error!("Failed to write seen id to {}: {}", filename.display(), e);
}
} else {
// TODO clear non-dev messages in cache
}
vec![true.into()]
}
}

View file

@ -5,6 +5,7 @@ pub mod cpu;
pub mod general;
pub mod gpu;
pub mod handler;
pub mod message;
mod utility;
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;

View file

@ -19,10 +19,7 @@ pub fn map_optional_result<T: Into<Primitive>>(
result: Result<Option<T>, SettingError>,
) -> super::ApiParameterType {
match result {
Ok(val) => match val {
Some(val) => vec![val.into()],
None => vec![Primitive::Empty],
},
Ok(val) => vec![map_optional(val)],
Err(e) => {
log::debug!("Mapping error to primitive: {}", e);
vec![e.msg.into()]
@ -30,6 +27,13 @@ pub fn map_optional_result<T: Into<Primitive>>(
}
}
pub fn map_optional<T: Into<Primitive>>(option: Option<T>) -> Primitive {
match option {
Some(val) => val.into(),
None => Primitive::Empty,
}
}
/*#[inline]
pub fn map_empty_result<T: Into<Primitive>>(
result: Result<(), SettingError>,

View file

@ -7,3 +7,5 @@ pub const DEFAULT_SETTINGS_FILE: &str = "default_settings.json";
pub const DEFAULT_SETTINGS_NAME: &str = "Main";
pub const LIMITS_FILE: &str = "limits_cache.json";
pub const MESSAGE_SEEN_ID_FILE: &str = "seen_message.bin";

View file

@ -73,10 +73,6 @@ fn main() -> Result<(), ()> {
}
let _limits_handle = crate::settings::limits_worker_spawn();
log::info!(
"Detected device automatically, starting with driver: {:?} (This can be overriden)",
crate::settings::auto_detect_provider()
);
let mut loaded_settings =
persist::SettingsJson::open(utility::settings_dir().join(DEFAULT_SETTINGS_FILE))
@ -88,6 +84,11 @@ fn main() -> Result<(), ()> {
)
});
log::info!(
"Detected device automatically {:?}, using driver: {:?} (This can be overriden)",
crate::settings::auto_detect_provider(), loaded_settings.cpus.provider()
);
log::debug!("Settings: {:?}", loaded_settings);
let (api_handler, api_sender) = crate::api::handler::ApiMessageHandler::new();
@ -96,6 +97,8 @@ fn main() -> Result<(), ()> {
let _resume_handle = resume_worker::spawn(api_sender.clone());
let _power_handle = power_worker::spawn(api_sender.clone());
let (message_getter, message_dismisser) = api::message::MessageHandler::new().to_callables();
let instance = Instance::new(PORT)
.register("V_INFO", |_: Vec<Primitive>| {
#[cfg(debug_assertions)]
@ -126,6 +129,10 @@ fn main() -> Result<(), ()> {
"BATTERY_charge_design",
api::battery::charge_design(api_sender.clone()),
)
.register_async(
"BATTERY_charge_power",
api::battery::charge_power(api_sender.clone()),
)
.register(
"BATTERY_set_charge_rate",
api::battery::set_charge_rate(api_sender.clone()),
@ -279,7 +286,13 @@ fn main() -> Result<(), ()> {
.register(
"GENERAL_on_unplugged",
api::battery::on_unplugged(api_sender.clone()),
);
)
.register_async(
"GENERAL_get_periodicals",
api::general::get_periodicals(api_sender.clone())
)
.register_async("MESSAGE_get", message_getter)
.register_async("MESSAGE_dismiss", message_dismisser);
if let Err(e) = loaded_settings.on_set() {
e.iter()

View file

@ -9,6 +9,8 @@ pub struct BatteryJson {
pub charge_mode: Option<String>,
#[serde(default)]
pub events: Vec<BatteryEventJson>,
#[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<String>,
}
#[derive(Serialize, Deserialize, Clone)]
@ -24,6 +26,7 @@ impl Default for BatteryJson {
charge_rate: None,
charge_mode: None,
events: Vec::new(),
root: None,
}
}
}

View file

@ -12,6 +12,8 @@ pub struct CpuJson {
pub online: bool,
pub clock_limits: Option<MinMaxJson<u64>>,
pub governor: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<String>,
}
impl Default for CpuJson {
@ -20,6 +22,7 @@ impl Default for CpuJson {
online: true,
clock_limits: None,
governor: "schedutil".to_owned(),
root: None,
}
}
}

View file

@ -5,6 +5,25 @@ use serde::{Deserialize, Serialize};
use super::JsonError;
use super::{BatteryJson, CpuJson, DriverJson, GpuJson};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OnEventJson {
pub on_save: Option<String>,
pub on_load: Option<String>,
pub on_set: Option<String>,
pub on_resume: Option<String>,
}
impl Default for OnEventJson {
fn default() -> Self {
Self {
on_save: None,
on_load: None,
on_set: None,
on_resume: None,
}
}
}
#[derive(Serialize, Deserialize)]
pub struct SettingsJson {
pub version: u64,
@ -14,6 +33,7 @@ pub struct SettingsJson {
pub gpu: GpuJson,
pub battery: BatteryJson,
pub provider: Option<DriverJson>,
pub events: Option<OnEventJson>,
}
impl Default for SettingsJson {
@ -26,6 +46,7 @@ impl Default for SettingsJson {
gpu: GpuJson::default(),
battery: BatteryJson::default(),
provider: None,
events: None,
}
}
}
@ -58,6 +79,6 @@ impl SettingsJson {
#[derive(Serialize, Deserialize, Clone)]
pub struct MinMaxJson<T> {
pub max: T,
pub min: T,
pub max: Option<T>,
pub min: Option<T>,
}

View file

@ -10,6 +10,8 @@ pub struct GpuJson {
pub slow_ppt: Option<u64>,
pub clock_limits: Option<MinMaxJson<u64>>,
pub slow_memory: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<String>,
}
impl Default for GpuJson {
@ -19,6 +21,7 @@ impl Default for GpuJson {
slow_ppt: None,
clock_limits: None,
slow_memory: false,
root: None,
}
}
}

View file

@ -8,7 +8,7 @@ mod gpu;
pub use battery::{BatteryEventJson, BatteryJson};
pub use cpu::CpuJson;
pub use driver::DriverJson;
pub use general::{MinMaxJson, SettingsJson};
pub use general::{MinMaxJson, OnEventJson, SettingsJson};
pub use gpu::GpuJson;
pub use error::JsonError;

View file

@ -5,7 +5,7 @@ use std::time::{Duration, Instant};
use crate::api::handler::ApiMessage;
//use crate::utility::unwrap_maybe_fatal;
const ALLOWED_ERROR: f64 = 100.0; // period of 10ms with 100x means sleep has to be >= 1s to be detected
const ALLOWED_ERROR: f64 = 20.0; // period of 50ms with 20x means sleep has to be >= 1s to be detected
pub fn spawn(sender: Sender<ApiMessage>) -> JoinHandle<()> {
thread::spawn(move || {

View file

@ -303,6 +303,7 @@ impl DriverBuilder {
path: json_path,
name: profile_name,
driver: DriverJson::AutoDetect,
events: Default::default(),
}),
cpus: None,
gpu: None,

View file

@ -3,3 +3,4 @@ pub mod limits_worker;
mod utility;
pub use auto_detect::{auto_detect0, auto_detect_provider};
pub use utility::get_dev_messages;

View file

@ -1,3 +1,19 @@
use limits_core::json::{DeveloperMessage, Base};
pub fn limits_path() -> std::path::PathBuf {
crate::utility::settings_dir().join(crate::consts::LIMITS_FILE)
}
// NOTE: eats errors
pub fn get_dev_messages() -> Vec<DeveloperMessage> {
let limits_path = limits_path();
if let Ok(file) = std::fs::File::open(&limits_path) {
if let Ok(base) = serde_json::from_reader::<_, Base>(file) {
base.messages
} else {
vec![]
}
} else {
vec![]
}
}

View file

@ -21,6 +21,7 @@ impl Driver {
path: json_path,
name: settings.name,
driver: DriverJson::SteamDeck,
events: settings.events.unwrap_or_default(),
}),
cpus: Box::new(super::steam_deck::Cpus::from_json(
settings.cpus,
@ -51,6 +52,7 @@ impl Driver {
path: json_path,
name: settings.name,
driver: DriverJson::SteamDeck,
events: settings.events.unwrap_or_default(),
}),
cpus: Box::new(super::steam_deck::Cpus::from_json(
settings.cpus,
@ -72,6 +74,7 @@ impl Driver {
path: json_path,
name: settings.name,
driver: DriverJson::SteamDeckAdvance,
events: settings.events.unwrap_or_default(),
}),
cpus: Box::new(super::steam_deck::Cpus::from_json(
settings.cpus,

View file

@ -34,16 +34,43 @@ pub struct General {
pub path: PathBuf,
pub name: String,
pub driver: crate::persist::DriverJson,
pub events: crate::persist::OnEventJson,
}
impl OnSet for General {
fn on_set(&mut self) -> Result<(), Vec<SettingError>> {
if let Some(event) = &self.events.on_set {
if !event.is_empty() {
std::process::Command::new("/bin/bash")
.args(&["-c", event])
.spawn()
.map_err(|e| {
vec![SettingError {
msg: format!("on_set event command error: {}", e),
setting: SettingVariant::General,
}]
})?;
}
}
Ok(())
}
}
impl OnResume for General {
fn on_resume(&self) -> Result<(), Vec<SettingError>> {
if let Some(event) = &self.events.on_resume {
if !event.is_empty() {
std::process::Command::new("/bin/bash")
.args(&["-c", event])
.spawn()
.map_err(|e| {
vec![SettingError {
msg: format!("on_resume event command error: {}", e),
setting: SettingVariant::General,
}]
})?;
}
}
Ok(())
}
}
@ -82,6 +109,10 @@ impl TGeneral for General {
fn provider(&self) -> crate::persist::DriverJson {
self.driver.clone()
}
fn on_event(&self) -> &crate::persist::OnEventJson {
&self.events
}
}
#[derive(Debug)]
@ -214,6 +245,17 @@ impl Settings {
*self.general.persistent() = false;
}
self.general.path(filename);
if let Some(event) = &self.general.on_event().on_load {
if !event.is_empty() {
std::process::Command::new("/bin/bash")
.args(&["-c", event])
.spawn()
.map_err(|e| SettingError {
msg: format!("on_save event command error: {}", e),
setting: SettingVariant::General,
})?;
}
}
Ok(*self.general.persistent())
}
@ -257,6 +299,7 @@ impl Settings {
gpu: self.gpu.json(),
battery: self.battery.json(),
provider: Some(self.general.provider()),
events: Some(self.general.on_event().clone()),
}
}
}

View file

@ -1,6 +1,7 @@
use std::convert::Into;
use limits_core::json::GenericBatteryLimit;
use sysfuss::SysEntity;
use crate::persist::BatteryJson;
use crate::settings::TBattery;
@ -10,6 +11,7 @@ use crate::settings::{OnResume, OnSet, SettingError};
pub struct Battery {
#[allow(dead_code)]
limits: GenericBatteryLimit,
sysfs: sysfuss::PowerSupplyPath,
}
impl Into<BatteryJson> for Battery {
@ -19,6 +21,7 @@ impl Into<BatteryJson> for Battery {
charge_rate: None,
charge_mode: None,
events: Vec::default(),
root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|s| s.to_owned())),
}
}
}
@ -37,18 +40,41 @@ impl Battery {
}
}
fn find_psu_sysfs(root: Option<impl AsRef<std::path::Path>>) -> sysfuss::PowerSupplyPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.power_supply(crate::settings::util::always_satisfied) {
Ok(mut iter) => {
iter.next()
.unwrap_or_else(|| {
log::error!("Failed to find generic battery power_supply in sysfs (no results), using naive fallback");
root.power_supply_by_name("BAT0")
})
},
Err(e) => {
log::error!("Failed to find generic battery power_supply in sysfs ({}), using naive fallback", e);
root.power_supply_by_name("BAT0")
}
}
}
pub fn from_limits(limits: limits_core::json::GenericBatteryLimit) -> Self {
// TODO
Self { limits }
Self {
limits,
sysfs: Self::find_psu_sysfs(None::<&'static str>),
}
}
pub fn from_json_and_limits(
_other: BatteryJson,
other: BatteryJson,
_version: u64,
limits: limits_core::json::GenericBatteryLimit,
) -> Self {
// TODO
Self { limits }
Self {
limits,
sysfs: Self::find_psu_sysfs(other.root)
}
}
}
@ -129,6 +155,10 @@ impl TBattery for Battery {
None
}
fn read_charge_power(&self) -> Option<f64> {
None
}
fn charge_limit(&mut self, _limit: Option<f64>) {}
fn get_charge_limit(&self) -> Option<f64> {

View file

@ -3,6 +3,7 @@ use std::convert::{AsMut, AsRef, Into};
use limits_core::json::GenericCpuLimit;
use super::FromGenericCpuInfo;
use crate::api::RangeLimit;
use crate::persist::CpuJson;
use crate::settings::{min_max_from_json, MinMax};
use crate::settings::{OnResume, OnSet, SettingError};
@ -201,6 +202,7 @@ pub struct Cpu {
limits: GenericCpuLimit,
index: usize,
state: crate::state::steam_deck::Cpu,
root: std::path::PathBuf,
}
/*impl Cpu {
@ -234,6 +236,7 @@ impl FromGenericCpuInfo for Cpu {
limits,
index: cpu_index,
state: crate::state::steam_deck::Cpu::default(),
root: "/".into(),
}
}
@ -257,6 +260,7 @@ impl FromGenericCpuInfo for Cpu {
limits,
index: i,
state: crate::state::steam_deck::Cpu::default(),
root: other.root.unwrap_or_else(|| "/".to_string()).into(),
},
_ => Self {
online: other.online,
@ -265,6 +269,7 @@ impl FromGenericCpuInfo for Cpu {
limits,
index: i,
state: crate::state::steam_deck::Cpu::default(),
root: other.root.unwrap_or_else(|| "/".to_string()).into(),
},
}
}
@ -330,8 +335,16 @@ impl Cpu {
fn limits(&self) -> crate::api::CpuLimits {
crate::api::CpuLimits {
clock_min_limits: self.limits.clock_min.clone().map(|x| x.into()),
clock_max_limits: self.limits.clock_max.clone().map(|x| x.into()),
clock_min_limits: self
.limits
.clock_min
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(5_000))),
clock_max_limits: self
.limits
.clock_max
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(5_000))),
clock_step: self.limits.clock_step,
governors: self.governors(),
}
@ -345,6 +358,7 @@ impl Into<CpuJson> for Cpu {
online: self.online,
clock_limits: self.clock_limits.map(|x| x.into()),
governor: self.governor,
root: self.root.to_str().map(|s| s.to_owned()),
}
}
}

View file

@ -1,7 +1,9 @@
use std::convert::Into;
use limits_core::json::GenericGpuLimit;
use sysfuss::{BasicEntityPath, SysEntity};
use crate::api::RangeLimit;
use crate::persist::GpuJson;
use crate::settings::TGpu;
use crate::settings::{min_max_from_json, MinMax};
@ -14,6 +16,7 @@ pub struct Gpu {
pub slow_ppt: Option<u64>,
pub clock_limits: Option<MinMax<u64>>,
limits: GenericGpuLimit,
sysfs: BasicEntityPath,
}
impl Gpu {
@ -30,6 +33,23 @@ impl Gpu {
}
}*/
fn find_card_sysfs(root: Option<impl AsRef<std::path::Path>>) -> BasicEntityPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.class("drm", crate::settings::util::always_satisfied) {
Ok(mut iter) => {
iter.next()
.unwrap_or_else(|| {
log::error!("Failed to find generic gpu drm in sysfs (no results), using naive fallback");
BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0"))
})
},
Err(e) => {
log::error!("Failed to find generic gpu drm in sysfs ({}), using naive fallback", e);
BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0"))
}
}
}
pub fn from_limits(limits: limits_core::json::GenericGpuLimit) -> Self {
Self {
slow_memory: false,
@ -37,6 +57,7 @@ impl Gpu {
slow_ppt: None,
clock_limits: None,
limits,
sysfs: Self::find_card_sysfs(None::<&'static str>),
}
}
@ -64,6 +85,7 @@ impl Gpu {
},
clock_limits: clock_lims,
limits,
sysfs: Self::find_card_sysfs(other.root)
}
}
}
@ -76,6 +98,7 @@ impl Into<GpuJson> for Gpu {
slow_ppt: self.slow_ppt,
clock_limits: self.clock_limits.map(|x| x.into()),
slow_memory: false,
root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|s| s.to_owned()))
}
}
}
@ -97,14 +120,38 @@ impl crate::settings::OnPowerEvent for Gpu {}
impl TGpu for Gpu {
fn limits(&self) -> crate::api::GpuLimits {
crate::api::GpuLimits {
fast_ppt_limits: self.limits.fast_ppt.clone().map(|x| x.into()),
slow_ppt_limits: self.limits.slow_ppt.clone().map(|x| x.into()),
fast_ppt_limits: self
.limits
.fast_ppt
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(15_000_000))),
slow_ppt_limits: self
.limits
.slow_ppt
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(15_000_000))),
ppt_step: self.limits.ppt_step.unwrap_or(1_000_000),
tdp_limits: self.limits.tdp.clone().map(|x| x.into()),
tdp_boost_limits: self.limits.tdp_boost.clone().map(|x| x.into()),
tdp_limits: self
.limits
.tdp
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(15_000_000))),
tdp_boost_limits: self
.limits
.tdp_boost
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(15_000_000))),
tdp_step: self.limits.tdp_step.unwrap_or(42),
clock_min_limits: self.limits.clock_min.clone().map(|x| x.into()),
clock_max_limits: self.limits.clock_max.clone().map(|x| x.into()),
clock_min_limits: self
.limits
.clock_min
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(3_000))),
clock_max_limits: self
.limits
.clock_max
.clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(3_000))),
clock_step: self.limits.clock_step.unwrap_or(100),
memory_control_capable: false,
}
@ -116,10 +163,20 @@ impl TGpu for Gpu {
fn ppt(&mut self, fast: Option<u64>, slow: Option<u64>) {
if let Some(fast_lims) = &self.limits.fast_ppt {
self.fast_ppt = fast.map(|x| x.clamp(fast_lims.min, fast_lims.max));
self.fast_ppt = fast.map(|x| {
x.clamp(
fast_lims.min.unwrap_or(0),
fast_lims.max.unwrap_or(u64::MAX),
)
});
}
if let Some(slow_lims) = &self.limits.slow_ppt {
self.slow_ppt = slow.map(|x| x.clamp(slow_lims.min, slow_lims.max));
self.slow_ppt = slow.map(|x| {
x.clamp(
slow_lims.min.unwrap_or(0),
slow_lims.max.unwrap_or(u64::MAX),
)
});
}
}

View file

@ -128,24 +128,22 @@ impl Gpu {
}
if let Some(clock_limits) = &self.generic.clock_limits {
self.state.clock_limits_set = true;
lock.set_max_gfxclk_freq(clock_limits.max as _)
.map_err(|e| SettingError {
msg: format!(
"RyzenAdj set_max_gfxclk_freq({}) err: {}",
clock_limits.max, e
),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
lock.set_min_gfxclk_freq(clock_limits.min as _)
.map_err(|e| SettingError {
msg: format!(
"RyzenAdj set_min_gfxclk_freq({}) err: {}",
clock_limits.min, e
),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
if let Some(max) = clock_limits.max {
lock.set_max_gfxclk_freq(max as _)
.map_err(|e| SettingError {
msg: format!("RyzenAdj set_max_gfxclk_freq({}) err: {}", max, e),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
}
if let Some(min) = clock_limits.min {
lock.set_min_gfxclk_freq(min as _)
.map_err(|e| SettingError {
msg: format!("RyzenAdj set_min_gfxclk_freq({}) err: {}", min, e),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
}
} else if self.state.clock_limits_set {
self.state.clock_limits_set = false;
let limits = self.generic.limits();
@ -218,24 +216,22 @@ impl Gpu {
.unwrap_or_else(|e| errors.push(e));
}
if let Some(clock_limits) = &self.generic.clock_limits {
lock.set_max_gfxclk_freq(clock_limits.max as _)
.map_err(|e| SettingError {
msg: format!(
"RyzenAdj set_max_gfxclk_freq({}) err: {}",
clock_limits.max, e
),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
lock.set_min_gfxclk_freq(clock_limits.min as _)
.map_err(|e| SettingError {
msg: format!(
"RyzenAdj set_min_gfxclk_freq({}) err: {}",
clock_limits.min, e
),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
if let Some(max) = clock_limits.max {
lock.set_max_gfxclk_freq(max as _)
.map_err(|e| SettingError {
msg: format!("RyzenAdj set_max_gfxclk_freq({}) err: {}", max, e),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
}
if let Some(min) = clock_limits.min {
lock.set_min_gfxclk_freq(min as _)
.map_err(|e| SettingError {
msg: format!("RyzenAdj set_min_gfxclk_freq({}) err: {}", min, e),
setting: SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
}
}
Ok(())
}

View file

@ -8,8 +8,8 @@ pub type MinMax<T> = RangeLimit<T>;
pub fn min_max_from_json<T, X: Into<T>>(other: MinMaxJson<X>, _version: u64) -> MinMax<T> {
MinMax {
max: other.max.into(),
min: other.min.into(),
max: other.max.map(|x| x.into()),
min: other.min.map(|x| x.into()),
}
}
@ -17,8 +17,8 @@ impl<X: Into<Y>, Y> Into<MinMaxJson<Y>> for RangeLimit<X> {
#[inline]
fn into(self) -> MinMaxJson<Y> {
MinMaxJson {
max: self.max.into(),
min: self.min.into(),
max: self.max.map(|x| x.into()),
min: self.min.map(|x| x.into()),
}
}
}

View file

@ -11,7 +11,7 @@ pub mod generic_amd;
pub mod steam_deck;
pub mod unknown;
pub use detect::{auto_detect0, auto_detect_provider, limits_worker::spawn as limits_worker_spawn};
pub use detect::{auto_detect0, auto_detect_provider, limits_worker::spawn as limits_worker_spawn, get_dev_messages};
pub use driver::Driver;
pub use general::{General, SettingVariant, Settings};
pub use min_max::{min_max_from_json, MinMax};

View file

@ -1,4 +1,8 @@
use std::convert::Into;
use std::sync::Arc;
use sysfuss::{PowerSupplyAttribute, PowerSupplyPath, HwMonAttribute, HwMonAttributeItem, HwMonAttributeType, HwMonPath, SysEntity, SysEntityAttributesExt, SysAttribute};
use sysfuss::capability::attributes;
use super::oc_limits::{BatteryLimits, OverclockLimits};
use super::util::ChargeMode;
@ -15,6 +19,8 @@ pub struct Battery {
limits: BatteryLimits,
state: crate::state::steam_deck::Battery,
driver_mode: crate::persist::DriverJson,
sysfs_bat: PowerSupplyPath,
sysfs_hwmon: Arc<HwMonPath>,
}
#[derive(Debug, Clone)]
@ -32,6 +38,7 @@ struct EventInstruction {
charge_rate: Option<u64>,
charge_mode: Option<ChargeMode>,
is_triggered: bool,
sysfs_hwmon: Arc<HwMonPath>,
}
impl OnPowerEvent for EventInstruction {
@ -99,17 +106,17 @@ impl EventInstruction {
.trim_start_matches('>')
.parse::<f64>()
.ok()
.map(|x| EventTrigger::BatteryAbove(x/100.0)),
.map(|x| EventTrigger::BatteryAbove(x / 100.0)),
s if s.starts_with('<') => s
.trim_start_matches('<')
.parse::<f64>()
.ok()
.map(|x| EventTrigger::BatteryBelow(x/100.0)),
.map(|x| EventTrigger::BatteryBelow(x / 100.0)),
_ => None,
}
}
fn from_json(other: BatteryEventJson, _version: u64) -> Self {
fn from_json(other: BatteryEventJson, _version: u64, hwmon: Arc<HwMonPath>) -> Self {
Self {
trigger: Self::str_to_trigger(&other.trigger).unwrap_or(EventTrigger::Ignored),
charge_rate: other.charge_rate,
@ -118,6 +125,7 @@ impl EventInstruction {
.map(|x| Battery::str_to_charge_mode(&x))
.flatten(),
is_triggered: false,
sysfs_hwmon: hwmon,
}
}
@ -136,12 +144,17 @@ impl EventInstruction {
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),
let attr = if MAX_BATTERY_CHARGE_RATE_ATTR.exists(&*self.sysfs_hwmon) {
MAX_BATTERY_CHARGE_RATE_ATTR
} else {
MAXIMUM_BATTERY_CHARGE_RATE_ATTR
};
self.sysfs_hwmon.set(attr, charge_rate).map_err(
|e| SettingError {
msg: format!("Failed to write to `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
})
.map(|_| ())
},
)
} else {
Ok(())
}
@ -173,12 +186,36 @@ impl Into<BatteryEventJson> for EventInstruction {
const BATTERY_VOLTAGE: f64 = 7.7;
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
/*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/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
const USB_PD_IN_CURRENT_PATH: &str = "/sys/class/hwmon/hwmon5/curr1_input"; // read-only*/
const BATTERY_NEEDS: &[PowerSupplyAttribute] = &[
PowerSupplyAttribute::Type,
PowerSupplyAttribute::CurrentNow,
PowerSupplyAttribute::ChargeNow,
PowerSupplyAttribute::ChargeFull,
PowerSupplyAttribute::ChargeFullDesign,
PowerSupplyAttribute::CycleCount,
PowerSupplyAttribute::Capacity,
PowerSupplyAttribute::CapacityLevel,
];
const HWMON_NEEDS: &[HwMonAttribute] = &[
HwMonAttribute::name(),
HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Input),
HwMonAttribute::new(HwMonAttributeType::Curr, 1, HwMonAttributeItem::Input),
//HwMonAttribute::custom("maximum_battery_charge_rate"), // NOTE: Cannot filter by custom capabilities
];
const MAXIMUM_BATTERY_CHARGE_RATE_ATTR: HwMonAttribute = HwMonAttribute::custom("maximum_battery_charge_rate");
const MAX_BATTERY_CHARGE_RATE_ATTR: HwMonAttribute = HwMonAttribute::custom("maximum_battery_charge_rate");
const MAX_BATTERY_CHARGE_LEVEL_ATTR: HwMonAttribute = HwMonAttribute::custom("max_battery_charge_level");
impl Battery {
#[inline]
@ -190,6 +227,7 @@ impl Battery {
} else {
crate::persist::DriverJson::SteamDeckAdvance
};
let hwmon_sys = Arc::new(Self::find_hwmon_sysfs(None::<&'static str>));
match version {
0 => Self {
charge_rate: other.charge_rate,
@ -200,11 +238,13 @@ impl Battery {
events: other
.events
.into_iter()
.map(|x| EventInstruction::from_json(x, version))
.map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone()))
.collect(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(),
driver_mode: driver,
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: hwmon_sys,
},
_ => Self {
charge_rate: other.charge_rate,
@ -215,15 +255,68 @@ impl Battery {
events: other
.events
.into_iter()
.map(|x| EventInstruction::from_json(x, version))
.map(|x| EventInstruction::from_json(x, version, hwmon_sys.clone()))
.collect(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(),
driver_mode: driver,
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: hwmon_sys,
},
}
}
fn find_battery_sysfs(root: Option<impl AsRef<std::path::Path>>) -> PowerSupplyPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.power_supply(attributes(BATTERY_NEEDS.into_iter().copied())) {
Ok(mut iter) => {
let psu = iter.next()
.unwrap_or_else(|| {
log::error!("Failed to find SteamDeck battery power_supply in sysfs (no results), using naive fallback");
root.power_supply_by_name("BAT1")
});
log::info!("Found SteamDeck battery power_supply in sysfs: {}", psu.as_ref().display());
psu
},
Err(e) => {
log::error!("Failed to find SteamDeck battery power_supply in sysfs ({}), using naive fallback", e);
root.power_supply_by_name("BAT1")
}
}
}
fn find_hwmon_sysfs(root: Option<impl AsRef<std::path::Path>>) -> HwMonPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.hwmon_by_name(super::util::JUPITER_HWMON_NAME) {
Ok(hwmon) => {
if !hwmon.capable(attributes(HWMON_NEEDS.into_iter().copied())) {
log::warn!("Found incapable SteamDeck battery hwmon in sysfs (hwmon by name {} exists but missing attributes), persevering because ignorance is bliss", super::util::JUPITER_HWMON_NAME);
} else {
log::info!("Found SteamDeck battery hwmon {} in sysfs: {}", super::util::JUPITER_HWMON_NAME, hwmon.as_ref().display());
}
hwmon
},
Err(e) => {
log::warn!("Failed to find SteamDeck battery hwmon {} in sysfs ({}), trying alternate name",
super::util::JUPITER_HWMON_NAME, e);
match root.hwmon_by_name(super::util::STEAMDECK_HWMON_NAME) {
Ok(hwmon) => {
if !hwmon.capable(attributes(HWMON_NEEDS.into_iter().copied())) {
log::warn!("Found incapable SteamDeck battery hwmon in sysfs (hwmon by name {} exists but missing attributes), persevering because ignorance is bliss", super::util::STEAMDECK_HWMON_NAME);
} else {
log::info!("Found SteamDeck battery hwmon {} in sysfs: {}", super::util::STEAMDECK_HWMON_NAME, hwmon.as_ref().display());
}
hwmon
},
Err(e) => {
log::error!("Failed to find SteamDeck battery hwmon {} in sysfs ({}), using naive fallback", super::util::STEAMDECK_HWMON_NAME, e);
root.hwmon_by_index(5)
}
}
}
}
}
#[inline]
fn charge_mode_to_str(mode: ChargeMode) -> String {
match mode {
@ -244,6 +337,40 @@ impl Battery {
}
}
fn set_charge_rate(&mut self) -> Result<(), SettingError> {
if let Some(charge_rate) = self.charge_rate {
self.state.charge_rate_set = true;
let attr = if MAX_BATTERY_CHARGE_RATE_ATTR.exists(&*self.sysfs_hwmon) {
MAX_BATTERY_CHARGE_RATE_ATTR
} else {
MAXIMUM_BATTERY_CHARGE_RATE_ATTR
};
let path = attr.path(&*self.sysfs_hwmon);
self.sysfs_hwmon.set(attr, charge_rate).map_err(
|e| SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Battery,
},
)
} else if self.state.charge_rate_set {
self.state.charge_rate_set = false;
let attr = if MAX_BATTERY_CHARGE_RATE_ATTR.exists(&*self.sysfs_hwmon) {
MAX_BATTERY_CHARGE_RATE_ATTR
} else {
MAXIMUM_BATTERY_CHARGE_RATE_ATTR
};
let path = attr.path(&*self.sysfs_hwmon);
self.sysfs_hwmon.set(attr, self.limits.charge_rate.max,).map_err(
|e| SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Battery,
},
)
} else {
Ok(())
}
}
fn set_charge_mode(&mut self) -> Result<(), SettingError> {
if let Some(charge_mode) = self.charge_mode {
self.state.charge_mode_set = true;
@ -268,26 +395,7 @@ impl Battery {
fn set_all(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new();
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,
})
.unwrap_or_else(|e| errors.push(e));
} else if self.state.charge_rate_set {
self.state.charge_rate_set = false;
usdpl_back::api::files::write_single(
BATTERY_CHARGE_RATE_PATH,
self.limits.charge_rate.max,
)
.map_err(|e| SettingError {
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
setting: crate::settings::SettingVariant::Battery,
})
.unwrap_or_else(|e| errors.push(e));
}
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(())
@ -303,10 +411,11 @@ impl Battery {
}
}
pub fn read_current_now() -> Result<u64, SettingError> {
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
pub fn read_current_now(&self) -> Result<u64, SettingError> {
let attr = PowerSupplyAttribute::CurrentNow;
match self.sysfs_bat.attribute::<u64, _>(attr) {
Err(e) => Err(SettingError {
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
msg: format!("Failed to read from `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
}),
// this value is in uA, while it's set in mA
@ -315,10 +424,17 @@ impl Battery {
}
}
pub fn read_charge_now() -> Result<f64, SettingError> {
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_NOW_PATH) {
pub fn read_charge_power(&self) -> Result<f64, SettingError> {
let current = self.read_usb_current()?;
let voltage = self.read_usb_voltage()?;
Ok(current * voltage)
}
pub fn read_charge_now(&self) -> Result<f64, SettingError> {
let attr = PowerSupplyAttribute::ChargeNow;
match self.sysfs_bat.attribute::<u64, _>(attr) {
Err(e) => Err(SettingError {
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_NOW_PATH, e),
msg: format!("Failed to read from `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
}),
// convert to Wh
@ -326,10 +442,11 @@ impl Battery {
}
}
pub fn read_charge_full() -> Result<f64, SettingError> {
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_FULL_PATH) {
pub fn read_charge_full(&self) -> Result<f64, SettingError> {
let attr = PowerSupplyAttribute::ChargeFull;
match self.sysfs_bat.attribute::<u64, _>(attr) {
Err(e) => Err(SettingError {
msg: format!("Failed to read from `{}`: {}", BATTERY_CHARGE_FULL_PATH, e),
msg: format!("Failed to read from `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
}),
// convert to Wh
@ -337,13 +454,11 @@ impl Battery {
}
}
pub fn read_charge_design() -> Result<f64, SettingError> {
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CHARGE_DESIGN_PATH) {
pub fn read_charge_design(&self) -> Result<f64, SettingError> {
let attr = PowerSupplyAttribute::ChargeFullDesign;
match self.sysfs_bat.attribute::<u64, _>(attr) {
Err(e) => Err(SettingError {
msg: format!(
"Failed to read from `{}`: {}",
BATTERY_CHARGE_DESIGN_PATH, e
),
msg: format!("Failed to read from `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
}),
// convert to Wh
@ -351,10 +466,11 @@ impl Battery {
}
}
pub fn read_usb_voltage() -> Result<f64, SettingError> {
match usdpl_back::api::files::read_single::<_, u64, _>(USB_PD_IN_MVOLTAGE_PATH) {
pub fn read_usb_voltage(&self) -> Result<f64, SettingError> {
let attr = HwMonAttribute::new(HwMonAttributeType::In, 0, HwMonAttributeItem::Input);
match self.sysfs_hwmon.attribute::<u64, _>(attr) {
Err(e) => Err(SettingError {
msg: format!("Failed to read from `{}`: {}", USB_PD_IN_MVOLTAGE_PATH, e),
msg: format!("Failed to read from `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
}),
// convert to V (from mV)
@ -362,6 +478,17 @@ impl Battery {
}
}
pub fn read_usb_current(&self) -> Result<f64, SettingError> {
let attr = HwMonAttribute::new(HwMonAttributeType::Curr, 1, HwMonAttributeItem::Input);
match self.sysfs_hwmon.attribute::<u64, _>(attr) {
Err(e) => Err(SettingError {
msg: format!("Failed to read `{:?}`: {}", attr, e),
setting: crate::settings::SettingVariant::Battery,
}),
Ok(val) => Ok((val as f64) / 1000.0), // mA -> A
}
}
pub fn system_default() -> Self {
let (oc_limits, is_default) = OverclockLimits::load_or_default();
let oc_limits = oc_limits.battery;
@ -377,6 +504,8 @@ impl Battery {
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(),
driver_mode: driver,
sysfs_bat: Self::find_battery_sysfs(None::<&'static str>),
sysfs_hwmon: Arc::new(Self::find_hwmon_sysfs(None::<&'static str>)),
}
}
@ -416,6 +545,7 @@ impl Into<BatteryJson> for Battery {
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(),
root: self.sysfs_bat.root().or(self.sysfs_hwmon.root()).and_then(|p| p.as_ref().to_str().map(|x| x.to_owned()))
}
}
}
@ -454,7 +584,22 @@ impl OnPowerEvent for Battery {
PowerMode::BatteryCharge(_) => Ok(()),
}
.unwrap_or_else(|mut e| errors.append(&mut e));
let attr_exists = MAX_BATTERY_CHARGE_LEVEL_ATTR.exists(&*self.sysfs_hwmon);
log::info!("Does battery limit attribute (max_battery_charge_level) exist? {}", attr_exists);
for ev in &mut self.events {
if attr_exists {
if let EventTrigger::BatteryAbove(level) = ev.trigger {
if let Some(ChargeMode::Idle) = ev.charge_mode {
self.sysfs_hwmon.set(MAX_BATTERY_CHARGE_LEVEL_ATTR, (level * 100.0).round() as u64)
.unwrap_or_else(|e| errors.push(
SettingError {
msg: format!("Failed to write to {:?}: {}", MAX_BATTERY_CHARGE_LEVEL_ATTR, e),
setting: crate::settings::SettingVariant::Battery,
}
));
}
}
}
ev.on_power_event(new_mode)
.unwrap_or_else(|mut e| errors.append(&mut e));
}
@ -508,7 +653,7 @@ impl TBattery for Battery {
}
fn read_charge_full(&self) -> Option<f64> {
match Self::read_charge_full() {
match self.read_charge_full() {
Ok(x) => Some(x),
Err(e) => {
log::warn!("read_charge_full err: {}", e.msg);
@ -518,7 +663,7 @@ impl TBattery for Battery {
}
fn read_charge_now(&self) -> Option<f64> {
match Self::read_charge_now() {
match self.read_charge_now() {
Ok(x) => Some(x),
Err(e) => {
log::warn!("read_charge_now err: {}", e.msg);
@ -528,7 +673,7 @@ impl TBattery for Battery {
}
fn read_charge_design(&self) -> Option<f64> {
match Self::read_charge_design() {
match self.read_charge_design() {
Ok(x) => Some(x),
Err(e) => {
log::warn!("read_charge_design err: {}", e.msg);
@ -538,12 +683,30 @@ impl TBattery for Battery {
}
fn read_current_now(&self) -> Option<f64> {
match Self::read_current_now() {
Ok(x) => Some(x as f64),
Err(e) => {
log::warn!("read_current_now err: {}", e.msg);
None
if self.limits.extra_readouts {
match self.read_current_now() {
Ok(x) => Some(x as f64),
Err(e) => {
log::warn!("read_current_now err: {}", e.msg);
None
}
}
} else {
None
}
}
fn read_charge_power(&self) -> Option<f64> {
if self.limits.extra_readouts {
match self.read_charge_power() {
Ok(x) => Some(x as f64),
Err(e) => {
log::warn!("read_current_now err: {}", e.msg);
None
}
}
} else {
None
}
}
@ -561,6 +724,7 @@ impl TBattery for Battery {
charge_rate: None,
charge_mode: Some(ChargeMode::Idle),
is_triggered: false,
sysfs_hwmon: self.sysfs_hwmon.clone(),
};
} else {
self.events.remove(index);
@ -575,6 +739,7 @@ impl TBattery for Battery {
charge_rate: None,
charge_mode: Some(ChargeMode::Idle),
is_triggered: false,
sysfs_hwmon: self.sysfs_hwmon.clone(),
});
}
// lower limit
@ -591,6 +756,7 @@ impl TBattery for Battery {
charge_rate: None,
charge_mode: Some(ChargeMode::Normal),
is_triggered: false,
sysfs_hwmon: self.sysfs_hwmon.clone(),
};
} else {
self.events.remove(index);
@ -606,6 +772,7 @@ impl TBattery for Battery {
charge_rate: None,
charge_mode: Some(ChargeMode::Normal),
is_triggered: false,
sysfs_hwmon: self.sysfs_hwmon.clone(),
});
}
}
@ -628,7 +795,7 @@ impl TBattery for Battery {
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()) {
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);
@ -637,7 +804,7 @@ impl TBattery for Battery {
(Err(e), _) => errors.push(e),
(_, Err(e)) => errors.push(e),
}
match Self::read_usb_voltage() {
match self.read_usb_voltage() {
Ok(voltage) => {
if voltage > 0.0
&& self.state.charger_state != crate::state::steam_deck::ChargeState::PluggedIn

View file

@ -1,5 +1,7 @@
use std::convert::Into;
use sysfuss::{BasicEntityPath, SysEntity, SysEntityAttributesExt};
use super::oc_limits::{CpuLimits, CpusLimits, OverclockLimits};
use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT;
use crate::api::RangeLimit;
@ -11,6 +13,10 @@ use crate::settings::{TCpu, TCpus};
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
const CARD_EXTENSIONS: &[&'static str] = &[
super::DPM_FORCE_LIMITS_ATTRIBUTE
];
#[derive(Debug, Clone)]
pub struct Cpus {
pub cpus: Vec<Cpu>,
@ -230,9 +236,16 @@ pub struct Cpu {
limits: CpuLimits,
index: usize,
state: crate::state::steam_deck::Cpu,
sysfs: BasicEntityPath,
}
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
//const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
const CPU_CLOCK_LIMITS_ATTRIBUTE: &str = "device/pp_od_clk_voltage";
enum ClockType {
Min = 0,
Max = 1,
}
impl Cpu {
#[inline]
@ -245,6 +258,7 @@ impl Cpu {
limits: oc_limits,
index: i,
state: crate::state::steam_deck::Cpu::default(),
sysfs: Self::find_card_sysfs(other.root),
},
_ => Self {
online: other.online,
@ -253,99 +267,161 @@ impl Cpu {
limits: oc_limits,
index: i,
state: crate::state::steam_deck::Cpu::default(),
sysfs: Self::find_card_sysfs(other.root),
},
}
}
fn find_card_sysfs(root: Option<impl AsRef<std::path::Path>>) -> BasicEntityPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.class("drm", sysfuss::capability::attributes(crate::settings::util::CARD_NEEDS.into_iter().map(|s| s.to_string()))) {
Ok(iter) => {
let card = iter
.filter(|ent| if let Ok(name) = ent.name() { name.starts_with("card")} else { false })
.filter(|ent| super::util::card_also_has(ent, CARD_EXTENSIONS))
.next()
.unwrap_or_else(|| {
log::error!("Failed to find SteamDeck drm in sysfs (no results), using naive fallback");
BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0"))
});
log::info!("Found SteamDeck drm in sysfs: {}", card.as_ref().display());
card
},
Err(e) => {
log::error!("Failed to find SteamDeck drm in sysfs ({}), using naive fallback", e);
BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0"))
}
}
}
fn set_clock_limit(&self, index: usize, speed: u64, mode: ClockType) -> Result<(), SettingError> {
let payload = format!("p {} {} {}\n", index / 2, mode as u8, speed);
self.sysfs.set(CPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), &payload).map_err(|e| {
SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload, CPU_CLOCK_LIMITS_ATTRIBUTE, e
),
setting: crate::settings::SettingVariant::Cpu,
}
})
}
fn set_clock_limits(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new();
if let Some(clock_limits) = &self.clock_limits {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(true, self.index);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs)?;
log::debug!(
"Setting CPU {} (min, max) clockspeed to ({:?}, {:?})",
self.index,
clock_limits.min,
clock_limits.max
);
self.state.clock_limits_set = true;
// max clock
if let Some(max) = clock_limits.max {
self.set_clock_limit(self.index, max, ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
}
// min clock
if let Some(min) = clock_limits.min {
let valid_min = if min < self.limits.clock_min.min {
self.limits.clock_min.min
} else {
min
};
self.set_clock_limit(self.index, valid_min, ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
} else if self.state.clock_limits_set
|| (self.state.is_resuming && !self.limits.skip_resume_reclock)
|| POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual()
{
let mut errors = Vec::new();
self.state.clock_limits_set = false;
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(false, self.index);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT
.enforce_level(&self.sysfs)?;
if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() {
// always set clock speeds, since it doesn't reset correctly (kernel/hardware bug)
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs)?;
// disable manual clock limits
log::debug!("Setting CPU {} to default clockspeed", self.index);
// max clock
self.set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
// min clock
self.set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
}
// TODO remove this when it's no longer needed
self.clock_unset_workaround().unwrap_or_else(|mut e| errors.append(&mut e));
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
} else {
Ok(())
}
}
// https://github.com/NGnius/PowerTools/issues/107
fn clock_unset_workaround(&self) -> Result<(), Vec<SettingError>> {
if !self.state.is_resuming {
let mut errors = Vec::new();
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(true, self.index);
// always set clock speeds, since it doesn't reset correctly (kernel/hardware bug)
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs)?;
// disable manual clock limits
log::debug!("Setting CPU {} to default clockspeed", self.index);
// max clock
self.set_clock_limit(self.index, self.limits.clock_max.max, ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
// min clock
self.set_clock_limit(self.index, self.limits.clock_min.min, ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
self.set_confirm().unwrap_or_else(|e| errors.push(e));
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(false, self.index);
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
} else {
Ok(())
}
}
fn set_confirm(&self) -> Result<(), SettingError> {
self.sysfs.set(CPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), "c\n").map_err(|e| {
SettingError {
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_ATTRIBUTE, e),
setting: crate::settings::SettingVariant::Cpu,
}
})
}
fn set_force_performance_related(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new();
// set clock limits
//log::debug!("Setting {} to manual", CPU_FORCE_LIMITS_PATH);
//let mode: String = usdpl_back::api::files::read_single(CPU_FORCE_LIMITS_PATH.to_owned()).unwrap();
if let Some(clock_limits) = &self.clock_limits {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(true, self.index);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?;
log::debug!(
"Setting CPU {} (min, max) clockspeed to ({}, {})",
self.index,
clock_limits.min,
clock_limits.max
);
self.state.clock_limits_set = true;
// max clock
let payload_max = format!("p {} 1 {}\n", self.index / 2, clock_limits.max);
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_max, CPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Cpu,
})
.unwrap_or_else(|e| errors.push(e));
// min clock
let valid_min = if clock_limits.min < self.limits.clock_min.min {
self.limits.clock_min.min
} else {
clock_limits.min
};
let payload_min = format!("p {} 0 {}\n", self.index / 2, valid_min);
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_min, CPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Cpu,
})
.unwrap_or_else(|e| errors.push(e));
} else if self.state.clock_limits_set
|| (self.state.is_resuming && !self.limits.skip_resume_reclock)
|| POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual()
{
self.state.clock_limits_set = false;
if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?;
// disable manual clock limits
log::debug!("Setting CPU {} to default clockspeed", self.index);
// max clock
let payload_max = format!("p {} 1 {}\n", self.index / 2, self.limits.clock_max.max);
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_max, CPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Cpu,
})
.unwrap_or_else(|e| errors.push(e));
// min clock
let payload_min = format!("p {} 0 {}\n", self.index / 2, self.limits.clock_min.min);
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_min, CPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Cpu,
})
.unwrap_or_else(|e| errors.push(e));
}
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(false, self.index);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT
.enforce_level()
.unwrap_or_else(|mut e| errors.append(&mut e));
}
self.set_clock_limits()
.unwrap_or_else(|mut e| errors.append(&mut e));
// commit changes (if no errors have already occured)
if errors.is_empty() {
if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() {
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
vec![SettingError {
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
setting: crate::settings::SettingVariant::Cpu,
}]
})
self.set_confirm().map_err(|e| vec![e])
} else {
Ok(())
}
@ -354,6 +430,23 @@ impl Cpu {
}
}
fn set_governor(&self) -> Result<(), SettingError> {
if self.index == 0 || self.online {
let governor_path = cpu_governor_path(self.index);
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&self.governor, &governor_path, e
),
setting: crate::settings::SettingVariant::Cpu,
}
})
} else {
Ok(())
}
}
fn set_all(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new();
// set cpu online/offline
@ -371,19 +464,8 @@ impl Cpu {
self.set_force_performance_related()
.unwrap_or_else(|mut e| errors.append(&mut e));
// set governor
if self.index == 0 || self.online {
let governor_path = cpu_governor_path(self.index);
usdpl_back::api::files::write_single(&governor_path, &self.governor)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&self.governor, &governor_path, e
),
setting: crate::settings::SettingVariant::Cpu,
})
.unwrap_or_else(|e| errors.push(e));
}
self.set_governor().unwrap_or_else(|e| errors.push(e));
if errors.is_empty() {
Ok(())
} else {
@ -393,12 +475,14 @@ impl Cpu {
fn clamp_all(&mut self) {
if let Some(clock_limits) = &mut self.clock_limits {
clock_limits.min = clock_limits
.min
.clamp(self.limits.clock_min.min, self.limits.clock_min.max);
clock_limits.max = clock_limits
.max
.clamp(self.limits.clock_max.min, self.limits.clock_max.max);
if let Some(min) = clock_limits.min {
clock_limits.min =
Some(min.clamp(self.limits.clock_min.min, self.limits.clock_min.max));
}
if let Some(max) = clock_limits.max {
clock_limits.max =
Some(max.clamp(self.limits.clock_max.min, self.limits.clock_max.max));
}
}
}
@ -422,6 +506,7 @@ impl Cpu {
limits: oc_limits,
index: cpu_index,
state: crate::state::steam_deck::Cpu::default(),
sysfs: Self::find_card_sysfs(None::<&'static str>)
}
}
@ -461,6 +546,7 @@ impl Into<CpuJson> for Cpu {
online: self.online,
clock_limits: self.clock_limits.map(|x| x.into()),
governor: self.governor,
root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|r| r.to_owned()))
}
}
}

View file

@ -1,5 +1,7 @@
use std::convert::Into;
use sysfuss::{BasicEntityPath, HwMonPath, SysEntity, capability::attributes, SysEntityAttributesExt, SysAttribute};
use super::oc_limits::{GpuLimits, OverclockLimits};
use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT;
use crate::api::RangeLimit;
@ -8,8 +10,9 @@ use crate::settings::TGpu;
use crate::settings::{min_max_from_json, MinMax};
use crate::settings::{OnResume, OnSet, SettingError};
const SLOW_PPT: u8 = 1;
const FAST_PPT: u8 = 2;
// usually in /sys/class/hwmon/hwmon4/<attribute>
const SLOW_PPT_ATTRIBUTE: sysfuss::HwMonAttribute = sysfuss::HwMonAttribute::custom("power1_cap");
const FAST_PPT_ATTRIBUTE: sysfuss::HwMonAttribute = sysfuss::HwMonAttribute::custom("power2_cap");
#[derive(Debug, Clone)]
pub struct Gpu {
@ -20,11 +23,27 @@ pub struct Gpu {
limits: GpuLimits,
state: crate::state::steam_deck::Gpu,
driver_mode: crate::persist::DriverJson,
sysfs_card: BasicEntityPath,
sysfs_hwmon: HwMonPath
}
// same as CPU
const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk";
//const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
//const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk";
const GPU_CLOCK_LIMITS_ATTRIBUTE: &str = "device/pp_od_clk_voltage";
const GPU_MEMORY_DOWNCLOCK_ATTRIBUTE: &str = "device/pp_dpm_fclk";
const CARD_EXTENSIONS: &[&'static str] = &[
GPU_CLOCK_LIMITS_ATTRIBUTE,
GPU_MEMORY_DOWNCLOCK_ATTRIBUTE,
super::DPM_FORCE_LIMITS_ATTRIBUTE,
];
enum ClockType {
Min = 0,
Max = 1,
}
impl Gpu {
#[inline]
@ -44,6 +63,8 @@ impl Gpu {
limits: oc_limits.gpu,
state: crate::state::steam_deck::Gpu::default(),
driver_mode: driver,
sysfs_card: Self::find_card_sysfs(other.root.clone()),
sysfs_hwmon: Self::find_hwmon_sysfs(other.root),
},
_ => Self {
fast_ppt: other.fast_ppt,
@ -53,93 +74,104 @@ impl Gpu {
limits: oc_limits.gpu,
state: crate::state::steam_deck::Gpu::default(),
driver_mode: driver,
sysfs_card: Self::find_card_sysfs(other.root.clone()),
sysfs_hwmon: Self::find_hwmon_sysfs(other.root),
},
}
}
fn find_card_sysfs(root: Option<impl AsRef<std::path::Path>>) -> BasicEntityPath {
let root = crate::settings::util::root_or_default_sysfs(root);
match root.class("drm", attributes(crate::settings::util::CARD_NEEDS.into_iter().map(|s| s.to_string()))) {
Ok(iter) => {
let card = iter
.filter(|ent| if let Ok(name) = ent.name() { name.starts_with("card")} else { false })
.filter(|ent| super::util::card_also_has(ent, CARD_EXTENSIONS))
.next()
.unwrap_or_else(|| {
log::error!("Failed to find SteamDeck gpu drm in sysfs (no results), using naive fallback");
BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0"))
});
log::info!("Found SteamDeck gpu drm in sysfs: {}", card.as_ref().display());
card
},
Err(e) => {
log::error!("Failed to find SteamDeck gpu drm in sysfs ({}), using naive fallback", e);
BasicEntityPath::new(root.as_ref().join("sys/class/drm/card0"))
}
}
}
fn find_hwmon_sysfs(root: Option<impl AsRef<std::path::Path>>) -> HwMonPath {
let root = crate::settings::util::root_or_default_sysfs(root);
let hwmon = root.hwmon_by_name(super::util::GPU_HWMON_NAME).unwrap_or_else(|e| {
log::error!("Failed to find SteamDeck gpu hwmon in sysfs ({}), using naive fallback", e);
root.hwmon_by_index(4)
});
log::info!("Found SteamDeck gpu hwmon {} in sysfs: {}", super::util::GPU_HWMON_NAME, hwmon.as_ref().display());
hwmon
}
fn set_clock_limit(&self, speed: u64, mode: ClockType) -> Result<(), SettingError> {
let payload = format!("s {} {}\n", mode as u8, speed);
let path = GPU_CLOCK_LIMITS_ATTRIBUTE.path(&self.sysfs_card);
self.sysfs_card.set(GPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), &payload).map_err(|e| {
SettingError {
msg: format!("Failed to write `{}` to `{}`: {}", &payload, path.display(), e),
setting: crate::settings::SettingVariant::Gpu,
}
})
}
fn set_confirm(&self) -> Result<(), SettingError> {
let path = GPU_CLOCK_LIMITS_ATTRIBUTE.path(&self.sysfs_card);
self.sysfs_card.set(GPU_CLOCK_LIMITS_ATTRIBUTE.to_owned(), "c\n").map_err(|e| {
SettingError {
msg: format!("Failed to write `c` to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Gpu,
}
})
}
fn set_clocks(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new();
if let Some(clock_limits) = &self.clock_limits {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?;
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?;
// set clock limits
self.state.clock_limits_set = true;
// max clock
let payload_max = format!("s 1 {}\n", clock_limits.max);
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_max, GPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
if let Some(max) = clock_limits.max {
self.set_clock_limit(max, ClockType::Max).unwrap_or_else(|e| errors.push(e));
}
// min clock
let payload_min = format!("s 0 {}\n", clock_limits.min);
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_min, GPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Gpu,
})
.unwrap_or_else(|e| errors.push(e));
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").unwrap_or_else(
|e| {
errors.push(SettingError {
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
setting: crate::settings::SettingVariant::Gpu,
})
},
);
if let Some(min) = clock_limits.min {
self.set_clock_limit(min, ClockType::Min).unwrap_or_else(|e| errors.push(e));
}
self.set_confirm().unwrap_or_else(|e| errors.push(e));
} else if self.state.clock_limits_set
|| (self.state.is_resuming && !self.limits.skip_resume_reclock)
|| POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual()
{
self.state.clock_limits_set = false;
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.slow_memory);
if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?;
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?;
// disable manual clock limits
// max clock
let payload_max = format!("s 1 {}\n", self.limits.clock_max.max);
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_max, GPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Gpu,
})
self.set_clock_limit(self.limits.clock_max.max, ClockType::Max)
.unwrap_or_else(|e| errors.push(e));
// min clock
let payload_min = format!("s 0 {}\n", self.limits.clock_min.min);
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
&payload_min, GPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Gpu,
})
self.set_clock_limit(self.limits.clock_min.min, ClockType::Min)
.unwrap_or_else(|e| errors.push(e));
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").unwrap_or_else(
|e| {
errors.push(SettingError {
msg: format!(
"Failed to write `c` to `{}`: {}",
GPU_CLOCK_LIMITS_PATH, e
),
setting: crate::settings::SettingVariant::Gpu,
})
},
);
self.set_confirm().unwrap_or_else(|e| errors.push(e));
} else {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT
.enforce_level(&self.sysfs_card)
.unwrap_or_else(|mut e| errors.append(&mut e));
}
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.slow_memory);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT
.enforce_level()
.unwrap_or_else(|mut e| errors.append(&mut e));
}
if errors.is_empty() {
Ok(())
@ -148,32 +180,40 @@ impl Gpu {
}
}
fn set_slow_memory(&self, slow: bool) -> Result<(), SettingError> {
let path = GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.path(&self.sysfs_card);
if slow {
self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), slow as u8).map_err(|e| {
SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Gpu,
}
})
} else {
// NOTE: there is a GPU driver/hardware bug that prevents this from working
self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), "0 1\n").map_err(|e| {
SettingError {
msg: format!("Failed to write to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::Gpu,
}
})
}
}
fn set_force_performance_related(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new();
// enable/disable downclock of GPU memory (to 400Mhz?)
if self.slow_memory {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true);
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT
.enforce_level()
.enforce_level(&self.sysfs_card)
.unwrap_or_else(|mut e| errors.append(&mut e));
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
.unwrap_or_else(|e| {
errors.push(SettingError {
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
setting: crate::settings::SettingVariant::Gpu,
});
});
self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e));
} else if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() {
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
.unwrap_or_else(|e| {
errors.push(SettingError {
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
setting: crate::settings::SettingVariant::Gpu,
});
});
self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e));
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.clock_limits.is_some());
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT
.enforce_level()
.enforce_level(&self.sysfs_card)
.unwrap_or_else(|mut e| errors.append(&mut e));
}
self.set_clocks()
@ -181,11 +221,9 @@ impl Gpu {
// commit changes (if no errors have already occured)
if errors.is_empty() {
if self.slow_memory || self.clock_limits.is_some() {
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
vec![SettingError {
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
setting: crate::settings::SettingVariant::Gpu,
}]
self.set_confirm().map_err(|e| {
errors.push(e);
errors
})
} else {
Ok(())
@ -200,12 +238,11 @@ impl Gpu {
// set fast PPT
if let Some(fast_ppt) = &self.fast_ppt {
self.state.fast_ppt_set = true;
let fast_ppt_path = gpu_power_path(FAST_PPT);
usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt)
self.sysfs_hwmon.set(FAST_PPT_ATTRIBUTE, fast_ppt)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
fast_ppt, &fast_ppt_path, e
"Failed to write `{}` to `{:?}`: {}",
fast_ppt, FAST_PPT_ATTRIBUTE, e
),
setting: crate::settings::SettingVariant::Gpu,
})
@ -215,12 +252,11 @@ impl Gpu {
} else if self.state.fast_ppt_set {
self.state.fast_ppt_set = false;
let fast_ppt = self.limits.fast_ppt_default;
let fast_ppt_path = gpu_power_path(FAST_PPT);
usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt)
self.sysfs_hwmon.set(FAST_PPT_ATTRIBUTE, fast_ppt)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
fast_ppt, &fast_ppt_path, e
"Failed to write `{}` to `{:?}`: {}",
fast_ppt, FAST_PPT_ATTRIBUTE, e
),
setting: crate::settings::SettingVariant::Gpu,
})
@ -231,12 +267,11 @@ impl Gpu {
// set slow PPT
if let Some(slow_ppt) = &self.slow_ppt {
self.state.slow_ppt_set = true;
let slow_ppt_path = gpu_power_path(SLOW_PPT);
usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt)
self.sysfs_hwmon.set(SLOW_PPT_ATTRIBUTE, slow_ppt)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
slow_ppt, &slow_ppt_path, e
"Failed to write `{}` to `{:?}`: {}",
slow_ppt, SLOW_PPT_ATTRIBUTE, e
),
setting: crate::settings::SettingVariant::Gpu,
})
@ -246,12 +281,11 @@ impl Gpu {
} else if self.state.slow_ppt_set {
self.state.slow_ppt_set = false;
let slow_ppt = self.limits.slow_ppt_default;
let slow_ppt_path = gpu_power_path(SLOW_PPT);
usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt)
self.sysfs_hwmon.set(SLOW_PPT_ATTRIBUTE, slow_ppt)
.map_err(|e| SettingError {
msg: format!(
"Failed to write `{}` to `{}`: {}",
slow_ppt, &slow_ppt_path, e
"Failed to write `{}` to `{:?}`: {}",
slow_ppt, SLOW_PPT_ATTRIBUTE, e
),
setting: crate::settings::SettingVariant::Gpu,
})
@ -276,12 +310,14 @@ impl Gpu {
*slow_ppt = (*slow_ppt).clamp(self.limits.slow_ppt.min, self.limits.slow_ppt.max);
}
if let Some(clock_limits) = &mut self.clock_limits {
clock_limits.min = clock_limits
.min
.clamp(self.limits.clock_min.min, self.limits.clock_min.max);
clock_limits.max = clock_limits
.max
.clamp(self.limits.clock_max.min, self.limits.clock_max.max);
if let Some(min) = clock_limits.min {
clock_limits.min =
Some(min.clamp(self.limits.clock_min.min, self.limits.clock_min.max));
}
if let Some(max) = clock_limits.max {
clock_limits.max =
Some(max.clamp(self.limits.clock_max.min, self.limits.clock_max.max));
}
}
}
@ -299,6 +335,8 @@ impl Gpu {
} else {
crate::persist::DriverJson::SteamDeckAdvance
},
sysfs_card: Self::find_card_sysfs(None::<&'static str>),
sysfs_hwmon: Self::find_hwmon_sysfs(None::<&'static str>),
}
}
}
@ -311,6 +349,7 @@ impl Into<GpuJson> for Gpu {
slow_ppt: self.slow_ppt,
clock_limits: self.clock_limits.map(|x| x.into()),
slow_memory: self.slow_memory,
root: self.sysfs_card.root().or(self.sysfs_hwmon.root()).and_then(|p| p.as_ref().to_str().map(|r| r.to_owned()))
}
}
}
@ -392,8 +431,3 @@ impl TGpu for Gpu {
self.driver_mode.clone()
}
}
#[inline]
fn gpu_power_path(power_number: u8) -> String {
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
}

View file

@ -8,6 +8,6 @@ mod util;
pub use battery::Battery;
pub use cpu::{Cpu, Cpus};
pub use gpu::Gpu;
pub(self) use power_dpm_force::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT;
pub(self) use power_dpm_force::{POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT, DPM_FORCE_LIMITS_ATTRIBUTE};
pub use util::flash_led;

View file

@ -1,4 +1,4 @@
use crate::settings::MinMax;
use crate::api::RangeLimit as MinMax;
use serde::{Deserialize, Serialize};
const OC_LIMITS_FILEPATH: &str = "pt_oc.json";
@ -67,6 +67,7 @@ impl OverclockLimits {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(super) struct BatteryLimits {
pub charge_rate: MinMax<u64>,
pub extra_readouts: bool,
}
impl Default for BatteryLimits {
@ -76,6 +77,7 @@ impl Default for BatteryLimits {
min: 250,
max: 2500,
},
extra_readouts: false,
}
}
}
@ -171,6 +173,7 @@ fn oc_limits_filepath() -> std::path::PathBuf {
mod tests {
use super::*;
#[cfg(not(feature = "dev_stuff"))] // this can fail due to reading from incompletely-written file otherwise
#[test]
fn load_pt_oc() {
let mut file = std::fs::File::open("../pt_oc.json").unwrap();

View file

@ -5,6 +5,8 @@
use std::sync::atomic::{AtomicU64, Ordering};
use sysfuss::{BasicEntityPath, SysEntityAttributesExt, SysAttribute};
use crate::settings::SettingError;
const DEFAULT_BITS: u64 = 0;
@ -19,7 +21,8 @@ pub struct PDFPLManager(AtomicU64);
const GPU_BIT: usize = 1;
const CPU_BITS_START: usize = 2;
const DPM_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
//const DPM_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
pub const DPM_FORCE_LIMITS_ATTRIBUTE: &str = "device/power_dpm_force_performance_level";
impl PDFPLManager {
#[inline]
@ -56,49 +59,44 @@ impl PDFPLManager {
self.set(DEFAULT_BITS);
}
pub fn enforce_level(&self) -> Result<(), Vec<SettingError>> {
pub fn enforce_level(&self, entity: &BasicEntityPath) -> Result<(), Vec<SettingError>> {
let needs = self.needs_manual();
let mut errors = Vec::new();
let mode: String = usdpl_back::api::files::read_single(DPM_FORCE_LIMITS_PATH.to_owned())
let path = DPM_FORCE_LIMITS_ATTRIBUTE.path(entity);
let mode: String = entity.attribute(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned())
.map_err(|e| {
vec![SettingError {
msg: format!("Failed to read `{}`: {}", DPM_FORCE_LIMITS_PATH, e),
msg: format!("Failed to read `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::General,
}]
})?;
if mode != "manual" && needs {
log::info!("Setting `{}` to manual", DPM_FORCE_LIMITS_PATH);
log::info!("Setting `{}` to manual", path.display());
// set manual control
usdpl_back::api::files::write_single(DPM_FORCE_LIMITS_PATH, "manual")
entity.set(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned(), "manual")
.map_err(|e| {
errors.push(SettingError {
msg: format!(
"Failed to write `manual` to `{}`: {}",
DPM_FORCE_LIMITS_PATH, e
),
msg: format!("Failed to write `manual` to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::General,
})
})
.unwrap_or(());
} else if mode != "auto" && !needs {
log::info!("Setting `{}` to auto", DPM_FORCE_LIMITS_PATH);
log::info!("Setting `{}` to auto", path.display());
// unset manual control
usdpl_back::api::files::write_single(DPM_FORCE_LIMITS_PATH, "auto")
entity.set(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned(), "auto")
.map_err(|e| {
errors.push(SettingError {
msg: format!(
"Failed to write `auto` to `{}`: {}",
DPM_FORCE_LIMITS_PATH, e
),
msg: format!("Failed to write `auto` to `{}`: {}", path.display(), e),
setting: crate::settings::SettingVariant::General,
})
})
.unwrap_or(());
}
if let Ok(mode_now) =
usdpl_back::api::files::read_single::<_, String, _>(DPM_FORCE_LIMITS_PATH.to_owned())
entity.attribute::<String, _>(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned())
{
log::debug!("Mode for `{}` is now `{}`", DPM_FORCE_LIMITS_PATH, mode_now);
log::debug!("Mode for `{}` is now `{}`", path.display(), mode_now);
} else {
log::debug!("Error getting new mode for debugging purposes");
}

View file

@ -8,6 +8,15 @@
use std::fs::OpenOptions;
use std::io::{Error, Read, Seek, SeekFrom, Write};
pub const JUPITER_HWMON_NAME: &'static str = "jupiter";
pub const STEAMDECK_HWMON_NAME: &'static str = "steamdeck_hwmon";
pub const GPU_HWMON_NAME: &'static str = "amdgpu";
pub fn card_also_has(card: &dyn sysfuss::SysEntity, extensions: &'static [&'static str]) -> bool {
extensions.iter()
.all(|ext| card.as_ref().join(ext).exists())
}
#[inline]
fn write2(p0: u8, p1: u8) -> Result<usize, Error> {
write_to(0x6c, 0x81)?;

View file

@ -104,6 +104,8 @@ pub trait TGeneral: OnSet + OnResume + OnPowerEvent + Debug + Send {
fn name(&mut self, name: String);
fn provider(&self) -> crate::persist::DriverJson;
fn on_event(&self) -> &'_ crate::persist::OnEventJson;
}
pub trait TBattery: OnSet + OnResume + OnPowerEvent + Debug + Send {
@ -127,6 +129,8 @@ pub trait TBattery: OnSet + OnResume + OnPowerEvent + Debug + Send {
fn read_current_now(&self) -> Option<f64>;
fn read_charge_power(&self) -> Option<f64>;
fn charge_limit(&mut self, limit: Option<f64>);
fn get_charge_limit(&self) -> Option<f64>;

View file

@ -14,6 +14,7 @@ impl Into<BatteryJson> for Battery {
charge_rate: None,
charge_mode: None,
events: Vec::default(),
root: None,
}
}
}
@ -75,6 +76,10 @@ impl TBattery for Battery {
None
}
fn read_charge_power(&self) -> Option<f64> {
None
}
fn charge_limit(&mut self, _limit: Option<f64>) {}
fn get_charge_limit(&self) -> Option<f64> {

View file

@ -180,6 +180,7 @@ pub struct Cpu {
pub governor: String,
index: usize,
state: crate::state::steam_deck::Cpu,
root: std::path::PathBuf,
}
impl Cpu {
@ -191,12 +192,14 @@ impl Cpu {
governor: other.governor,
index: i,
state: crate::state::steam_deck::Cpu::default(),
root: other.root.unwrap_or_else(|| "/".to_owned()).into(),
},
_ => Self {
online: other.online,
governor: other.governor,
index: i,
state: crate::state::steam_deck::Cpu::default(),
root: other.root.unwrap_or_else(|| "/".to_owned()).into(),
},
}
}
@ -243,6 +246,7 @@ impl Cpu {
.unwrap_or("schedutil".to_owned()),
index: cpu_index,
state: crate::state::steam_deck::Cpu::default(),
root: "/".into()
}
}
@ -263,6 +267,7 @@ impl Into<CpuJson> for Cpu {
online: self.online,
clock_limits: None,
governor: self.governor,
root: self.root.to_str().map(|s| s.to_owned()),
}
}
}

View file

@ -29,6 +29,7 @@ impl Into<GpuJson> for Gpu {
slow_ppt: None,
clock_limits: None,
slow_memory: false,
root: None,
}
}
}

View file

@ -6,6 +6,23 @@ pub fn guess_smt(cpus: &Vec<crate::persist::CpuJson>) -> bool {
guess
}
pub fn root_or_default_sysfs(root: Option<impl AsRef<std::path::Path>>) -> sysfuss::SysPath {
if let Some(root) = root {
sysfuss::SysPath::path(root)
} else {
sysfuss::SysPath::default()
}
}
pub fn always_satisfied<'a, X>(_: &'a X) -> bool {
true
}
pub const CARD_NEEDS: &[&'static str] = &[
"dev",
"uevent"
];
#[cfg(test)]
mod test {
use super::*;

View file

@ -76,6 +76,9 @@ fn version_filepath() -> std::path::PathBuf {
pub fn save_version_file() -> std::io::Result<usize> {
let path = version_filepath();
if let Some(parent_dir) = path.parent() {
std::fs::create_dir_all(parent_dir)?;
}
std::fs::File::create(path)?.write(crate::consts::PACKAGE_VERSION.as_bytes())
}

View file

@ -1,6 +1,6 @@
{
"name": "PowerTools",
"version": "1.3.1",
"version": "1.4.0",
"description": "Power tweaks for power users",
"scripts": {
"build": "shx rm -rf dist && rollup -c",
@ -9,7 +9,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/NGnius/PowerTools.git"
"url": "git+https://git.ngni.us/NG-SD-Plugins/PowerTools.git"
},
"keywords": [
"plugin",
@ -21,9 +21,9 @@
"author": "NGnius (Graham) <ngniusness@gmail.com>",
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/NGnius/PowerTools/issues"
"url": "https://git.ngni.us/NG-SD-Plugins/PowerTools/issues"
},
"homepage": "https://github.com/NGnius/PowerTools#readme",
"homepage": "https://git.ngni.us/NG-SD-Plugins/PowerTools#readme",
"devDependencies": {
"@rollup/plugin-commonjs": "^21.1.0",
"@rollup/plugin-json": "^4.1.0",
@ -35,12 +35,12 @@
"rollup": "^2.79.1",
"rollup-plugin-import-assets": "^1.1.1",
"shx": "^0.3.4",
"tslib": "^2.5.0",
"tslib": "^2.5.3",
"typescript": "^4.9.5"
},
"dependencies": {
"decky-frontend-lib": "~3.20.7",
"react-icons": "^4.8.0",
"decky-frontend-lib": "~3.21.1",
"react-icons": "^4.9.0",
"usdpl-front": "file:src/usdpl_front"
}
}

View file

@ -6,6 +6,6 @@
"discord_id": "106537989684887552",
"description": "Power tweaks for power users",
"tags": [ "utility", "power-management", "root" ],
"image": "https://raw.githubusercontent.com/NGnius/PowerTools/main/assets/thumbnail.png"
"image": "https://git.ngni.us/NG-SD-Plugins/PowerTools/raw/branch/main/assets/thumbnail.png"
}
}

View file

@ -1,12 +1,16 @@
lockfileVersion: '6.0'
lockfileVersion: '6.1'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
decky-frontend-lib:
specifier: ~3.20.7
version: 3.20.7
specifier: ~3.21.1
version: 3.21.1
react-icons:
specifier: ^4.8.0
version: 4.8.0(react@18.2.0)
specifier: ^4.9.0
version: 4.9.0(react@18.2.0)
usdpl-front:
specifier: file:src/usdpl_front
version: file:src/usdpl_front
@ -26,7 +30,7 @@ devDependencies:
version: 4.0.0(rollup@2.79.1)
'@rollup/plugin-typescript':
specifier: ^8.5.0
version: 8.5.0(rollup@2.79.1)(tslib@2.5.0)(typescript@4.9.5)
version: 8.5.0(rollup@2.79.1)(tslib@2.5.3)(typescript@4.9.5)
'@types/react':
specifier: 16.14.0
version: 16.14.0
@ -43,21 +47,21 @@ devDependencies:
specifier: ^0.3.4
version: 0.3.4
tslib:
specifier: ^2.5.0
version: 2.5.0
specifier: ^2.5.3
version: 2.5.3
typescript:
specifier: ^4.9.5
version: 4.9.5
packages:
/@jridgewell/gen-mapping@0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.17
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.18
dev: true
/@jridgewell/resolve-uri@3.1.0:
@ -70,19 +74,23 @@ packages:
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/source-map@0.3.2:
resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
/@jridgewell/source-map@0.3.3:
resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==}
dependencies:
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.18
dev: true
/@jridgewell/sourcemap-codec@1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: true
/@jridgewell/trace-mapping@0.3.17:
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
/@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
dev: true
/@jridgewell/trace-mapping@0.3.18:
resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
@ -138,7 +146,7 @@ packages:
rollup: 2.79.1
dev: true
/@rollup/plugin-typescript@8.5.0(rollup@2.79.1)(tslib@2.5.0)(typescript@4.9.5):
/@rollup/plugin-typescript@8.5.0(rollup@2.79.1)(tslib@2.5.3)(typescript@4.9.5):
resolution: {integrity: sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==}
engines: {node: '>=8.0.0'}
peerDependencies:
@ -152,7 +160,7 @@ packages:
'@rollup/pluginutils': 3.1.0(rollup@2.79.1)
resolve: 1.22.2
rollup: 2.79.1
tslib: 2.5.0
tslib: 2.5.3
typescript: 4.9.5
dev: true
@ -171,35 +179,31 @@ packages:
/@types/eslint-scope@3.7.4:
resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
dependencies:
'@types/eslint': 8.37.0
'@types/estree': 0.0.51
'@types/eslint': 8.40.2
'@types/estree': 1.0.1
dev: true
/@types/eslint@8.37.0:
resolution: {integrity: sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==}
/@types/eslint@8.40.2:
resolution: {integrity: sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==}
dependencies:
'@types/estree': 0.0.51
'@types/json-schema': 7.0.11
'@types/estree': 1.0.1
'@types/json-schema': 7.0.12
dev: true
/@types/estree@0.0.39:
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
dev: true
/@types/estree@0.0.51:
resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
/@types/estree@1.0.1:
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
dev: true
/@types/estree@1.0.0:
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
/@types/json-schema@7.0.12:
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
dev: true
/@types/json-schema@7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true
/@types/node@18.15.11:
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
/@types/node@20.3.1:
resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==}
dev: true
/@types/prop-types@15.7.5:
@ -216,15 +220,15 @@ packages:
/@types/resolve@1.17.1:
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
dependencies:
'@types/node': 18.15.11
'@types/node': 20.3.1
dev: true
/@types/webpack@5.28.1:
resolution: {integrity: sha512-qw1MqGZclCoBrpiSe/hokSgQM/su8Ocpl3L/YHE0L6moyaypg4+5F7Uzq7NgaPKPxUxUbQ4fLPLpDWdR27bCZw==}
dependencies:
'@types/node': 18.15.11
'@types/node': 20.3.1
tapable: 2.2.1
webpack: 5.78.0
webpack: 5.87.0
transitivePeerDependencies:
- '@swc/core'
- esbuild
@ -232,109 +236,109 @@ packages:
- webpack-cli
dev: true
/@webassemblyjs/ast@1.11.1:
resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
/@webassemblyjs/ast@1.11.6:
resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
dependencies:
'@webassemblyjs/helper-numbers': 1.11.1
'@webassemblyjs/helper-wasm-bytecode': 1.11.1
'@webassemblyjs/helper-numbers': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
dev: true
/@webassemblyjs/floating-point-hex-parser@1.11.1:
resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==}
/@webassemblyjs/floating-point-hex-parser@1.11.6:
resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==}
dev: true
/@webassemblyjs/helper-api-error@1.11.1:
resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==}
/@webassemblyjs/helper-api-error@1.11.6:
resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==}
dev: true
/@webassemblyjs/helper-buffer@1.11.1:
resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==}
/@webassemblyjs/helper-buffer@1.11.6:
resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==}
dev: true
/@webassemblyjs/helper-numbers@1.11.1:
resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==}
/@webassemblyjs/helper-numbers@1.11.6:
resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==}
dependencies:
'@webassemblyjs/floating-point-hex-parser': 1.11.1
'@webassemblyjs/helper-api-error': 1.11.1
'@webassemblyjs/floating-point-hex-parser': 1.11.6
'@webassemblyjs/helper-api-error': 1.11.6
'@xtuc/long': 4.2.2
dev: true
/@webassemblyjs/helper-wasm-bytecode@1.11.1:
resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==}
/@webassemblyjs/helper-wasm-bytecode@1.11.6:
resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==}
dev: true
/@webassemblyjs/helper-wasm-section@1.11.1:
resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==}
/@webassemblyjs/helper-wasm-section@1.11.6:
resolution: {integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==}
dependencies:
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/helper-buffer': 1.11.1
'@webassemblyjs/helper-wasm-bytecode': 1.11.1
'@webassemblyjs/wasm-gen': 1.11.1
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/helper-buffer': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
'@webassemblyjs/wasm-gen': 1.11.6
dev: true
/@webassemblyjs/ieee754@1.11.1:
resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==}
/@webassemblyjs/ieee754@1.11.6:
resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==}
dependencies:
'@xtuc/ieee754': 1.2.0
dev: true
/@webassemblyjs/leb128@1.11.1:
resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==}
/@webassemblyjs/leb128@1.11.6:
resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==}
dependencies:
'@xtuc/long': 4.2.2
dev: true
/@webassemblyjs/utf8@1.11.1:
resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==}
/@webassemblyjs/utf8@1.11.6:
resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==}
dev: true
/@webassemblyjs/wasm-edit@1.11.1:
resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==}
/@webassemblyjs/wasm-edit@1.11.6:
resolution: {integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==}
dependencies:
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/helper-buffer': 1.11.1
'@webassemblyjs/helper-wasm-bytecode': 1.11.1
'@webassemblyjs/helper-wasm-section': 1.11.1
'@webassemblyjs/wasm-gen': 1.11.1
'@webassemblyjs/wasm-opt': 1.11.1
'@webassemblyjs/wasm-parser': 1.11.1
'@webassemblyjs/wast-printer': 1.11.1
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/helper-buffer': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
'@webassemblyjs/helper-wasm-section': 1.11.6
'@webassemblyjs/wasm-gen': 1.11.6
'@webassemblyjs/wasm-opt': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
'@webassemblyjs/wast-printer': 1.11.6
dev: true
/@webassemblyjs/wasm-gen@1.11.1:
resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==}
/@webassemblyjs/wasm-gen@1.11.6:
resolution: {integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==}
dependencies:
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/helper-wasm-bytecode': 1.11.1
'@webassemblyjs/ieee754': 1.11.1
'@webassemblyjs/leb128': 1.11.1
'@webassemblyjs/utf8': 1.11.1
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
'@webassemblyjs/ieee754': 1.11.6
'@webassemblyjs/leb128': 1.11.6
'@webassemblyjs/utf8': 1.11.6
dev: true
/@webassemblyjs/wasm-opt@1.11.1:
resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==}
/@webassemblyjs/wasm-opt@1.11.6:
resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==}
dependencies:
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/helper-buffer': 1.11.1
'@webassemblyjs/wasm-gen': 1.11.1
'@webassemblyjs/wasm-parser': 1.11.1
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/helper-buffer': 1.11.6
'@webassemblyjs/wasm-gen': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
dev: true
/@webassemblyjs/wasm-parser@1.11.1:
resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==}
/@webassemblyjs/wasm-parser@1.11.6:
resolution: {integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==}
dependencies:
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/helper-api-error': 1.11.1
'@webassemblyjs/helper-wasm-bytecode': 1.11.1
'@webassemblyjs/ieee754': 1.11.1
'@webassemblyjs/leb128': 1.11.1
'@webassemblyjs/utf8': 1.11.1
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/helper-api-error': 1.11.6
'@webassemblyjs/helper-wasm-bytecode': 1.11.6
'@webassemblyjs/ieee754': 1.11.6
'@webassemblyjs/leb128': 1.11.6
'@webassemblyjs/utf8': 1.11.6
dev: true
/@webassemblyjs/wast-printer@1.11.1:
resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==}
/@webassemblyjs/wast-printer@1.11.6:
resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==}
dependencies:
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/ast': 1.11.6
'@xtuc/long': 4.2.2
dev: true
@ -346,16 +350,16 @@ packages:
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
dev: true
/acorn-import-assertions@1.8.0(acorn@8.8.2):
resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==}
/acorn-import-assertions@1.9.0(acorn@8.9.0):
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
peerDependencies:
acorn: ^8
dependencies:
acorn: 8.8.2
acorn: 8.9.0
dev: true
/acorn@8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
/acorn@8.9.0:
resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
@ -388,15 +392,15 @@ packages:
concat-map: 0.0.1
dev: true
/browserslist@4.21.5:
resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
/browserslist@4.21.9:
resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001474
electron-to-chromium: 1.4.353
node-releases: 2.0.10
update-browserslist-db: 1.0.10(browserslist@4.21.5)
caniuse-lite: 1.0.30001503
electron-to-chromium: 1.4.433
node-releases: 2.0.12
update-browserslist-db: 1.0.11(browserslist@4.21.9)
dev: true
/buffer-from@1.1.2:
@ -408,8 +412,8 @@ packages:
engines: {node: '>=6'}
dev: true
/caniuse-lite@1.0.30001474:
resolution: {integrity: sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q==}
/caniuse-lite@1.0.30001503:
resolution: {integrity: sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==}
dev: true
/chrome-trace-event@1.0.3:
@ -433,8 +437,8 @@ packages:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
dev: true
/decky-frontend-lib@3.20.7:
resolution: {integrity: sha512-Zwwbo50cqpTbCfSCZaqITgTRvWs7pK9KO1A+Oo2sCC/DqOfyUtEH5niNPid4Qxu+yh4lsbEjTurJk1nCfd+nZw==}
/decky-frontend-lib@3.21.1:
resolution: {integrity: sha512-30605ET9qqZ6St6I9WmMmLGgSrTIdMwo7xy85+lRaF1miUd2icOGEJjwnbVcZDdkal+1fJ3tNEDXlchVfG4TrA==}
dev: false
/deepmerge@4.3.1:
@ -442,20 +446,20 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/electron-to-chromium@1.4.353:
resolution: {integrity: sha512-IdJVpMHJoBT/nn0GQ02wPfbhogDVpd1ud95lP//FTf5l35wzxKJwibB4HBdY7Q+xKPA1nkZ0UDLOMyRj5U5IAQ==}
/electron-to-chromium@1.4.433:
resolution: {integrity: sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ==}
dev: true
/enhanced-resolve@5.12.0:
resolution: {integrity: sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==}
/enhanced-resolve@5.15.0:
resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
engines: {node: '>=10.13.0'}
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.1
dev: true
/es-module-lexer@0.9.3:
resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
/es-module-lexer@1.3.0:
resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==}
dev: true
/escalade@3.1.1:
@ -583,8 +587,8 @@ packages:
builtin-modules: 3.3.0
dev: true
/is-core-module@2.11.0:
resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
/is-core-module@2.12.1:
resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==}
dependencies:
has: 1.0.3
dev: true
@ -596,14 +600,14 @@ packages:
/is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
dependencies:
'@types/estree': 1.0.0
'@types/estree': 1.0.1
dev: true
/jest-worker@27.5.1:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'}
dependencies:
'@types/node': 18.15.11
'@types/node': 20.3.1
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
@ -668,8 +672,8 @@ packages:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: true
/node-releases@2.0.10:
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
/node-releases@2.0.12:
resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==}
dev: true
/once@1.4.0:
@ -707,8 +711,8 @@ packages:
safe-buffer: 5.2.1
dev: true
/react-icons@4.8.0(react@18.2.0):
resolution: {integrity: sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==}
/react-icons@4.9.0(react@18.2.0):
resolution: {integrity: sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==}
peerDependencies:
react: '*'
dependencies:
@ -733,7 +737,7 @@ packages:
resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==}
hasBin: true
dependencies:
is-core-module: 2.11.0
is-core-module: 2.12.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
@ -766,11 +770,11 @@ packages:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: true
/schema-utils@3.1.1:
resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==}
/schema-utils@3.3.0:
resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
engines: {node: '>= 10.13.0'}
dependencies:
'@types/json-schema': 7.0.11
'@types/json-schema': 7.0.12
ajv: 6.12.6
ajv-keywords: 3.5.2(ajv@6.12.6)
dev: true
@ -834,8 +838,8 @@ packages:
engines: {node: '>=6'}
dev: true
/terser-webpack-plugin@5.3.7(webpack@5.78.0):
resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==}
/terser-webpack-plugin@5.3.9(webpack@5.87.0):
resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
engines: {node: '>= 10.13.0'}
peerDependencies:
'@swc/core': '*'
@ -850,27 +854,27 @@ packages:
uglify-js:
optional: true
dependencies:
'@jridgewell/trace-mapping': 0.3.17
'@jridgewell/trace-mapping': 0.3.18
jest-worker: 27.5.1
schema-utils: 3.1.1
schema-utils: 3.3.0
serialize-javascript: 6.0.1
terser: 5.16.8
webpack: 5.78.0
terser: 5.18.0
webpack: 5.87.0
dev: true
/terser@5.16.8:
resolution: {integrity: sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==}
/terser@5.18.0:
resolution: {integrity: sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==}
engines: {node: '>=10'}
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.2
acorn: 8.8.2
'@jridgewell/source-map': 0.3.3
acorn: 8.9.0
commander: 2.20.3
source-map-support: 0.5.21
dev: true
/tslib@2.5.0:
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
/tslib@2.5.3:
resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
dev: true
/typescript@4.9.5:
@ -879,13 +883,13 @@ packages:
hasBin: true
dev: true
/update-browserslist-db@1.0.10(browserslist@4.21.5):
resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
/update-browserslist-db@1.0.11(browserslist@4.21.9):
resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.21.5
browserslist: 4.21.9
escalade: 3.1.1
picocolors: 1.0.0
dev: true
@ -913,8 +917,8 @@ packages:
engines: {node: '>=10.13.0'}
dev: true
/webpack@5.78.0:
resolution: {integrity: sha512-gT5DP72KInmE/3azEaQrISjTvLYlSM0j1Ezhht/KLVkrqtv10JoP/RXhwmX/frrutOPuSq3o5Vq0ehR/4Vmd1g==}
/webpack@5.87.0:
resolution: {integrity: sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==}
engines: {node: '>=10.13.0'}
hasBin: true
peerDependencies:
@ -924,16 +928,16 @@ packages:
optional: true
dependencies:
'@types/eslint-scope': 3.7.4
'@types/estree': 0.0.51
'@webassemblyjs/ast': 1.11.1
'@webassemblyjs/wasm-edit': 1.11.1
'@webassemblyjs/wasm-parser': 1.11.1
acorn: 8.8.2
acorn-import-assertions: 1.8.0(acorn@8.8.2)
browserslist: 4.21.5
'@types/estree': 1.0.1
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/wasm-edit': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
acorn: 8.9.0
acorn-import-assertions: 1.9.0(acorn@8.9.0)
browserslist: 4.21.9
chrome-trace-event: 1.0.3
enhanced-resolve: 5.12.0
es-module-lexer: 0.9.3
enhanced-resolve: 5.15.0
es-module-lexer: 1.3.0
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1
@ -942,9 +946,9 @@ packages:
loader-runner: 4.3.0
mime-types: 2.1.35
neo-async: 2.6.2
schema-utils: 3.1.1
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.7(webpack@5.78.0)
terser-webpack-plugin: 5.3.9(webpack@5.87.0)
watchpack: 2.4.0
webpack-sources: 3.2.3
transitivePeerDependencies:

View file

@ -3,7 +3,8 @@
"charge_rate": {
"min": 250,
"max": 2500
}
},
"extra_readouts": false
},
"cpus": {
"cpus": [

View file

@ -117,6 +117,10 @@ export async function getBatteryChargeDesign(): Promise<number> {
return (await call_backend("BATTERY_charge_design", []))[0];
}
export async function getBatteryChargePower(): Promise<number> {
return (await call_backend("BATTERY_charge_power", []))[0];
}
export async function getBatteryChargeRate(): Promise<number | null> {
return (await call_backend("BATTERY_get_charge_rate", []))[0];
}
@ -302,3 +306,41 @@ export async function onPluggedIn(): Promise<boolean> {
export async function onUnplugged(): Promise<boolean> {
return (await call_backend("GENERAL_on_unplugged", []))[0];
}
export type Message = {
/// Message identifier
id: number | null,
/// Message title
title: string,
/// Message content
body: string,
/// Link for further information
url: string | null,
};
export async function getMessages(since: number | null): Promise<Message[]> {
return (await call_backend("MESSAGE_get", [since]));
}
export async function dismissMessage(id: number): Promise<boolean> {
return (await call_backend("MESSAGE_dismiss", [id]))[0];
}
export type Periodicals = {
battery_current: number | null,
battery_charge_now: number | null,
battery_charge_full: number | null,
battery_charge_power: number | null,
settings_path: string | null,
};
export async function getPeriodicals(): Promise<Periodicals> {
const result: any[] = await call_backend("GENERAL_get_periodicals", []);
return {
battery_current: result[0],
battery_charge_now: result[1],
battery_charge_full: result[2],
battery_charge_power: result[3],
settings_path: result[4],
};
}

View file

@ -20,6 +20,7 @@ import {
CHARGE_MODE_BATT,
CURRENT_BATT,
CHARGE_LIMIT_BATT,
CHARGE_POWER_BATT,
} from "../consts";
import { set_value, get_value} from "usdpl-front";
@ -54,12 +55,18 @@ 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>
{get_value(CHARGE_POWER_BATT) != null && get_value(CHARGE_POWER_BATT) > 0 && <PanelSectionRow>
<Field
label={tr("Charge Power")}>
{get_value(CHARGE_POWER_BATT).toFixed(2)} W
</Field>
</PanelSectionRow>}
{get_value(CURRENT_BATT) != null && <PanelSectionRow>
<Field
label={tr("Current")}>
{get_value(CURRENT_BATT)} mA
</Field>
</PanelSectionRow>
</PanelSectionRow>}
{(get_value(LIMITS_INFO) as backend.SettingsLimits).battery.charge_current != null && <PanelSectionRow>
<ToggleField
checked={get_value(CHARGE_RATE_BATT) != null}

View file

@ -0,0 +1,58 @@
import { Component, Fragment } from "react";
import * as backend from "../backend";
import {
Field,
staticClasses,
PanelSectionRow,
ButtonItem,
Navigation,
} from "decky-frontend-lib";
import { MESSAGE_LIST } from "../consts";
import { set_value, get_value, tr } from "usdpl-front";
export class DevMessages extends Component<backend.IdcProps> {
constructor(props: backend.IdcProps) {
super(props);
this.state = {
reloadThingy: "/shrug",
};
}
render() {
const reloadGUI = (x: string) => this.setState({reloadThingy: x});
const messages: backend.Message[] = get_value(MESSAGE_LIST) as backend.Message[];
if (messages.length != 0) {
const message = messages[0];
return (<Fragment>
<div className={staticClasses.PanelSectionTitle}>
{message.title}
</div>
<PanelSectionRow>
<Field
onClick={()=> { if (message.url) { Navigation.NavigateToExternalWeb(message.url); } }}>
{message.body}
</Field>
<ButtonItem
layout="below"
onClick={(_: MouseEvent) => {
if (message.id) {
backend.dismissMessage(message.id);
}
messages.shift();
set_value(MESSAGE_LIST, messages);
reloadGUI("MessageDismissed");
}}
>
{tr("Dismiss")}
</ButtonItem>
</PanelSectionRow>
</Fragment>
)
} else {
return <Fragment></Fragment>
}
}
}

View file

@ -10,6 +10,7 @@ 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";
export const CHARGE_POWER_BATT = "BATTERY_charge_power";
//export const TOTAL_CPUS = "CPUs_total";
export const ONLINE_CPUS = "CPUs_online";
@ -30,3 +31,8 @@ export const PERSISTENT_GEN = "GENERAL_persistent";
export const NAME_GEN = "GENERAL_name";
export const PATH_GEN = "GENERAL_path";
export const MESSAGE_LIST = "MESSAGE_messages";
export const PERIODICAL_BACKEND_PERIOD = 5000; // milliseconds
export const AUTOMATIC_REAPPLY_WAIT = 2000; // milliseconds

View file

@ -39,6 +39,7 @@ import {
CHARGE_NOW_BATT,
CHARGE_FULL_BATT,
CHARGE_DESIGN_BATT,
CHARGE_POWER_BATT,
ONLINE_CPUS,
ONLINE_STATUS_CPUS,
@ -57,16 +58,23 @@ import {
PERSISTENT_GEN,
NAME_GEN,
PATH_GEN,
MESSAGE_LIST,
PERIODICAL_BACKEND_PERIOD,
AUTOMATIC_REAPPLY_WAIT,
} from "./consts";
import { set_value, get_value } from "usdpl-front";
import { Debug } from "./components/debug";
import { Gpu } from "./components/gpu";
import { Battery } from "./components/battery";
import { Cpus } from "./components/cpus";
import { DevMessages } from "./components/message";
var periodicHook: NodeJS.Timer | null = null;
var lifetimeHook: any = null;
var startHook: any = null;
var endHook: any = null;
var usdplReady = false;
type MinMax = {
@ -113,6 +121,7 @@ const reload = function() {
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) });
backend.resolve(backend.getBatteryChargePower(), (rate: number) => { set_value(CHARGE_POWER_BATT, rate) });
//backend.resolve(backend.getCpuCount(), (count: number) => { set_value(TOTAL_CPUS, count)});
backend.resolve(backend.getCpusOnline(), (statii: boolean[]) => {
@ -150,6 +159,8 @@ const reload = function() {
backend.resolve(backend.getInfo(), (info: string) => { set_value(BACKEND_INFO, info) });
backend.resolve(backend.getDriverProviderName("gpu"), (driver: string) => { set_value(DRIVER_INFO, driver) });
backend.resolve(backend.getMessages(null), (messages: backend.Message[]) => { set_value(MESSAGE_LIST, messages) });
};
// init USDPL WASM and connection to back-end
@ -185,22 +196,30 @@ const reload = function() {
);
});
//@ts-ignore
endHook = SteamClient.Apps.RegisterForGameActionEnd((actionType) => {
backend.log(backend.LogLevel.Info, "RegisterForGameActionEnd callback(" + actionType + ")");
setTimeout(() => backend.forceApplySettings(), AUTOMATIC_REAPPLY_WAIT);
});
backend.log(backend.LogLevel.Debug, "Registered PowerTools callbacks, hello!");
})();
const periodicals = function() {
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
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.getPeriodicals(), (periodicals) => {
set_value(CURRENT_BATT, periodicals.battery_current);
set_value(CHARGE_NOW_BATT, periodicals.battery_charge_now);
set_value(CHARGE_FULL_BATT, periodicals.battery_charge_full);
set_value(CHARGE_POWER_BATT, periodicals.battery_charge_power);
backend.resolve(backend.getGeneralSettingsPath(), (path: string) => {
const path = periodicals.settings_path;
const oldValue = get_value(PATH_GEN);
set_value(PATH_GEN, path);
if (path != oldValue) {
backend.log(backend.LogLevel.Info, "Frontend values reload triggered by path change: " + oldValue + " -> " + path);
reload();
}
});
})
};
const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
@ -215,7 +234,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
periodicHook = setInterval(function() {
periodicals();
reloadGUI("periodic" + (new Date()).getTime().toString());
}, 1000);
}, PERIODICAL_BACKEND_PERIOD);
if (!usdplReady || !get_value(LIMITS_INFO)) {
// Not translated on purpose (to avoid USDPL issues)
@ -237,6 +256,8 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
return (
<PanelSection>
<DevMessages idc={idc}/>
<Cpus idc={idc}/>
<Gpu idc={idc}/>
@ -314,15 +335,16 @@ export default definePlugin((serverApi: ServerAPI) => {
ico = <span><GiDynamite /><GiTimeTrap /><GiTimeBomb /></span>;
}
return {
title: <div className={staticClasses.Title}>I'm a tool</div>,
title: <div className={staticClasses.Title}>PowerTools</div>,
content: <Content serverAPI={serverApi} />,
icon: ico,
onDismount() {
backend.log(backend.LogLevel.Debug, "PowerTools shutting down");
clearInterval(periodicHook!);
periodicHook = null;
lifetimeHook!.unregister();
startHook!.unregister();
lifetimeHook?.unregister();
startHook?.unregister();
endHook?.unregister();
//serverApi.routerHook.removeRoute("/decky-plugin-test");
backend.log(backend.LogLevel.Debug, "Unregistered PowerTools callbacks, so long and thanks for all the fish.");
},

Binary file not shown.

View file

@ -65,6 +65,11 @@ msgstr "Présentement (Charge)"
msgid "Max (Design)"
msgstr "Max (Conçue)"
#: components/battery.tsx:60
# (Wattage of battery charging input)
msgid "Charge Power"
msgstr "Puissance de charge"
# (Charge current limit override toggle)
#: components/battery.tsx:59
msgid "Charge Current Limits"

Binary file not shown.

View file

@ -64,6 +64,11 @@ msgstr ""
msgid "Max (Design)"
msgstr ""
#: components/battery.tsx:60
# (Wattage of battery charging input)
msgid "Charge Power"
msgstr ""
#: components/battery.tsx:59
# (Charge current limit override toggle)
msgid "Charge Current Limits"

BIN
translations/ru-RU.mo Normal file

Binary file not shown.

Binary file not shown.

View file

@ -1,227 +0,0 @@
# TEMPLATE TITLE.
# Copyright (C) 2023 NGnius
# This file is distributed under the same license as the PowerTools package.
# NGnius (Graham) <ngniusness@gmail.com>, 2023.
msgid ""
msgstr ""
"Project-Id-Version: v1.1\n"
"Report-Msgid-Bugs-To: https://github.com/NGnius/PowerTools/issues\n"
"POT-Creation-Date: 2023-01-10 20:06-0500\n"
"PO-Revision-Date: 2023-01-10 20:06-0500\n"
"Last-Translator: NGnius <ngniusness@gmail.com>\n"
"Language-Team: NGnius <ngniusness@gmail.com>\n"
"Language: conlang\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
# -- index.tsx --
#: index.tsx:226
# (Section title)
msgid "Miscellaneous"
msgstr "test123"
#: index.tsx:226
# (Profile persistence toggle)
msgid "Persistent"
msgstr "test123"
#: index.tsx:227
# (Profile persistence toggle description)
msgid "Save profile and load it next time"
msgstr "test123"
#: index.tsx:239
# (Profile display)
msgid "Profile"
msgstr "test123"
# -- components/battery.tsx --
#: components/battery.tsx:42
# (Battery section title)
msgid "Battery"
msgstr "test123"
#: components/battery.tsx:46
# (Charge of battery at this moment, with percentage of expected full charge in brackets)
msgid "Now (Charge)"
msgstr "test123"
#: components/battery.tsx:52
# (Maximum capacity of battery, with percentage of design capacity in brackets)
msgid "Max (Design)"
msgstr "test123"
#: components/battery.tsx:59
# (Charge current limit override toggle)
msgid "Charge Current Limits"
msgstr "test123"
#: components/battery.tsx:60
# (Charge current limit override toggle description)
msgid "Control battery charge rate when awake"
msgstr "test123"
#: components/battery.tsx:74
# (Battery maximum input current with unit)
msgid "Maximum (mA)"
msgstr "test123"
#: components/battery.tsx:97,115
# (Battery charge mode override toggle)
msgid "Charge Mode"
msgstr "test123"
#: components/battery.tsx:98
# (Battery charge mode override toggle description)
msgid "Force battery charge mode"
msgstr "test123"
#: components/battery.tsx:112
# (Battery charge mode dropdown)
msgid "Mode"
msgstr "test123"
#: components/battery.tsx:133
# (Battery current display)
msgid "Current"
msgstr "test123"
# -- components/cpus.tsx --
#: components/cpus.tsx:64
# (CPU section title)
msgid "CPU"
msgstr "test123"
#: components/cpus.tsx:70
# (CPU advanced mode toggle)
msgid "Advanced"
msgstr "test123"
#: components/cpus.tsx:71
# (CPU advanced mode toggle description)
msgid "Enables per-thread configuration"
msgstr "test123"
#: components/cpus.tsx:88
# (CPU Simultaneous MultiThreading toggle)
msgid "SMT"
msgstr "test123"
#: components/cpus.tsx:89
# (CPU SMT toggle description)
msgid "Enables odd-numbered CPUs"
msgstr "test123"
#: components/cpus.tsx:106
# (CPU thread count slider)
msgid "Threads"
msgstr "test123"
#: components/cpus.tsx:137
#: components/gpu.tsx:112
# (Clock speed override toggle)
msgid "Frequency Limits"
msgstr "test123"
#: components/cpus.tsx:138
#: components/gpu.tsx:113
# (Clock speed override toggle description)
msgid "Set bounds on clock speed"
msgstr "test123"
#: components/cpus.tsx:165
#: components/gpu.tsx:137
# (Minimum clock speed with unit)
msgid "Minimum (MHz)"
msgstr "test123"
#: components/cpus.tsx:195
#: components/gpu.tsx:160
# (Maximum clock speed with unit)
msgid "Maximum (MHz)"
msgstr "test123"
# advanced mode
#: components/cpus.tsx:226
# (CPU selection slider)
msgid "Selected CPU"
msgstr "test123"
#: components/cpus.tsx:246
# (CPU Online toggle)
msgid "Online"
msgstr "test123"
#: components/cpus.tsx:247
# (CPU Online description)
msgid "Allow the CPU thread to do work"
msgstr "test123"
#: components/cpus.tsx:342
# (CPU scheduling governor dropdown -- governor names are not translated)
msgid "Governor"
msgstr "test123"
# -- components/debug.tsx --
#: components/debug.tsx:29
# (Debug section title)
msgid "Debug"
msgstr "test123"
#: components/debug.tsx:33
# (Version display for native back-end of PowerTools)
msgid "Native"
msgstr "test123"
#: components/debug.tsx:47
# (Mode display for framework of USDPL API)
msgid "Framework"
msgstr "test123"
#: components/debug.tsx:54
# (Display for software implementation in PowerTools which applies settings)
msgid "Driver"
msgstr "test123"
# -- components/gpu.tsx --
#: components/gpu.tsx:34
# (GPU section title)
msgid "GPU"
msgstr "test123"
#: components/gpu.tsx:39
# (PPT Limits override toggle)
msgid "PowerPlay Limits"
msgstr "test123"
#: components/gpu.tsx:40
# (PPT Limits override toggle description)
msgid "Override APU TDP settings"
msgstr "test123"
#: components/gpu.tsx:63
# (SlowPPT slider with unit)
msgid "SlowPPT (W)"
msgstr "test123"
#: components/gpu.tsx:87
# (FastPPT slider with unit)
msgid "FastPPT (W)"
msgstr "test123"
#: components/gpu.tsx:112
# (Reduce memory clock speed toggle)
msgid "Downclock Memory"
msgstr "test123"
#: components/gpu.tsx:112
# (Reduce memory clock speed toggle description)
msgid "Force RAM into low-power mode"
msgstr "test123"

BIN
translations/uk-UA.mo Normal file

Binary file not shown.