45 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
25b93e7597 Initial plan 2025-11-02 10:47:52 +00:00
Stephen Horvath
90a994a685 Disable blue power button colour
Early Intel models report that their power button LED supports blue
colour, but in reality it just turns it off.
2025-10-20 15:07:37 +10:00
Stephen Horvath
7b123da001 Handle custom power LED brightness levels 2025-10-16 22:45:23 +10:00
Stephen Horvath
c8fd626446 Update about text to match README 2025-10-04 10:32:09 +10:00
Stephen Horvath
0f6fee40ae Add disclaimer to README 2025-10-04 10:29:23 +10:00
Stephen Horvath
4a788c2395 Merge pull request #11 from OrioleNix/patch-1
Change title from gtk3 to gtk4
2025-10-04 07:05:08 +10:00
OrioleNix
d2f34c9b5a Update main.py 2025-10-03 11:22:34 -04:00
Stephen Horvath
4958722edd Update README.md 2025-09-28 23:26:39 +10:00
Stephen Horvath
23b54ee397 Add FUNDING.yml 2025-09-28 23:24:51 +10:00
Stephen Horvath
73bb7abf6e Release 0.6 2025-09-28 13:35:13 +10:00
Stephen Horvath
9f0fabb91f Fix metainfo screenshots 2025-09-28 12:55:28 +10:00
Stephen Horvath
a31f851707 More changes for Flathub submission 2025-09-27 18:21:11 +10:00
Stephen Horvath
70d3788dd6 Add sensors to Hardware/Sensors page 2025-09-23 12:54:51 +10:00
Stephen Horvath
f3b74ccc02 Rework advanced LEDs 2025-09-21 22:30:24 +10:00
Stephen Horvath
335a0881b3 Preparation for Flathub publishing 2025-09-21 13:37:35 +10:00
Stephen Horvath
c39a88b7db Remove hard edges from logo 2025-09-19 13:36:33 +10:00
Stephen Horvath
ba78392b24 Add Windows ZIP build 2025-09-18 15:15:55 +10:00
Stephen Horvath
c1205c7661 Add board model to debug info 2025-09-17 21:48:34 +10:00
Stephen Horvath
973ff26fa7 Fix stutter when opening About dialogue 2025-09-17 21:48:06 +10:00
Stephen Horvath
4a8dfbad47 Update flatpak versioning 2025-09-17 17:19:58 +10:00
Stephen Horvath
2a0675d3be Fix pyinstaller for CI and Windows 2025-09-17 16:51:33 +10:00
Stephen Horvath
55580535db Add new logo and splash screen 2025-09-17 16:26:21 +10:00
Stephen Horvath
8fcb2098cb Release 0.5 2025-09-16 11:26:31 +10:00
Stephen Horvath
3c55f8fdd6 Hide the minimum charge limiter 2025-09-16 11:20:13 +10:00
Stephen Horvath
b5dfa93dcf Update PawnIO Module 2025-09-13 18:36:51 +10:00
Stephen Horvath
154be557ed Add traceback to troubleshooting information 2025-09-12 10:52:02 +10:00
Stephen Horvath
624fed2c71 Round scaling instead of flooring 2025-09-09 17:39:53 +10:00
Stephen Horvath
f50a11deaa Disable unsupported advanced led controls 2025-09-08 19:02:41 +10:00
Stephen Horvath
923c06a3f1 Release 0.4 2025-09-06 12:14:34 +10:00
Stephen Horvath
4aa15708be Manually set LD_LIBRARY_PATH on CI 2025-09-06 10:51:15 +10:00
Stephen Horvath
1c6131c61d Run actions on any branch 2025-09-06 10:50:28 +10:00
Stephen Horvath
3129e10de2 Loosen strict version requirement on gtk and adw 2025-09-06 09:58:11 +10:00
Stephen Horvath
875c06dad1 Update README 2025-09-05 20:28:04 +10:00
Stephen Horvath
ad099ff5e7 Add LpcCrOSEC.bin 2025-09-05 19:46:25 +10:00
Stephen Horvath
5f514ab6bb Add debug info to the about dialog 2025-09-05 19:43:44 +10:00
Stephen Horvath
1dd85c5fa3 Add pyinstaller Windows CI build 2025-08-22 20:27:12 +10:00
Stephen Horvath
2c02c00268 Add pyinstaller linux CI build 2025-08-22 19:20:24 +10:00
Stephen Horvath
e7b4e4d194 Add live battery stats 2025-08-15 19:34:11 +10:00
Stephen Horvath
fa5442d5fd Release 0.3 2025-04-13 15:30:04 +10:00
Stephen Horvath
09f072a150 PyInstaller changes for PawnIO 2025-04-13 15:23:20 +10:00
Stephen Horvath
f2cf2c7923 Read the EC for the battery limiter 2025-04-13 12:52:02 +10:00
Stephen Horvath
35f9c766b9 Show battery limiter percentage & fix icons for pip 2025-04-13 11:55:06 +10:00
Stephen Horvath
8b33f75b86 Release 0.2 2025-03-24 21:38:13 +10:00
Stephen Horvath
076f903fbc Add PyInstaller spec for Windows 2025-03-24 20:50:34 +10:00
Stephen Horvath
d65a033523 Fix led colours being added multiple times 2025-03-24 20:47:26 +10:00
49 changed files with 1905 additions and 917 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [Steve-Tech]
thanks_dev: u/gh/steve-tech

View File

@@ -2,18 +2,18 @@ name: Flatpak Build
on: on:
push: push:
branches: [main]
pull_request: pull_request:
jobs: jobs:
flatpak: flatpak:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: bilelmoussaoui/flatpak-github-actions:gnome-47 image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-49
options: --privileged options: --privileged
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6 - name: Build Flatpak
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with: with:
bundle: yafi-${{ github.sha }}.flatpak bundle: yafi-${{ github.sha }}.flatpak
manifest-path: au.stevetech.yafi.json manifest-path: au.stevetech.yafi.json

30
.github/workflows/pyinstaller-linux.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: PyInstaller Build Linux
on:
push:
pull_request:
jobs:
pyinstaller-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# We shouldn't use actions/setup-python since PyGObject is compiled against the system Python
- name: Install System Dependencies
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: python3-gi gir1.2-gtk-4.0 gir1.2-adw-1 libglib2.0-bin python3-tk
version: 0
- name: Install PyInstaller
run: pip install pyinstaller
- name: Build YAFI via Pip
run: pip install .
- name: Build with PyInstaller
# pyinstaller doesn't find the GTK libraries after caching?
run: LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu pyinstaller yafi.spec -- --onefile
working-directory: pyinstaller
- name: Upload PyInstaller Artifact
uses: actions/upload-artifact@v4
with:
path: pyinstaller/dist/YAFI
name: yafi-linux-${{ github.sha }}

View File

@@ -0,0 +1,62 @@
name: PyInstaller Build Windows
on:
push:
pull_request:
jobs:
pyinstaller-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: 'pip'
- name: Cache GTK4
id: cache-gtk4
uses: actions/cache@v4
with:
path: C:\gtk
key: Gvsbuild_2025.9.0
- name: Download GTK4 Gvsbuild zip
if: steps.cache-gtk4.outputs.cache-hit != 'true'
run: Start-BitsTransfer -Source https://github.com/wingtk/gvsbuild/releases/download/2025.9.0/GTK4_Gvsbuild_2025.9.0_x64.zip -Destination Gvsbuild.zip
- name: Extract Gvsbuild zip
if: steps.cache-gtk4.outputs.cache-hit != 'true'
run: Expand-Archive -Path Gvsbuild.zip -DestinationPath C:\gtk -Force
- name: Add GTK bin to PATH
run: echo "C:\gtk\bin" | Out-File -Append -Encoding ascii $env:GITHUB_PATH
- name: Install PyGObject wheel
run: pip install --force-reinstall (Resolve-Path C:\gtk\wheels\PyGObject*.whl) (Resolve-Path C:\gtk\wheels\pycairo*.whl)
- name: Install PyInstaller
run: pip install pyinstaller
- name: Build YAFI via Pip
run: pip install .
- name: Build with PyInstaller (ZIP)
run: python -m PyInstaller yafi.spec
working-directory: pyinstaller
- name: Upload PyInstaller Artifact (ZIP)
uses: actions/upload-artifact@v4
with:
path: pyinstaller/dist/YAFI/*
name: yafi-windows-${{ github.sha }}
- name: Build with PyInstaller (Standalone)
run: python -m PyInstaller yafi.spec --noconfirm -- --onefile
working-directory: pyinstaller
- name: Upload PyInstaller Artifact (Standalone)
uses: actions/upload-artifact@v4
with:
path: pyinstaller/dist/YAFI.exe
name: yafi-windows-standalone-${{ github.sha }}

6
.gitignore vendored
View File

@@ -30,7 +30,7 @@ MANIFEST
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it. # before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest *.manifest
*.spec #*.spec
# Installer logs # Installer logs
pip-log.txt pip-log.txt
@@ -175,3 +175,7 @@ cython_debug/
# Flatpak builder # Flatpak builder
.flatpak-builder/ .flatpak-builder/
# Binary files
*.bin
!pyinstaller/LpcCrOSEC.bin

View File

@@ -1,2 +1 @@
include yafi/ui/*.ui
include yafi/yafi.gresource include yafi/yafi.gresource

View File

@@ -1,15 +1,31 @@
# Yet Another Framework Interface # Yet Another Framework Interface
YAFI is another GUI for the Framework Laptop Embedded Controller. YAFI is another GUI for the Framework Laptop Embedded Controller.
It is written in Python with a GTK4 Adwaita theme, and uses the `CrOS_EC_Python` library to communicate with the EC. It is written in Python with a GTK4 Adwaita theme, and uses the [`CrOS_EC_Python`](https://github.com/Steve-Tech/CrOS_EC_Python) library to communicate with the EC.
It has support for fan control, temperature monitoring, LED control, and battery limiting. It has support for fan control, temperature monitoring, LED control, and battery limiting.
## Installation ## Installation
### udev Rules (MUST READ) You can download the latest release from the [Releases page](https://github.com/Steve-Tech/YAFI/releases).
To allow YAFI to communicate with the EC, you need to copy the [`60-cros_ec_python.rules`](https://github.com/Steve-Tech/YAFI/blob/main/60-cros_ec_python.rules) file to `/etc/udev/rules.d/` and reload the rules with `sudo udevadm control --reload-rules && sudo udevadm trigger`. There are builds for Flatpak, and PyInstaller for portable execution on Linux or Windows.
YAFI is also available on [Flathub](https://flathub.org/en/apps/au.stevetech.yafi): `flatpak install flathub au.stevetech.yafi`.
### Linux
To allow YAFI to communicate with the EC, you need to copy the [`60-cros_ec_python.rules`](60-cros_ec_python.rules) file to `/etc/udev/rules.d/` and reload the rules with `sudo udevadm control --reload-rules && sudo udevadm trigger`.
### Windows
If your Laptop's BIOS supports Framework's EC driver, there is no need to install any third-party drivers. YAFI should also work without administrator privileges.
Otherwise, YAFI supports the [PawnIO](https://pawnio.eu/) driver, and will be automatically used if installed and there is no Framework driver available. YAFI will need to be run as administrator to communicate with the driver.
Currently the PawnIO driver does not support Framework 13 mainboards with 11th, 12th, or 13th Gen Intel CPUs.
## Building
### Flatpak ### Flatpak
@@ -21,14 +37,12 @@ You can also create a flatpak bundle with `flatpak-builder --repo=repo build au.
#### System Dependencies #### System Dependencies
The following system dependencies are required for `PyGObject`: This project requires PyGObject, and the introspection data for GTK4 and Adwaita.
On Debian/Ubuntu, you can install these with:
- `python3-dev` ```bash
- `libcairo2-dev` sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-adw-1
- `libgirepository-2.0-dev` ```
- `gir1.2-adw-1`
There's probably more, but I happened to have them installed.
#### Install #### Install
@@ -36,6 +50,10 @@ Install the package with `pip install yafi`.
Pipx is also supported. Pipx is also supported.
### Windows
It is possible to run YAFI on Windows using [gvsbuild](https://github.com/wingtk/gvsbuild/) and installing YAFI via pip, but it can be complicated and is therefore not recommended.
## Screenshots ## Screenshots
### Fan Control and Temperature Monitoring ### Fan Control and Temperature Monitoring
@@ -46,17 +64,21 @@ Pipx is also supported.
![LEDs Page](docs/2-leds.png) ![LEDs Page](docs/2-leds.png)
### Battery Limiting ### Battery Statistics
![Battery Page](docs/3-battery.png) ![Battery Page](docs/3-battery.png)
### Battery Limiting
![Battery Limiter Page](docs/4-battery-limit.png)
#### Battery Extender #### Battery Extender
![Battery Extender](docs/3a-battery-ext.png) ![Battery Extender](docs/4a-battery-ext.png)
### Hardware Info ### Hardware Info
![Hardware Page](docs/4-hardware.png) ![Hardware Page](docs/5-hardware.png)
## Troubleshooting ## Troubleshooting
@@ -70,3 +92,9 @@ This error occurs when `/dev/cros_ec` is not found, and the `CrOS_EC_Python` lib
You can either update your kernel to have a working `cros_ec_dev` driver, or run YAFI as root. You can either update your kernel to have a working `cros_ec_dev` driver, or run YAFI as root.
It can also occur if you do not have a CrOS EC, like on non Framework laptops. It can also occur if you do not have a CrOS EC, like on non Framework laptops.
## Disclaimer
YAFI is not affiliated with Framework Computer Inc. in any way.
YAFI is licensed under the [GPL-2.0-or-later license](COPYING), and comes with no warranty or guarantee of any kind. Use at your own risk.

View File

@@ -1,13 +1,14 @@
{ {
"id" : "au.stevetech.yafi", "id" : "au.stevetech.yafi",
"runtime" : "org.gnome.Platform", "runtime" : "org.gnome.Platform",
"runtime-version" : "48", "runtime-version" : "49",
"sdk" : "org.gnome.Sdk", "sdk" : "org.gnome.Sdk",
"command" : "yafi", "command" : "yafi",
"finish-args" : [ "finish-args" : [
"--device=all", "--device=all",
"--socket=fallback-x11", "--socket=fallback-x11",
"--socket=wayland" "--socket=wayland",
"--share=ipc"
], ],
"cleanup" : [ "cleanup" : [
"/include", "/include",
@@ -21,6 +22,7 @@
"*.a" "*.a"
], ],
"modules" : [ "modules" : [
"python3-cros_ec_python.json",
{ {
"name" : "yafi", "name" : "yafi",
"builddir" : true, "builddir" : true,
@@ -31,18 +33,6 @@
"path" : "." "path" : "."
} }
] ]
},
{
"name": "cros_ec_python",
"buildsystem": "simple",
"build-options": {
"build-args": [
"--share=network"
]
},
"build-commands": [
"pip3 install --prefix=${FLATPAK_DEST} --no-cache-dir \"cros_ec_python>=0.0.2\""
]
} }
] ]
} }

View File

@@ -6,6 +6,6 @@ Comment=Yet Another Framework Interface
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Utility; Categories=Utility;
Keywords=GTK; Keywords=Framework;EC;Embedded Controller;
StartupNotify=true StartupNotify=true
DBusActivatable=true DBusActivatable=true

View File

@@ -7,11 +7,23 @@
<name>Yet Another Framework Interface</name> <name>Yet Another Framework Interface</name>
<summary>YAFI is another GUI for the Framework Laptop Embedded Controller</summary> <summary>YAFI is another GUI for the Framework Laptop Embedded Controller</summary>
<description> <description>
<p>It is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.</p> <p>It is written in Python with a GTK4 Adwaita theme, and uses the CrOS_EC_Python library to communicate with the EC.</p>
<p>YAFI has the capability for the following features:</p>
<ul>
<li>Fan control and temperature monitoring</li>
<li>LED control</li>
<li>Battery statistics</li>
<li>Battery limiting</li>
<li>Hardware information</li>
</ul>
<p>You will need to install the udev rules to allow non-root access to the EC device. See the README for more information.</p>
<p>YAFI is not affiliated with Framework Computer Inc. in any way.</p>
</description> </description>
<developer id="au.stevetech"> <developer id="au.stevetech">
<name>Stephen Horvath</name> <name translate="no">Stephen Horvath</name>
</developer> </developer>
<!-- Required: Should be a link to the upstream homepage for the component --> <!-- Required: Should be a link to the upstream homepage for the component -->
@@ -27,7 +39,7 @@
<!-- Should provide a web link to an online user's reference, a software manual or help page --> <!-- Should provide a web link to an online user's reference, a software manual or help page -->
<!-- <url type="help">https://example.org/help</url> --> <!-- <url type="help">https://example.org/help</url> -->
<!-- URLs of this type should point to a webpage showing information on how to donate to the described software project --> <!-- URLs of this type should point to a webpage showing information on how to donate to the described software project -->
<!-- <url type="donation">https://example.org/donate</url> --> <url type="donation">https://github.com/sponsors/Steve-Tech</url>
<!-- This could for example be an HTTPS URL to an online form or a page describing how to contact the developer --> <!-- This could for example be an HTTPS URL to an online form or a page describing how to contact the developer -->
<!-- <url type="contact">https://example.org/contact</url> --> <!-- <url type="contact">https://example.org/contact</url> -->
<!-- URLs of this type should point to a webpage showing information on how to contribute to the described software project --> <!-- URLs of this type should point to a webpage showing information on how to contribute to the described software project -->
@@ -42,42 +54,86 @@
<!-- Applications should set a brand color in both light and dark variants like so --> <!-- Applications should set a brand color in both light and dark variants like so -->
<branding> <branding>
<color type="primary" scheme_preference="light">#ff00ff</color> <color type="primary" scheme_preference="light">#7c916f</color>
<color type="primary" scheme_preference="dark">#993d3d</color> <color type="primary" scheme_preference="dark">#282000</color>
</branding> </branding>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/1-thermal.png</image> <image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/1-thermals.png</image>
<caption>The Thermal page</caption> <caption>Fan Control and Temperature Monitoring</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/2-leds.png</image> <image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/2-leds.png</image>
<caption>The LED page</caption> <caption>LED Control</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/3-battery.png</image> <image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/3-battery.png</image>
<caption>The Battery page</caption> <caption>Battery Statistics</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/4-hardware.png</image> <image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/4-battery-limit.png</image>
<caption>The Hardware page</caption> <caption>Battery Limiting</caption>
</screenshot>
<screenshot>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/4a-battery-ext.png</image>
<caption>Battery Extender</caption>
</screenshot>
<screenshot>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/5-hardware.png</image>
<caption>Hardware Info</caption>
</screenshot> </screenshot>
</screenshots> </screenshots>
<releases> <releases>
<release version="0.1.0" date="2025-03-20"> <release version="0.6" date="2025-09-28">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.1.0</url> <url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.6</url>
<description translate="no"> <description>
<p>First release</p> <p>The following changes have been made:</p>
<ul> <ul>
<li>Added Thermal page</li> <li>YAFI now has a new logo!</li>
<li>Added LED page</li> <li>The advanced LED settings now detect available LEDs.</li>
<li>Added Battery page</li> <li>The ALS and lid open sensor has been added.</li>
<li>Added Hardware page</li> <li>Stuttering has been reduced when opening the about dialog.</li>
</ul> </ul>
</description> </description>
</release> </release>
<release version="0.5" date="2025-09-16">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.5</url>
<description>
<p>The following bugs have been fixed:</p>
<ul>
<li>LED page crashing when encountering unsupported LEDs.</li>
<li>Scaling issues on Windows.</li>
<li>Minimum charge limiter doesn't work.</li>
</ul>
<p>This release also adds a traceback to the debug info, when there is an error during initialisation.</p>
</description>
</release>
<release version="0.4" date="2025-09-06">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.4</url>
<description>
<p>This release also adds a new battery statistics page.</p>
</description>
</release>
<release version="0.3" date="2025-04-13">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.3</url>
<description>
<p>YAFI now shows the battery limiter value.</p>
</description>
</release>
<release version="0.2" date="2025-03-24">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.2</url>
<description>
<p>This fixes an LED list bug.</p>
</description>
</release>
<release version="0.1" date="2025-03-20">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.1</url>
<description>
<p>This is the first release of YAFI.</p>
</description>
</release>
</releases> </releases>
</component> </component>

View File

@@ -1,36 +1,92 @@
<?xml version="1.0" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" <!-- Created with Inkscape (http://www.inkscape.org/) -->
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" <svg
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000" width="128"
preserveAspectRatio="xMidYMid meet"> height="128"
<metadata> viewBox="0 0 128 128"
Created by potrace 1.11, written by Peter Selinger 2001-2013 version="1.1"
</metadata> id="svg1"
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)" xml:space="preserve"
fill="#000000" stroke="none"> xmlns="http://www.w3.org/2000/svg"
<path d="M1755 4883 c-120 -22 -418 -180 -609 -322 -118 -89 -148 -118 -177 xmlns:svg="http://www.w3.org/2000/svg"><defs
-177 -23 -48 -24 -57 -27 -314 -1 -146 -7 -294 -13 -330 -18 -110 -78 -222 id="defs1" /><g
-163 -303 -46 -43 -69 -58 -305 -197 -191 -112 -222 -139 -257 -214 -33 -73 id="layer1"><g
-39 -145 -39 -461 0 -297 5 -360 33 -440 25 -73 77 -118 257 -224 88 -51 187 id="icon"><g
-111 220 -132 123 -79 192 -169 245 -319 8 -21 14 -110 16 -230 8 -403 9 -423 id="chip"
29 -460 32 -61 56 -88 134 -148 234 -181 550 -347 676 -356 75 -5 116 12 306 style="stroke-width:0;stroke-dasharray:none"><path
125 181 108 307 175 330 176 8 1 35 5 61 11 70 14 174 5 251 -23 38 -13 164 style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
-81 281 -149 286 -168 312 -173 494 -90 41 19 75 34 77 34 4 0 201 110 243 d="m 34,8 v 8"
135 45 27 147 100 213 153 51 40 83 75 104 113 l30 54 6 310 c5 280 8 315 27 id="pin1" /><path
365 58 156 137 245 295 336 246 140 346 205 376 243 35 44 60 106 67 166 2 22 style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
8 74 13 115 9 78 7 442 -4 520 -12 97 -16 116 -27 150 -16 52 -61 113 -105 d="M 54,8.0000019 V 16.000002"
142 -21 14 -126 78 -233 141 -107 64 -209 128 -225 144 -51 48 -101 115 -128 id="pin2" /><path
173 -48 105 -56 163 -56 454 -1 262 -1 270 -25 321 -24 53 -65 100 -124 145 style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
-19 14 -46 34 -60 46 -42 32 -190 128 -257 164 -250 138 -384 177 -476 140 d="M 74.000009,8.0000019 V 16.000002"
-19 -8 -101 -54 -184 -104 -316 -188 -340 -198 -480 -199 -153 -1 -181 10 id="pin3" /><path
-501 201 -178 106 -237 128 -309 115z m984 -848 c149 -19 308 -68 441 -135 68 style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
-34 181 -100 190 -110 3 -3 21 -17 40 -30 52 -37 75 -57 166 -148 150 -151 d="m 93.999992,8 v 8"
281 -364 345 -562 33 -102 32 -99 56 -210 18 -86 26 -378 13 -455 -5 -27 -12 id="pin4" /><path
-68 -15 -90 -16 -102 -64 -250 -117 -364 -211 -450 -634 -764 -1113 -828 -113 style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
-15 -425 -7 -455 12 -4 2 -22 7 -41 10 -150 26 -404 140 -531 239 -14 12 -41 d="m 120,34.000001 h -8"
32 -59 46 -115 85 -291 299 -371 449 -78 148 -144 353 -165 516 -10 76 -10 id="pin5" /><path
311 0 390 15 117 58 274 106 390 116 278 347 543 608 696 158 92 386 169 549 style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
184 38 4 70 8 72 9 5 6 229 -2 281 -9z"/> d="m 120,54.000001 h -8"
</g> id="pin6" /><path
</svg> style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,74.000009 h -8"
id="pin7" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,93.999992 h -8"
id="pin8" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 94.000009,112 v 8"
id="pin9" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 74.000026,112 v 8"
id="pin10" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 54.000017,112 v 8"
id="pin11" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34.000017,112 v 8"
id="pin12" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,93.999992 H 8.0000019"
id="pin13" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,74.000009 H 8.0000019"
id="pin14" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,54.000001 H 8.0000019"
id="pin15" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,34.000001 H 8.0000019"
id="pin16" /><rect
style="fill:#000000;fill-opacity:0.5;stroke-width:0;stroke-dasharray:none"
id="shadow"
width="104"
height="104"
x="16"
y="16"
rx="16.000002"
ry="16.000002" /><rect
style="fill:#808080;stroke-width:0;stroke-dasharray:none"
id="package"
width="104"
height="104"
x="12.000001"
y="12.000001"
rx="16.000002"
ry="16.000002" /><circle
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
id="dot"
cx="22"
cy="22"
r="2" /></g><path
id="cog"
style="display:inline;fill:#282000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 47.818359,18 2.542969,2.630859 c -2.531091,-2.605911 -6.371601,-0.420022 -6.429687,-0.386718 l -0.0039,0.002 -7.783203,4.49414 c -0.03804,0.02206 -3.86275,2.252383 -2.888672,5.746094 l 0.002,0.01172 0.0059,0.01563 3.027344,10.585937 C 33.755122,44.16501 31.7167,47.65852 30.302828,51.455131 l -10.679687,2.671875 c -3.549895,0.894185 -3.550781,5.380768 -3.550781,5.380859 v 8.984376 c 0,9.1e-5 0.001,4.493173 3.558593,5.382812 l 10.671875,2.669922 c 1.413872,3.79649 3.452294,7.288523 5.988282,10.353516 l -3.027344,10.585937 -0.0059,0.01758 c -1.79e-4,6.36e-4 1.79e-4,0.0013 0,0.002 -0.9887,3.517182 2.886719,5.755862 2.886719,5.755862 l 7.783203,4.49414 c 0,0 0.0038,0.002 0.0039,0.002 0.02177,0.0125 3.888449,2.23357 6.43164,-0.38867 L 47.818359,110 l 2.548829,-2.63477 7.652343,-7.914058 c 1.945156,0.325909 3.942752,0.494141 5.980469,0.49414 2.037713,0 4.035313,-0.168231 5.980469,-0.49414 l 7.65039,7.912108 0.002,0.002 2.548829,2.63477 -2.544922,-2.63281 c 2.522803,2.60122 6.349136,0.43583 6.43164,0.38867 l 7.78711,-4.49609 c 0,0 3.87542,-2.23868 2.886719,-5.755864 v -0.002 l -0.0059,-0.01758 -3.027344,-10.585937 c 2.536165,-3.064993 4.574289,-6.557026 5.988282,-10.353516 L 108.37109,73.875 c 3.55762,-0.889648 3.55664,-5.382812 3.55664,-5.382812 v -8.984376 c 0,10e-7 9.8e-4,-4.493163 -3.55664,-5.382812 L 97.697266,51.455078 C 96.283273,47.658406 94.245149,44.164957 91.708984,41.099609 l 3.027344,-10.585937 0.0059,-0.01563 0.002,-0.01172 c 0.976385,-3.502191 -2.869106,-5.73477 -2.888672,-5.746094 l -7.783203,-4.49414 c -9.1e-5,-5.2e-5 -0.0038,-0.0019 -0.0039,-0.002 -0.05432,-0.03116 -3.897327,-2.220492 -6.429687,0.386718 L 80.181641,18 l -2.550782,2.636719 -7.65039,7.912109 C 68.035313,28.223033 66.037713,28.054688 64,28.054688 c -2.037717,0 -4.035313,0.168337 -5.980469,0.49414 l -7.65039,-7.912109 z M 64,36 A 27.999999,27.999997 0 0 1 92,64 27.999999,27.999997 0 0 1 64,92 27.999999,27.999997 0 0 1 36,64 27.999999,27.999997 0 0 1 64,36 Z" /><path
id="Y"
style="fill:none;stroke:#282000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 64.000002,62.00022 50.000001,48.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,36 +1,21 @@
<?xml version="1.0" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" <!-- Created with Inkscape (http://www.inkscape.org/) -->
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" <svg
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000" width="96"
preserveAspectRatio="xMidYMid meet"> height="96"
<metadata> viewBox="0 0 96 96"
Created by potrace 1.11, written by Peter Selinger 2001-2013 version="1.1"
</metadata> id="svg1"
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)" xml:space="preserve"
fill="#000000" stroke="none"> xmlns="http://www.w3.org/2000/svg"
<path d="M1755 4883 c-120 -22 -418 -180 -609 -322 -118 -89 -148 -118 -177 xmlns:svg="http://www.w3.org/2000/svg"><defs
-177 -23 -48 -24 -57 -27 -314 -1 -146 -7 -294 -13 -330 -18 -110 -78 -222 id="defs1" /><g
-163 -303 -46 -43 -69 -58 -305 -197 -191 -112 -222 -139 -257 -214 -33 -73 id="layer1"><g
-39 -145 -39 -461 0 -297 5 -360 33 -440 25 -73 77 -118 257 -224 88 -51 187 id="icon"><path
-111 220 -132 123 -79 192 -169 245 -319 8 -21 14 -110 16 -230 8 -403 9 -423 id="cog"
29 -460 32 -61 56 -88 134 -148 234 -181 550 -347 676 -356 75 -5 116 12 306 style="display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
125 181 108 307 175 330 176 8 1 35 5 61 11 70 14 174 5 251 -23 38 -13 164 d="m 31.818314,2 2.542969,2.630859 C 31.830192,2.024948 27.989682,4.210837 27.931596,4.244141 l -0.0039,0.002 -7.783203,4.49414 c -0.03804,0.02206 -3.86275,2.252383 -2.888672,5.746094 l 0.002,0.01172 0.0059,0.01563 3.027344,10.585937 c -2.535988,3.065348 -4.57441,6.558858 -5.988282,10.355469 L 3.623096,38.127006 c -3.549895,0.894185 -3.550781,5.380768 -3.550781,5.380859 v 8.984376 c 0,9.1e-5 0.001,4.493173 3.558593,5.382812 l 10.671875,2.669922 c 1.413872,3.79649 3.452294,7.288523 5.988282,10.353516 l -3.027344,10.585937 -0.0059,0.01758 c -1.79e-4,6.36e-4 1.79e-4,0.0013 0,0.002 -0.9887,3.517182 2.886719,5.755862 2.886719,5.755862 l 7.783203,4.49414 c 0,0 0.0038,0.002 0.0039,0.002 0.02177,0.0125 3.888449,2.23357 6.43164,-0.38867 L 31.818314,94 l 2.548829,-2.63477 7.652343,-7.914058 c 1.945156,0.325909 3.942752,0.494141 5.980469,0.49414 2.037713,0 4.035313,-0.168231 5.980469,-0.49414 l 7.65039,7.912108 0.002,0.002 2.548829,2.63477 -2.544922,-2.63281 c 2.522803,2.60122 6.349136,0.43583 6.43164,0.38867 l 7.78711,-4.49609 c 0,0 3.87542,-2.23868 2.886719,-5.755864 v -0.002 l -0.0059,-0.01758 -3.027344,-10.585937 c 2.536165,-3.064993 4.574289,-6.557026 5.988282,-10.353516 L 92.371045,57.875 c 3.55762,-0.889648 3.55664,-5.382812 3.55664,-5.382812 v -8.984376 c 0,10e-7 9.8e-4,-4.493163 -3.55664,-5.382812 L 81.697221,35.455078 C 80.283228,31.658406 78.245104,28.164957 75.708939,25.099609 l 3.027344,-10.585937 0.0059,-0.01563 0.002,-0.01172 c 0.976385,-3.502191 -2.869106,-5.73477 -2.888672,-5.746094 l -7.783203,-4.49414 c -9.1e-5,-5.2e-5 -0.0038,-0.0019 -0.0039,-0.002 -0.05432,-0.03116 -3.897327,-2.220492 -6.429687,0.386718 L 64.181596,2 l -2.550782,2.636719 -7.65039,7.912109 c -1.945156,-0.325795 -3.942756,-0.49414 -5.980469,-0.49414 -2.037717,0 -4.035313,0.168337 -5.980469,0.49414 l -7.65039,-7.912109 z m 16.181641,18 a 27.999999,27.999997 0 0 1 28,28 27.999999,27.999997 0 0 1 -28,28 27.999999,27.999997 0 0 1 -28,-28 27.999999,27.999997 0 0 1 28,-28 z" /><path
-81 281 -149 286 -168 312 -173 494 -90 41 19 75 34 77 34 4 0 201 110 243 id="Y"
135 45 27 147 100 213 153 51 40 83 75 104 113 l30 54 6 310 c5 280 8 315 27 style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
365 58 156 137 245 295 336 246 140 346 205 376 243 35 44 60 106 67 166 2 22 d="M 48.000002,46.00022 34.000001,32.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003" /></g></g></svg>
8 74 13 115 9 78 7 442 -4 520 -12 97 -16 116 -27 150 -16 52 -61 113 -105
142 -21 14 -126 78 -233 141 -107 64 -209 128 -225 144 -51 48 -101 115 -128
173 -48 105 -56 163 -56 454 -1 262 -1 270 -25 321 -24 53 -65 100 -124 145
-19 14 -46 34 -60 46 -42 32 -190 128 -257 164 -250 138 -384 177 -476 140
-19 -8 -101 -54 -184 -104 -316 -188 -340 -198 -480 -199 -153 -1 -181 10
-501 201 -178 106 -237 128 -309 115z m984 -848 c149 -19 308 -68 441 -135 68
-34 181 -100 190 -110 3 -3 21 -17 40 -30 52 -37 75 -57 166 -148 150 -151
281 -364 345 -562 33 -102 32 -99 56 -210 18 -86 26 -378 13 -455 -5 -27 -12
-68 -15 -90 -16 -102 -64 -250 -117 -364 -211 -450 -634 -764 -1113 -828 -113
-15 -425 -7 -455 12 -4 2 -22 7 -41 10 -150 26 -404 140 -531 239 -14 12 -41
32 -59 46 -115 85 -291 299 -371 449 -78 148 -144 353 -165 516 -10 76 -10
311 0 390 15 117 58 274 106 390 116 278 347 543 608 696 158 92 386 169 549
184 38 4 70 8 72 9 5 6 229 -2 281 -9z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
docs/1-thermals.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/2-leds.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/3-battery.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

BIN
docs/4-battery-limit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

BIN
docs/4a-battery-ext.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
docs/5-hardware.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

3
flathub.json Normal file
View File

@@ -0,0 +1,3 @@
{
"only-arches": ["x86_64"]
}

162
logo.svg Normal file
View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128"
height="128"
viewBox="0 0 128 128"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="logo.svg"
inkscape:export-filename="logo-plain.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
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="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="5.6568542"
inkscape:cx="38.802485"
inkscape:cy="83.261824"
inkscape:window-width="1876"
inkscape:window-height="1025"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="icon" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><g
id="icon"
inkscape:label="icon"
inkscape:export-filename="icon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"><g
id="chip"
inkscape:label="chip"
style="stroke-width:0;stroke-dasharray:none"><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 34,8 v 8"
id="pin1"
sodipodi:nodetypes="cc"
inkscape:label="pin1" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 54,8.0000019 V 16.000002"
id="pin2"
sodipodi:nodetypes="cc"
inkscape:label="pin2" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 74.000009,8.0000019 V 16.000002"
id="pin3"
sodipodi:nodetypes="cc"
inkscape:label="pin3" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 93.999992,8 v 8"
id="pin4"
sodipodi:nodetypes="cc"
inkscape:label="pin4" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,34.000001 h -8"
id="pin5"
sodipodi:nodetypes="cc"
inkscape:label="pin5" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,54.000001 h -8"
id="pin6"
sodipodi:nodetypes="cc"
inkscape:label="pin6" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,74.000009 h -8"
id="pin7"
sodipodi:nodetypes="cc"
inkscape:label="pin7" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,93.999992 h -8"
id="pin8"
sodipodi:nodetypes="cc"
inkscape:label="pin8" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 94.000009,112 v 8"
id="pin9"
sodipodi:nodetypes="cc"
inkscape:label="pin9" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 74.000026,112 v 8"
id="pin10"
sodipodi:nodetypes="cc"
inkscape:label="pin10" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 54.000017,112 v 8"
id="pin11"
sodipodi:nodetypes="cc"
inkscape:label="pin11" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 34.000017,112 v 8"
id="pin12"
sodipodi:nodetypes="cc"
inkscape:label="pin12" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,93.999992 H 8.0000019"
id="pin13"
sodipodi:nodetypes="cc"
inkscape:label="pin13" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,74.000009 H 8.0000019"
id="pin14"
sodipodi:nodetypes="cc"
inkscape:label="pin14" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,54.000001 H 8.0000019"
id="pin15"
sodipodi:nodetypes="cc"
inkscape:label="pin15" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,34.000001 H 8.0000019"
id="pin16"
sodipodi:nodetypes="cc"
inkscape:label="pin16" /><rect
style="fill:#000000;stroke-width:0;stroke-dasharray:none;fill-opacity:0.5"
id="shadow"
width="104"
height="104"
x="16"
y="16"
rx="16.000002"
ry="16.000002"
inkscape:label="shadow" /><rect
style="fill:#808080;stroke-width:0;stroke-dasharray:none"
id="package"
width="104"
height="104"
x="12.000001"
y="12.000001"
rx="16.000002"
ry="16.000002"
inkscape:label="package" /><circle
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
id="dot"
cx="22"
cy="22"
r="2"
inkscape:label="dot" /></g><path
id="cog"
style="display:inline;fill:#282000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
inkscape:label="cog"
d="M 47.818359 18 L 50.361328 20.630859 C 47.830237 18.024948 43.989727 20.210837 43.931641 20.244141 L 43.927734 20.246094 L 36.144531 24.740234 C 36.106488 24.762299 32.281781 26.992617 33.255859 30.486328 L 33.257812 30.498047 L 33.263672 30.513672 L 36.291016 41.099609 C 33.755028 44.164957 31.716606 47.658467 30.302734 51.455078 L 19.623047 54.126953 C 16.073152 55.021138 16.072266 59.507721 16.072266 59.507812 L 16.072266 68.492188 C 16.072266 68.492279 16.073278 72.985361 19.630859 73.875 L 30.302734 76.544922 C 31.716606 80.341412 33.755028 83.833445 36.291016 86.898438 L 33.263672 97.484375 L 33.257812 97.501953 C 33.257633 97.502589 33.257991 97.50327 33.257812 97.503906 C 32.269112 101.02109 36.144531 103.25977 36.144531 103.25977 L 43.927734 107.75391 C 43.927734 107.75391 43.931579 107.75582 43.931641 107.75586 C 43.953413 107.7684 47.82009 109.98943 50.363281 107.36719 L 47.818359 110 L 50.367188 107.36523 L 58.019531 99.451172 C 59.964687 99.777081 61.962283 99.945313 64 99.945312 C 66.037713 99.945312 68.035313 99.777081 69.980469 99.451172 L 77.630859 107.36328 L 77.632812 107.36523 L 80.181641 110 L 77.636719 107.36719 C 80.159522 109.96841 83.985855 107.80302 84.068359 107.75586 L 91.855469 103.25977 C 91.855469 103.25977 95.730889 101.02109 94.742188 97.503906 L 94.742188 97.501953 L 94.736328 97.484375 L 91.708984 86.898438 C 94.245149 83.833445 96.283273 80.341412 97.697266 76.544922 L 108.37109 73.875 C 111.92871 72.985352 111.92773 68.492188 111.92773 68.492188 L 111.92773 59.507812 C 111.92773 59.507813 111.92871 55.014649 108.37109 54.125 L 97.697266 51.455078 C 96.283273 47.658406 94.245149 44.164957 91.708984 41.099609 L 94.736328 30.513672 L 94.742188 30.498047 L 94.744141 30.486328 C 95.720526 26.984137 91.875035 24.751558 91.855469 24.740234 L 84.072266 20.246094 C 84.072175 20.246042 84.068504 20.244224 84.068359 20.244141 C 84.01404 20.212982 80.171032 18.023649 77.638672 20.630859 L 80.181641 18 L 77.630859 20.636719 L 69.980469 28.548828 C 68.035313 28.223033 66.037713 28.054688 64 28.054688 C 61.962283 28.054688 59.964687 28.223025 58.019531 28.548828 L 50.369141 20.636719 L 47.818359 18 z M 64 36 A 27.999999 27.999997 0 0 1 92 64 A 27.999999 27.999997 0 0 1 64 92 A 27.999999 27.999997 0 0 1 36 64 A 27.999999 27.999997 0 0 1 64 36 z " /><path
id="Y"
style="fill:none;stroke:#282000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 64.000002,62.00022 50.000001,48.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003"
inkscape:label="Y"
sodipodi:nodetypes="cccccccc" /></g></g></svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,5 +1,5 @@
project('yafi', project('yafi',
version: '0.1.0', version: '0.6',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
default_options: [ 'warning_level=2', 'werror=false', ], default_options: [ 'warning_level=2', 'werror=false', ],
) )

View File

@@ -8,9 +8,11 @@ yafi/window.py
yafi/thermals.py yafi/thermals.py
yafi/leds.py yafi/leds.py
yafi/battery.py yafi/battery.py
yafi/battery_limiter.py
yafi/hardware.py yafi/hardware.py
yafi/ui/window.ui yafi/ui/window.ui
yafi/ui/thermals.ui yafi/ui/thermals.ui
yafi/ui/leds.ui yafi/ui/leds.ui
yafi/ui/battery.ui yafi/ui/battery.ui
yafi/ui/battery_limiter.ui
yafi/ui/hardware.ui yafi/ui/hardware.ui

BIN
pyinstaller/LpcCrOSEC.bin Normal file

Binary file not shown.

View File

@@ -0,0 +1,8 @@
import os
if os.name == 'nt' and os.environ.get('GDK_SCALE') is None:
import ctypes
scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
os.environ["GDK_SCALE"] = f"{round(scale_factor, -2)//100}"
from yafi import main
main()

BIN
pyinstaller/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

384
pyinstaller/splash.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="256"
height="256"
viewBox="0 0 256 256"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="splash.svg"
inkscape:export-filename="splash.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180"
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="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showguides="false"
inkscape:zoom="2.8284271"
inkscape:cx="118.26361"
inkscape:cy="131.87541"
inkscape:window-width="1876"
inkscape:window-height="1025"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter151"
x="0"
y="0"
width="1.0384614"
height="1.0384614">
<feFlood
result="flood"
in="SourceGraphic"
flood-opacity="0.498039"
flood-color="rgb(0,0,0)"
id="feFlood150" />
<feGaussianBlur
result="blur"
in="SourceGraphic"
stdDeviation="0.000000"
id="feGaussianBlur150" />
<feOffset
result="offset"
in="blur"
dx="4.000000"
dy="4.000000"
id="feOffset150" />
<feComposite
result="comp1"
operator="in"
in="flood"
in2="offset"
id="feComposite150" />
<feComposite
result="comp2"
operator="over"
in="SourceGraphic"
in2="comp1"
id="feComposite151" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter151-6"
x="0"
y="0"
width="1.0384615"
height="1.0384615">
<feFlood
result="flood"
in="SourceGraphic"
flood-opacity="0.498039"
flood-color="rgb(0,0,0)"
id="feFlood150-1" />
<feGaussianBlur
result="blur"
in="SourceGraphic"
stdDeviation="0.000000"
id="feGaussianBlur150-8" />
<feOffset
result="offset"
in="blur"
dx="4.000000"
dy="4.000000"
id="feOffset150-7" />
<feComposite
result="comp1"
operator="in"
in="flood"
in2="offset"
id="feComposite150-9" />
<feComposite
result="comp2"
operator="over"
in="SourceGraphic"
in2="comp1"
id="feComposite151-2" />
</filter>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#7c916f;stroke-width:1;paint-order:stroke fill markers"
id="rect1"
width="256"
height="256"
x="0"
y="0" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,117.875 H 23.874996 L 43.75,97.999992 H 76"
id="path4"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,97.875016 H 23.874996 L 43.75,78.000008 H 76"
id="path4-4"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,77.875008 H 23.874996 L 43.75,58 H 76"
id="path4-9"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,57.875008 H 23.874996 L 43.75,38 H 76"
id="path4-2"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 97.999999,0 V 16.000001"
id="path5"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 118,0 V 16.000001"
id="path5-8"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 138.00001,0 V 16.000001"
id="path5-4"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 157.99999,0 V 16.000001"
id="path5-89"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 256,117.87502 H 232.125 L 212.25,98.000008 H 180"
id="path4-1"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 255.625,97.875035 H 231.75 L 211.875,78.000023 H 180"
id="path4-1-5"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 255.625,77.875012 H 231.75 L 211.875,58 H 180"
id="path4-1-6"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 255.625,57.875012 H 231.75 L 211.875,38 H 180"
id="path4-1-63"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="m 192.16536,142.16357 -9.15729,-9.15728 H 163.00629 L 158.00001,128 l 0,-8"
id="path9" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="m 147.1875,143.0625 v -5.875 L 138.00001,128 l 0,-8"
id="path10" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 119.98796,141.81833 118,139.83037 V 128 120"
id="path11" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="m 101,142.125 -3.000001,-3 V 128 120"
id="path12" />
<text
xml:space="preserve"
style="font-weight:bold;font-size:64px;line-height:1;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';text-align:center;text-decoration-color:#000000;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#282000;stroke-width:0.264583;paint-order:stroke fill markers"
x="131.10406"
y="189.98389"
id="text1"><tspan
sodipodi:role="line"
id="tspan1"
style="font-size:64px;fill:#282000;stroke-width:0.264583"
x="131.10406"
y="189.98389">YAFI</tspan></text>
<text
xml:space="preserve"
style="font-weight:bold;font-size:21.3333px;line-height:1;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';text-align:center;text-decoration-color:#000000;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#282000;stroke-width:0.264583;paint-order:stroke fill markers"
x="128.032"
y="214.66127"
id="text1-6"><tspan
sodipodi:role="line"
id="tspan1-0"
style="font-size:21.3333px;fill:#282000;stroke-width:0.264583"
x="128.032"
y="214.66127">Yet Another</tspan><tspan
sodipodi:role="line"
style="font-size:21.3333px;fill:#282000;stroke-width:0.264583"
x="128.032"
y="235.99457"
id="tspan2">Framework Interface</tspan></text>
<g
id="icon"
inkscape:label="icon"
transform="translate(63.999984,4)">
<g
id="chip"
inkscape:label="chip"
style="stroke-width:0;stroke-dasharray:none">
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34,8 v 8"
id="pin1"
sodipodi:nodetypes="cc"
inkscape:label="pin1" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 54,8.0000019 V 16.000002"
id="pin2"
sodipodi:nodetypes="cc"
inkscape:label="pin2" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 74.000009,8.0000019 V 16.000002"
id="pin3"
sodipodi:nodetypes="cc"
inkscape:label="pin3" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 93.999992,8 v 8"
id="pin4"
sodipodi:nodetypes="cc"
inkscape:label="pin4" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,34.000001 h -8"
id="pin5"
sodipodi:nodetypes="cc"
inkscape:label="pin5" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,54.000001 h -8"
id="pin6"
sodipodi:nodetypes="cc"
inkscape:label="pin6" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,74.000009 h -8"
id="pin7"
sodipodi:nodetypes="cc"
inkscape:label="pin7" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,93.999992 h -8"
id="pin8"
sodipodi:nodetypes="cc"
inkscape:label="pin8" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 94.000009,112 v 8"
id="pin9"
sodipodi:nodetypes="cc"
inkscape:label="pin9" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 74.000026,112 v 8"
id="pin10"
sodipodi:nodetypes="cc"
inkscape:label="pin10" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 54.000017,112 v 8"
id="pin11"
sodipodi:nodetypes="cc"
inkscape:label="pin11" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34.000017,112 v 8"
id="pin12"
sodipodi:nodetypes="cc"
inkscape:label="pin12" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,93.999992 H 8.0000019"
id="pin13"
sodipodi:nodetypes="cc"
inkscape:label="pin13" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,74.000009 H 8.0000019"
id="pin14"
sodipodi:nodetypes="cc"
inkscape:label="pin14" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,54.000001 H 8.0000019"
id="pin15"
sodipodi:nodetypes="cc"
inkscape:label="pin15" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,34.000001 H 8.0000019"
id="pin16"
sodipodi:nodetypes="cc"
inkscape:label="pin16" />
<rect
style="fill:#000000;fill-opacity:0.5;stroke-width:0;stroke-dasharray:none"
id="shadow"
width="104"
height="104"
x="16"
y="16"
rx="16.000002"
ry="16.000002"
inkscape:label="shadow" />
<rect
style="fill:#808080;stroke-width:0;stroke-dasharray:none"
id="package"
width="104"
height="104"
x="12.000001"
y="12.000001"
rx="16.000002"
ry="16.000002"
inkscape:label="package" />
<circle
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
id="dot"
cx="22"
cy="22"
r="2"
inkscape:label="dot" />
</g>
<path
id="cog"
style="display:inline;fill:#282000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
inkscape:label="cog"
d="m 47.818359,18 2.542969,2.630859 c -2.531091,-2.605911 -6.371601,-0.420022 -6.429687,-0.386718 l -0.0039,0.002 -7.783203,4.49414 c -0.03804,0.02206 -3.86275,2.252383 -2.888672,5.746094 l 0.002,0.01172 0.0059,0.01563 3.027344,10.585937 C 33.755122,44.16501 31.7167,47.65852 30.302828,51.455131 l -10.679687,2.671875 c -3.549895,0.894185 -3.550781,5.380768 -3.550781,5.380859 v 8.984376 c 0,9.1e-5 0.001,4.493173 3.558593,5.382812 l 10.671875,2.669922 c 1.413872,3.79649 3.452294,7.288523 5.988282,10.353516 l -3.027344,10.585937 -0.0059,0.01758 c -1.79e-4,6.36e-4 1.79e-4,0.0013 0,0.002 -0.9887,3.517182 2.886719,5.755862 2.886719,5.755862 l 7.783203,4.49414 c 0,0 0.0038,0.002 0.0039,0.002 0.02177,0.0125 3.888449,2.23357 6.43164,-0.38867 L 47.818359,110 l 2.548829,-2.63477 7.652343,-7.914058 c 1.945156,0.325909 3.942752,0.494141 5.980469,0.49414 2.037713,0 4.035313,-0.168231 5.980469,-0.49414 l 7.65039,7.912108 0.002,0.002 2.548829,2.63477 -2.544922,-2.63281 c 2.522803,2.60122 6.349136,0.43583 6.43164,0.38867 l 7.78711,-4.49609 c 0,0 3.87542,-2.23868 2.886719,-5.755864 v -0.002 l -0.0059,-0.01758 -3.027344,-10.585937 c 2.536165,-3.064993 4.574289,-6.557026 5.988282,-10.353516 L 108.37109,73.875 c 3.55762,-0.889648 3.55664,-5.382812 3.55664,-5.382812 v -8.984376 c 0,10e-7 9.8e-4,-4.493163 -3.55664,-5.382812 L 97.697266,51.455078 C 96.283273,47.658406 94.245149,44.164957 91.708984,41.099609 l 3.027344,-10.585937 0.0059,-0.01563 0.002,-0.01172 c 0.976385,-3.502191 -2.869106,-5.73477 -2.888672,-5.746094 l -7.783203,-4.49414 c -9.1e-5,-5.2e-5 -0.0038,-0.0019 -0.0039,-0.002 -0.05432,-0.03116 -3.897327,-2.220492 -6.429687,0.386718 L 80.181641,18 l -2.550782,2.636719 -7.65039,7.912109 C 68.035313,28.223033 66.037713,28.054688 64,28.054688 c -2.037717,0 -4.035313,0.168337 -5.980469,0.49414 l -7.65039,-7.912109 z M 64,36 A 27.999999,27.999997 0 0 1 92,64 27.999999,27.999997 0 0 1 64,92 27.999999,27.999997 0 0 1 36,64 27.999999,27.999997 0 0 1 64,36 Z" />
<path
id="Y"
style="fill:none;stroke:#282000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 64.000002,62.00022 50.000001,48.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003"
inkscape:label="Y"
sodipodi:nodetypes="cccccccc" />
</g>
<rect
style="fill:#ffcc00;stroke:none;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;paint-order:normal"
id="rect12"
width="256"
height="12"
x="0"
y="244" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

BIN
pyinstaller/yafi.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

81
pyinstaller/yafi.spec Executable file
View File

@@ -0,0 +1,81 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
import os
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--onefile", action="store_true")
options = parser.parse_args()
datas = [('LpcCrOSEC.bin', '.')] if os.name == 'nt' and os.path.exists('LpcCrOSEC.bin') else []
datas += collect_data_files('yafi')
a = Analysis(
['entrypoint.py'],
pathex=[],
binaries=[],
datas=datas,
hiddenimports=['cros_ec_python'],
hookspath=[],
hooksconfig={"gi":{"module-versions": {"Gtk": "4.0"}}},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=2,
)
pyz = PYZ(a.pure)
if options.onefile:
splash = Splash(
'splash.png',
binaries=a.binaries,
datas=a.datas,
text_pos=(4, 480),
# Text doesn't scale on Linux, but does on Windows
text_size=12 if os.name == 'nt' else 6,
minify_script=True,
always_on_top=True,
)
exe_args = (
[
a.scripts,
a.binaries,
a.datas,
splash,
splash.binaries,
]
if options.onefile
else [a.scripts]
)
exe = EXE(
pyz,
*exe_args,
exclude_binaries=not options.onefile,
name='YAFI',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['yafi.ico'],
)
if not options.onefile:
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='YAFI',
)

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "yafi" name = "yafi"
version = "0.1" version = "0.6"
authors = [ authors = [
{ name="Steve-Tech" } { name="Steve-Tech" }
] ]
@@ -8,11 +8,11 @@ description = "Yet Another Framework Interface"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
"cros_ec_python >= 0.0.2", "cros_ec_python >= 0.2.0",
"PyGObject" "PyGObject"
] ]
classifiers = [ classifiers = [
"Development Status :: 2 - Pre-Alpha", "Development Status :: 3 - Alpha",
"Intended Audience :: End Users/Desktop", "Intended Audience :: End Users/Desktop",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",

View File

@@ -0,0 +1,14 @@
{
"name": "python3-cros_ec_python",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"cros_ec_python\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/33/11/c23a7acaa333589921a2f524517eb719dfb72628153ae22fcdf2e9052ac6/cros_ec_python-0.2.0-py3-none-any.whl",
"sha256": "d38e493fbcaf23bc4b613d1342a036cecc6506284afc74f37013a3eac85a01b9"
}
]
}

View File

@@ -18,198 +18,86 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw from gi.repository import Gtk, Adw, GLib
from gi.repository import Gtk
from gi.repository import GLib
import cros_ec_python.commands as ec_commands import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions import cros_ec_python.exceptions as ec_exceptions
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/battery.ui')
@Gtk.Template(resource_path="/au/stevetech/yafi/ui/battery.ui")
class BatteryPage(Gtk.Box): class BatteryPage(Gtk.Box):
__gtype_name__ = 'BatteryPage' __gtype_name__ = "BatteryPage"
chg_limit_enable = Gtk.Template.Child() batt_status = Gtk.Template.Child()
chg_limit = Gtk.Template.Child()
chg_limit_scale = Gtk.Template.Child()
bat_limit = Gtk.Template.Child()
bat_limit_scale = Gtk.Template.Child()
chg_limit_override = Gtk.Template.Child()
chg_limit_override_btn = Gtk.Template.Child()
bat_ext_group = Gtk.Template.Child() batt_charge = Gtk.Template.Child()
bat_ext_enable = Gtk.Template.Child() batt_health = Gtk.Template.Child()
bat_ext_stage = Gtk.Template.Child() batt_cycles_label = Gtk.Template.Child()
bat_ext_trigger_time = Gtk.Template.Child() batt_volts_label = Gtk.Template.Child()
bat_ext_reset_time = Gtk.Template.Child() batt_watts_label = Gtk.Template.Child()
bat_ext_trigger = Gtk.Template.Child() batt_cap_rem_label = Gtk.Template.Child()
bat_ext_reset = Gtk.Template.Child() batt_cap_full_label = Gtk.Template.Child()
batt_manu = Gtk.Template.Child()
batt_model = Gtk.Template.Child()
batt_serial = Gtk.Template.Child()
batt_type = Gtk.Template.Child()
batt_orig_cap = Gtk.Template.Child()
batt_orig_volts = Gtk.Template.Child()
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
def setup(self, app): def setup(self, app):
# Charge limiter battery = ec_commands.memmap.get_battery_values(app.cros_ec)
try: self.batt_manu.set_subtitle(battery["manufacturer"])
ec_limit = ec_commands.framework_laptop.get_charge_limit(app.cros_ec) self.batt_model.set_subtitle(battery["model"])
ec_limit_enabled = ec_limit != (0, 0) self.batt_serial.set_subtitle(battery["serial"])
self.chg_limit_enable.set_active(ec_limit_enabled) self.batt_type.set_subtitle(battery["type"])
if ec_limit_enabled: self.batt_orig_cap.set_subtitle(f"{self._get_watts(battery, 'design_capacity'):.2f}Wh")
self.chg_limit_scale.set_value(ec_limit[0]) self.batt_orig_volts.set_subtitle(f"{battery['design_voltage']/1000}V")
self.bat_limit_scale.set_value(ec_limit[1]) self._update_battery(app, battery)
self.chg_limit.set_sensitive(True)
self.bat_limit.set_sensitive(True)
self.chg_limit_override.set_sensitive(True)
def handle_chg_limit_change(min, max):
ec_commands.framework_laptop.set_charge_limit(
app.cros_ec, int(min), int(max)
)
def handle_chg_limit_enable(switch):
active = switch.get_active()
if active:
handle_chg_limit_change(
self.chg_limit_scale.get_value(), self.bat_limit_scale.get_value()
)
else:
ec_commands.framework_laptop.disable_charge_limit(app.cros_ec)
self.chg_limit.set_sensitive(active)
self.bat_limit.set_sensitive(active)
self.chg_limit_override.set_sensitive(active)
self.chg_limit_enable.connect(
"notify::active", lambda switch, _: handle_chg_limit_enable(switch)
)
self.chg_limit_scale.connect(
"value-changed",
lambda scale: handle_chg_limit_change(
scale.get_value(), self.bat_limit_scale.get_value()
),
)
self.bat_limit_scale.connect(
"value-changed",
lambda scale: handle_chg_limit_change(
self.chg_limit_scale.get_value(), scale.get_value()
),
)
self.chg_limit_override_btn.connect(
"clicked",
lambda _: ec_commands.framework_laptop.override_charge_limit(
app.cros_ec
),
)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT)
self.chg_limit_enable.set_sensitive(False)
else:
raise e
# Battery Extender
try:
ec_extender = ec_commands.framework_laptop.get_battery_extender(
app.cros_ec
)
self.bat_ext_enable.set_active(not ec_extender["disable"])
self.bat_ext_stage.set_sensitive(not ec_extender["disable"])
self.bat_ext_trigger_time.set_sensitive(not ec_extender["disable"])
self.bat_ext_reset_time.set_sensitive(not ec_extender["disable"])
self.bat_ext_trigger.set_sensitive(not ec_extender["disable"])
self.bat_ext_reset.set_sensitive(not ec_extender["disable"])
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
self.bat_ext_trigger_time.set_subtitle(
format_timedelta(ec_extender["trigger_timedelta"])
)
self.bat_ext_reset_time.set_subtitle(
format_timedelta(ec_extender["reset_timedelta"])
)
self.bat_ext_trigger.set_value(ec_extender["trigger_days"])
self.bat_ext_reset.set_value(ec_extender["reset_minutes"])
def handle_extender_enable(switch):
active = switch.get_active()
ec_commands.framework_laptop.set_battery_extender(
app.cros_ec,
not active,
int(self.bat_ext_trigger.get_value()),
int(self.bat_ext_reset.get_value()),
)
self.bat_ext_stage.set_sensitive(active)
self.bat_ext_trigger_time.set_sensitive(active)
self.bat_ext_reset_time.set_sensitive(active)
self.bat_ext_trigger.set_sensitive(active)
self.bat_ext_reset.set_sensitive(active)
self.bat_ext_enable.connect(
"notify::active", lambda switch, _: handle_extender_enable(switch)
)
self.bat_ext_trigger.connect(
"notify::value",
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
app.cros_ec,
not self.bat_ext_enable.get_active(),
int(scale.get_value()),
int(self.bat_ext_reset.get_value()),
),
)
self.bat_ext_reset.connect(
"notify::value",
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
app.cros_ec,
not self.bat_ext_enable.get_active(),
int(self.bat_ext_trigger.get_value()),
int(scale.get_value()),
),
)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
)
self.bat_ext_group.set_visible(False)
else:
raise e
# Schedule _update_battery to run every second # Schedule _update_battery to run every second
GLib.timeout_add_seconds( GLib.timeout_add_seconds(1, self._update_battery, app)
1,
self._update_battery,
app
)
def _update_battery(self, app): def _get_watts(self, battery, key, volt_key="design_voltage"):
if ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in app.no_support: return (battery[key] * battery[volt_key]) / 1000_000
return False
try: def _update_battery(self, app, battery=None):
ec_extender = ec_commands.framework_laptop.get_battery_extender( if battery is None:
app.cros_ec battery = ec_commands.memmap.get_battery_values(app.cros_ec)
)
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"])) status_messages = []
self.bat_ext_trigger_time.set_subtitle( if battery["invalid_data"]:
format_timedelta(ec_extender["trigger_timedelta"]) status_messages.append("Invalid Data")
if not battery["batt_present"]:
status_messages.append("No Battery")
if battery["ac_present"]:
status_messages.append("Plugged in")
if battery["level_critical"]:
status_messages.append("Critical")
if battery["discharging"]:
status_messages.append("Discharging")
if battery["charging"]:
status_messages.append("Charging")
self.batt_status.set_subtitle(", ".join(status_messages))
self.batt_charge.set_fraction(
battery["capacity"] / battery["last_full_charge_capacity"]
) )
self.bat_ext_reset_time.set_subtitle( self.batt_health.set_fraction(
format_timedelta(ec_extender["reset_timedelta"]) battery["last_full_charge_capacity"] / battery["design_capacity"]
) )
except ec_exceptions.ECError as e: self.batt_cycles_label.set_label(str(battery["cycle_count"]))
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND: self.batt_volts_label.set_label(f"{battery['volt']/1000:.2f}V")
app.no_support.append( self.batt_watts_label.set_label(
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER f"{self._get_watts(battery, 'rate', 'volt') * (-1 if battery['charging'] else 1):.2f}W"
)
self.batt_cap_rem_label.set_label(
f"{self._get_watts(battery, 'capacity'):.2f}Wh"
)
self.batt_cap_full_label.set_label(
f"{self._get_watts(battery, 'last_full_charge_capacity'):.2f}Wh"
) )
return False
else:
raise e
return app.current_page == 2 return app.current_page == 2
def format_timedelta(timedelta):
days = f"{timedelta.days} days, " if timedelta.days else ""
hours, remainder = divmod(timedelta.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return days + f"{hours}:{minutes:02}:{seconds:02}"

233
yafi/battery_limiter.py Normal file
View File

@@ -0,0 +1,233 @@
# battery_limiter.py
#
# Copyright 2025 Stephen Horvath
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Gtk, Adw, GLib
import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/battery-limiter.ui')
class BatteryLimiterPage(Gtk.Box):
__gtype_name__ = 'BatteryLimiterPage'
chg_limit_enable = Gtk.Template.Child()
chg_limit = Gtk.Template.Child()
chg_limit_label = Gtk.Template.Child()
chg_limit_scale = Gtk.Template.Child()
bat_limit = Gtk.Template.Child()
bat_limit_label = Gtk.Template.Child()
bat_limit_scale = Gtk.Template.Child()
chg_limit_override = Gtk.Template.Child()
chg_limit_override_btn = Gtk.Template.Child()
bat_ext_group = Gtk.Template.Child()
bat_ext_enable = Gtk.Template.Child()
bat_ext_stage = Gtk.Template.Child()
bat_ext_trigger_time = Gtk.Template.Child()
bat_ext_reset_time = Gtk.Template.Child()
bat_ext_trigger = Gtk.Template.Child()
bat_ext_reset = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
def setup(self, app):
# Charge limiter
try:
ec_limit = ec_commands.framework_laptop.get_charge_limit(app.cros_ec)
ec_limit_enabled = ec_limit != (0, 0)
self.chg_limit_enable.set_active(ec_limit_enabled)
if ec_limit_enabled:
self.chg_limit_scale.set_value(ec_limit[0])
self.chg_limit_label.set_label(f"{ec_limit[0]}%")
self.bat_limit_scale.set_value(ec_limit[1])
self.bat_limit_label.set_label(f"{ec_limit[1]}%")
self.chg_limit.set_sensitive(True)
self.bat_limit.set_sensitive(True)
self.chg_limit_override.set_sensitive(True)
def handle_chg_limit_change(min, max):
ec_commands.framework_laptop.set_charge_limit(
app.cros_ec, int(min), int(max)
)
def handle_chg_limit_enable(switch):
active = switch.get_active()
if active:
handle_chg_limit_change(
self.chg_limit_scale.get_value(), self.bat_limit_scale.get_value()
)
else:
ec_commands.framework_laptop.disable_charge_limit(app.cros_ec)
self.chg_limit.set_sensitive(active)
self.bat_limit.set_sensitive(active)
self.chg_limit_override.set_sensitive(active)
self.chg_limit_enable.connect(
"notify::active", lambda switch, _: handle_chg_limit_enable(switch)
)
self.chg_limit_scale.connect(
"value-changed",
lambda scale: handle_chg_limit_change(
scale.get_value(), self.bat_limit_scale.get_value()
),
)
self.bat_limit_scale.connect(
"value-changed",
lambda scale: handle_chg_limit_change(
self.chg_limit_scale.get_value(), scale.get_value()
),
)
self.chg_limit_override_btn.connect(
"clicked",
lambda _: ec_commands.framework_laptop.override_charge_limit(
app.cros_ec
),
)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL)
self.chg_limit_enable.set_sensitive(False)
else:
raise e
# Battery Extender
try:
ec_extender = ec_commands.framework_laptop.get_battery_extender(
app.cros_ec
)
self.bat_ext_enable.set_active(not ec_extender["disable"])
self.bat_ext_stage.set_sensitive(not ec_extender["disable"])
self.bat_ext_trigger_time.set_sensitive(not ec_extender["disable"])
self.bat_ext_reset_time.set_sensitive(not ec_extender["disable"])
self.bat_ext_trigger.set_sensitive(not ec_extender["disable"])
self.bat_ext_reset.set_sensitive(not ec_extender["disable"])
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
self.bat_ext_trigger_time.set_subtitle(
format_timedelta(ec_extender["trigger_timedelta"])
)
self.bat_ext_reset_time.set_subtitle(
format_timedelta(ec_extender["reset_timedelta"])
)
self.bat_ext_trigger.set_value(ec_extender["trigger_days"])
self.bat_ext_reset.set_value(ec_extender["reset_minutes"])
def handle_extender_enable(switch):
active = switch.get_active()
ec_commands.framework_laptop.set_battery_extender(
app.cros_ec,
not active,
int(self.bat_ext_trigger.get_value()),
int(self.bat_ext_reset.get_value()),
)
self.bat_ext_stage.set_sensitive(active)
self.bat_ext_trigger_time.set_sensitive(active)
self.bat_ext_reset_time.set_sensitive(active)
self.bat_ext_trigger.set_sensitive(active)
self.bat_ext_reset.set_sensitive(active)
self.bat_ext_enable.connect(
"notify::active", lambda switch, _: handle_extender_enable(switch)
)
self.bat_ext_trigger.connect(
"notify::value",
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
app.cros_ec,
not self.bat_ext_enable.get_active(),
int(scale.get_value()),
int(self.bat_ext_reset.get_value()),
),
)
self.bat_ext_reset.connect(
"notify::value",
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
app.cros_ec,
not self.bat_ext_enable.get_active(),
int(self.bat_ext_trigger.get_value()),
int(scale.get_value()),
),
)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
)
self.bat_ext_group.set_visible(False)
else:
raise e
# Schedule _update_battery to run every second
GLib.timeout_add_seconds(
1,
self._update_battery,
app
)
def _update_battery(self, app):
success = False
# Charge Limiter
if not ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL in app.no_support:
try:
ec_limit = ec_commands.framework_laptop.get_charge_limit(app.cros_ec)
self.chg_limit_label.set_label(f"{ec_limit[0]}%")
self.bat_limit_label.set_label(f"{ec_limit[1]}%")
success = True
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL)
else:
raise e
# Battery Extender
if not ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in app.no_support:
try:
ec_extender = ec_commands.framework_laptop.get_battery_extender(
app.cros_ec
)
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
self.bat_ext_trigger_time.set_subtitle(
format_timedelta(ec_extender["trigger_timedelta"])
)
self.bat_ext_reset_time.set_subtitle(
format_timedelta(ec_extender["reset_timedelta"])
)
success = True
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
)
else:
raise e
return app.current_page == 3 and success
def format_timedelta(timedelta):
days = f"{timedelta.days} days, " if timedelta.days else ""
hours, remainder = divmod(timedelta.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return days + f"{hours}:{minutes:02}:{seconds:02}"

View File

@@ -18,9 +18,7 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw from gi.repository import Gtk, Adw, GLib
from gi.repository import Gtk
from gi.repository import GLib
import cros_ec_python.commands as ec_commands import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions import cros_ec_python.exceptions as ec_exceptions
@@ -29,17 +27,24 @@ import cros_ec_python.exceptions as ec_exceptions
class HardwarePage(Gtk.Box): class HardwarePage(Gtk.Box):
__gtype_name__ = 'HardwarePage' __gtype_name__ = 'HardwarePage'
hw_als = Gtk.Template.Child()
hw_als_label = Gtk.Template.Child()
hw_chassis = Gtk.Template.Child() hw_chassis = Gtk.Template.Child()
hw_chassis_label = Gtk.Template.Child() hw_chassis_label = Gtk.Template.Child()
hw_fp_pwr = Gtk.Template.Child()
hw_fp_pwr_en = Gtk.Template.Child()
hw_fp_pwr_dis = Gtk.Template.Child()
hw_priv_cam = Gtk.Template.Child() hw_priv_cam = Gtk.Template.Child()
hw_priv_cam_sw = Gtk.Template.Child() hw_priv_cam_sw = Gtk.Template.Child()
hw_priv_mic = Gtk.Template.Child() hw_priv_mic = Gtk.Template.Child()
hw_priv_mic_sw = Gtk.Template.Child() hw_priv_mic_sw = Gtk.Template.Child()
hw_lid_open = Gtk.Template.Child()
hw_fp_pwr = Gtk.Template.Child() hw_lid_open_sw = Gtk.Template.Child()
hw_fp_pwr_en = Gtk.Template.Child() hw_pwr_btn = Gtk.Template.Child()
hw_fp_pwr_dis = Gtk.Template.Child() hw_pwr_btn_sw = Gtk.Template.Child()
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@@ -68,7 +73,13 @@ class HardwarePage(Gtk.Box):
GLib.timeout_add_seconds(1, self._update_hardware, app) GLib.timeout_add_seconds(1, self._update_hardware, app)
def _update_hardware(self, app): def _update_hardware(self, app):
success = False # Memmap (ALS and Lid Open)
als = ec_commands.memmap.get_als(app.cros_ec)
self.hw_als_label.set_label(f"{als[0]} lux" if als[0] != 65535 else "MAX")
switches = ec_commands.memmap.get_switches(app.cros_ec)
self.hw_lid_open_sw.set_active(switches["lid_open"])
self.hw_pwr_btn_sw.set_active(switches["power_button_pressed"])
# Chassis # Chassis
if not ec_commands.framework_laptop.EC_CMD_CHASSIS_INTRUSION in app.no_support: if not ec_commands.framework_laptop.EC_CMD_CHASSIS_INTRUSION in app.no_support:
try: try:
@@ -84,8 +95,6 @@ class HardwarePage(Gtk.Box):
self.hw_chassis.set_subtitle( self.hw_chassis.set_subtitle(
"Currently " + ("Open" if ec_chassis_open else "Closed") "Currently " + ("Open" if ec_chassis_open else "Closed")
) )
success = True
except ec_exceptions.ECError as e: except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND: if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append( app.no_support.append(
@@ -106,8 +115,6 @@ class HardwarePage(Gtk.Box):
) )
self.hw_priv_cam_sw.set_active(ec_privacy["camera"]) self.hw_priv_cam_sw.set_active(ec_privacy["camera"])
self.hw_priv_mic_sw.set_active(ec_privacy["microphone"]) self.hw_priv_mic_sw.set_active(ec_privacy["microphone"])
success = True
except ec_exceptions.ECError as e: except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND: if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append( app.no_support.append(
@@ -118,4 +125,4 @@ class HardwarePage(Gtk.Box):
else: else:
raise e raise e
return app.current_page == 3 and success return app.current_page == 4

View File

@@ -18,8 +18,7 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw from gi.repository import Gtk, Adw, GLib
from gi.repository import Gtk
import cros_ec_python.commands as ec_commands import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions import cros_ec_python.exceptions as ec_exceptions
@@ -28,6 +27,8 @@ import cros_ec_python.exceptions as ec_exceptions
class LedsPage(Gtk.Box): class LedsPage(Gtk.Box):
__gtype_name__ = 'LedsPage' __gtype_name__ = 'LedsPage'
first_run = True
led_pwr = Gtk.Template.Child() led_pwr = Gtk.Template.Child()
led_pwr_scale = Gtk.Template.Child() led_pwr_scale = Gtk.Template.Child()
@@ -36,27 +37,30 @@ class LedsPage(Gtk.Box):
led_advanced = Gtk.Template.Child() led_advanced = Gtk.Template.Child()
led_pwr_colour = Gtk.Template.Child()
led_chg_colour = Gtk.Template.Child()
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
def setup(self, app): def setup(self, app):
# Power LED # Power LED
try: try:
def handle_led_pwr(scale): def handle_led_pwr(scale):
value = int(abs(scale.get_value() - 2)) value = int(abs(scale.get_value() - 2))
ec_commands.framework_laptop.set_fp_led_level(app.cros_ec, value) ec_commands.framework_laptop.set_fp_led_level(app.cros_ec, value)
self.led_pwr.set_subtitle(["High", "Medium", "Low"][value]) self.led_pwr.set_subtitle(["High", "Medium", "Low"][value])
try:
current_fp_level = ec_commands.framework_laptop.get_fp_led_level( current_fp_level = ec_commands.framework_laptop.get_fp_led_level(
app.cros_ec app.cros_ec
).value ).value
self.led_pwr_scale.set_value(abs(current_fp_level - 2)) self.led_pwr_scale.set_value(abs(current_fp_level - 2))
self.led_pwr.set_subtitle(["High", "Medium", "Low"][current_fp_level]) self.led_pwr.set_subtitle(["High", "Medium", "Low"][current_fp_level])
except ValueError:
# LED isn't a normal value
current_fp_level = ec_commands.framework_laptop.get_fp_led_level_int(
app.cros_ec
)
self.led_pwr.set_subtitle(f"Custom ({current_fp_level}%)")
self.led_pwr_scale.connect("value-changed", handle_led_pwr) self.led_pwr_scale.connect("value-changed", handle_led_pwr)
except ec_exceptions.ECError as e: except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND: if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
@@ -85,26 +89,38 @@ class LedsPage(Gtk.Box):
self.led_kbd.set_visible(False) self.led_kbd.set_visible(False)
# Advanced options # Advanced options
if ec_commands.general.get_cmd_versions( if (
ec_commands.general.get_cmd_versions(
app.cros_ec, ec_commands.leds.EC_CMD_LED_CONTROL app.cros_ec, ec_commands.leds.EC_CMD_LED_CONTROL
)
and self.first_run
): ):
# Advanced: Power LED
led_pwr_colour_strings = self.led_pwr_colour.get_model()
all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"] all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"]
led_names = {
def add_colours(strings, led_id): ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED: "Battery LED",
supported_colours = ec_commands.leds.led_control_get_max_values( ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED: "Power LED",
ec_commands.leds.EcLedId.EC_LED_ID_ADAPTER_LED: "Adapter LED",
ec_commands.leds.EcLedId.EC_LED_ID_LEFT_LED: "Left LED",
ec_commands.leds.EcLedId.EC_LED_ID_RIGHT_LED: "Right LED",
ec_commands.leds.EcLedId.EC_LED_ID_RECOVERY_HW_REINIT_LED: "Recovery LED",
ec_commands.leds.EcLedId.EC_LED_ID_SYSRQ_DEBUG_LED: "SysRq LED",
}
leds = {}
for i in range(ec_commands.leds.EcLedId.EC_LED_ID_COUNT.value):
try:
led_id = ec_commands.leds.EcLedId(i)
leds[led_id] = ec_commands.leds.led_control_get_max_values(
app.cros_ec, led_id app.cros_ec, led_id
) )
for i, colour in enumerate(all_colours): except ec_exceptions.ECError as e:
if supported_colours[i]: if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_PARAM:
strings.append(colour) continue
else:
raise e
add_colours( # Power LED does not support Blue, even though Intel models think they do
led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED leds[ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED][2] = 0
)
def handle_led_colour(combobox, led_id): def handle_led_colour(combobox, led_id):
colour = combobox.get_selected() - 2 colour = combobox.get_selected() - 2
@@ -128,27 +144,20 @@ class LedsPage(Gtk.Box):
100, 100,
ec_commands.leds.EcLedColors(colour_idx), ec_commands.leds.EcLedColors(colour_idx),
) )
for led_id, supported_colours in leds.items():
self.led_pwr_colour.connect( if any(supported_colours):
combo = Adw.ComboRow(title=led_names[led_id])
model = Gtk.StringList.new(["Auto", "Off"])
for i, colour in enumerate(all_colours):
if supported_colours[i]:
model.append(colour)
combo.set_model(model)
combo.connect(
"notify::selected", "notify::selected",
lambda combo, _: handle_led_colour( lambda combobox, _, led_id=led_id: handle_led_colour(
combo, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED combobox, led_id
), ),
) )
self.led_advanced.add_row(combo)
# Advanced: Charging LED self.first_run = False
led_chg_colour_strings = self.led_chg_colour.get_model()
add_colours(
led_chg_colour_strings,
ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED,
)
self.led_chg_colour.connect(
"notify::selected",
lambda combo, _: handle_led_colour(
combo, ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED
),
)
else:
self.led_advanced.set_visible(False)

View File

@@ -18,19 +18,21 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
import os
import sys import sys
import traceback import traceback
import threading
import gi import gi
from gi.repository import Gio
gi.require_version('Gtk', '4.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1') gi.require_version('Adw', '1')
from gi.repository import Gtk, Gio, Adw from gi.repository import Gtk, Gio, Adw, GLib
from .window import YafiWindow from .window import YafiWindow
from .thermals import ThermalsPage from .thermals import ThermalsPage
from .leds import LedsPage from .leds import LedsPage
from .battery import BatteryPage from .battery import BatteryPage
from .battery_limiter import BatteryLimiterPage
from .hardware import HardwarePage from .hardware import HardwarePage
from cros_ec_python import get_cros_ec from cros_ec_python import get_cros_ec
@@ -65,12 +67,20 @@ class YafiApplication(Adw.Application):
if not self.win: if not self.win:
self.win = YafiWindow(application=self) self.win = YafiWindow(application=self)
# Update the splash screen
splash = getattr(sys, 'frozen', False) and '_PYI_SPLASH_IPC' in os.environ
if splash:
import pyi_splash
pyi_splash.update_text("Detecting EC")
try: try:
self.cros_ec = get_cros_ec() self.cros_ec = get_cros_ec()
pass pass
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
self.error = e
message = ( message = (
str(e) str(e)
+ "\n\n" + "\n\n"
@@ -78,16 +88,24 @@ class YafiApplication(Adw.Application):
+ "If you are using a Framework Laptop, there are additional troubleshooting steps in the README." + "If you are using a Framework Laptop, there are additional troubleshooting steps in the README."
) )
self.show_error("EC Initalisation Error", message) self.show_error("EC Initalisation Error", message)
if splash:
pyi_splash.close()
self.win.present() self.win.present()
return return
if splash:
pyi_splash.update_text("Building Interface")
self.change_page(self.win.content, ThermalsPage()) self.change_page(self.win.content, ThermalsPage())
pages = ( pages = (
("Thermals", ThermalsPage()), ("Thermals", ThermalsPage()),
("LEDs", LedsPage()), ("LEDs", LedsPage()),
("Battery", BatteryPage()), ("Battery", BatteryPage()),
("Hardware", HardwarePage()), ("Battery Limiter", BatteryLimiterPage()),
("Sensors", HardwarePage()),
("About", None), ("About", None),
) )
@@ -107,6 +125,9 @@ class YafiApplication(Adw.Application):
self.win.navbar.connect("row-activated", lambda box, row: switch_page(row.get_index())) self.win.navbar.connect("row-activated", lambda box, row: switch_page(row.get_index()))
if splash:
pyi_splash.close()
self.win.present() self.win.present()
def on_about_action(self, *args): def on_about_action(self, *args):
@@ -114,25 +135,122 @@ class YafiApplication(Adw.Application):
about = Adw.AboutDialog( about = Adw.AboutDialog(
application_icon="au.stevetech.yafi", application_icon="au.stevetech.yafi",
application_name="Yet Another Framework Interface", application_name="Yet Another Framework Interface",
comments="YAFI is another GUI for the Framework Laptop Embedded Controller.\n" comments="YAFI is another GUI for the Framework Laptop Embedded Controller.\n\n"
+ "It is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.", + "It is written in Python with a GTK4 Adwaita theme, and uses the CrOS_EC_Python library to communicate with the EC.\n\n"
+ "YAFI is not affiliated with Framework Computer Inc. in any way.",
copyright="© 2025 Stephen Horvath", copyright="© 2025 Stephen Horvath",
developer_name="Stephen Horvath", developer_name="Stephen Horvath",
developers=["Stephen Horvath"], developers=["Stephen Horvath https://github.com/Steve-Tech"],
issue_url="https://github.com/Steve-Tech/YAFI/issues", issue_url="https://github.com/Steve-Tech/YAFI/issues",
license_type=Gtk.License.GPL_2_0, license_type=Gtk.License.GPL_2_0,
version="0.1.0", version="0.6",
website="https://github.com/Steve-Tech/YAFI", website="https://github.com/Steve-Tech/YAFI",
) )
about.add_acknowledgement_section(None, ["Framework Computer Inc. https://frame.work/"]) about.add_acknowledgement_section(None, ["Framework Computer Inc. https://frame.work/"])
about.present(self.props.active_window) about.present(self.props.active_window)
if hasattr(self, 'debug_info'):
about.set_debug_info(self.debug_info)
else:
threading.Thread(target=lambda: GLib.idle_add(about.set_debug_info, self.generate_debug_info())).start()
def show_error(self, heading, message): def show_error(self, heading, message):
dialog = Adw.AlertDialog(heading=heading, body=message) dialog = Adw.AlertDialog(heading=heading, body=message)
dialog.add_response("exit", "Exit") dialog.add_response("exit", "Exit")
dialog.connect("response", lambda d, r: self.win.destroy()) dialog.add_response("about", "About")
dialog.set_default_response("exit")
dialog.connect("response", lambda d, r: self.on_about_action() if r == "about" else self.win.destroy())
dialog.present(self.win) dialog.present(self.win)
def generate_debug_info(self):
if hasattr(self, 'debug_info'):
return self.debug_info
info = "YAFI Debug Information\n\n"
if hasattr(self, 'error'):
if isinstance(self.error, Exception):
info += f"{type(self.error).__name__}: {self.error}\n\n"
info += ''.join(traceback.format_exception(type(self.error), self.error, self.error.__traceback__))
info += "\n\n"
else:
info += f"Error: {self.error}\n\n"
info += f"Python Version: {sys.version}\n"
info += f"GTK Version: {Gtk.get_major_version()}.{Gtk.get_minor_version()}.{Gtk.get_micro_version()}\n"
info += f"Adwaita Version: {Adw.get_major_version()}.{Adw.get_minor_version()}.{Adw.get_micro_version()}\n"
try:
import platform
info += f"Platform: {platform.platform()}\n"
info += f"Processor: {platform.processor() or platform.machine()}\n"
except Exception as e:
info += f"Platform Error: {type(e).__name__}: {e}\n"
try:
import importlib.metadata
info += f"Installed Packages: {[(dist.metadata['Name'], dist.version) for dist in importlib.metadata.distributions()]}\n"
except Exception as e:
info += f"Importlib Error: {type(e).__name__}: {e}\n"
try:
if sys.platform == "linux":
with open('/sys/devices/virtual/dmi/id/sys_vendor', 'r') as f:
info += f"Manufacturer: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/product_name', 'r') as f:
info += f"Model: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/product_sku', 'r') as f:
info += f"SKU: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/board_name', 'r') as f:
info += f"Board: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/bios_vendor', 'r') as f:
info += f"BIOS Vendor: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/bios_version', 'r') as f:
info += f"BIOS Version: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/bios_date', 'r') as f:
info += f"BIOS Date: {f.read().strip()}\n"
elif sys.platform == "win32":
import subprocess
ps_cmd = (
"powershell -Command "
"\"$cs = Get-CimInstance -ClassName Win32_ComputerSystem; "
"$board = Get-CimInstance -ClassName Win32_BaseBoard; "
"$bios = Get-CimInstance -ClassName Win32_BIOS; "
"Write-Output $cs.Manufacturer; "
"Write-Output $cs.Model; "
"Write-Output $cs.SystemSKUNumber; "
"Write-Output $board.Product; "
"Write-Output $bios.Manufacturer; "
"Write-Output $bios.Name; "
"Write-Output $bios.ReleaseDate\""
)
output = subprocess.check_output(ps_cmd, shell=True).decode().splitlines()
info += f"Manufacturer: {output[0]}\n"
info += f"Model: {output[1]}\n"
info += f"SKU: {output[2]}\n"
info += f"Board: {output[3]}\n"
info += f"BIOS Vendor: {output[4]}\n"
info += f"BIOS Version: {output[5]}\n"
# Blank line in the output for some reason
info += f"BIOS Date: {output[7]}\n"
except Exception as e:
info += f"System Info Error: {type(e).__name__}: {e}\n"
if self.cros_ec:
info += f"EC Interface: {type(self.cros_ec).__name__}\n"
try:
import cros_ec_python.commands as ec_commands
info += f"EC Version: {ec_commands.general.get_version(self.cros_ec)["version_string_ro"]}\n"
info += f"EC Chip: {ec_commands.general.get_chip_info(self.cros_ec)}\n"
info += f"EC Build Info: {ec_commands.general.get_build_info(self.cros_ec)}\n"
info += f"EC Protocol Version: {ec_commands.general.proto_version(self.cros_ec)}\n"
info += f"EC Protocol Info: {ec_commands.general.get_protocol_info(self.cros_ec)}\n"
except Exception as e:
info += f"EC Info Error: {type(e).__name__}: {e}\n"
self.debug_info = info
return info
def main(): def main():
"""The application's entry point.""" """The application's entry point."""

View File

@@ -33,6 +33,7 @@ yafi_sources = [
'thermals.py', 'thermals.py',
'leds.py', 'leds.py',
'battery.py', 'battery.py',
'battery_limiter.py',
'hardware.py', 'hardware.py',
] ]

View File

@@ -18,9 +18,7 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw from gi.repository import Gtk, Adw, GLib
from gi.repository import Gtk
from gi.repository import GLib
import cros_ec_python.commands as ec_commands import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions import cros_ec_python.exceptions as ec_exceptions

184
yafi/ui/battery-limiter.ui Normal file
View File

@@ -0,0 +1,184 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.96.1 -->
<interface>
<!-- interface-name battery-limiter.ui -->
<!-- interface-description The Battery page for YAFI -->
<!-- interface-authors Steve-Tech -->
<requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.0"/>
<template class="BatteryLimiterPage" parent="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkListBox">
<property name="margin-end">10</property>
<property name="margin-start">10</property>
<child>
<object class="AdwActionRow">
<property name="selectable">False</property>
<property name="title">Battery Limiters</property>
</object>
</child>
<child>
<object class="AdwSwitchRow" id="chg_limit_enable">
<property name="title">Enable Charge Limiter</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="chg_limit">
<property name="sensitive">False</property>
<property name="subtitle">Limit the maximum charge</property>
<property name="title">Charge Limit</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkLabel" id="chg_limit_label"/>
</child>
<child>
<object class="GtkScale" id="chg_limit_scale">
<property name="adjustment">
<object class="GtkAdjustment">
<property name="page-increment">10.0</property>
<property name="step-increment">1.0</property>
<property name="upper">100.0</property>
<property name="value">100.0</property>
</object>
</property>
<property name="hexpand">True</property>
<property name="round-digits">0</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="bat_limit">
<property name="sensitive">False</property>
<property name="subtitle">Limit the minimum charge</property>
<property name="title">Discharge Limit</property>
<property name="visible">False</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkLabel" id="bat_limit_label"/>
</child>
<child>
<object class="GtkScale" id="bat_limit_scale">
<property name="adjustment">
<object class="GtkAdjustment">
<property name="page-increment">10.0</property>
<property name="step-increment">1.0</property>
<property name="upper">100.0</property>
</object>
</property>
<property name="hexpand">True</property>
<property name="round-digits">0</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="chg_limit_override">
<property name="sensitive">False</property>
<property name="subtitle">Disables the limiter for one charge</property>
<property name="title">Override Charge Limiter</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="chg_limit_override_btn">
<property name="label">Override</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="bat_ext_group">
<property name="description">Preserve the battery lifespan by gradually lowering battery charge voltage automatically if the system is connected to AC for more than the set day limit.</property>
<property name="margin-bottom">5</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">5</property>
<property name="title">Battery Extender</property>
<child>
<object class="AdwSwitchRow" id="bat_ext_enable">
<property name="title">Enable</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="bat_ext_stage">
<property name="selectable">False</property>
<property name="sensitive">False</property>
<property name="title">Current Stage (0 to 2)</property>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow" id="bat_ext_trigger_time">
<property name="selectable">False</property>
<property name="sensitive">False</property>
<property name="title">Time Until Trigger</property>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow" id="bat_ext_reset_time">
<property name="selectable">False</property>
<property name="sensitive">False</property>
<property name="title">Time Until Reset</property>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwSpinRow" id="bat_ext_trigger">
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">1.0</property>
<property name="page-increment">5.0</property>
<property name="step-increment">1.0</property>
<property name="upper">99.0</property>
<property name="value">5.0</property>
</object>
</property>
<property name="sensitive">False</property>
<property name="subtitle">Number of days on charge before reducing charge limit</property>
<property name="title">Trigger Days</property>
</object>
</child>
<child>
<object class="AdwSpinRow" id="bat_ext_reset">
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">1.0</property>
<property name="page-increment">5.0</property>
<property name="step-increment">1.0</property>
<property name="upper">9999.0</property>
<property name="value">30.0</property>
</object>
</property>
<property name="sensitive">False</property>
<property name="subtitle">Number of minutes off charge before resetting charge limit</property>
<property name="title">Reset Minutes</property>
</object>
</child>
</object>
</child>
<style>
<class name="boxed-list"/>
</style>
</object>
</child>
</template>
</interface>

View File

@@ -1,89 +1,85 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.94.1 --> <!-- Created with Cambalache 0.96.1 -->
<interface> <interface>
<!-- interface-description The Battery page for YAFI --> <!-- interface-name battery.ui -->
<!-- interface-authors Steve-Tech --> <requires lib="gtk" version="4.0"/>
<requires lib="gtk" version="4.12"/> <requires lib="libadwaita" version="1.0"/>
<requires lib="libadwaita" version="1.6"/>
<template class="BatteryPage" parent="GtkBox"> <template class="BatteryPage" parent="GtkBox">
<property name="homogeneous">True</property>
<child>
<object class="GtkPaned">
<property name="end-child">
<object class="GtkListBox">
<property name="width-request">100</property>
<child>
<object class="AdwActionRow" id="batt_manu">
<property name="css-classes">property</property>
<property name="selectable">False</property>
<property name="title">Manufacturer</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_model">
<property name="css-classes">property</property>
<property name="selectable">False</property>
<property name="title">Model</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_serial">
<property name="css-classes">property</property>
<property name="selectable">False</property>
<property name="title">Serial</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_type">
<property name="css-classes">property</property>
<property name="selectable">False</property>
<property name="title">Type</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_orig_cap">
<property name="css-classes">property</property>
<property name="selectable">False</property>
<property name="title">Design Capacity</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_orig_volts">
<property name="css-classes">property</property>
<property name="selectable">False</property>
<property name="title">Design Voltage</property>
</object>
</child>
</object>
</property>
<property name="start-child">
<object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkListBox"> <object class="GtkListBox">
<property name="margin-end">10</property> <property name="margin-end">10</property>
<property name="margin-start">10</property> <property name="margin-start">10</property>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow" id="batt_status">
<property name="selectable">False</property> <property name="selectable">False</property>
<property name="title">Battery Limiters</property> <property name="title">Battery Statistics</property>
</object> </object>
</child> </child>
<child> <child>
<object class="AdwSwitchRow" id="chg_limit_enable"> <object class="AdwActionRow">
<property name="title">Enable Charge Limiter</property> <property name="title">Charge</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="chg_limit">
<property name="sensitive">False</property>
<property name="subtitle">Limit the maximum charge</property>
<property name="title">Charge Limit</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkScale" id="chg_limit_scale">
<property name="adjustment">
<object class="GtkAdjustment">
<property name="page-increment">10.0</property>
<property name="step-increment">1.0</property>
<property name="upper">100.0</property>
<property name="value">100.0</property>
</object>
</property>
<property name="hexpand">True</property>
<property name="round-digits">0</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="bat_limit">
<property name="sensitive">False</property>
<property name="subtitle">Limit the minimum charge</property>
<property name="title">Discharge Limit</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkScale" id="bat_limit_scale">
<property name="adjustment">
<object class="GtkAdjustment">
<property name="page-increment">10.0</property>
<property name="step-increment">1.0</property>
<property name="upper">100.0</property>
</object>
</property>
<property name="hexpand">True</property>
<property name="round-digits">0</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="chg_limit_override">
<property name="sensitive">False</property>
<property name="subtitle">Disables the limiter for one charge cycle</property>
<property name="title">Override Charge Limiter</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="halign">end</property> <property name="halign">end</property>
<property name="homogeneous">True</property> <property name="homogeneous">True</property>
<property name="valign">center</property> <property name="valign">center</property>
<child> <child>
<object class="GtkButton" id="chg_limit_override_btn"> <object class="GtkProgressBar" id="batt_charge">
<property name="label">Override</property> <property name="show-text">True</property>
<property name="width-request">300</property>
</object> </object>
</child> </child>
</object> </object>
@@ -91,79 +87,60 @@
</object> </object>
</child> </child>
<child> <child>
<object class="AdwPreferencesGroup" id="bat_ext_group"> <object class="AdwActionRow">
<property name="description">Preserve the battery lifespan by gradually lowering battery charge voltage automatically if the system is connected to AC for more than the set day limit.</property> <property name="title">Health</property>
<property name="margin-bottom">5</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">5</property>
<property name="title">Battery Extender</property>
<child> <child>
<object class="AdwSwitchRow" id="bat_ext_enable"> <object class="GtkBox">
<property name="title">Enable</property> <property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="valign">center</property>
<child>
<object class="GtkProgressBar" id="batt_health">
<property name="show-text">True</property>
<property name="width-request">300</property>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow" id="bat_ext_stage"> <object class="AdwActionRow" id="batt_cycles">
<property name="selectable">False</property> <property name="title">Charge Cycles</property>
<property name="sensitive">False</property> <child>
<property name="title">Current Stage (0 to 2)</property> <object class="GtkLabel" id="batt_cycles_label"/>
<style> </child>
<class name="property"/>
</style>
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow" id="bat_ext_trigger_time"> <object class="AdwActionRow" id="batt_volts">
<property name="selectable">False</property> <property name="title">Current Voltage</property>
<property name="sensitive">False</property> <child>
<property name="title">Time Until Trigger</property> <object class="GtkLabel" id="batt_volts_label"/>
<style> </child>
<class name="property"/>
</style>
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow" id="bat_ext_reset_time"> <object class="AdwActionRow" id="batt_watts">
<property name="selectable">False</property> <property name="title">Current Wattage</property>
<property name="sensitive">False</property> <child>
<property name="title">Time Until Reset</property> <object class="GtkLabel" id="batt_watts_label"/>
<style> </child>
<class name="property"/>
</style>
</object> </object>
</child> </child>
<child> <child>
<object class="AdwSpinRow" id="bat_ext_trigger"> <object class="AdwActionRow" id="batt_cap_rem">
<property name="adjustment"> <property name="title">Remaining Charge</property>
<object class="GtkAdjustment"> <child>
<property name="lower">1.0</property> <object class="GtkLabel" id="batt_cap_rem_label"/>
<property name="page-increment">5.0</property> </child>
<property name="step-increment">1.0</property>
<property name="upper">99.0</property>
<property name="value">5.0</property>
</object>
</property>
<property name="sensitive">False</property>
<property name="subtitle">Number of days on charge before reducing charge limit</property>
<property name="title">Trigger Days</property>
</object> </object>
</child> </child>
<child> <child>
<object class="AdwSpinRow" id="bat_ext_reset"> <object class="AdwActionRow" id="batt_cap_full">
<property name="adjustment"> <property name="title">Last Full Charge</property>
<object class="GtkAdjustment"> <child>
<property name="lower">1.0</property> <object class="GtkLabel" id="batt_cap_full_label"/>
<property name="page-increment">5.0</property>
<property name="step-increment">1.0</property>
<property name="upper">9999.0</property>
<property name="value">30.0</property>
</object>
</property>
<property name="sensitive">False</property>
<property name="subtitle">Number of minutes off charge before resetting charge limit</property>
<property name="title">Reset Minutes</property>
</object>
</child> </child>
</object> </object>
</child> </child>
@@ -172,5 +149,9 @@
</style> </style>
</object> </object>
</child> </child>
</object>
</property>
</object>
</child>
</template> </template>
</interface> </interface>

View File

@@ -1,10 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.94.1 --> <!-- Created with Cambalache 0.96.1 -->
<interface> <interface>
<!-- interface-description The Hardware page for YAFI --> <!-- interface-name hardware.ui -->
<!-- interface-description The Sensors page for YAFI -->
<!-- interface-copyright Steve-Tech --> <!-- interface-copyright Steve-Tech -->
<requires lib="gtk" version="4.12"/> <requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.3"/> <requires lib="libadwaita" version="1.0"/>
<template class="HardwarePage" parent="GtkBox"> <template class="HardwarePage" parent="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
@@ -14,7 +15,15 @@
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow">
<property name="selectable">False</property> <property name="selectable">False</property>
<property name="title">Hardware Status</property> <property name="title">Sensors</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="hw_als">
<property name="title">Ambient Light Sensor</property>
<child>
<object class="GtkLabel" id="hw_als_label"/>
</child>
</object> </object>
</child> </child>
<child> <child>
@@ -25,6 +34,30 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow" id="hw_fp_pwr">
<property name="title">Fingerprint</property>
<property name="visible">False</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="spacing">5</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="hw_fp_pwr_en">
<property name="label">Enable</property>
</object>
</child>
<child>
<object class="GtkButton" id="hw_fp_pwr_dis">
<property name="label">Disable</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child> <child>
<object class="AdwActionRow" id="hw_priv_cam"> <object class="AdwActionRow" id="hw_priv_cam">
<property name="subtitle">Use Privacy Switch</property> <property name="subtitle">Use Privacy Switch</property>
@@ -64,23 +97,35 @@
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow" id="hw_fp_pwr"> <object class="AdwActionRow" id="hw_lid_open">
<property name="title">Fingerprint</property> <property name="title">Lid Open</property>
<property name="visible">False</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="halign">end</property> <property name="halign">end</property>
<property name="homogeneous">True</property> <property name="homogeneous">True</property>
<property name="spacing">5</property>
<property name="valign">center</property> <property name="valign">center</property>
<child> <child>
<object class="GtkButton" id="hw_fp_pwr_en"> <object class="GtkSwitch" id="hw_lid_open_sw">
<property name="label">Enable</property> <property name="active">True</property>
<property name="sensitive">False</property>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkButton" id="hw_fp_pwr_dis"> <object class="AdwActionRow" id="hw_pwr_btn">
<property name="label">Disable</property> <property name="title">Power Button Pressed</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="valign">center</property>
<child>
<object class="GtkSwitch" id="hw_pwr_btn_sw">
<property name="active">True</property>
<property name="sensitive">False</property>
</object> </object>
</child> </child>
</object> </object>

View File

@@ -1,10 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.94.1 --> <!-- Created with Cambalache 0.96.1 -->
<interface> <interface>
<!-- interface-name leds.ui -->
<!-- interface-description The LEDs page for YAFI --> <!-- interface-description The LEDs page for YAFI -->
<!-- interface-authors Steve-Tech --> <!-- interface-authors Steve-Tech -->
<requires lib="gtk" version="4.0"/> <requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.6"/> <requires lib="libadwaita" version="1.0"/>
<template class="LedsPage" parent="GtkBox"> <template class="LedsPage" parent="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
@@ -68,52 +69,6 @@
<property name="selectable">False</property> <property name="selectable">False</property>
<property name="subtitle">These options break normal functionality</property> <property name="subtitle">These options break normal functionality</property>
<property name="title">Advanced Options</property> <property name="title">Advanced Options</property>
<child>
<object class="AdwPreferencesGroup">
<property name="description">When using these options, the power LED may not turn off with the computer.</property>
<property name="margin-bottom">5</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">5</property>
<property name="title">Power Button LED</property>
<child>
<object class="AdwComboRow" id="led_pwr_colour">
<property name="model">
<object class="GtkStringList">
<items>
<item>Auto</item>
<item>Off</item>
</items>
</object>
</property>
<property name="title">Colour</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="description">When using these options, the charging indicator will no longer indicate charging.</property>
<property name="margin-bottom">5</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">5</property>
<property name="title">Charging Indicators</property>
<child>
<object class="AdwComboRow" id="led_chg_colour">
<property name="model">
<object class="GtkStringList">
<items>
<item>Auto</item>
<item>Off</item>
</items>
</object>
</property>
<property name="title">Colour</property>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
<style> <style>

View File

@@ -1,10 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.94.1 --> <!-- Created with Cambalache 0.96.1 -->
<interface> <interface>
<!-- interface-name thermals.ui -->
<!-- interface-description The Thermals page for YAFI --> <!-- interface-description The Thermals page for YAFI -->
<!-- interface-authors Steve-Tech --> <!-- interface-authors Steve-Tech -->
<requires lib="gtk" version="4.12"/> <requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.6"/> <requires lib="libadwaita" version="1.0"/>
<template class="ThermalsPage" parent="GtkBox"> <template class="ThermalsPage" parent="GtkBox">
<property name="homogeneous">True</property> <property name="homogeneous">True</property>
<child> <child>

View File

@@ -1,313 +1,11 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?> <?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd"> <!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<cambalache-project version="0.94.0" target_tk="gtk-4.0"> <!-- Created with Cambalache 0.96.1 -->
<ui> <cambalache-project version="0.96.0" target_tk="gtk-4.0">
(1,1,"yafi.ui","yafi.ui","YAFI is another GUI for the Framework Laptop Embedded Controller.",None,"Steve-Tech",None,None,None,None), <ui template-class="YafiWindow" filename="yafi.ui" sha256="9d1b2f030e4a816eb0b1aa53ae1d80c5b50a2f4646e32c7a64803eb6f6ed3947"/>
(2,17,None,"thermals.ui","The Thermals page for YAFI",None,"Steve-Tech",None,None,None,None), <ui template-class="ThermalsPage" filename="thermals.ui" sha256="e301e65649005315ff60d250b60a47f6250ad6feb27db104051fcf0143cde173"/>
(3,4,None,"leds.ui","The LEDs page for YAFI",None,"Steve-Tech",None,None,None,None), <ui template-class="LedsPage" filename="leds.ui" sha256="abc3ee759974a5c92feb48cc258dbe7271d0402facf71fd5e779f2bb1a277e16"/>
(4,1,None,"battery.ui","The Battery page for YAFI",None,"Steve-Tech",None,None,None,None), <ui template-class="BatteryLimiterPage" filename="battery-limiter.ui" sha256="b5d41b19cb1fb7ca5b4bcfae43244e54111f5e8d8c51d95448d6a92b5185d2c4"/>
(5,1,None,"hardware.ui","The Hardware page for YAFI","Steve-Tech",None,None,None,None,None) <ui template-class="HardwarePage" filename="hardware.ui" sha256="37ea282198d9f60435f80e4adf8256cd2249e590dcad4b63af634d828673f1bf"/>
</ui> <ui template-class="BatteryPage" filename="battery.ui" sha256="d495280cb543a26cdee1c2939e5af980d8d3878c326e360eeab4f05195cdbdc9"/>
<object>
(1,1,"AdwApplicationWindow","YafiWindow",None,None,None,None,0,None,None),
(1,2,"AdwNavigationSplitView",None,1,None,None,None,0,None,None),
(1,6,"AdwNavigationPage",None,2,None,None,None,0,None,None),
(1,7,"AdwNavigationPage",None,2,None,None,None,1,None,None),
(1,10,"AdwHeaderBar",None,11,None,"top",None,0,None,None),
(1,11,"AdwToolbarView",None,7,None,None,None,0,None,None),
(1,13,"AdwToolbarView",None,6,None,None,None,0,None,None),
(1,14,"AdwHeaderBar",None,13,None,"top",None,0,None,None),
(1,16,"GtkScrolledWindow",None,13,None,None,None,1,None,None),
(1,17,"GtkListBox","navbar",16,None,None,None,0,None,None),
(1,52,"GtkBox","content",53,None,None,None,0,None,None),
(1,53,"GtkScrolledWindow",None,11,None,None,None,1,None,None),
(2,2,"GtkPaned",None,17,None,None,None,0,None,None),
(2,3,"GtkListBox","temperatures",2,None,None,None,1,None,None),
(2,4,"AdwActionRow",None,3,None,None,None,0,None,None),
(2,5,"GtkBox",None,2,None,None,None,0,None,None),
(2,6,"GtkListBox",None,5,None,None,None,0,None,None),
(2,7,"AdwActionRow",None,6,None,None,None,0,None,None),
(2,8,"AdwActionRow","fan_rpm",6,None,None,None,1,None,None),
(2,9,"AdwComboRow","fan_mode",6,None,None,None,2,None,None),
(2,10,"GtkStringList",None,9,None,None,None,0,None,None),
(2,11,"AdwActionRow","fan_set_percent",6,None,None,None,3,None,None),
(2,12,"GtkBox",None,11,None,None,None,0,None,None),
(2,13,"GtkScale","fan_percent_scale",12,None,None,None,0,None,None),
(2,14,"GtkAdjustment",None,13,None,None,None,0,None,None),
(2,15,"AdwSpinRow","fan_set_rpm",6,None,None,None,4,None,None),
(2,16,"GtkAdjustment",None,15,None,None,None,0,None,None),
(2,17,"GtkBox","ThermalsPage",None,None,None,None,1,None,None),
(3,4,"GtkBox","LedsPage",None,None,None,None,1,None,None),
(3,5,"GtkListBox",None,4,None,None,None,0,None,None),
(3,6,"AdwActionRow",None,5,None,None,None,0,None,None),
(3,10,"AdwActionRow","led_pwr",5,None,None,None,1,None,None),
(3,11,"GtkBox",None,10,None,None,None,0,None,None),
(3,12,"GtkScale","led_pwr_scale",11,None,None,None,0,None,None),
(3,13,"GtkAdjustment",None,12,None,None,None,0,None,None),
(3,14,"AdwActionRow","led_kbd",5,None,None,None,2,None,None),
(3,15,"GtkBox",None,14,None,None,None,0,None,None),
(3,16,"GtkScale","led_kbd_scale",15,None,None,None,0,None,None),
(3,17,"GtkAdjustment",None,16,None,None,None,0,None,None),
(3,18,"AdwExpanderRow","led_advanced",5,None,None,None,3,None,None),
(3,23,"AdwComboRow","led_pwr_colour",31,None,None,None,0,None,None),
(3,24,"GtkStringList",None,23,None,None,None,0,None,None),
(3,25,"AdwComboRow","led_chg_colour",32,None,None,None,0,None,None),
(3,26,"GtkStringList",None,25,None,None,None,0,None,None),
(3,31,"AdwPreferencesGroup",None,18,None,None,None,0,None,None),
(3,32,"AdwPreferencesGroup",None,18,None,None,None,1,None,None),
(4,1,"GtkBox","BatteryPage",None,None,None,None,0,None,None),
(4,2,"GtkListBox",None,1,None,None,None,0,None,None),
(4,3,"AdwActionRow",None,2,None,None,None,0,None,None),
(4,4,"AdwActionRow","chg_limit",2,None,None,None,2,None,None),
(4,5,"GtkBox",None,4,None,None,None,0,None,None),
(4,6,"GtkScale","chg_limit_scale",5,None,None,None,0,None,None),
(4,7,"GtkAdjustment",None,6,None,None,None,0,None,None),
(4,13,"AdwPreferencesGroup","bat_ext_group",2,None,None,None,5,None,None),
(4,27,"AdwSpinRow","bat_ext_trigger",13,None,None,None,5,None,None),
(4,28,"GtkAdjustment",None,27,None,None,None,0,None,None),
(4,29,"AdwSpinRow","bat_ext_reset",13,None,None,None,6,None,None),
(4,30,"GtkAdjustment",None,29,None,None,None,0,None,None),
(4,31,"AdwActionRow","bat_ext_stage",13,None,None,None,2,None,None),
(4,32,"AdwSwitchRow","bat_ext_enable",13,None,None,None,1,None,None),
(4,33,"AdwActionRow","bat_limit",2,None,None,None,3,None,None),
(4,34,"GtkBox",None,33,None,None,None,0,None,None),
(4,35,"GtkScale","bat_limit_scale",34,None,None,None,0,None,None),
(4,36,"GtkAdjustment",None,35,None,None,None,0,None,None),
(4,37,"AdwActionRow","chg_limit_override",2,None,None,None,4,None,None),
(4,38,"GtkBox",None,37,None,None,None,0,None,None),
(4,39,"GtkButton","chg_limit_override_btn",38,None,None,None,0,None,None),
(4,40,"AdwSwitchRow","chg_limit_enable",2,None,None,None,1,None,None),
(4,41,"AdwActionRow","bat_ext_trigger_time",13,None,None,None,3,None,None),
(4,42,"AdwActionRow","bat_ext_reset_time",13,None,None,None,4,None,None),
(5,1,"GtkBox","HardwarePage",None,None,None,None,0,None,None),
(5,2,"GtkListBox",None,1,None,None,None,0,None,None),
(5,3,"AdwActionRow",None,2,None,None,None,0,None,None),
(5,4,"AdwActionRow","hw_chassis",2,None,None,None,1,None,None),
(5,5,"GtkLabel","hw_chassis_label",4,None,None,None,0,None,None),
(5,6,"AdwActionRow","hw_priv_cam",2,None,None,None,2,None,None),
(5,9,"AdwActionRow","hw_priv_mic",2,None,None,None,3,None,None),
(5,11,"GtkSwitch","hw_priv_mic_sw",13,None,None,None,0,None,None),
(5,13,"GtkBox",None,9,None,None,None,0,None,None),
(5,14,"GtkBox",None,6,None,None,None,0,None,None),
(5,15,"GtkSwitch","hw_priv_cam_sw",14,None,None,None,0,None,None),
(5,16,"AdwActionRow","hw_fp_pwr",2,None,None,None,4,None,None),
(5,17,"GtkBox",None,16,None,None,None,0,None,None),
(5,18,"GtkButton","hw_fp_pwr_en",17,None,None,None,0,None,None),
(5,19,"GtkButton","hw_fp_pwr_dis",17,None,None,None,1,None,None)
</object>
<object_property>
(1,1,"GtkWindow","default-height","500",None,None,None,None,None,None,None,None,None),
(1,1,"GtkWindow","default-width","800",None,None,None,None,None,None,None,None,None),
(1,2,"AdwNavigationSplitView","content",None,None,None,None,None,7,None,None,None,None),
(1,2,"AdwNavigationSplitView","sidebar",None,None,None,None,None,6,None,None,None,None),
(1,6,"AdwNavigationPage","title","Settings",None,None,None,None,None,None,None,None,None),
(1,7,"AdwNavigationPage","title","Yet Another Framework Interface",None,None,None,None,None,None,None,None,None),
(1,52,"GtkBox","homogeneous","True",None,None,None,None,None,None,None,None,None),
(1,52,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(2,2,"GtkPaned","end-child",None,0,None,None,None,3,None,None,None,None),
(2,2,"GtkPaned","start-child",None,0,None,None,None,5,None,None,None,None),
(2,4,"AdwPreferencesRow","title","Temperatures",0,None,None,None,None,None,None,None,None),
(2,4,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(2,5,"GtkOrientable","orientation","vertical",0,None,None,None,None,None,None,None,None),
(2,6,"GtkWidget","margin-end","10",0,None,None,None,None,None,None,None,None),
(2,6,"GtkWidget","margin-start","10",0,None,None,None,None,None,None,None,None),
(2,7,"AdwPreferencesRow","title","Fan Control",0,None,None,None,None,None,None,None,None),
(2,7,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(2,8,"AdwPreferencesRow","title","Current Speed",0,None,None,None,None,None,None,None,None),
(2,9,"AdwComboRow","model",None,0,None,None,None,10,None,None,None,None),
(2,9,"AdwPreferencesRow","title","Speed Set Mode",0,None,None,None,None,None,None,None,None),
(2,11,"AdwPreferencesRow","title","Fan Speed",0,None,None,None,None,None,None,None,None),
(2,11,"GtkWidget","visible","False",0,None,None,None,None,None,None,None,None),
(2,13,"GtkRange","adjustment",None,0,None,None,None,14,None,None,None,None),
(2,13,"GtkRange","round-digits","0",0,None,None,None,None,None,None,None,None),
(2,13,"GtkWidget","hexpand","True",0,None,None,None,None,None,None,None,None),
(2,14,"GtkAdjustment","page-increment","10.0",0,None,None,None,None,None,None,None,None),
(2,14,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(2,14,"GtkAdjustment","upper","100.0",0,None,None,None,None,None,None,None,None),
(2,14,"GtkAdjustment","value","100.0",0,None,None,None,None,None,None,None,None),
(2,15,"AdwPreferencesRow","title","Fan RPM Target",0,None,None,None,None,None,None,None,None),
(2,15,"AdwSpinRow","adjustment",None,0,None,None,None,16,None,None,None,None),
(2,15,"GtkWidget","visible","False",0,None,None,None,None,None,None,None,None),
(2,16,"GtkAdjustment","page-increment","1000.0",0,None,None,None,None,None,None,None,None),
(2,16,"GtkAdjustment","step-increment","100.0",0,None,None,None,None,None,None,None,None),
(2,16,"GtkAdjustment","upper","65535.0",0,None,None,None,None,None,None,None,None),
(2,17,"GtkBox","homogeneous","True",None,None,None,None,None,None,None,None,None),
(3,4,"GtkOrientable","orientation","vertical",0,None,None,None,None,None,None,None,None),
(3,5,"GtkWidget","margin-end","10",0,None,None,None,None,None,None,None,None),
(3,5,"GtkWidget","margin-start","10",0,None,None,None,None,None,None,None,None),
(3,6,"AdwPreferencesRow","title","LED Control",0,None,None,None,None,None,None,None,None),
(3,6,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(3,10,"AdwPreferencesRow","title","Power Button",0,None,None,None,None,None,None,None,None),
(3,12,"GtkRange","adjustment",None,0,None,None,None,13,None,None,None,None),
(3,12,"GtkRange","round-digits","0",0,None,None,None,None,None,None,None,None),
(3,12,"GtkWidget","hexpand","True",0,None,None,None,None,None,None,None,None),
(3,13,"GtkAdjustment","page-increment","1.0",0,None,None,None,None,None,None,None,None),
(3,13,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(3,13,"GtkAdjustment","upper","2.0",0,None,None,None,None,None,None,None,None),
(3,13,"GtkAdjustment","value","2.0",0,None,None,None,None,None,None,None,None),
(3,14,"AdwPreferencesRow","title","Keyboard Backlight",0,None,None,None,None,None,None,None,None),
(3,16,"GtkRange","adjustment",None,0,None,None,None,17,None,None,None,None),
(3,16,"GtkRange","round-digits","0",0,None,None,None,None,None,None,None,None),
(3,16,"GtkWidget","hexpand","True",0,None,None,None,None,None,None,None,None),
(3,17,"GtkAdjustment","page-increment","10.0",0,None,None,None,None,None,None,None,None),
(3,17,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(3,17,"GtkAdjustment","upper","100.0",0,None,None,None,None,None,None,None,None),
(3,17,"GtkAdjustment","value","100.0",0,None,None,None,None,None,None,None,None),
(3,18,"AdwExpanderRow","subtitle","These options break normal functionality",None,None,None,None,None,None,None,None,None),
(3,18,"AdwPreferencesRow","title","Advanced Options",None,None,None,None,None,None,None,None,None),
(3,18,"GtkListBoxRow","selectable","False",None,None,None,None,None,None,None,None,None),
(3,23,"AdwComboRow","model",None,0,None,None,None,24,None,None,None,None),
(3,23,"AdwPreferencesRow","title","Colour",0,None,None,None,None,None,None,None,None),
(3,25,"AdwComboRow","model",None,0,None,None,None,26,None,None,None,None),
(3,25,"AdwPreferencesRow","title","Colour",0,None,None,None,None,None,None,None,None),
(3,31,"AdwPreferencesGroup","description","When using these options, the power LED may not turn off with the computer.",None,None,None,None,None,None,None,None,None),
(3,31,"AdwPreferencesGroup","title","Power Button LED",None,None,None,None,None,None,None,None,None),
(3,31,"GtkWidget","margin-bottom","5",None,None,None,None,None,None,None,None,None),
(3,31,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None),
(3,31,"GtkWidget","margin-start","5",None,None,None,None,None,None,None,None,None),
(3,31,"GtkWidget","margin-top","5",None,None,None,None,None,None,None,None,None),
(3,32,"AdwPreferencesGroup","description","When using these options, the charging indicator will no longer indicate charging.",None,None,None,None,None,None,None,None,None),
(3,32,"AdwPreferencesGroup","title","Charging Indicators",None,None,None,None,None,None,None,None,None),
(3,32,"GtkWidget","margin-bottom","5",None,None,None,None,None,None,None,None,None),
(3,32,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None),
(3,32,"GtkWidget","margin-start","5",None,None,None,None,None,None,None,None,None),
(3,32,"GtkWidget","margin-top","5",None,None,None,None,None,None,None,None,None),
(4,1,"GtkOrientable","orientation","vertical",0,None,None,None,None,None,None,None,None),
(4,2,"GtkWidget","margin-end","10",0,None,None,None,None,None,None,None,None),
(4,2,"GtkWidget","margin-start","10",0,None,None,None,None,None,None,None,None),
(4,3,"AdwPreferencesRow","title","Battery Limiters",0,None,None,None,None,None,None,None,None),
(4,3,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(4,4,"AdwActionRow","subtitle","Limit the maximum charge",None,None,None,None,None,None,None,None,None),
(4,4,"AdwPreferencesRow","title","Charge Limit",0,None,None,None,None,None,None,None,None),
(4,4,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,6,"GtkRange","adjustment",None,0,None,None,None,7,None,None,None,None),
(4,6,"GtkRange","round-digits","0",0,None,None,None,None,None,None,None,None),
(4,6,"GtkWidget","hexpand","True",0,None,None,None,None,None,None,None,None),
(4,7,"GtkAdjustment","page-increment","10.0",0,None,None,None,None,None,None,None,None),
(4,7,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(4,7,"GtkAdjustment","upper","100.0",0,None,None,None,None,None,None,None,None),
(4,7,"GtkAdjustment","value","100.0",0,None,None,None,None,None,None,None,None),
(4,13,"AdwPreferencesGroup","description","Preserve the battery lifespan by gradually lowering battery charge voltage automatically if the system is connected to AC for more than the set day limit.",0,None,None,None,None,None,None,None,None),
(4,13,"AdwPreferencesGroup","title","Battery Extender",0,None,None,None,None,None,None,None,None),
(4,13,"GtkWidget","margin-bottom","5",0,None,None,None,None,None,None,None,None),
(4,13,"GtkWidget","margin-end","5",0,None,None,None,None,None,None,None,None),
(4,13,"GtkWidget","margin-start","5",0,None,None,None,None,None,None,None,None),
(4,13,"GtkWidget","margin-top","5",0,None,None,None,None,None,None,None,None),
(4,27,"AdwActionRow","subtitle","Number of days on charge before reducing charge limit",None,None,None,None,None,None,None,None,None),
(4,27,"AdwPreferencesRow","title","Trigger Days",0,None,None,None,None,None,None,None,None),
(4,27,"AdwSpinRow","adjustment",None,0,None,None,None,28,None,None,None,None),
(4,27,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,28,"GtkAdjustment","lower","1.0",None,None,None,None,None,None,None,None,None),
(4,28,"GtkAdjustment","page-increment","5.0",0,None,None,None,None,None,None,None,None),
(4,28,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(4,28,"GtkAdjustment","upper","99.0",0,None,None,None,None,None,None,None,None),
(4,28,"GtkAdjustment","value","5.0",None,None,None,None,None,None,None,None,None),
(4,29,"AdwActionRow","subtitle","Number of minutes off charge before resetting charge limit",0,None,None,None,None,None,None,None,None),
(4,29,"AdwPreferencesRow","title","Reset Minutes",0,None,None,None,None,None,None,None,None),
(4,29,"AdwSpinRow","adjustment",None,0,None,None,None,30,None,None,None,None),
(4,29,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,30,"GtkAdjustment","lower","1.0",None,None,None,None,None,None,None,None,None),
(4,30,"GtkAdjustment","page-increment","5.0",0,None,None,None,None,None,None,None,None),
(4,30,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(4,30,"GtkAdjustment","upper","9999.0",0,None,None,None,None,None,None,None,None),
(4,30,"GtkAdjustment","value","30.0",None,None,None,None,None,None,None,None,None),
(4,31,"AdwPreferencesRow","title","Current Stage (0 to 2)",0,None,None,None,None,None,None,None,None),
(4,31,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(4,31,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,32,"AdwPreferencesRow","title","Enable",None,None,None,None,None,None,None,None,None),
(4,33,"AdwActionRow","subtitle","Limit the minimum charge",0,None,None,None,None,None,None,None,None),
(4,33,"AdwPreferencesRow","title","Discharge Limit",0,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,35,"GtkRange","adjustment",None,0,None,None,None,36,None,None,None,None),
(4,35,"GtkRange","round-digits","0",0,None,None,None,None,None,None,None,None),
(4,35,"GtkWidget","hexpand","True",0,None,None,None,None,None,None,None,None),
(4,36,"GtkAdjustment","page-increment","10.0",0,None,None,None,None,None,None,None,None),
(4,36,"GtkAdjustment","step-increment","1.0",0,None,None,None,None,None,None,None,None),
(4,36,"GtkAdjustment","upper","100.0",0,None,None,None,None,None,None,None,None),
(4,37,"AdwActionRow","subtitle","Disables the limiter for one charge cycle",0,None,None,None,None,None,None,None,None),
(4,37,"AdwPreferencesRow","title","Override Charge Limiter",0,None,None,None,None,None,None,None,None),
(4,37,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,38,"GtkBox","homogeneous","True",None,None,None,None,None,None,None,None,None),
(4,38,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None),
(4,38,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(4,39,"GtkButton","label","Override",None,None,None,None,None,None,None,None,None),
(4,40,"AdwPreferencesRow","title","Enable Charge Limiter",None,None,None,None,None,None,None,None,None),
(4,41,"AdwPreferencesRow","title","Time Until Trigger",0,None,None,None,None,None,None,None,None),
(4,41,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(4,41,"GtkWidget","sensitive","False",0,None,None,None,None,None,None,None,None),
(4,42,"AdwPreferencesRow","title","Time Until Reset",0,None,None,None,None,None,None,None,None),
(4,42,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(4,42,"GtkWidget","sensitive","False",0,None,None,None,None,None,None,None,None),
(5,1,"GtkOrientable","orientation","vertical",0,None,None,None,None,None,None,None,None),
(5,2,"GtkWidget","margin-end","10",0,None,None,None,None,None,None,None,None),
(5,2,"GtkWidget","margin-start","10",0,None,None,None,None,None,None,None,None),
(5,3,"AdwPreferencesRow","title","Hardware Status",0,None,None,None,None,None,None,None,None),
(5,3,"GtkListBoxRow","selectable","False",0,None,None,None,None,None,None,None,None),
(5,4,"AdwPreferencesRow","title","Chassis Open Count",0,None,None,None,None,None,None,None,None),
(5,6,"AdwActionRow","subtitle","Use Privacy Switch",None,None,None,None,None,None,None,None,None),
(5,6,"AdwPreferencesRow","title","Camera Enabled",0,None,None,None,None,None,None,None,None),
(5,9,"AdwActionRow","subtitle","Use Privacy Switch",None,None,None,None,None,None,None,None,None),
(5,9,"AdwPreferencesRow","title","Microphone Enabled",0,None,None,None,None,None,None,None,None),
(5,11,"GtkSwitch","active","True",0,None,None,None,None,None,None,None,None),
(5,11,"GtkWidget","sensitive","False",0,None,None,None,None,None,None,None,None),
(5,13,"GtkBox","homogeneous","True",None,None,None,None,None,None,None,None,None),
(5,13,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None),
(5,13,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(5,14,"GtkBox","homogeneous","True",0,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","halign","end",0,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","valign","center",0,None,None,None,None,None,None,None,None),
(5,15,"GtkSwitch","active","True",0,None,None,None,None,None,None,None,None),
(5,15,"GtkWidget","sensitive","False",0,None,None,None,None,None,None,None,None),
(5,16,"AdwPreferencesRow","title","Fingerprint",0,None,None,None,None,None,None,None,None),
(5,16,"GtkWidget","visible","False",None,None,None,None,None,None,None,None,None),
(5,17,"GtkBox","homogeneous","True",0,None,None,None,None,None,None,None,None),
(5,17,"GtkBox","spacing","5",None,None,None,None,None,None,None,None,None),
(5,17,"GtkWidget","halign","end",0,None,None,None,None,None,None,None,None),
(5,17,"GtkWidget","valign","center",0,None,None,None,None,None,None,None,None),
(5,18,"GtkButton","label","Enable",None,None,None,None,None,None,None,None,None),
(5,19,"GtkButton","label","Disable",None,None,None,None,None,None,None,None,None)
</object_property>
<object_data>
(1,17,"GtkWidget",1,1,None,None,None,None,None,None),
(1,17,"GtkWidget",2,2,None,1,None,None,None,None),
(2,8,"GtkWidget",1,1,None,None,None,None,None,None),
(2,8,"GtkWidget",2,2,None,1,None,None,None,None),
(2,10,"GtkStringList",1,1,None,None,None,None,None,None),
(2,10,"GtkStringList",2,2,"Auto",1,None,0,None,None),
(2,10,"GtkStringList",2,3,"Percent",1,None,0,None,None),
(2,10,"GtkStringList",2,4,"RPM",1,None,0,None,None),
(2,6,"GtkWidget",1,1,None,None,None,None,None,None),
(2,6,"GtkWidget",2,2,None,1,None,None,None,None),
(3,5,"GtkWidget",1,1,None,None,None,None,None,None),
(3,5,"GtkWidget",2,2,None,1,None,None,None,None),
(3,24,"GtkStringList",1,1,None,None,None,None,None,None),
(3,24,"GtkStringList",2,2,"Auto",1,None,0,None,None),
(3,24,"GtkStringList",2,3,"Off",1,None,0,None,None),
(3,26,"GtkStringList",1,1,None,None,None,None,None,None),
(3,26,"GtkStringList",2,2,"Auto",1,None,0,None,None),
(3,26,"GtkStringList",2,3,"Off",1,None,0,None,None),
(3,18,"GtkWidget",2,2,None,1,None,None,None,None),
(4,2,"GtkWidget",1,1,None,None,None,None,None,None),
(4,2,"GtkWidget",2,2,None,1,None,None,None,None),
(4,31,"GtkWidget",1,1,None,None,None,None,None,None),
(4,31,"GtkWidget",2,2,None,1,None,None,None,None),
(5,2,"GtkWidget",1,1,None,None,None,None,None,None),
(5,2,"GtkWidget",2,2,None,1,None,None,None,None),
(4,41,"GtkWidget",1,1,None,None,None,None,None,None),
(4,41,"GtkWidget",2,2,None,1,None,None,None,None),
(4,42,"GtkWidget",1,1,None,None,None,None,None,None),
(4,42,"GtkWidget",2,2,None,1,None,None,None,None)
</object_data>
<object_data_arg>
(1,17,"GtkWidget",2,2,"name","navigation-sidebar"),
(2,8,"GtkWidget",2,2,"name","property"),
(2,6,"GtkWidget",2,2,"name","boxed-list"),
(3,5,"GtkWidget",2,2,"name","boxed-list"),
(3,18,"GtkWidget",2,2,"name","destructive-action"),
(4,2,"GtkWidget",2,2,"name","boxed-list"),
(4,31,"GtkWidget",2,2,"name","property"),
(5,2,"GtkWidget",2,2,"name","boxed-list"),
(4,41,"GtkWidget",2,2,"name","property"),
(4,42,"GtkWidget",2,2,"name","property")
</object_data_arg>
</cambalache-project> </cambalache-project>

View File

@@ -1,11 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.94.1 --> <!-- Created with Cambalache 0.96.1 -->
<interface> <interface>
<!-- interface-name yafi.ui --> <!-- interface-name yafi.ui -->
<!-- interface-description YAFI is another GUI for the Framework Laptop Embedded Controller. --> <!-- interface-description YAFI is another GUI for the Framework Laptop Embedded Controller. -->
<!-- interface-authors Steve-Tech --> <!-- interface-authors Steve-Tech -->
<requires lib="gtk" version="4.12"/> <requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.4"/> <requires lib="libadwaita" version="1.0"/>
<template class="YafiWindow" parent="AdwApplicationWindow"> <template class="YafiWindow" parent="AdwApplicationWindow">
<property name="default-height">500</property> <property name="default-height">500</property>
<property name="default-width">800</property> <property name="default-width">800</property>

View File

@@ -18,8 +18,7 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw from gi.repository import Gtk, Adw
from gi.repository import Gtk
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/yafi.ui') @Gtk.Template(resource_path='/au/stevetech/yafi/ui/yafi.ui')
class YafiWindow(Adw.ApplicationWindow): class YafiWindow(Adw.ApplicationWindow):

Binary file not shown.

View File

@@ -5,6 +5,11 @@
<file preprocess="xml-stripblanks">ui/thermals.ui</file> <file preprocess="xml-stripblanks">ui/thermals.ui</file>
<file preprocess="xml-stripblanks">ui/leds.ui</file> <file preprocess="xml-stripblanks">ui/leds.ui</file>
<file preprocess="xml-stripblanks">ui/battery.ui</file> <file preprocess="xml-stripblanks">ui/battery.ui</file>
<file preprocess="xml-stripblanks">ui/battery-limiter.ui</file>
<file preprocess="xml-stripblanks">ui/hardware.ui</file> <file preprocess="xml-stripblanks">ui/hardware.ui</file>
</gresource> </gresource>
<gresource prefix="/au/stevetech/yafi/icons/scalable/actions">
<file alias="au.stevetech.yafi.svg" preprocess="xml-stripblanks">../data/icons/hicolor/scalable/apps/au.stevetech.yafi.svg</file>
<file alias="au.stevetech.yafi-symbolic.svg" preprocess="xml-stripblanks">../data/icons/hicolor/symbolic/apps/au.stevetech.yafi-symbolic.svg</file>
</gresource>
</gresources> </gresources>