23 Commits
0.1 ... 0.5

Author SHA1 Message Date
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
36 changed files with 941 additions and 688 deletions

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-48
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
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
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,52 @@
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.8.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.8.0/GTK4_Gvsbuild_2025.8.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
run: python -m PyInstaller yafi.spec
working-directory: pyinstaller
- name: Upload PyInstaller Artifact
uses: actions/upload-artifact@v4
with:
path: pyinstaller/dist/YAFI.exe
name: yafi-windows-${{ 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,27 @@
# 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.
### 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.
## Building
### Flatpak ### Flatpak
@@ -21,14 +33,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 +46,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 +60,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

View File

@@ -41,7 +41,7 @@
] ]
}, },
"build-commands": [ "build-commands": [
"pip3 install --prefix=${FLATPAK_DEST} --no-cache-dir \"cros_ec_python>=0.0.2\"" "pip3 install --prefix=${FLATPAK_DEST} --no-cache-dir \"cros_ec_python>=0.2.0\""
] ]
} }
] ]

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 94 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 92 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 140 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: 150 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: 157 KiB

BIN
docs/5-hardware.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

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/yafi.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

44
pyinstaller/yafi.spec Executable file
View File

@@ -0,0 +1,44 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
import os
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)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
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'],
)

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "yafi" name = "yafi"
version = "0.1" version = "0.5"
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

@@ -25,191 +25,81 @@ 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, def _get_watts(self, battery, key, volt_key="design_voltage"):
app return (battery[key] * battery[volt_key]) / 1000_000
def _update_battery(self, app, battery=None):
if battery is None:
battery = ec_commands.memmap.get_battery_values(app.cros_ec)
status_messages = []
if battery["invalid_data"]:
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.batt_health.set_fraction(
battery["last_full_charge_capacity"] / battery["design_capacity"]
)
self.batt_cycles_label.set_label(str(battery["cycle_count"]))
self.batt_volts_label.set_label(f"{battery['volt']/1000:.2f}V")
self.batt_watts_label.set_label(
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"
) )
def _update_battery(self, app):
if ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in app.no_support:
return False
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"])
)
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
)
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}"

235
yafi/battery_limiter.py Normal file
View File

@@ -0,0 +1,235 @@
# 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 Adw
from gi.repository import Gtk
from gi.repository import 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

@@ -118,4 +118,4 @@ class HardwarePage(Gtk.Box):
else: else:
raise e raise e
return app.current_page == 3 and success return app.current_page == 4 and success

View File

@@ -95,16 +95,21 @@ class LedsPage(Gtk.Box):
all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"] all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"]
def add_colours(strings, led_id): def add_colours(strings, led_id):
supported_colours = ec_commands.leds.led_control_get_max_values( # Auto and Off should already be present
app.cros_ec, led_id if strings.get_n_items() <= 2:
) supported_colours = ec_commands.leds.led_control_get_max_values(
for i, colour in enumerate(all_colours): app.cros_ec, led_id
if supported_colours[i]: )
strings.append(colour) for i, colour in enumerate(all_colours):
if supported_colours[i]:
strings.append(colour)
add_colours( try:
led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED add_colours(
) led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
)
except ec_exceptions.ECError as e:
self.led_pwr_colour.set_sensitive(False)
def handle_led_colour(combobox, led_id): def handle_led_colour(combobox, led_id):
colour = combobox.get_selected() - 2 colour = combobox.get_selected() - 2
@@ -139,10 +144,13 @@ class LedsPage(Gtk.Box):
# Advanced: Charging LED # Advanced: Charging LED
led_chg_colour_strings = self.led_chg_colour.get_model() led_chg_colour_strings = self.led_chg_colour.get_model()
add_colours( try:
led_chg_colour_strings, add_colours(
ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED, led_chg_colour_strings,
) ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED,
)
except ec_exceptions.ECError as e:
self.led_chg_colour.set_sensitive(False)
self.led_chg_colour.connect( self.led_chg_colour.connect(
"notify::selected", "notify::selected",

View File

@@ -31,6 +31,7 @@ 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
@@ -71,6 +72,8 @@ class YafiApplication(Adw.Application):
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"
@@ -87,6 +90,7 @@ class YafiApplication(Adw.Application):
("Thermals", ThermalsPage()), ("Thermals", ThermalsPage()),
("LEDs", LedsPage()), ("LEDs", LedsPage()),
("Battery", BatteryPage()), ("Battery", BatteryPage()),
("Battery Limiter", BatteryLimiterPage()),
("Hardware", HardwarePage()), ("Hardware", HardwarePage()),
("About", None), ("About", None),
) )
@@ -117,11 +121,12 @@ class YafiApplication(Adw.Application):
comments="YAFI is another GUI for the Framework Laptop Embedded Controller.\n" comments="YAFI is another GUI for the Framework Laptop Embedded Controller.\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 GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.",
copyright="© 2025 Stephen Horvath", copyright="© 2025 Stephen Horvath",
debug_info=self.generate_debug_info(),
developer_name="Stephen Horvath", developer_name="Stephen Horvath",
developers=["Stephen Horvath"], developers=["Stephen Horvath"],
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.5",
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/"])
@@ -130,9 +135,95 @@ class YafiApplication(Adw.Application):
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/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; "
"$bios = Get-CimInstance -ClassName Win32_BIOS; "
"Write-Output $cs.Manufacturer; "
"Write-Output $cs.Model; "
"Write-Output $cs.SystemSKUNumber; "
"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"BIOS Vendor: {output[3]}\n"
info += f"BIOS Version: {output[4]}\n"
# Blank line in the output for some reason
info += f"BIOS Date: {output[6]}\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',
] ]

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,175 +1,156 @@
<?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="orientation">vertical</property> <property name="homogeneous">True</property>
<child> <child>
<object class="GtkListBox"> <object class="GtkPaned">
<property name="margin-end">10</property> <property name="end-child">
<property name="margin-start">10</property> <object class="GtkListBox">
<child> <property name="width-request">100</property>
<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> <child>
<object class="GtkBox"> <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>
<child>
<object class="GtkListBox">
<property name="margin-end">10</property>
<property name="margin-start">10</property>
<child> <child>
<object class="GtkScale" id="chg_limit_scale"> <object class="AdwActionRow" id="batt_status">
<property name="adjustment"> <property name="selectable">False</property>
<object class="GtkAdjustment"> <property name="title">Battery Statistics</property>
<property name="page-increment">10.0</property> </object>
<property name="step-increment">1.0</property> </child>
<property name="upper">100.0</property> <child>
<property name="value">100.0</property> <object class="AdwActionRow">
<property name="title">Charge</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="valign">center</property>
<child>
<object class="GtkProgressBar" id="batt_charge">
<property name="show-text">True</property>
<property name="width-request">300</property>
</object>
</child>
</object> </object>
</property> </child>
<property name="hexpand">True</property>
<property name="round-digits">0</property>
</object> </object>
</child> </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> <child>
<object class="GtkScale" id="bat_limit_scale"> <object class="AdwActionRow">
<property name="adjustment"> <property name="title">Health</property>
<object class="GtkAdjustment"> <child>
<property name="page-increment">10.0</property> <object class="GtkBox">
<property name="step-increment">1.0</property> <property name="halign">end</property>
<property name="upper">100.0</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> </object>
</property> </child>
<property name="hexpand">True</property>
<property name="round-digits">0</property>
</object> </object>
</child> </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>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="valign">center</property>
<child> <child>
<object class="GtkButton" id="chg_limit_override_btn"> <object class="AdwActionRow" id="batt_cycles">
<property name="label">Override</property> <property name="title">Charge Cycles</property>
<child>
<object class="GtkLabel" id="batt_cycles_label"/>
</child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow" id="batt_volts">
<property name="title">Current Voltage</property>
<child>
<object class="GtkLabel" id="batt_volts_label"/>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_watts">
<property name="title">Current Wattage</property>
<child>
<object class="GtkLabel" id="batt_watts_label"/>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_cap_rem">
<property name="title">Remaining Charge</property>
<child>
<object class="GtkLabel" id="batt_cap_rem_label"/>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="batt_cap_full">
<property name="title">Last Full Charge</property>
<child>
<object class="GtkLabel" id="batt_cap_full_label"/>
</child>
</object>
</child>
<style>
<class name="boxed-list"/>
</style>
</object> </object>
</child> </child>
</object> </object>
</child> </property>
<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> </object>
</child> </child>
</template> </template>

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 hardware.ui -->
<!-- interface-description The Hardware page for YAFI --> <!-- interface-description The Hardware 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>

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>

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="0350d22d570de039d63602ba40925f17fff6680fd5909d2ebf19600f351eb0f2"/>
(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="f4deec4e38e683fde97656802dbfb2a638ab46a0e36af5c9a37f277f49e2aabb"/>
</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>

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>