7 Commits
0.1 ... 0.3

Author SHA1 Message Date
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
15 changed files with 127 additions and 37 deletions

5
.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,6 @@ cython_debug/
# Flatpak builder # Flatpak builder
.flatpak-builder/ .flatpak-builder/
# Binary files
*.bin

View File

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

View File

@@ -1,7 +1,7 @@
# 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.
@@ -36,6 +36,11 @@ 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. You will also need to copy `WinRing0x64.dll` and `WinRing0x64.sys` to either the same
directory as `python.exe`, or to `C:\Windows\System32`.
## Screenshots ## Screenshots
### Fan Control and Temperature Monitoring ### Fan Control and Temperature Monitoring

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.0.4\""
] ]
} }
] ]

5
pyinstaller/winyafi.py Normal file
View File

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

44
pyinstaller/winyafi.spec Executable file
View File

@@ -0,0 +1,44 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
datas = [('LpcCrOSEC.bin', '.')]
datas += collect_data_files('yafi')
a = Analysis(
['winyafi.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='winyafi',
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'],
uac_admin=True,
)

BIN
pyinstaller/yafi.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "yafi" name = "yafi"
version = "0.1" version = "0.3"
authors = [ authors = [
{ name="Steve-Tech" } { name="Steve-Tech" }
] ]
@@ -8,7 +8,7 @@ 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.0.4",
"PyGObject" "PyGObject"
] ]
classifiers = [ classifiers = [

View File

@@ -31,8 +31,10 @@ class BatteryPage(Gtk.Box):
chg_limit_enable = Gtk.Template.Child() chg_limit_enable = Gtk.Template.Child()
chg_limit = Gtk.Template.Child() chg_limit = Gtk.Template.Child()
chg_limit_label = Gtk.Template.Child()
chg_limit_scale = Gtk.Template.Child() chg_limit_scale = Gtk.Template.Child()
bat_limit = Gtk.Template.Child() bat_limit = Gtk.Template.Child()
bat_limit_label = Gtk.Template.Child()
bat_limit_scale = Gtk.Template.Child() bat_limit_scale = Gtk.Template.Child()
chg_limit_override = Gtk.Template.Child() chg_limit_override = Gtk.Template.Child()
chg_limit_override_btn = Gtk.Template.Child() chg_limit_override_btn = Gtk.Template.Child()
@@ -56,7 +58,9 @@ class BatteryPage(Gtk.Box):
self.chg_limit_enable.set_active(ec_limit_enabled) self.chg_limit_enable.set_active(ec_limit_enabled)
if ec_limit_enabled: if ec_limit_enabled:
self.chg_limit_scale.set_value(ec_limit[0]) 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_scale.set_value(ec_limit[1])
self.bat_limit_label.set_label(f"{ec_limit[1]}%")
self.chg_limit.set_sensitive(True) self.chg_limit.set_sensitive(True)
self.bat_limit.set_sensitive(True) self.bat_limit.set_sensitive(True)
self.chg_limit_override.set_sensitive(True) self.chg_limit_override.set_sensitive(True)
@@ -103,7 +107,7 @@ class BatteryPage(Gtk.Box):
) )
except ec_exceptions.ECError as e: except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND: if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT) app.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL)
self.chg_limit_enable.set_sensitive(False) self.chg_limit_enable.set_sensitive(False)
else: else:
raise e raise e
@@ -182,31 +186,47 @@ class BatteryPage(Gtk.Box):
) )
def _update_battery(self, app): def _update_battery(self, app):
if ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in app.no_support: success = False
return False
try: # Charge Limiter
ec_extender = ec_commands.framework_laptop.get_battery_extender( if not ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT_CONTROL in app.no_support:
app.cros_ec 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]}%")
self.bat_ext_stage.set_subtitle(str(ec_extender["current_stage"])) success = True
self.bat_ext_trigger_time.set_subtitle( except ec_exceptions.ECError as e:
format_timedelta(ec_extender["trigger_timedelta"]) 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.bat_ext_reset_time.set_subtitle( else:
format_timedelta(ec_extender["reset_timedelta"]) raise e
)
except ec_exceptions.ECError as e: # Battery Extender
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND: if not ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in app.no_support:
app.no_support.append( try:
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER ec_extender = ec_commands.framework_laptop.get_battery_extender(
app.cros_ec
) )
return False
else:
raise e
return app.current_page == 2 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): def format_timedelta(timedelta):
days = f"{timedelta.days} days, " if timedelta.days else "" days = f"{timedelta.days} days, " if timedelta.days else ""

View File

@@ -95,12 +95,14 @@ 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( add_colours(
led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED

View File

@@ -121,7 +121,7 @@ class YafiApplication(Adw.Application):
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.3",
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/"])

View File

@@ -29,6 +29,9 @@
<property name="title">Charge Limit</property> <property name="title">Charge Limit</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<child>
<object class="GtkLabel" id="chg_limit_label"/>
</child>
<child> <child>
<object class="GtkScale" id="chg_limit_scale"> <object class="GtkScale" id="chg_limit_scale">
<property name="adjustment"> <property name="adjustment">
@@ -54,6 +57,9 @@
<property name="title">Discharge Limit</property> <property name="title">Discharge Limit</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<child>
<object class="GtkLabel" id="bat_limit_label"/>
</child>
<child> <child>
<object class="GtkScale" id="bat_limit_scale"> <object class="GtkScale" id="bat_limit_scale">
<property name="adjustment"> <property name="adjustment">

View File

@@ -60,7 +60,7 @@
(4,3,"AdwActionRow",None,2,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,4,"AdwActionRow","chg_limit",2,None,None,None,2,None,None),
(4,5,"GtkBox",None,4,None,None,None,0,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,6,"GtkScale","chg_limit_scale",5,None,None,None,1,None,None),
(4,7,"GtkAdjustment",None,6,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,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,27,"AdwSpinRow","bat_ext_trigger",13,None,None,None,5,None,None),
@@ -71,7 +71,7 @@
(4,32,"AdwSwitchRow","bat_ext_enable",13,None,None,None,1,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,33,"AdwActionRow","bat_limit",2,None,None,None,3,None,None),
(4,34,"GtkBox",None,33,None,None,None,0,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,35,"GtkScale","bat_limit_scale",34,None,None,None,1,None,None),
(4,36,"GtkAdjustment",None,35,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,37,"AdwActionRow","chg_limit_override",2,None,None,None,4,None,None),
(4,38,"GtkBox",None,37,None,None,None,0,None,None), (4,38,"GtkBox",None,37,None,None,None,0,None,None),
@@ -79,6 +79,8 @@
(4,40,"AdwSwitchRow","chg_limit_enable",2,None,None,None,1,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,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,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,1,"GtkBox","HardwarePage",None,None,None,None,0,None,None),
(5,2,"GtkListBox",None,1,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,3,"AdwActionRow",None,2,None,None,None,0,None,None),

Binary file not shown.

View File

@@ -7,4 +7,8 @@
<file preprocess="xml-stripblanks">ui/battery.ui</file> <file preprocess="xml-stripblanks">ui/battery.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>