10 Commits
0.3 ... 0.4

Author SHA1 Message Date
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
34 changed files with 904 additions and 750 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 }}

1
.gitignore vendored
View File

@@ -178,3 +178,4 @@ cython_debug/
# Binary files # Binary files
*.bin *.bin
!pyinstaller/LpcCrOSEC.bin

View File

@@ -7,9 +7,21 @@ It has support for fan control, temperature monitoring, LED control, and battery
## 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
@@ -38,8 +48,7 @@ Pipx is also supported.
### Windows ### Windows
It is possible to run YAFI on Windows using [gvsbuild](https://github.com/wingtk/gvsbuild/) and installing YAFI via pip. You will also need to copy `WinRing0x64.dll` and `WinRing0x64.sys` to either the same 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.
directory as `python.exe`, or to `C:\Windows\System32`.
## Screenshots ## Screenshots
@@ -51,17 +60,21 @@ directory as `python.exe`, or to `C:\Windows\System32`.
![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.4\"" "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"{scale_factor//100}"
from yafi import main
main()

View File

@@ -1,5 +0,0 @@
import os
os.environ["GDK_SCALE"] = "2"
from yafi import main
main()

View File

@@ -1,44 +1,44 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files from PyInstaller.utils.hooks import collect_data_files
import os
datas = [('LpcCrOSEC.bin', '.')]
datas += collect_data_files('yafi') datas = [('LpcCrOSEC.bin', '.')] if os.name == 'nt' and os.path.exists('LpcCrOSEC.bin') else []
datas += collect_data_files('yafi')
a = Analysis(
['winyafi.py'], a = Analysis(
pathex=[], ['entrypoint.py'],
binaries=[], pathex=[],
datas=datas, binaries=[],
hiddenimports=['cros_ec_python'], datas=datas,
hookspath=[], hiddenimports=['cros_ec_python'],
hooksconfig={"gi":{"module-versions": {"Gtk": "4.0"}}}, hookspath=[],
runtime_hooks=[], hooksconfig={"gi":{"module-versions": {"Gtk": "4.0"}}},
excludes=[], runtime_hooks=[],
noarchive=False, excludes=[],
optimize=2, noarchive=False,
) optimize=2,
pyz = PYZ(a.pure) )
pyz = PYZ(a.pure)
exe = EXE(
pyz, exe = EXE(
a.scripts, pyz,
a.binaries, a.scripts,
a.datas, a.binaries,
[], a.datas,
name='winyafi', [],
debug=False, name='YAFI',
bootloader_ignore_signals=False, debug=False,
strip=False, bootloader_ignore_signals=False,
upx=True, strip=False,
upx_exclude=[], upx=True,
runtime_tmpdir=None, upx_exclude=[],
console=False, runtime_tmpdir=None,
disable_windowed_traceback=False, console=False,
argv_emulation=False, disable_windowed_traceback=False,
target_arch=None, argv_emulation=False,
codesign_identity=None, target_arch=None,
entitlements_file=None, codesign_identity=None,
icon=['yafi.ico'], entitlements_file=None,
uac_admin=True, icon=['yafi.ico'],
) )

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "yafi" name = "yafi"
version = "0.3" version = "0.4"
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.4", "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,211 +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_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() 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.chg_limit_label.set_label(f"{ec_limit[0]}%") self._update_battery(app, battery)
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 # 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): return app.current_page == 2
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 == 2 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}"

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

@@ -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.3", version="0.4",
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,93 @@ 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"
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',
] ]

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

@@ -0,0 +1,183 @@
<?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>
<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,181 +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="GtkLabel" id="chg_limit_label"/> <object class="AdwActionRow" id="batt_status">
<property name="selectable">False</property>
<property name="title">Battery Statistics</property>
</object>
</child> </child>
<child> <child>
<object class="GtkScale" id="chg_limit_scale"> <object class="AdwActionRow">
<property name="adjustment"> <property name="title">Charge</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="value">100.0</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="GtkLabel" id="bat_limit_label"/> <object class="AdwActionRow">
</child> <property name="title">Health</property>
<child> <child>
<object class="GtkScale" id="bat_limit_scale"> <object class="GtkBox">
<property name="adjustment"> <property name="halign">end</property>
<object class="GtkAdjustment"> <property name="homogeneous">True</property>
<property name="page-increment">10.0</property> <property name="valign">center</property>
<property name="step-increment">1.0</property> <child>
<property name="upper">100.0</property> <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,315 +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="656384cbc7dbfc0f2fd85b3f8c2d5e35066ec3368c5f464c31c8b82c5da334de"/>
(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,1,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,1,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),
(4,43,"GtkLabel","chg_limit_label",5,None,None,None,0,None,None),
(4,44,"GtkLabel","bat_limit_label",34,None,None,None,0,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,7 @@
<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"> <gresource prefix="/au/stevetech/yafi/icons/scalable/actions">