Major refactor with GNOME Builder & Flatpak support
7
60-cros_ec_python.rules
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# CrOS_EC_Python udev rules
|
||||||
|
|
||||||
|
# LPC Access
|
||||||
|
KERNEL=="port", TAG+="uaccess"
|
||||||
|
|
||||||
|
# /dev/cros_ec Access
|
||||||
|
KERNEL=="cros_ec", TAG+="uaccess"
|
||||||
@@ -337,3 +337,4 @@ proprietary programs. If your program is a subroutine library, you may
|
|||||||
consider it more useful to permit linking proprietary applications with the
|
consider it more useful to permit linking proprietary applications with the
|
||||||
library. If this is what you want to do, use the GNU Lesser General
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License.
|
Public License instead of this License.
|
||||||
|
|
||||||
36
README.md
@@ -1,4 +1,38 @@
|
|||||||
# 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 GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.
|
It is written in Python with a GTK4 Adwaita theme, and uses the `CrOS_EC_Python` library to communicate with the EC.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Fan Control and Temperature Monitoring
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### LED Control
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Battery Limiting
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Battery Extender
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Hardware Info
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### udev Rules (MUST READ)
|
||||||
|
|
||||||
|
To allow YAFI to communicate with the EC, you need to copy the `60-cros_ec_python.rules` file to `/etc/udev/rules.d/` and reload the rules with `sudo udevadm control --reload-rules && sudo udevadm trigger`.
|
||||||
|
|
||||||
|
### Flatpak
|
||||||
|
|
||||||
|
Build and install the Flatpak package with `flatpak-builder --install --user build au.stevetech.yafi.json`.
|
||||||
|
|
||||||
|
You can also create a flatpak bundle with `flatpak-builder --repo=repo build au.stevetech.yafi.json` and install it with `flatpak install --user repo au.stevetech.yafi.flatpak`.
|
||||||
|
|||||||
48
au.stevetech.yafi.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"id" : "au.stevetech.yafi",
|
||||||
|
"runtime" : "org.gnome.Platform",
|
||||||
|
"runtime-version" : "48",
|
||||||
|
"sdk" : "org.gnome.Sdk",
|
||||||
|
"command" : "yafi",
|
||||||
|
"finish-args" : [
|
||||||
|
"--device=all",
|
||||||
|
"--socket=fallback-x11",
|
||||||
|
"--socket=wayland"
|
||||||
|
],
|
||||||
|
"cleanup" : [
|
||||||
|
"/include",
|
||||||
|
"/lib/pkgconfig",
|
||||||
|
"/man",
|
||||||
|
"/share/doc",
|
||||||
|
"/share/gtk-doc",
|
||||||
|
"/share/man",
|
||||||
|
"/share/pkgconfig",
|
||||||
|
"*.la",
|
||||||
|
"*.a"
|
||||||
|
],
|
||||||
|
"modules" : [
|
||||||
|
{
|
||||||
|
"name" : "yafi",
|
||||||
|
"builddir" : true,
|
||||||
|
"buildsystem" : "meson",
|
||||||
|
"sources" : [
|
||||||
|
{
|
||||||
|
"type" : "dir",
|
||||||
|
"path" : "."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cros_ec_python",
|
||||||
|
"buildsystem": "simple",
|
||||||
|
"build-options": {
|
||||||
|
"build-args": [
|
||||||
|
"--share=network"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"build-commands": [
|
||||||
|
"pip3 install --prefix=${FLATPAK_DEST} --no-cache-dir \"cros_ec_python>=0.0.2\""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
data/au.stevetech.yafi.desktop.in
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=YAFI
|
||||||
|
Exec=yafi
|
||||||
|
Icon=au.stevetech.yafi
|
||||||
|
Comment=Yet Another Framework Interface
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Utility;
|
||||||
|
Keywords=GTK;
|
||||||
|
StartupNotify=true
|
||||||
|
DBusActivatable=true
|
||||||
5
data/au.stevetech.yafi.gschema.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<schemalist gettext-domain="yafi">
|
||||||
|
<schema id="au.stevetech.yafi" path="/au/stevetech/yafi/">
|
||||||
|
</schema>
|
||||||
|
</schemalist>
|
||||||
83
data/au.stevetech.yafi.metainfo.xml.in
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>au.stevetech.yafi</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-2.0-or-later</project_license>
|
||||||
|
|
||||||
|
<name>Yet Another Framework Interface</name>
|
||||||
|
<summary>YAFI is another GUI for the Framework Laptop Embedded Controller</summary>
|
||||||
|
<description>
|
||||||
|
<p>It is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.</p>
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<developer id="au.stevetech">
|
||||||
|
<name>Stephen Horvath</name>
|
||||||
|
</developer>
|
||||||
|
|
||||||
|
<!-- Required: Should be a link to the upstream homepage for the component -->
|
||||||
|
<url type="homepage">https://github.com/Steve-Tech/YAFI</url>
|
||||||
|
<!-- Recommended: It is highly recommended for open-source projects to display the source code repository -->
|
||||||
|
<url type="vcs-browser">https://github.com/Steve-Tech/YAFI</url>
|
||||||
|
<!-- Should point to the software's bug tracking system, for users to report new bugs -->
|
||||||
|
<url type="bugtracker">https://github.com/Steve-Tech/YAFI/issues</url>
|
||||||
|
<!-- Should link a FAQ page for this software, to answer some of the most-asked questions in detail -->
|
||||||
|
<!-- URLs of this type should point to a webpage where users can submit or modify translations of the upstream project -->
|
||||||
|
<!-- <url type="translate">https://example.org/translate</url> -->
|
||||||
|
<!-- <url type="faq">https://example.org/faq</url> -->
|
||||||
|
<!-- Should provide a web link to an online user's reference, a software manual or help page -->
|
||||||
|
<!-- <url type="help">https://example.org/help</url> -->
|
||||||
|
<!-- URLs of this type should point to a webpage showing information on how to donate to the described software project -->
|
||||||
|
<!-- <url type="donation">https://example.org/donate</url> -->
|
||||||
|
<!-- This could for example be an HTTPS URL to an online form or a page describing how to contact the developer -->
|
||||||
|
<!-- <url type="contact">https://example.org/contact</url> -->
|
||||||
|
<!-- URLs of this type should point to a webpage showing information on how to contribute to the described software project -->
|
||||||
|
<!-- <url type="contribute">https://example.org/contribute</url> -->
|
||||||
|
|
||||||
|
<translation type="gettext">yafi</translation>
|
||||||
|
<!-- All graphical applications having a desktop file must have this tag in the MetaInfo.
|
||||||
|
If this is present, appstreamcli compose will pull icons, keywords and categories from the desktop file. -->
|
||||||
|
<launchable type="desktop-id">au.stevetech.yafi.desktop</launchable>
|
||||||
|
<!-- Use the OARS website (https://hughsie.github.io/oars/generate.html) to generate these and make sure to use oars-1.1 -->
|
||||||
|
<content_rating type="oars-1.1" />
|
||||||
|
|
||||||
|
<!-- Applications should set a brand color in both light and dark variants like so -->
|
||||||
|
<branding>
|
||||||
|
<color type="primary" scheme_preference="light">#ff00ff</color>
|
||||||
|
<color type="primary" scheme_preference="dark">#993d3d</color>
|
||||||
|
</branding>
|
||||||
|
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/1-thermal.png</image>
|
||||||
|
<caption>The Thermal page</caption>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/2-leds.png</image>
|
||||||
|
<caption>The LED page</caption>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/3-battery.png</image>
|
||||||
|
<caption>The Battery page</caption>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/4-hardware.png</image>
|
||||||
|
<caption>The Hardware page</caption>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
|
||||||
|
<releases>
|
||||||
|
<release version="0.1.0" date="2025-03-20">
|
||||||
|
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.1.0</url>
|
||||||
|
<description translate="no">
|
||||||
|
<p>First release</p>
|
||||||
|
<ul>
|
||||||
|
<li>Added Thermal page</li>
|
||||||
|
<li>Added LED page</li>
|
||||||
|
<li>Added Battery page</li>
|
||||||
|
<li>Added Hardware page</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
</releases>
|
||||||
|
|
||||||
|
</component>
|
||||||
3
data/au.stevetech.yafi.service.in
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[D-BUS Service]
|
||||||
|
Name=au.stevetech.yafi
|
||||||
|
Exec=@bindir@/yafi --gapplication-service
|
||||||
36
data/icons/hicolor/scalable/apps/au.stevetech.yafi.svg
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M1755 4883 c-120 -22 -418 -180 -609 -322 -118 -89 -148 -118 -177
|
||||||
|
-177 -23 -48 -24 -57 -27 -314 -1 -146 -7 -294 -13 -330 -18 -110 -78 -222
|
||||||
|
-163 -303 -46 -43 -69 -58 -305 -197 -191 -112 -222 -139 -257 -214 -33 -73
|
||||||
|
-39 -145 -39 -461 0 -297 5 -360 33 -440 25 -73 77 -118 257 -224 88 -51 187
|
||||||
|
-111 220 -132 123 -79 192 -169 245 -319 8 -21 14 -110 16 -230 8 -403 9 -423
|
||||||
|
29 -460 32 -61 56 -88 134 -148 234 -181 550 -347 676 -356 75 -5 116 12 306
|
||||||
|
125 181 108 307 175 330 176 8 1 35 5 61 11 70 14 174 5 251 -23 38 -13 164
|
||||||
|
-81 281 -149 286 -168 312 -173 494 -90 41 19 75 34 77 34 4 0 201 110 243
|
||||||
|
135 45 27 147 100 213 153 51 40 83 75 104 113 l30 54 6 310 c5 280 8 315 27
|
||||||
|
365 58 156 137 245 295 336 246 140 346 205 376 243 35 44 60 106 67 166 2 22
|
||||||
|
8 74 13 115 9 78 7 442 -4 520 -12 97 -16 116 -27 150 -16 52 -61 113 -105
|
||||||
|
142 -21 14 -126 78 -233 141 -107 64 -209 128 -225 144 -51 48 -101 115 -128
|
||||||
|
173 -48 105 -56 163 -56 454 -1 262 -1 270 -25 321 -24 53 -65 100 -124 145
|
||||||
|
-19 14 -46 34 -60 46 -42 32 -190 128 -257 164 -250 138 -384 177 -476 140
|
||||||
|
-19 -8 -101 -54 -184 -104 -316 -188 -340 -198 -480 -199 -153 -1 -181 10
|
||||||
|
-501 201 -178 106 -237 128 -309 115z m984 -848 c149 -19 308 -68 441 -135 68
|
||||||
|
-34 181 -100 190 -110 3 -3 21 -17 40 -30 52 -37 75 -57 166 -148 150 -151
|
||||||
|
281 -364 345 -562 33 -102 32 -99 56 -210 18 -86 26 -378 13 -455 -5 -27 -12
|
||||||
|
-68 -15 -90 -16 -102 -64 -250 -117 -364 -211 -450 -634 -764 -1113 -828 -113
|
||||||
|
-15 -425 -7 -455 12 -4 2 -22 7 -41 10 -150 26 -404 140 -531 239 -14 12 -41
|
||||||
|
32 -59 46 -115 85 -291 299 -371 449 -78 148 -144 353 -165 516 -10 76 -10
|
||||||
|
311 0 390 15 117 58 274 106 390 116 278 347 543 608 696 158 92 386 169 549
|
||||||
|
184 38 4 70 8 72 9 5 6 229 -2 281 -9z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M1755 4883 c-120 -22 -418 -180 -609 -322 -118 -89 -148 -118 -177
|
||||||
|
-177 -23 -48 -24 -57 -27 -314 -1 -146 -7 -294 -13 -330 -18 -110 -78 -222
|
||||||
|
-163 -303 -46 -43 -69 -58 -305 -197 -191 -112 -222 -139 -257 -214 -33 -73
|
||||||
|
-39 -145 -39 -461 0 -297 5 -360 33 -440 25 -73 77 -118 257 -224 88 -51 187
|
||||||
|
-111 220 -132 123 -79 192 -169 245 -319 8 -21 14 -110 16 -230 8 -403 9 -423
|
||||||
|
29 -460 32 -61 56 -88 134 -148 234 -181 550 -347 676 -356 75 -5 116 12 306
|
||||||
|
125 181 108 307 175 330 176 8 1 35 5 61 11 70 14 174 5 251 -23 38 -13 164
|
||||||
|
-81 281 -149 286 -168 312 -173 494 -90 41 19 75 34 77 34 4 0 201 110 243
|
||||||
|
135 45 27 147 100 213 153 51 40 83 75 104 113 l30 54 6 310 c5 280 8 315 27
|
||||||
|
365 58 156 137 245 295 336 246 140 346 205 376 243 35 44 60 106 67 166 2 22
|
||||||
|
8 74 13 115 9 78 7 442 -4 520 -12 97 -16 116 -27 150 -16 52 -61 113 -105
|
||||||
|
142 -21 14 -126 78 -233 141 -107 64 -209 128 -225 144 -51 48 -101 115 -128
|
||||||
|
173 -48 105 -56 163 -56 454 -1 262 -1 270 -25 321 -24 53 -65 100 -124 145
|
||||||
|
-19 14 -46 34 -60 46 -42 32 -190 128 -257 164 -250 138 -384 177 -476 140
|
||||||
|
-19 -8 -101 -54 -184 -104 -316 -188 -340 -198 -480 -199 -153 -1 -181 10
|
||||||
|
-501 201 -178 106 -237 128 -309 115z m984 -848 c149 -19 308 -68 441 -135 68
|
||||||
|
-34 181 -100 190 -110 3 -3 21 -17 40 -30 52 -37 75 -57 166 -148 150 -151
|
||||||
|
281 -364 345 -562 33 -102 32 -99 56 -210 18 -86 26 -378 13 -455 -5 -27 -12
|
||||||
|
-68 -15 -90 -16 -102 -64 -250 -117 -364 -211 -450 -634 -764 -1113 -828 -113
|
||||||
|
-15 -425 -7 -455 12 -4 2 -22 7 -41 10 -150 26 -404 140 -531 239 -14 12 -41
|
||||||
|
32 -59 46 -115 85 -291 299 -371 449 -78 148 -144 353 -165 516 -10 76 -10
|
||||||
|
311 0 390 15 117 58 274 106 390 116 278 347 543 608 696 158 92 386 169 549
|
||||||
|
184 38 4 70 8 72 9 5 6 229 -2 281 -9z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
13
data/icons/meson.build
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
application_id = 'au.stevetech.yafi'
|
||||||
|
|
||||||
|
scalable_dir = 'hicolor' / 'scalable' / 'apps'
|
||||||
|
install_data(
|
||||||
|
scalable_dir / ('@0@.svg').format(application_id),
|
||||||
|
install_dir: get_option('datadir') / 'icons' / scalable_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
symbolic_dir = 'hicolor' / 'symbolic' / 'apps'
|
||||||
|
install_data(
|
||||||
|
symbolic_dir / ('@0@-symbolic.svg').format(application_id),
|
||||||
|
install_dir: get_option('datadir') / 'icons' / symbolic_dir
|
||||||
|
)
|
||||||
46
data/meson.build
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
desktop_file = i18n.merge_file(
|
||||||
|
input: 'au.stevetech.yafi.desktop.in',
|
||||||
|
output: 'au.stevetech.yafi.desktop',
|
||||||
|
type: 'desktop',
|
||||||
|
po_dir: '../po',
|
||||||
|
install: true,
|
||||||
|
install_dir: get_option('datadir') / 'applications'
|
||||||
|
)
|
||||||
|
|
||||||
|
desktop_utils = find_program('desktop-file-validate', required: false)
|
||||||
|
if desktop_utils.found()
|
||||||
|
test('Validate desktop file', desktop_utils, args: [desktop_file])
|
||||||
|
endif
|
||||||
|
|
||||||
|
appstream_file = i18n.merge_file(
|
||||||
|
input: 'au.stevetech.yafi.metainfo.xml.in',
|
||||||
|
output: 'au.stevetech.yafi.metainfo.xml',
|
||||||
|
po_dir: '../po',
|
||||||
|
install: true,
|
||||||
|
install_dir: get_option('datadir') / 'metainfo'
|
||||||
|
)
|
||||||
|
|
||||||
|
appstreamcli = find_program('appstreamcli', required: false, disabler: true)
|
||||||
|
test('Validate appstream file', appstreamcli,
|
||||||
|
args: ['validate', '--no-net', '--explain', appstream_file])
|
||||||
|
|
||||||
|
install_data('au.stevetech.yafi.gschema.xml',
|
||||||
|
install_dir: get_option('datadir') / 'glib-2.0' / 'schemas'
|
||||||
|
)
|
||||||
|
|
||||||
|
compile_schemas = find_program('glib-compile-schemas', required: false, disabler: true)
|
||||||
|
test('Validate schema file',
|
||||||
|
compile_schemas,
|
||||||
|
args: ['--strict', '--dry-run', meson.current_source_dir()])
|
||||||
|
|
||||||
|
|
||||||
|
service_conf = configuration_data()
|
||||||
|
service_conf.set('bindir', get_option('prefix') / get_option('bindir'))
|
||||||
|
configure_file(
|
||||||
|
input: 'au.stevetech.yafi.service.in',
|
||||||
|
output: 'au.stevetech.yafi.service',
|
||||||
|
configuration: service_conf,
|
||||||
|
install_dir: get_option('datadir') / 'dbus-1' / 'services'
|
||||||
|
)
|
||||||
|
|
||||||
|
subdir('icons')
|
||||||
BIN
docs/1-thermals.png
Executable file
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/2-leds.png
Executable file
|
After Width: | Height: | Size: 86 KiB |
BIN
docs/3-battery.png
Executable file
|
After Width: | Height: | Size: 144 KiB |
BIN
docs/3a-battery-ext.png
Executable file
|
After Width: | Height: | Size: 154 KiB |
BIN
docs/4-hardware.png
Executable file
|
After Width: | Height: | Size: 90 KiB |
20
meson.build
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
project('yafi',
|
||||||
|
version: '0.1.0',
|
||||||
|
meson_version: '>= 1.0.0',
|
||||||
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
|
)
|
||||||
|
|
||||||
|
i18n = import('i18n')
|
||||||
|
gnome = import('gnome')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
subdir('data')
|
||||||
|
subdir('yafi')
|
||||||
|
|
||||||
|
gnome.post_install(
|
||||||
|
glib_compile_schemas: true,
|
||||||
|
gtk_update_icon_cache: true,
|
||||||
|
update_desktop_database: true,
|
||||||
|
)
|
||||||
1
po/LINGUAS
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Please keep this file sorted alphabetically.
|
||||||
16
po/POTFILES.in
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# List of source files containing translatable strings.
|
||||||
|
# Please keep this file sorted alphabetically.
|
||||||
|
data/au.stevetech.test.desktop.in
|
||||||
|
data/au.stevetech.test.metainfo.xml.in
|
||||||
|
data/au.stevetech.test.gschema.xml
|
||||||
|
yafi/main.py
|
||||||
|
yafi/window.py
|
||||||
|
yafi/thermals.py
|
||||||
|
yafi/leds.py
|
||||||
|
yafi/battery.py
|
||||||
|
yafi/hardware.py
|
||||||
|
yafi/ui/window.ui
|
||||||
|
yafi/ui/thermals.ui
|
||||||
|
yafi/ui/leds.ui
|
||||||
|
yafi/ui/battery.ui
|
||||||
|
yafi/ui/hardware.ui
|
||||||
1
po/meson.build
Normal file
@@ -0,0 +1 @@
|
|||||||
|
i18n.gettext('test', preset: 'glib')
|
||||||
@@ -1 +1 @@
|
|||||||
from .yafi import main
|
from . import main
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from . import yafi
|
from . import main
|
||||||
yafi.main()
|
|
||||||
|
main.main()
|
||||||
215
yafi/battery.py
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# battery.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.ui')
|
||||||
|
class BatteryPage(Gtk.Box):
|
||||||
|
__gtype_name__ = 'BatteryPage'
|
||||||
|
|
||||||
|
chg_limit_enable = 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()
|
||||||
|
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.bat_limit_scale.set_value(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)
|
||||||
|
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):
|
||||||
|
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
|
||||||
|
|
||||||
|
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}"
|
||||||
119
yafi/hardware.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# hardware.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/hardware.ui')
|
||||||
|
class HardwarePage(Gtk.Box):
|
||||||
|
__gtype_name__ = 'HardwarePage'
|
||||||
|
|
||||||
|
hw_chassis = Gtk.Template.Child()
|
||||||
|
hw_chassis_label = Gtk.Template.Child()
|
||||||
|
|
||||||
|
hw_priv_cam = Gtk.Template.Child()
|
||||||
|
hw_priv_cam_sw = Gtk.Template.Child()
|
||||||
|
hw_priv_mic = Gtk.Template.Child()
|
||||||
|
hw_priv_mic_sw = Gtk.Template.Child()
|
||||||
|
|
||||||
|
hw_fp_pwr = Gtk.Template.Child()
|
||||||
|
hw_fp_pwr_en = Gtk.Template.Child()
|
||||||
|
hw_fp_pwr_dis = Gtk.Template.Child()
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def setup(self, app):
|
||||||
|
# Fingerprint Power (Untested)
|
||||||
|
if ec_commands.general.get_cmd_versions(
|
||||||
|
app.cros_ec, ec_commands.framework_laptop.EC_CMD_FP_CONTROL
|
||||||
|
):
|
||||||
|
self.hw_fp_pwr_en.connect(
|
||||||
|
"clicked",
|
||||||
|
lambda _: ec_commands.framework_laptop.fp_control(app.cros_ec, True),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.hw_fp_pwr_dis.connect(
|
||||||
|
"clicked",
|
||||||
|
lambda _: ec_commands.framework_laptop.fp_control(app.cros_ec, False),
|
||||||
|
)
|
||||||
|
self.hw_fp_pwr.set_visible(True)
|
||||||
|
else:
|
||||||
|
self.hw_fp_pwr.set_visible(False)
|
||||||
|
|
||||||
|
# Schedule _update_hardware to run every second
|
||||||
|
GLib.timeout_add_seconds(1, self._update_hardware, app)
|
||||||
|
|
||||||
|
def _update_hardware(self, app):
|
||||||
|
success = False
|
||||||
|
# Chassis
|
||||||
|
if not ec_commands.framework_laptop.EC_CMD_CHASSIS_INTRUSION in app.no_support:
|
||||||
|
try:
|
||||||
|
ec_chassis = ec_commands.framework_laptop.get_chassis_intrusion(
|
||||||
|
app.cros_ec
|
||||||
|
)
|
||||||
|
|
||||||
|
self.hw_chassis_label.set_label(str(ec_chassis["total_open_count"]))
|
||||||
|
|
||||||
|
ec_chassis_open = ec_commands.framework_laptop.get_chassis_open_check(
|
||||||
|
app.cros_ec
|
||||||
|
)
|
||||||
|
self.hw_chassis.set_subtitle(
|
||||||
|
"Currently " + ("Open" if ec_chassis_open else "Closed")
|
||||||
|
)
|
||||||
|
|
||||||
|
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_CHASSIS_INTRUSION
|
||||||
|
)
|
||||||
|
self.hw_chassis.set_visible(False)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# Privacy Switches
|
||||||
|
if (
|
||||||
|
not ec_commands.framework_laptop.EC_CMD_PRIVACY_SWITCHES_CHECK_MODE
|
||||||
|
in app.no_support
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
ec_privacy = ec_commands.framework_laptop.get_privacy_switches(
|
||||||
|
app.cros_ec
|
||||||
|
)
|
||||||
|
self.hw_priv_cam_sw.set_active(ec_privacy["camera"])
|
||||||
|
self.hw_priv_mic_sw.set_active(ec_privacy["microphone"])
|
||||||
|
|
||||||
|
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_PRIVACY_SWITCHES_CHECK_MODE
|
||||||
|
)
|
||||||
|
self.hw_priv_cam.set_visible(False)
|
||||||
|
self.hw_priv_mic.set_visible(False)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return app.current_page == 3 and success
|
||||||
154
yafi/leds.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# leds.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
|
||||||
|
|
||||||
|
import cros_ec_python.commands as ec_commands
|
||||||
|
import cros_ec_python.exceptions as ec_exceptions
|
||||||
|
|
||||||
|
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/leds.ui')
|
||||||
|
class LedsPage(Gtk.Box):
|
||||||
|
__gtype_name__ = 'LedsPage'
|
||||||
|
|
||||||
|
led_pwr = Gtk.Template.Child()
|
||||||
|
led_pwr_scale = Gtk.Template.Child()
|
||||||
|
|
||||||
|
led_kbd = Gtk.Template.Child()
|
||||||
|
led_kbd_scale = Gtk.Template.Child()
|
||||||
|
|
||||||
|
led_advanced = Gtk.Template.Child()
|
||||||
|
|
||||||
|
led_pwr_colour = Gtk.Template.Child()
|
||||||
|
|
||||||
|
led_chg_colour = Gtk.Template.Child()
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def setup(self, app):
|
||||||
|
# Power LED
|
||||||
|
try:
|
||||||
|
|
||||||
|
def handle_led_pwr(scale):
|
||||||
|
value = int(abs(scale.get_value() - 2))
|
||||||
|
ec_commands.framework_laptop.set_fp_led_level(app.cros_ec, value)
|
||||||
|
self.led_pwr.set_subtitle(["High", "Medium", "Low"][value])
|
||||||
|
|
||||||
|
current_fp_level = ec_commands.framework_laptop.get_fp_led_level(
|
||||||
|
app.cros_ec
|
||||||
|
).value
|
||||||
|
self.led_pwr_scale.set_value(abs(current_fp_level - 2))
|
||||||
|
self.led_pwr.set_subtitle(["High", "Medium", "Low"][current_fp_level])
|
||||||
|
self.led_pwr_scale.connect("value-changed", handle_led_pwr)
|
||||||
|
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_FP_LED_LEVEL)
|
||||||
|
self.led_pwr.set_visible(False)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# Keyboard backlight
|
||||||
|
if ec_commands.general.get_cmd_versions(
|
||||||
|
app.cros_ec, ec_commands.pwm.EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT
|
||||||
|
):
|
||||||
|
|
||||||
|
def handle_led_kbd(scale):
|
||||||
|
value = int(scale.get_value())
|
||||||
|
ec_commands.pwm.pwm_set_keyboard_backlight(app.cros_ec, value)
|
||||||
|
self.led_kbd.set_subtitle(f"{value} %")
|
||||||
|
|
||||||
|
current_kb_level = ec_commands.pwm.pwm_get_keyboard_backlight(app.cros_ec)[
|
||||||
|
"percent"
|
||||||
|
]
|
||||||
|
self.led_kbd_scale.set_value(current_kb_level)
|
||||||
|
self.led_kbd.set_subtitle(f"{current_kb_level} %")
|
||||||
|
self.led_kbd_scale.connect("value-changed", handle_led_kbd)
|
||||||
|
else:
|
||||||
|
self.led_kbd.set_visible(False)
|
||||||
|
|
||||||
|
# Advanced options
|
||||||
|
if ec_commands.general.get_cmd_versions(
|
||||||
|
app.cros_ec, ec_commands.leds.EC_CMD_LED_CONTROL
|
||||||
|
):
|
||||||
|
|
||||||
|
# Advanced: Power LED
|
||||||
|
led_pwr_colour_strings = self.led_pwr_colour.get_model()
|
||||||
|
|
||||||
|
all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"]
|
||||||
|
|
||||||
|
def add_colours(strings, led_id):
|
||||||
|
supported_colours = ec_commands.leds.led_control_get_max_values(
|
||||||
|
app.cros_ec, led_id
|
||||||
|
)
|
||||||
|
for i, colour in enumerate(all_colours):
|
||||||
|
if supported_colours[i]:
|
||||||
|
strings.append(colour)
|
||||||
|
|
||||||
|
add_colours(
|
||||||
|
led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_led_colour(combobox, led_id):
|
||||||
|
colour = combobox.get_selected() - 2
|
||||||
|
match colour:
|
||||||
|
case -2: # Auto
|
||||||
|
ec_commands.leds.led_control_set_auto(app.cros_ec, led_id)
|
||||||
|
case -1: # Off
|
||||||
|
ec_commands.leds.led_control(
|
||||||
|
app.cros_ec,
|
||||||
|
led_id,
|
||||||
|
0,
|
||||||
|
[0] * ec_commands.leds.EcLedColors.EC_LED_COLOR_COUNT.value,
|
||||||
|
)
|
||||||
|
case _: # Colour
|
||||||
|
colour_idx = all_colours.index(
|
||||||
|
combobox.get_selected_item().get_string()
|
||||||
|
)
|
||||||
|
ec_commands.leds.led_control_set_color(
|
||||||
|
app.cros_ec,
|
||||||
|
led_id,
|
||||||
|
100,
|
||||||
|
ec_commands.leds.EcLedColors(colour_idx),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.led_pwr_colour.connect(
|
||||||
|
"notify::selected",
|
||||||
|
lambda combo, _: handle_led_colour(
|
||||||
|
combo, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Advanced: Charging LED
|
||||||
|
led_chg_colour_strings = self.led_chg_colour.get_model()
|
||||||
|
|
||||||
|
add_colours(
|
||||||
|
led_chg_colour_strings,
|
||||||
|
ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.led_chg_colour.connect(
|
||||||
|
"notify::selected",
|
||||||
|
lambda combo, _: handle_led_colour(
|
||||||
|
combo, ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.led_advanced.set_visible(False)
|
||||||
140
yafi/main.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# main.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
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import gi
|
||||||
|
import os
|
||||||
|
from gi.repository import Gio
|
||||||
|
|
||||||
|
gi.require_version('Gtk', '4.0')
|
||||||
|
gi.require_version('Adw', '1')
|
||||||
|
|
||||||
|
from gi.repository import Gtk, Gio, Adw
|
||||||
|
from .window import YafiWindow
|
||||||
|
from .thermals import ThermalsPage
|
||||||
|
from .leds import LedsPage
|
||||||
|
from .battery import BatteryPage
|
||||||
|
from .hardware import HardwarePage
|
||||||
|
|
||||||
|
from cros_ec_python import get_cros_ec
|
||||||
|
|
||||||
|
class YafiApplication(Adw.Application):
|
||||||
|
"""The main application singleton class."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(application_id='au.stevetech.yafi',
|
||||||
|
flags=Gio.ApplicationFlags.DEFAULT_FLAGS,
|
||||||
|
resource_base_path='/au/stevetech/yafi')
|
||||||
|
|
||||||
|
self.current_page = 0
|
||||||
|
self.no_support = []
|
||||||
|
self.cros_ec = None
|
||||||
|
self.win = None
|
||||||
|
|
||||||
|
def change_page(self, content, page):
|
||||||
|
page.setup(self)
|
||||||
|
while content_child := content.get_last_child():
|
||||||
|
content.remove(content_child)
|
||||||
|
content.append(page)
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
"""Called when the application is activated.
|
||||||
|
|
||||||
|
We raise the application's main window, creating it if
|
||||||
|
necessary.
|
||||||
|
"""
|
||||||
|
self.win = self.props.active_window
|
||||||
|
if not self.win:
|
||||||
|
self.win = YafiWindow(application=self)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.cros_ec = get_cros_ec()
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
message = (
|
||||||
|
str(e)
|
||||||
|
+ "\n\n"
|
||||||
|
+ "This application only supports Framework Laptops.\n"
|
||||||
|
+ "If you are using a Framework Laptop, there are additional troubleshooting steps in the README."
|
||||||
|
)
|
||||||
|
self.show_error("EC Initalisation Error", message)
|
||||||
|
self.win.present()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.change_page(self.win.content, ThermalsPage())
|
||||||
|
|
||||||
|
pages = (
|
||||||
|
("Thermals", ThermalsPage()),
|
||||||
|
("LEDs", LedsPage()),
|
||||||
|
("Battery", BatteryPage()),
|
||||||
|
("Hardware", HardwarePage()),
|
||||||
|
("About", None),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build the navbar
|
||||||
|
for page in pages:
|
||||||
|
row = Gtk.ListBoxRow()
|
||||||
|
row.set_child(Gtk.Label(label=page[0]))
|
||||||
|
self.win.navbar.append(row)
|
||||||
|
|
||||||
|
def switch_page(page):
|
||||||
|
# About page is a special case
|
||||||
|
if pages[page][1]:
|
||||||
|
self.current_page = page
|
||||||
|
self.change_page(self.win.content, pages[page][1])
|
||||||
|
else:
|
||||||
|
self.on_about_action()
|
||||||
|
|
||||||
|
self.win.navbar.connect("row-activated", lambda box, row: switch_page(row.get_index()))
|
||||||
|
|
||||||
|
self.win.present()
|
||||||
|
|
||||||
|
def on_about_action(self, *args):
|
||||||
|
"""Callback for the app.about action."""
|
||||||
|
about = Adw.AboutDialog(
|
||||||
|
application_icon="au.stevetech.yafi",
|
||||||
|
application_name="Yet Another Framework Interface",
|
||||||
|
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.",
|
||||||
|
copyright="© 2025 Stephen Horvath",
|
||||||
|
developer_name="Stephen Horvath",
|
||||||
|
developers=["Stephen Horvath"],
|
||||||
|
issue_url="https://github.com/Steve-Tech/YAFI/issues",
|
||||||
|
license_type=Gtk.License.GPL_2_0,
|
||||||
|
version="0.1.0",
|
||||||
|
website="https://github.com/Steve-Tech/YAFI",
|
||||||
|
)
|
||||||
|
about.add_acknowledgement_section(None, ["Framework Computer Inc. https://frame.work/"])
|
||||||
|
about.present(self.props.active_window)
|
||||||
|
|
||||||
|
def show_error(self, heading, message):
|
||||||
|
dialog = Adw.AlertDialog(heading=heading, body=message)
|
||||||
|
dialog.add_response("exit", "Exit")
|
||||||
|
dialog.connect("response", lambda d, r: self.win.destroy())
|
||||||
|
dialog.present(self.win)
|
||||||
|
|
||||||
|
|
||||||
|
def main(version):
|
||||||
|
"""The application's entry point."""
|
||||||
|
app = YafiApplication()
|
||||||
|
return app.run(sys.argv)
|
||||||
39
yafi/meson.build
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name()
|
||||||
|
moduledir = pkgdatadir / 'yafi'
|
||||||
|
gnome = import('gnome')
|
||||||
|
|
||||||
|
gnome.compile_resources('yafi',
|
||||||
|
'yafi.gresource.xml',
|
||||||
|
gresource_bundle: true,
|
||||||
|
install: true,
|
||||||
|
install_dir: pkgdatadir,
|
||||||
|
)
|
||||||
|
|
||||||
|
python = import('python')
|
||||||
|
|
||||||
|
conf = configuration_data()
|
||||||
|
conf.set('PYTHON', python.find_installation('python3').full_path())
|
||||||
|
conf.set('VERSION', meson.project_version())
|
||||||
|
conf.set('localedir', get_option('prefix') / get_option('localedir'))
|
||||||
|
conf.set('pkgdatadir', pkgdatadir)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
input: 'yafi.in',
|
||||||
|
output: 'yafi',
|
||||||
|
configuration: conf,
|
||||||
|
install: true,
|
||||||
|
install_dir: get_option('bindir'),
|
||||||
|
install_mode: 'r-xr-xr-x'
|
||||||
|
)
|
||||||
|
|
||||||
|
yafi_sources = [
|
||||||
|
'__init__.py',
|
||||||
|
'main.py',
|
||||||
|
'window.py',
|
||||||
|
'thermals.py',
|
||||||
|
'leds.py',
|
||||||
|
'battery.py',
|
||||||
|
'hardware.py',
|
||||||
|
]
|
||||||
|
|
||||||
|
install_data(yafi_sources, install_dir: moduledir)
|
||||||
145
yafi/thermals.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# thermals.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/thermals.ui')
|
||||||
|
class ThermalsPage(Gtk.Box):
|
||||||
|
__gtype_name__ = 'ThermalsPage'
|
||||||
|
|
||||||
|
fan_rpm = Gtk.Template.Child()
|
||||||
|
fan_mode = Gtk.Template.Child()
|
||||||
|
fan_set_rpm = Gtk.Template.Child()
|
||||||
|
fan_set_percent = Gtk.Template.Child()
|
||||||
|
fan_percent_scale = Gtk.Template.Child()
|
||||||
|
|
||||||
|
temperatures = Gtk.Template.Child()
|
||||||
|
temp_items = []
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def setup(self, app):
|
||||||
|
# Don't let the user change the fans if they can't get back to auto
|
||||||
|
if ec_commands.general.get_cmd_versions(
|
||||||
|
app.cros_ec, ec_commands.thermal.EC_CMD_THERMAL_AUTO_FAN_CTRL
|
||||||
|
):
|
||||||
|
|
||||||
|
def handle_fan_mode(mode):
|
||||||
|
match mode:
|
||||||
|
case 0: # Auto
|
||||||
|
self.fan_set_rpm.set_visible(False)
|
||||||
|
self.fan_set_percent.set_visible(False)
|
||||||
|
ec_commands.thermal.thermal_auto_fan_ctrl(app.cros_ec)
|
||||||
|
case 1: # Percent
|
||||||
|
self.fan_set_rpm.set_visible(False)
|
||||||
|
self.fan_set_percent.set_visible(True)
|
||||||
|
case 2: # RPM
|
||||||
|
self.fan_set_rpm.set_visible(True)
|
||||||
|
self.fan_set_percent.set_visible(False)
|
||||||
|
|
||||||
|
self.fan_mode.connect(
|
||||||
|
"notify::selected",
|
||||||
|
lambda combo, _: handle_fan_mode(combo.get_selected()),
|
||||||
|
)
|
||||||
|
|
||||||
|
if ec_commands.general.get_cmd_versions(
|
||||||
|
app.cros_ec, ec_commands.pwm.EC_CMD_PWM_SET_FAN_DUTY
|
||||||
|
):
|
||||||
|
|
||||||
|
def handle_fan_percent(scale):
|
||||||
|
percent = int(scale.get_value())
|
||||||
|
ec_commands.pwm.pwm_set_fan_duty(app.cros_ec, percent)
|
||||||
|
self.fan_set_percent.set_subtitle(f"{percent} %")
|
||||||
|
|
||||||
|
self.fan_percent_scale.connect("value-changed", handle_fan_percent)
|
||||||
|
else:
|
||||||
|
self.fan_set_percent.set_sensitive(False)
|
||||||
|
|
||||||
|
if ec_commands.general.get_cmd_versions(
|
||||||
|
app.cros_ec, ec_commands.pwm.EC_CMD_PWM_SET_FAN_TARGET_RPM
|
||||||
|
):
|
||||||
|
|
||||||
|
def handle_fan_rpm(entry):
|
||||||
|
rpm = int(entry.get_text())
|
||||||
|
ec_commands.pwm.pwm_set_fan_rpm(app.cros_ec, rpm)
|
||||||
|
|
||||||
|
self.fan_set_rpm.connect(
|
||||||
|
"notify::text", lambda entry, _: handle_fan_rpm(entry)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.fan_set_rpm.set_sensitive(False)
|
||||||
|
else:
|
||||||
|
self.fan_mode.set_sensitive(False)
|
||||||
|
|
||||||
|
# Temperature sensors
|
||||||
|
try:
|
||||||
|
ec_temp_sensors = ec_commands.thermal.get_temp_sensors(app.cros_ec)
|
||||||
|
except ec_exceptions.ECError as e:
|
||||||
|
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
||||||
|
# Generate some labels if the command is not supported
|
||||||
|
ec_temp_sensors = {}
|
||||||
|
temps = ec_commands.memmap.get_temps(app.cros_ec)
|
||||||
|
for i, temp in enumerate(temps):
|
||||||
|
ec_temp_sensors[f"Sensor {i}"] = (temp, None)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
for key, value in ec_temp_sensors.items():
|
||||||
|
new_row = Adw.ActionRow(title=key, subtitle=f"{value[0]}°C")
|
||||||
|
new_row.add_css_class("property")
|
||||||
|
self.temperatures.append(new_row)
|
||||||
|
self.temp_items.append(new_row)
|
||||||
|
|
||||||
|
self._update_thermals(app)
|
||||||
|
|
||||||
|
# Schedule _update_thermals to run every second
|
||||||
|
GLib.timeout_add_seconds(1, self._update_thermals, app)
|
||||||
|
|
||||||
|
def _update_thermals(self, app):
|
||||||
|
# memmap reads should always be supported
|
||||||
|
ec_fans = ec_commands.memmap.get_fans(app.cros_ec)
|
||||||
|
self.fan_rpm.set_subtitle(f"{ec_fans[0]} RPM")
|
||||||
|
|
||||||
|
ec_temp_sensors = ec_commands.memmap.get_temps(app.cros_ec)
|
||||||
|
# The temp sensors disappear sometimes, so we need to handle that
|
||||||
|
for i in range(min(len(self.temp_items), len(ec_temp_sensors))):
|
||||||
|
self.temp_items[i].set_subtitle(f"{ec_temp_sensors[i]}°C")
|
||||||
|
|
||||||
|
# Check if this has already failed and skip if it has
|
||||||
|
if not ec_commands.pwm.EC_CMD_PWM_GET_FAN_TARGET_RPM in app.no_support:
|
||||||
|
try:
|
||||||
|
ec_target_rpm = ec_commands.pwm.pwm_get_fan_rpm(app.cros_ec)
|
||||||
|
self.fan_set_rpm.set_subtitle(f"{ec_target_rpm} RPM")
|
||||||
|
except ec_exceptions.ECError as e:
|
||||||
|
# If the command is not supported, we can ignore it
|
||||||
|
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
||||||
|
app.no_support.append(ec_commands.pwm.EC_CMD_PWM_GET_FAN_TARGET_RPM)
|
||||||
|
self.fan_set_rpm.set_subtitle("")
|
||||||
|
else:
|
||||||
|
# If it's another error, we should raise it
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return app.current_page == 0
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<!-- Created with Cambalache 0.94.1 -->
|
|
||||||
<interface>
|
|
||||||
<!-- interface-description The About page for YAFI -->
|
|
||||||
<!-- interface-copyright Steve-Tech -->
|
|
||||||
<requires lib="libadwaita" version="1.2"/>
|
|
||||||
<object class="AdwAboutWindow" id="about-root">
|
|
||||||
<property name="application-icon">application-default-icon</property>
|
|
||||||
<property name="application-name">Yet Another Framework Interface</property>
|
|
||||||
<property name="comments">YAFI is another GUI for the Framework Laptop Embedded Controller.
|
|
||||||
It is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.</property>
|
|
||||||
<property name="copyright">Copyright © 2025 Stephen Horvath</property>
|
|
||||||
<property name="developers">Stephen Horvath (Steve-Tech)</property>
|
|
||||||
<property name="issue-url">https://github.com/Steve-Tech/YAFI/issues</property>
|
|
||||||
<property name="license-type">gpl-2-0</property>
|
|
||||||
<property name="version">0.1</property>
|
|
||||||
<property name="website">https://github.com/Steve-Tech/YAFI</property>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<!-- interface-authors Steve-Tech -->
|
<!-- interface-authors Steve-Tech -->
|
||||||
<requires lib="gtk" version="4.12"/>
|
<requires lib="gtk" version="4.12"/>
|
||||||
<requires lib="libadwaita" version="1.6"/>
|
<requires lib="libadwaita" version="1.6"/>
|
||||||
<object class="GtkBox" id="battery-root">
|
<template class="BatteryPage" parent="GtkBox">
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox">
|
<object class="GtkListBox">
|
||||||
@@ -18,19 +18,19 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwSwitchRow" id="chg-limit-enable">
|
<object class="AdwSwitchRow" id="chg_limit_enable">
|
||||||
<property name="title">Enable Charge Limiter</property>
|
<property name="title">Enable Charge Limiter</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="chg-limit">
|
<object class="AdwActionRow" id="chg_limit">
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="subtitle">Limit the maximum charge</property>
|
<property name="subtitle">Limit the maximum charge</property>
|
||||||
<property name="title">Charge Limit</property>
|
<property name="title">Charge Limit</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScale" id="chg-limit-scale">
|
<object class="GtkScale" id="chg_limit_scale">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="page-increment">10.0</property>
|
<property name="page-increment">10.0</property>
|
||||||
@@ -48,14 +48,14 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="bat-limit">
|
<object class="AdwActionRow" id="bat_limit">
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="subtitle">Limit the minimum charge</property>
|
<property name="subtitle">Limit the minimum charge</property>
|
||||||
<property name="title">Discharge Limit</property>
|
<property name="title">Discharge Limit</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScale" id="bat-limit-scale">
|
<object class="GtkScale" id="bat_limit_scale">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="page-increment">10.0</property>
|
<property name="page-increment">10.0</property>
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="chg-limit-override">
|
<object class="AdwActionRow" id="chg_limit_override">
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="subtitle">Disables the limiter for one charge cycle</property>
|
<property name="subtitle">Disables the limiter for one charge cycle</property>
|
||||||
<property name="title">Override Charge Limiter</property>
|
<property name="title">Override Charge Limiter</property>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="chg-limit-override-btn">
|
<object class="GtkButton" id="chg_limit_override_btn">
|
||||||
<property name="label">Override</property>
|
<property name="label">Override</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesGroup" id="bat-ext-group">
|
<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="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-bottom">5</property>
|
||||||
<property name="margin-end">5</property>
|
<property name="margin-end">5</property>
|
||||||
@@ -99,12 +99,12 @@
|
|||||||
<property name="margin-top">5</property>
|
<property name="margin-top">5</property>
|
||||||
<property name="title">Battery Extender</property>
|
<property name="title">Battery Extender</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwSwitchRow" id="bat-ext-enable">
|
<object class="AdwSwitchRow" id="bat_ext_enable">
|
||||||
<property name="title">Enable</property>
|
<property name="title">Enable</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="bat-ext-stage">
|
<object class="AdwActionRow" id="bat_ext_stage">
|
||||||
<property name="selectable">False</property>
|
<property name="selectable">False</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="title">Current Stage (0 to 2)</property>
|
<property name="title">Current Stage (0 to 2)</property>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="bat-ext-trigger-time">
|
<object class="AdwActionRow" id="bat_ext_trigger_time">
|
||||||
<property name="selectable">False</property>
|
<property name="selectable">False</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="title">Time Until Trigger</property>
|
<property name="title">Time Until Trigger</property>
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="bat-ext-reset-time">
|
<object class="AdwActionRow" id="bat_ext_reset_time">
|
||||||
<property name="selectable">False</property>
|
<property name="selectable">False</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="title">Time Until Reset</property>
|
<property name="title">Time Until Reset</property>
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwSpinRow" id="bat-ext-trigger">
|
<object class="AdwSpinRow" id="bat_ext_trigger">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="lower">1.0</property>
|
<property name="lower">1.0</property>
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwSpinRow" id="bat-ext-reset">
|
<object class="AdwSpinRow" id="bat_ext_reset">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="lower">1.0</property>
|
<property name="lower">1.0</property>
|
||||||
@@ -172,5 +172,5 @@
|
|||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<!-- interface-copyright Steve-Tech -->
|
<!-- interface-copyright Steve-Tech -->
|
||||||
<requires lib="gtk" version="4.12"/>
|
<requires lib="gtk" version="4.12"/>
|
||||||
<requires lib="libadwaita" version="1.3"/>
|
<requires lib="libadwaita" version="1.3"/>
|
||||||
<object class="GtkBox" id="hardware-root">
|
<template class="HardwarePage" parent="GtkBox">
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox">
|
<object class="GtkListBox">
|
||||||
@@ -18,15 +18,15 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="hw-chassis">
|
<object class="AdwActionRow" id="hw_chassis">
|
||||||
<property name="title">Chassis Open Count</property>
|
<property name="title">Chassis Open Count</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="hw-chassis-label"/>
|
<object class="GtkLabel" id="hw_chassis_label"/>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="hw-priv-cam">
|
<object class="AdwActionRow" id="hw_priv_cam">
|
||||||
<property name="subtitle">Use Privacy Switch</property>
|
<property name="subtitle">Use Privacy Switch</property>
|
||||||
<property name="title">Camera Enabled</property>
|
<property name="title">Camera Enabled</property>
|
||||||
<child>
|
<child>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSwitch" id="hw-priv-cam-sw">
|
<object class="GtkSwitch" id="hw_priv_cam_sw">
|
||||||
<property name="active">True</property>
|
<property name="active">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
</object>
|
</object>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="hw-priv-mic">
|
<object class="AdwActionRow" id="hw_priv_mic">
|
||||||
<property name="subtitle">Use Privacy Switch</property>
|
<property name="subtitle">Use Privacy Switch</property>
|
||||||
<property name="title">Microphone Enabled</property>
|
<property name="title">Microphone Enabled</property>
|
||||||
<child>
|
<child>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSwitch" id="hw-priv-mic-sw">
|
<object class="GtkSwitch" id="hw_priv_mic_sw">
|
||||||
<property name="active">True</property>
|
<property name="active">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
</object>
|
</object>
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="hw-fp-pwr">
|
<object class="AdwActionRow" id="hw_fp_pwr">
|
||||||
<property name="title">Fingerprint</property>
|
<property name="title">Fingerprint</property>
|
||||||
<property name="visible">False</property>
|
<property name="visible">False</property>
|
||||||
<child>
|
<child>
|
||||||
@@ -74,12 +74,12 @@
|
|||||||
<property name="spacing">5</property>
|
<property name="spacing">5</property>
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="hw-fp-pwr-en">
|
<object class="GtkButton" id="hw_fp_pwr_en">
|
||||||
<property name="label">Enable</property>
|
<property name="label">Enable</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="hw-fp-pwr-dis">
|
<object class="GtkButton" id="hw_fp_pwr_dis">
|
||||||
<property name="label">Disable</property>
|
<property name="label">Disable</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@@ -92,5 +92,5 @@
|
|||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<!-- 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.6"/>
|
||||||
<object class="GtkBox" id="leds-root">
|
<template class="LedsPage" parent="GtkBox">
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox">
|
<object class="GtkListBox">
|
||||||
@@ -18,12 +18,12 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="led-pwr">
|
<object class="AdwActionRow" id="led_pwr">
|
||||||
<property name="title">Power Button</property>
|
<property name="title">Power Button</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScale" id="led-pwr-scale">
|
<object class="GtkScale" id="led_pwr_scale">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="page-increment">1.0</property>
|
<property name="page-increment">1.0</property>
|
||||||
@@ -41,12 +41,12 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="led-kbd">
|
<object class="AdwActionRow" id="led_kbd">
|
||||||
<property name="title">Keyboard Backlight</property>
|
<property name="title">Keyboard Backlight</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScale" id="led-kbd-scale">
|
<object class="GtkScale" id="led_kbd_scale">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="page-increment">10.0</property>
|
<property name="page-increment">10.0</property>
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwExpanderRow">
|
<object class="AdwExpanderRow" id="led_advanced">
|
||||||
<property name="selectable">False</property>
|
<property name="selectable">False</property>
|
||||||
<property name="subtitle">These options break normal functionality</property>
|
<property name="subtitle">These options break normal functionality</property>
|
||||||
<property name="title">Advanced Options</property>
|
<property name="title">Advanced Options</property>
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
<property name="margin-top">5</property>
|
<property name="margin-top">5</property>
|
||||||
<property name="title">Power Button LED</property>
|
<property name="title">Power Button LED</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwComboRow" id="led-pwr-colour">
|
<object class="AdwComboRow" id="led_pwr_colour">
|
||||||
<property name="model">
|
<property name="model">
|
||||||
<object class="GtkStringList">
|
<object class="GtkStringList">
|
||||||
<items>
|
<items>
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
<property name="margin-top">5</property>
|
<property name="margin-top">5</property>
|
||||||
<property name="title">Charging Indicators</property>
|
<property name="title">Charging Indicators</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwComboRow" id="led-chg-colour">
|
<object class="AdwComboRow" id="led_chg_colour">
|
||||||
<property name="model">
|
<property name="model">
|
||||||
<object class="GtkStringList">
|
<object class="GtkStringList">
|
||||||
<items>
|
<items>
|
||||||
@@ -121,5 +121,5 @@
|
|||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
<interface>
|
<interface>
|
||||||
<!-- 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.0"/>
|
<requires lib="gtk" version="4.12"/>
|
||||||
<requires lib="libadwaita" version="1.6"/>
|
<requires lib="libadwaita" version="1.6"/>
|
||||||
<object class="GtkPaned" id="thermals-root">
|
<template class="ThermalsPage" parent="GtkBox">
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkPaned">
|
||||||
<property name="end-child">
|
<property name="end-child">
|
||||||
<object class="GtkListBox" id="temperatures">
|
<object class="GtkListBox" id="temperatures">
|
||||||
<child>
|
<child>
|
||||||
@@ -30,7 +33,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="fan-rpm">
|
<object class="AdwActionRow" id="fan_rpm">
|
||||||
<property name="title">Current Speed</property>
|
<property name="title">Current Speed</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="property"/>
|
<class name="property"/>
|
||||||
@@ -38,7 +41,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwComboRow" id="fan-mode">
|
<object class="AdwComboRow" id="fan_mode">
|
||||||
<property name="model">
|
<property name="model">
|
||||||
<object class="GtkStringList">
|
<object class="GtkStringList">
|
||||||
<items>
|
<items>
|
||||||
@@ -52,13 +55,13 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="fan-set-percent">
|
<object class="AdwActionRow" id="fan_set_percent">
|
||||||
<property name="title">Fan Speed</property>
|
<property name="title">Fan Speed</property>
|
||||||
<property name="visible">False</property>
|
<property name="visible">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScale" id="fan-percent-scale">
|
<object class="GtkScale" id="fan_percent_scale">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="page-increment">10.0</property>
|
<property name="page-increment">10.0</property>
|
||||||
@@ -76,7 +79,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwSpinRow" id="fan-set-rpm">
|
<object class="AdwSpinRow" id="fan_set_rpm">
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment">
|
<object class="GtkAdjustment">
|
||||||
<property name="page-increment">1000.0</property>
|
<property name="page-increment">1000.0</property>
|
||||||
@@ -96,4 +99,6 @@
|
|||||||
</object>
|
</object>
|
||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
105
yafi/ui/yafi.cmb
@@ -2,15 +2,14 @@
|
|||||||
<!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">
|
<cambalache-project version="0.94.0" target_tk="gtk-4.0">
|
||||||
<ui>
|
<ui>
|
||||||
(1,None,"yafi.ui","yafi.ui","YAFI is another GUI for the Framework Laptop Embedded Controller.",None,"Steve-Tech",None,None,None,None),
|
(1,1,"yafi.ui","yafi.ui","YAFI is another GUI for the Framework Laptop Embedded Controller.",None,"Steve-Tech",None,None,None,None),
|
||||||
(2,None,None,"thermals.ui","The Thermals page for YAFI",None,"Steve-Tech",None,None,None,None),
|
(2,17,None,"thermals.ui","The Thermals page for YAFI",None,"Steve-Tech",None,None,None,None),
|
||||||
(3,None,None,"leds.ui","The LEDs page for YAFI",None,"Steve-Tech",None,None,None,None),
|
(3,4,None,"leds.ui","The LEDs page for YAFI",None,"Steve-Tech",None,None,None,None),
|
||||||
(4,None,None,"battery.ui","The Battery page for YAFI",None,"Steve-Tech",None,None,None,None),
|
(4,1,None,"battery.ui","The Battery page for YAFI",None,"Steve-Tech",None,None,None,None),
|
||||||
(5,None,None,"hardware.ui","The Hardware page for YAFI","Steve-Tech",None,None,None,None,None),
|
(5,1,None,"hardware.ui","The Hardware page for YAFI","Steve-Tech",None,None,None,None,None)
|
||||||
(6,None,None,"about.ui","The About page for YAFI","Steve-Tech",None,None,None,None,None)
|
|
||||||
</ui>
|
</ui>
|
||||||
<object>
|
<object>
|
||||||
(1,1,"AdwApplicationWindow","root",None,None,None,None,0,None,None),
|
(1,1,"AdwApplicationWindow","YafiWindow",None,None,None,None,0,None,None),
|
||||||
(1,2,"AdwNavigationSplitView",None,1,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,6,"AdwNavigationPage",None,2,None,None,None,0,None,None),
|
||||||
(1,7,"AdwNavigationPage",None,2,None,None,None,1,None,None),
|
(1,7,"AdwNavigationPage",None,2,None,None,None,1,None,None),
|
||||||
@@ -22,79 +21,79 @@
|
|||||||
(1,17,"GtkListBox","navbar",16,None,None,None,0,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,52,"GtkBox","content",53,None,None,None,0,None,None),
|
||||||
(1,53,"GtkScrolledWindow",None,11,None,None,None,1,None,None),
|
(1,53,"GtkScrolledWindow",None,11,None,None,None,1,None,None),
|
||||||
(2,2,"GtkPaned","thermals-root",None,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,3,"GtkListBox","temperatures",2,None,None,None,1,None,None),
|
||||||
(2,4,"AdwActionRow",None,3,None,None,None,0,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,5,"GtkBox",None,2,None,None,None,0,None,None),
|
||||||
(2,6,"GtkListBox",None,5,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,7,"AdwActionRow",None,6,None,None,None,0,None,None),
|
||||||
(2,8,"AdwActionRow","fan-rpm",6,None,None,None,1,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,9,"AdwComboRow","fan_mode",6,None,None,None,2,None,None),
|
||||||
(2,10,"GtkStringList",None,9,None,None,None,0,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,11,"AdwActionRow","fan_set_percent",6,None,None,None,3,None,None),
|
||||||
(2,12,"GtkBox",None,11,None,None,None,0,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,13,"GtkScale","fan_percent_scale",12,None,None,None,0,None,None),
|
||||||
(2,14,"GtkAdjustment",None,13,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,15,"AdwSpinRow","fan_set_rpm",6,None,None,None,4,None,None),
|
||||||
(2,16,"GtkAdjustment",None,15,None,None,None,0,None,None),
|
(2,16,"GtkAdjustment",None,15,None,None,None,0,None,None),
|
||||||
(3,4,"GtkBox","leds-root",None,None,None,None,1,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,5,"GtkListBox",None,4,None,None,None,0,None,None),
|
||||||
(3,6,"AdwActionRow",None,5,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,10,"AdwActionRow","led_pwr",5,None,None,None,1,None,None),
|
||||||
(3,11,"GtkBox",None,10,None,None,None,0,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,12,"GtkScale","led_pwr_scale",11,None,None,None,0,None,None),
|
||||||
(3,13,"GtkAdjustment",None,12,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,14,"AdwActionRow","led_kbd",5,None,None,None,2,None,None),
|
||||||
(3,15,"GtkBox",None,14,None,None,None,0,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,16,"GtkScale","led_kbd_scale",15,None,None,None,0,None,None),
|
||||||
(3,17,"GtkAdjustment",None,16,None,None,None,0,None,None),
|
(3,17,"GtkAdjustment",None,16,None,None,None,0,None,None),
|
||||||
(3,18,"AdwExpanderRow",None,5,None,None,None,3,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,23,"AdwComboRow","led_pwr_colour",31,None,None,None,0,None,None),
|
||||||
(3,24,"GtkStringList",None,23,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,25,"AdwComboRow","led_chg_colour",32,None,None,None,0,None,None),
|
||||||
(3,26,"GtkStringList",None,25,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,31,"AdwPreferencesGroup",None,18,None,None,None,0,None,None),
|
||||||
(3,32,"AdwPreferencesGroup",None,18,None,None,None,1,None,None),
|
(3,32,"AdwPreferencesGroup",None,18,None,None,None,1,None,None),
|
||||||
(4,1,"GtkBox","battery-root",None,None,None,None,0,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,2,"GtkListBox",None,1,None,None,None,0,None,None),
|
||||||
(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,0,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),
|
||||||
(4,28,"GtkAdjustment",None,27,None,None,None,0,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,29,"AdwSpinRow","bat_ext_reset",13,None,None,None,6,None,None),
|
||||||
(4,30,"GtkAdjustment",None,29,None,None,None,0,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,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,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,0,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),
|
||||||
(4,39,"GtkButton","chg-limit-override-btn",38,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,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),
|
||||||
(5,1,"GtkBox","hardware-root",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),
|
||||||
(5,4,"AdwActionRow","hw-chassis",2,None,None,None,1,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,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,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,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,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,13,"GtkBox",None,9,None,None,None,0,None,None),
|
||||||
(5,14,"GtkBox",None,6,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,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,16,"AdwActionRow","hw_fp_pwr",2,None,None,None,4,None,None),
|
||||||
(5,17,"GtkBox",None,16,None,None,None,0,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,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),
|
(5,19,"GtkButton","hw_fp_pwr_dis",17,None,None,None,1,None,None)
|
||||||
(6,1,"AdwAboutWindow","about-root",None,None,None,None,0,None,None)
|
|
||||||
</object>
|
</object>
|
||||||
<object_property>
|
<object_property>
|
||||||
(1,1,"GtkWindow","default-height","500",None,None,None,None,None,None,None,None,None),
|
(1,1,"GtkWindow","default-height","500",None,None,None,None,None,None,None,None,None),
|
||||||
@@ -132,6 +131,7 @@
|
|||||||
(2,16,"GtkAdjustment","page-increment","1000.0",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","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,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,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-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,5,"GtkWidget","margin-start","10",0,None,None,None,None,None,None,None,None),
|
||||||
@@ -265,16 +265,7 @@
|
|||||||
(5,17,"GtkWidget","halign","end",0,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,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,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),
|
(5,19,"GtkButton","label","Disable",None,None,None,None,None,None,None,None,None)
|
||||||
(6,1,"AdwAboutWindow","application-icon","application-default-icon",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","application-name","Yet Another Framework Interface",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","comments","YAFI is another GUI for the Framework Laptop Embedded Controller.\nIt is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","copyright","Copyright © 2025 Stephen Horvath",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","developers","Stephen Horvath (Steve-Tech)",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","issue-url","https://github.com/Steve-Tech/YAFI/issues",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","license-type","gpl-2-0",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","version","0.1",None,None,None,None,None,None,None,None,None),
|
|
||||||
(6,1,"AdwAboutWindow","website","https://github.com/Steve-Tech/YAFI",None,None,None,None,None,None,None,None,None)
|
|
||||||
</object_property>
|
</object_property>
|
||||||
<object_data>
|
<object_data>
|
||||||
(1,17,"GtkWidget",1,1,None,None,None,None,None,None),
|
(1,17,"GtkWidget",1,1,None,None,None,None,None,None),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<!-- interface-authors Steve-Tech -->
|
<!-- interface-authors Steve-Tech -->
|
||||||
<requires lib="gtk" version="4.12"/>
|
<requires lib="gtk" version="4.12"/>
|
||||||
<requires lib="libadwaita" version="1.4"/>
|
<requires lib="libadwaita" version="1.4"/>
|
||||||
<object class="AdwApplicationWindow" id="root">
|
<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>
|
||||||
<child>
|
<child>
|
||||||
@@ -58,5 +58,5 @@
|
|||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
32
yafi/window.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# window.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
|
||||||
|
|
||||||
|
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/yafi.ui')
|
||||||
|
class YafiWindow(Adw.ApplicationWindow):
|
||||||
|
__gtype_name__ = 'YafiWindow'
|
||||||
|
|
||||||
|
content = Gtk.Template.Child()
|
||||||
|
navbar = Gtk.Template.Child()
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
10
yafi/yafi.gresource.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/au/stevetech/yafi">
|
||||||
|
<file preprocess="xml-stripblanks">ui/yafi.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/thermals.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/leds.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/battery.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/hardware.ui</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
||||||
47
yafi/yafi.in
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
#!@PYTHON@
|
||||||
|
|
||||||
|
# yafi.in
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
import locale
|
||||||
|
import gettext
|
||||||
|
|
||||||
|
VERSION = '@VERSION@'
|
||||||
|
pkgdatadir = '@pkgdatadir@'
|
||||||
|
localedir = '@localedir@'
|
||||||
|
|
||||||
|
sys.path.insert(1, pkgdatadir)
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
locale.bindtextdomain('yafi', localedir)
|
||||||
|
locale.textdomain('yafi')
|
||||||
|
gettext.install('yafi', localedir)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import gi
|
||||||
|
|
||||||
|
from gi.repository import Gio
|
||||||
|
resource = Gio.Resource.load(os.path.join(pkgdatadir, 'yafi.gresource'))
|
||||||
|
resource._register()
|
||||||
|
|
||||||
|
from yafi import main
|
||||||
|
sys.exit(main.main(VERSION))
|
||||||
659
yafi/yafi.py
@@ -1,659 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import gi
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
gi.require_version("Gtk", "4.0")
|
|
||||||
gi.require_version("Adw", "1")
|
|
||||||
from gi.repository import Gtk, Adw, GLib
|
|
||||||
|
|
||||||
from cros_ec_python import get_cros_ec
|
|
||||||
import cros_ec_python.commands as ec_commands
|
|
||||||
import cros_ec_python.exceptions as ec_exceptions
|
|
||||||
|
|
||||||
|
|
||||||
class YAFI(Adw.Application):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
self.script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
self.no_support = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.cros_ec = get_cros_ec()
|
|
||||||
|
|
||||||
self.connect("activate", self.on_activate)
|
|
||||||
except Exception as e:
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
message = (
|
|
||||||
str(e) + "\n\n" + "This application only supports Framework Laptops.\n" +
|
|
||||||
"If you are using a Framework Laptop, there are additional troubleshooting steps in the README."
|
|
||||||
)
|
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(heading="EC Initalisation Error", body=message)
|
|
||||||
dialog.add_response("exit", "Exit")
|
|
||||||
dialog.connect("response", lambda d, r: self.win.destroy())
|
|
||||||
|
|
||||||
self.connect(
|
|
||||||
"activate",
|
|
||||||
lambda app: self.minimal_activate(
|
|
||||||
app, lambda: dialog.present(self.win)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _change_page(self, builder, page):
|
|
||||||
content = builder.get_object("content")
|
|
||||||
while content_child := content.get_last_child():
|
|
||||||
content.remove(content_child)
|
|
||||||
content.append(page)
|
|
||||||
|
|
||||||
def _update_thermals(self, fan_rpm, temp_items, fan_rpm_target):
|
|
||||||
# memmap reads should always be supported
|
|
||||||
ec_fans = ec_commands.memmap.get_fans(self.cros_ec)
|
|
||||||
fan_rpm.set_subtitle(f"{ec_fans[0]} RPM")
|
|
||||||
|
|
||||||
ec_temp_sensors = ec_commands.memmap.get_temps(self.cros_ec)
|
|
||||||
# The temp sensors disappear sometimes, so we need to handle that
|
|
||||||
for i in range(min(len(temp_items), len(ec_temp_sensors))):
|
|
||||||
temp_items[i].set_subtitle(f"{ec_temp_sensors[i]}°C")
|
|
||||||
|
|
||||||
# Check if this has already failed and skip if it has
|
|
||||||
if not ec_commands.pwm.EC_CMD_PWM_GET_FAN_TARGET_RPM in self.no_support:
|
|
||||||
try:
|
|
||||||
ec_target_rpm = ec_commands.pwm.pwm_get_fan_rpm(self.cros_ec)
|
|
||||||
fan_rpm_target.set_subtitle(f"{ec_target_rpm} RPM")
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
# If the command is not supported, we can ignore it
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
self.no_support.append(
|
|
||||||
ec_commands.pwm.EC_CMD_PWM_GET_FAN_TARGET_RPM
|
|
||||||
)
|
|
||||||
fan_rpm_target.set_subtitle("")
|
|
||||||
else:
|
|
||||||
# If it's another error, we should raise it
|
|
||||||
raise e
|
|
||||||
|
|
||||||
return self.current_page == 0
|
|
||||||
|
|
||||||
def _thermals_page(self, builder):
|
|
||||||
# Load the thermals.ui file
|
|
||||||
thermals_builder = Gtk.Builder()
|
|
||||||
thermals_builder.add_from_file(os.path.join(self.script_dir, "ui/thermals.ui"))
|
|
||||||
|
|
||||||
# Get the root widget from the thermals.ui file
|
|
||||||
thermals_root = thermals_builder.get_object("thermals-root")
|
|
||||||
|
|
||||||
self._change_page(builder, thermals_root)
|
|
||||||
|
|
||||||
# Fan control
|
|
||||||
fan_rpm = thermals_builder.get_object("fan-rpm")
|
|
||||||
fan_mode = thermals_builder.get_object("fan-mode")
|
|
||||||
fan_set_rpm = thermals_builder.get_object("fan-set-rpm")
|
|
||||||
fan_set_percent = thermals_builder.get_object("fan-set-percent")
|
|
||||||
fan_percent_scale = thermals_builder.get_object("fan-percent-scale")
|
|
||||||
|
|
||||||
# Don't let the user change the fans if they can't get back to auto
|
|
||||||
if ec_commands.general.get_cmd_versions(
|
|
||||||
self.cros_ec, ec_commands.thermal.EC_CMD_THERMAL_AUTO_FAN_CTRL
|
|
||||||
):
|
|
||||||
|
|
||||||
def handle_fan_mode(mode):
|
|
||||||
match mode:
|
|
||||||
case 0: # Auto
|
|
||||||
fan_set_rpm.set_visible(False)
|
|
||||||
fan_set_percent.set_visible(False)
|
|
||||||
ec_commands.thermal.thermal_auto_fan_ctrl(self.cros_ec)
|
|
||||||
case 1: # Percent
|
|
||||||
fan_set_rpm.set_visible(False)
|
|
||||||
fan_set_percent.set_visible(True)
|
|
||||||
case 2: # RPM
|
|
||||||
fan_set_rpm.set_visible(True)
|
|
||||||
fan_set_percent.set_visible(False)
|
|
||||||
|
|
||||||
fan_mode.connect(
|
|
||||||
"notify::selected",
|
|
||||||
lambda combo, _: handle_fan_mode(combo.get_selected()),
|
|
||||||
)
|
|
||||||
|
|
||||||
if ec_commands.general.get_cmd_versions(
|
|
||||||
self.cros_ec, ec_commands.pwm.EC_CMD_PWM_SET_FAN_DUTY
|
|
||||||
):
|
|
||||||
|
|
||||||
def handle_fan_percent(scale):
|
|
||||||
percent = int(scale.get_value())
|
|
||||||
ec_commands.pwm.pwm_set_fan_duty(self.cros_ec, percent)
|
|
||||||
fan_set_percent.set_subtitle(f"{percent} %")
|
|
||||||
|
|
||||||
fan_percent_scale.connect("value-changed", handle_fan_percent)
|
|
||||||
else:
|
|
||||||
fan_set_percent.set_sensitive(False)
|
|
||||||
|
|
||||||
if ec_commands.general.get_cmd_versions(
|
|
||||||
self.cros_ec, ec_commands.pwm.EC_CMD_PWM_SET_FAN_TARGET_RPM
|
|
||||||
):
|
|
||||||
|
|
||||||
def handle_fan_rpm(entry):
|
|
||||||
rpm = int(entry.get_text())
|
|
||||||
ec_commands.pwm.pwm_set_fan_rpm(self.cros_ec, rpm)
|
|
||||||
|
|
||||||
fan_set_rpm.connect(
|
|
||||||
"notify::text", lambda entry, _: handle_fan_rpm(entry)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
fan_set_rpm.set_sensitive(False)
|
|
||||||
else:
|
|
||||||
fan_mode.set_sensitive(False)
|
|
||||||
|
|
||||||
# Temperature sensors
|
|
||||||
temperatures = thermals_builder.get_object("temperatures")
|
|
||||||
temp_items = []
|
|
||||||
try:
|
|
||||||
ec_temp_sensors = ec_commands.thermal.get_temp_sensors(self.cros_ec)
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
# Generate some labels if the command is not supported
|
|
||||||
ec_temp_sensors = {}
|
|
||||||
temps = ec_commands.memmap.get_temps(self.cros_ec)
|
|
||||||
for i, temp in enumerate(temps):
|
|
||||||
ec_temp_sensors[f"Sensor {i}"] = (temp, None)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
for key, value in ec_temp_sensors.items():
|
|
||||||
new_row = Adw.ActionRow(title=key, subtitle=f"{value[0]}°C")
|
|
||||||
new_row.add_css_class("property")
|
|
||||||
temperatures.append(new_row)
|
|
||||||
temp_items.append(new_row)
|
|
||||||
|
|
||||||
self._update_thermals(fan_rpm, temp_items, fan_set_rpm)
|
|
||||||
|
|
||||||
# Schedule _update_thermals to run every second
|
|
||||||
GLib.timeout_add_seconds(
|
|
||||||
1, self._update_thermals, fan_rpm, temp_items, fan_set_rpm
|
|
||||||
)
|
|
||||||
|
|
||||||
def _leds_page(self, builder):
|
|
||||||
# Load the leds.ui file
|
|
||||||
leds_builder = Gtk.Builder()
|
|
||||||
leds_builder.add_from_file(os.path.join(self.script_dir, "ui/leds.ui"))
|
|
||||||
|
|
||||||
# Get the root widget from the leds.ui file
|
|
||||||
leds_root = leds_builder.get_object("leds-root")
|
|
||||||
|
|
||||||
self._change_page(builder, leds_root)
|
|
||||||
|
|
||||||
# Power LED
|
|
||||||
led_pwr = leds_builder.get_object("led-pwr")
|
|
||||||
led_pwr_scale = leds_builder.get_object("led-pwr-scale")
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
def handle_led_pwr(scale):
|
|
||||||
value = int(abs(scale.get_value() - 2))
|
|
||||||
ec_commands.framework_laptop.set_fp_led_level(self.cros_ec, value)
|
|
||||||
led_pwr.set_subtitle(["High", "Medium", "Low"][value])
|
|
||||||
|
|
||||||
current_fp_level = ec_commands.framework_laptop.get_fp_led_level(
|
|
||||||
self.cros_ec
|
|
||||||
).value
|
|
||||||
led_pwr_scale.set_value(abs(current_fp_level - 2))
|
|
||||||
led_pwr.set_subtitle(["High", "Medium", "Low"][current_fp_level])
|
|
||||||
led_pwr_scale.connect("value-changed", handle_led_pwr)
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
self.no_support.append(ec_commands.framework_laptop.EC_CMD_FP_LED_LEVEL)
|
|
||||||
led_pwr.set_visible(False)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# Keyboard backlight
|
|
||||||
led_kbd = leds_builder.get_object("led-kbd")
|
|
||||||
led_kbd_scale = leds_builder.get_object("led-kbd-scale")
|
|
||||||
|
|
||||||
if ec_commands.general.get_cmd_versions(
|
|
||||||
self.cros_ec, ec_commands.pwm.EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT
|
|
||||||
):
|
|
||||||
|
|
||||||
def handle_led_kbd(scale):
|
|
||||||
value = int(scale.get_value())
|
|
||||||
ec_commands.pwm.pwm_set_keyboard_backlight(self.cros_ec, value)
|
|
||||||
led_kbd.set_subtitle(f"{value} %")
|
|
||||||
|
|
||||||
current_kb_level = ec_commands.pwm.pwm_get_keyboard_backlight(self.cros_ec)[
|
|
||||||
"percent"
|
|
||||||
]
|
|
||||||
led_kbd_scale.set_value(current_kb_level)
|
|
||||||
led_kbd.set_subtitle(f"{current_kb_level} %")
|
|
||||||
led_kbd_scale.connect("value-changed", handle_led_kbd)
|
|
||||||
else:
|
|
||||||
led_kbd.set_visible(False)
|
|
||||||
|
|
||||||
# Advanced options
|
|
||||||
if ec_commands.general.get_cmd_versions(
|
|
||||||
self.cros_ec, ec_commands.leds.EC_CMD_LED_CONTROL
|
|
||||||
):
|
|
||||||
|
|
||||||
# Advanced: Power LED
|
|
||||||
led_pwr_colour = leds_builder.get_object("led-pwr-colour")
|
|
||||||
led_pwr_colour_strings = led_pwr_colour.get_model()
|
|
||||||
|
|
||||||
all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"]
|
|
||||||
|
|
||||||
def add_colours(strings, led_id):
|
|
||||||
supported_colours = ec_commands.leds.led_control_get_max_values(
|
|
||||||
self.cros_ec, led_id
|
|
||||||
)
|
|
||||||
for i, colour in enumerate(all_colours):
|
|
||||||
if supported_colours[i]:
|
|
||||||
strings.append(colour)
|
|
||||||
|
|
||||||
add_colours(
|
|
||||||
led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle_led_colour(combobox, led_id):
|
|
||||||
colour = combobox.get_selected() - 2
|
|
||||||
match colour:
|
|
||||||
case -2: # Auto
|
|
||||||
ec_commands.leds.led_control_set_auto(self.cros_ec, led_id)
|
|
||||||
case -1: # Off
|
|
||||||
ec_commands.leds.led_control(
|
|
||||||
self.cros_ec,
|
|
||||||
led_id,
|
|
||||||
0,
|
|
||||||
[0] * ec_commands.leds.EcLedColors.EC_LED_COLOR_COUNT.value,
|
|
||||||
)
|
|
||||||
case _: # Colour
|
|
||||||
colour_idx = all_colours.index(
|
|
||||||
combobox.get_selected_item().get_string()
|
|
||||||
)
|
|
||||||
ec_commands.leds.led_control_set_color(
|
|
||||||
self.cros_ec,
|
|
||||||
led_id,
|
|
||||||
100,
|
|
||||||
ec_commands.leds.EcLedColors(colour_idx),
|
|
||||||
)
|
|
||||||
|
|
||||||
led_pwr_colour.connect(
|
|
||||||
"notify::selected",
|
|
||||||
lambda combo, _: handle_led_colour(
|
|
||||||
combo, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Advanced: Charging LED
|
|
||||||
led_charge_colour = leds_builder.get_object("led-chg-colour")
|
|
||||||
led_charge_colour_strings = led_charge_colour.get_model()
|
|
||||||
|
|
||||||
add_colours(
|
|
||||||
led_charge_colour_strings,
|
|
||||||
ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED,
|
|
||||||
)
|
|
||||||
|
|
||||||
led_charge_colour.connect(
|
|
||||||
"notify::selected",
|
|
||||||
lambda combo, _: handle_led_colour(
|
|
||||||
combo, ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
leds_builder.get_object("led-advanced").set_visible(False)
|
|
||||||
|
|
||||||
def _format_timedelta(self, 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}"
|
|
||||||
|
|
||||||
def _update_battery(self, bat_ext_stage, bat_ext_trigger_time, bat_ext_reset_time):
|
|
||||||
if ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER in self.no_support:
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
ec_extender = ec_commands.framework_laptop.get_battery_extender(
|
|
||||||
self.cros_ec
|
|
||||||
)
|
|
||||||
|
|
||||||
bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
|
|
||||||
bat_ext_trigger_time.set_subtitle(
|
|
||||||
self._format_timedelta(ec_extender["trigger_timedelta"])
|
|
||||||
)
|
|
||||||
bat_ext_reset_time.set_subtitle(
|
|
||||||
self._format_timedelta(ec_extender["reset_timedelta"])
|
|
||||||
)
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
self.no_support.append(
|
|
||||||
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
return self.current_page == 2
|
|
||||||
|
|
||||||
def _battery_page(self, builder):
|
|
||||||
# Load the battery.ui file
|
|
||||||
battery_builder = Gtk.Builder()
|
|
||||||
battery_builder.add_from_file(os.path.join(self.script_dir, "ui/battery.ui"))
|
|
||||||
|
|
||||||
# Get the root widget from the battery.ui file
|
|
||||||
battery_root = battery_builder.get_object("battery-root")
|
|
||||||
|
|
||||||
self._change_page(builder, battery_root)
|
|
||||||
|
|
||||||
# Charge limiter
|
|
||||||
chg_limit_enable = battery_builder.get_object("chg-limit-enable")
|
|
||||||
chg_limit = battery_builder.get_object("chg-limit")
|
|
||||||
chg_limit_scale = battery_builder.get_object("chg-limit-scale")
|
|
||||||
bat_limit = battery_builder.get_object("bat-limit")
|
|
||||||
bat_limit_scale = battery_builder.get_object("bat-limit-scale")
|
|
||||||
chg_limit_override = battery_builder.get_object("chg-limit-override")
|
|
||||||
chg_limit_override_btn = battery_builder.get_object("chg-limit-override-btn")
|
|
||||||
|
|
||||||
try:
|
|
||||||
ec_limit = ec_commands.framework_laptop.get_charge_limit(self.cros_ec)
|
|
||||||
ec_limit_enabled = ec_limit != (0, 0)
|
|
||||||
chg_limit_enable.set_active(ec_limit_enabled)
|
|
||||||
if ec_limit_enabled:
|
|
||||||
chg_limit_scale.set_value(ec_limit[0])
|
|
||||||
bat_limit_scale.set_value(ec_limit[1])
|
|
||||||
chg_limit.set_sensitive(True)
|
|
||||||
bat_limit.set_sensitive(True)
|
|
||||||
chg_limit_override.set_sensitive(True)
|
|
||||||
|
|
||||||
def handle_chg_limit_change(min, max):
|
|
||||||
ec_commands.framework_laptop.set_charge_limit(
|
|
||||||
self.cros_ec, int(min), int(max)
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle_chg_limit_enable(switch):
|
|
||||||
active = switch.get_active()
|
|
||||||
if active:
|
|
||||||
handle_chg_limit_change(
|
|
||||||
chg_limit_scale.get_value(), bat_limit_scale.get_value()
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
ec_commands.framework_laptop.disable_charge_limit(self.cros_ec)
|
|
||||||
|
|
||||||
chg_limit.set_sensitive(active)
|
|
||||||
bat_limit.set_sensitive(active)
|
|
||||||
chg_limit_override.set_sensitive(active)
|
|
||||||
|
|
||||||
chg_limit_enable.connect(
|
|
||||||
"notify::active", lambda switch, _: handle_chg_limit_enable(switch)
|
|
||||||
)
|
|
||||||
chg_limit_scale.connect(
|
|
||||||
"value-changed",
|
|
||||||
lambda scale: handle_chg_limit_change(
|
|
||||||
scale.get_value(), bat_limit_scale.get_value()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
bat_limit_scale.connect(
|
|
||||||
"value-changed",
|
|
||||||
lambda scale: handle_chg_limit_change(
|
|
||||||
chg_limit_scale.get_value(), scale.get_value()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
chg_limit_override_btn.connect(
|
|
||||||
"clicked",
|
|
||||||
lambda _: ec_commands.framework_laptop.override_charge_limit(
|
|
||||||
self.cros_ec
|
|
||||||
),
|
|
||||||
)
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
self.no_support.append(ec_commands.framework_laptop.EC_CMD_CHARGE_LIMIT)
|
|
||||||
chg_limit_enable.set_sensitive(False)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# Battery Extender
|
|
||||||
bat_ext_group = battery_builder.get_object("bat-ext-group")
|
|
||||||
bat_ext_enable = battery_builder.get_object("bat-ext-enable")
|
|
||||||
bat_ext_stage = battery_builder.get_object("bat-ext-stage")
|
|
||||||
bat_ext_trigger_time = battery_builder.get_object("bat-ext-trigger-time")
|
|
||||||
bat_ext_reset_time = battery_builder.get_object("bat-ext-reset-time")
|
|
||||||
bat_ext_trigger = battery_builder.get_object("bat-ext-trigger")
|
|
||||||
bat_ext_reset = battery_builder.get_object("bat-ext-reset")
|
|
||||||
|
|
||||||
try:
|
|
||||||
ec_extender = ec_commands.framework_laptop.get_battery_extender(
|
|
||||||
self.cros_ec
|
|
||||||
)
|
|
||||||
bat_ext_enable.set_active(not ec_extender["disable"])
|
|
||||||
bat_ext_stage.set_sensitive(not ec_extender["disable"])
|
|
||||||
bat_ext_trigger_time.set_sensitive(not ec_extender["disable"])
|
|
||||||
bat_ext_reset_time.set_sensitive(not ec_extender["disable"])
|
|
||||||
bat_ext_trigger.set_sensitive(not ec_extender["disable"])
|
|
||||||
bat_ext_reset.set_sensitive(not ec_extender["disable"])
|
|
||||||
|
|
||||||
bat_ext_stage.set_subtitle(str(ec_extender["current_stage"]))
|
|
||||||
bat_ext_trigger_time.set_subtitle(
|
|
||||||
self._format_timedelta(ec_extender["trigger_timedelta"])
|
|
||||||
)
|
|
||||||
bat_ext_reset_time.set_subtitle(
|
|
||||||
self._format_timedelta(ec_extender["reset_timedelta"])
|
|
||||||
)
|
|
||||||
bat_ext_trigger.set_value(ec_extender["trigger_days"])
|
|
||||||
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(
|
|
||||||
self.cros_ec,
|
|
||||||
not active,
|
|
||||||
int(bat_ext_trigger.get_value()),
|
|
||||||
int(bat_ext_reset.get_value()),
|
|
||||||
)
|
|
||||||
bat_ext_stage.set_sensitive(active)
|
|
||||||
bat_ext_trigger_time.set_sensitive(active)
|
|
||||||
bat_ext_reset_time.set_sensitive(active)
|
|
||||||
bat_ext_trigger.set_sensitive(active)
|
|
||||||
bat_ext_reset.set_sensitive(active)
|
|
||||||
|
|
||||||
bat_ext_enable.connect(
|
|
||||||
"notify::active", lambda switch, _: handle_extender_enable(switch)
|
|
||||||
)
|
|
||||||
bat_ext_trigger.connect(
|
|
||||||
"notify::value",
|
|
||||||
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
|
|
||||||
self.cros_ec,
|
|
||||||
not bat_ext_enable.get_active(),
|
|
||||||
int(scale.get_value()),
|
|
||||||
int(bat_ext_reset.get_value()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
bat_ext_reset.connect(
|
|
||||||
"notify::value",
|
|
||||||
lambda scale, _: ec_commands.framework_laptop.set_battery_extender(
|
|
||||||
self.cros_ec,
|
|
||||||
not bat_ext_enable.get_active(),
|
|
||||||
int(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:
|
|
||||||
self.no_support.append(
|
|
||||||
ec_commands.framework_laptop.EC_CMD_BATTERY_EXTENDER
|
|
||||||
)
|
|
||||||
bat_ext_group.set_visible(False)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# Schedule _update_battery to run every second
|
|
||||||
GLib.timeout_add_seconds(
|
|
||||||
1,
|
|
||||||
self._update_battery,
|
|
||||||
bat_ext_stage,
|
|
||||||
bat_ext_trigger_time,
|
|
||||||
bat_ext_reset_time,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _update_hardware(self, hardware_builder):
|
|
||||||
success = False
|
|
||||||
# Chassis
|
|
||||||
if not ec_commands.framework_laptop.EC_CMD_CHASSIS_INTRUSION in self.no_support:
|
|
||||||
hw_chassis = hardware_builder.get_object("hw-chassis")
|
|
||||||
hw_chassis_label = hardware_builder.get_object("hw-chassis-label")
|
|
||||||
try:
|
|
||||||
ec_chassis = ec_commands.framework_laptop.get_chassis_intrusion(
|
|
||||||
self.cros_ec
|
|
||||||
)
|
|
||||||
|
|
||||||
hw_chassis_label.set_label(str(ec_chassis["total_open_count"]))
|
|
||||||
|
|
||||||
ec_chassis_open = ec_commands.framework_laptop.get_chassis_open_check(
|
|
||||||
self.cros_ec
|
|
||||||
)
|
|
||||||
hw_chassis.set_subtitle(
|
|
||||||
"Currently " + ("Open" if ec_chassis_open else "Closed")
|
|
||||||
)
|
|
||||||
|
|
||||||
success = True
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
self.no_support.append(
|
|
||||||
ec_commands.framework_laptop.EC_CMD_CHASSIS_INTRUSION
|
|
||||||
)
|
|
||||||
hw_chassis.set_visible(False)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# Privacy Switches
|
|
||||||
if (
|
|
||||||
not ec_commands.framework_laptop.EC_CMD_PRIVACY_SWITCHES_CHECK_MODE
|
|
||||||
in self.no_support
|
|
||||||
):
|
|
||||||
hw_priv_cam = hardware_builder.get_object("hw-priv-cam")
|
|
||||||
hw_priv_mic = hardware_builder.get_object("hw-priv-mic")
|
|
||||||
hw_priv_cam_sw = hardware_builder.get_object("hw-priv-cam-sw")
|
|
||||||
hw_priv_mic_sw = hardware_builder.get_object("hw-priv-mic-sw")
|
|
||||||
|
|
||||||
try:
|
|
||||||
ec_privacy = ec_commands.framework_laptop.get_privacy_switches(
|
|
||||||
self.cros_ec
|
|
||||||
)
|
|
||||||
hw_priv_cam_sw.set_active(ec_privacy["camera"])
|
|
||||||
hw_priv_mic_sw.set_active(ec_privacy["microphone"])
|
|
||||||
|
|
||||||
success = True
|
|
||||||
except ec_exceptions.ECError as e:
|
|
||||||
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
|
|
||||||
self.no_support.append(
|
|
||||||
ec_commands.framework_laptop.EC_CMD_PRIVACY_SWITCHES_CHECK_MODE
|
|
||||||
)
|
|
||||||
hw_priv_cam.set_visible(False)
|
|
||||||
hw_priv_mic.set_visible(False)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
return self.current_page == 3 and success
|
|
||||||
|
|
||||||
def _hardware_page(self, builder):
|
|
||||||
# Load the hardware.ui file
|
|
||||||
hardware_builder = Gtk.Builder()
|
|
||||||
hardware_builder.add_from_file(os.path.join(self.script_dir, "ui/hardware.ui"))
|
|
||||||
|
|
||||||
# Get the root widget from the hardware.ui file
|
|
||||||
hardware_root = hardware_builder.get_object("hardware-root")
|
|
||||||
|
|
||||||
self._change_page(builder, hardware_root)
|
|
||||||
|
|
||||||
self._update_hardware(hardware_builder)
|
|
||||||
|
|
||||||
# Fingerprint Power
|
|
||||||
hw_fp_pwr = hardware_builder.get_object("hw-fp-pwr")
|
|
||||||
if ec_commands.general.get_cmd_versions(
|
|
||||||
self.cros_ec, ec_commands.framework_laptop.EC_CMD_FP_CONTROL
|
|
||||||
):
|
|
||||||
hw_fp_pwr_en = hardware_builder.get_object("hw-fp-pwr-en")
|
|
||||||
hw_fp_pwr_dis = hardware_builder.get_object("hw-fp-pwr-dis")
|
|
||||||
|
|
||||||
hw_fp_pwr_en.connect(
|
|
||||||
"clicked",
|
|
||||||
lambda _: ec_commands.framework_laptop.fp_control(self.cros_ec, True),
|
|
||||||
)
|
|
||||||
|
|
||||||
hw_fp_pwr_dis.connect(
|
|
||||||
"clicked",
|
|
||||||
lambda _: ec_commands.framework_laptop.fp_control(self.cros_ec, False),
|
|
||||||
)
|
|
||||||
hw_fp_pwr.set_visible(True)
|
|
||||||
else:
|
|
||||||
hw_fp_pwr.set_visible(False)
|
|
||||||
|
|
||||||
# Schedule _update_hardware to run every second
|
|
||||||
GLib.timeout_add_seconds(1, self._update_hardware, hardware_builder)
|
|
||||||
|
|
||||||
def _about_page(self, app_builder):
|
|
||||||
# Open About dialog
|
|
||||||
builder = Gtk.Builder()
|
|
||||||
builder.add_from_file(os.path.join(self.script_dir, "ui/about.ui"))
|
|
||||||
|
|
||||||
about = builder.get_object("about-root")
|
|
||||||
about.set_modal(True)
|
|
||||||
about.set_transient_for(self.win)
|
|
||||||
|
|
||||||
# Reset the selection in the navbar
|
|
||||||
navbar = app_builder.get_object("navbar")
|
|
||||||
about.connect(
|
|
||||||
"close-request",
|
|
||||||
lambda _: navbar.select_row(navbar.get_row_at_index(self.current_page)),
|
|
||||||
)
|
|
||||||
|
|
||||||
about.present()
|
|
||||||
|
|
||||||
def minimal_activate(self, app, callback):
|
|
||||||
builder = Gtk.Builder()
|
|
||||||
builder.add_from_file(os.path.join(self.script_dir, "ui/yafi.ui"))
|
|
||||||
|
|
||||||
self.win = builder.get_object("root")
|
|
||||||
self.win.set_application(self)
|
|
||||||
self.win.present()
|
|
||||||
|
|
||||||
callback()
|
|
||||||
|
|
||||||
def on_activate(self, app):
|
|
||||||
builder = Gtk.Builder()
|
|
||||||
builder.add_from_file(os.path.join(self.script_dir, "ui/yafi.ui"))
|
|
||||||
|
|
||||||
self.win = builder.get_object("root")
|
|
||||||
self.win.set_application(self)
|
|
||||||
|
|
||||||
self.current_page = 0
|
|
||||||
self._thermals_page(builder)
|
|
||||||
|
|
||||||
pages = (
|
|
||||||
("Thermals", self._thermals_page),
|
|
||||||
("LEDs", self._leds_page),
|
|
||||||
("Battery", self._battery_page),
|
|
||||||
("Hardware", self._hardware_page),
|
|
||||||
("About", self._about_page),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Build the navbar
|
|
||||||
navbar = builder.get_object("navbar")
|
|
||||||
for page in pages:
|
|
||||||
row = Gtk.ListBoxRow()
|
|
||||||
row.set_child(Gtk.Label(label=page[0]))
|
|
||||||
navbar.append(row)
|
|
||||||
|
|
||||||
def switch_page(page):
|
|
||||||
# About page is a special case
|
|
||||||
if page != len(pages) - 1:
|
|
||||||
self.current_page = page
|
|
||||||
pages[page][1](builder)
|
|
||||||
|
|
||||||
navbar.connect("row-activated", lambda box, row: switch_page(row.get_index()))
|
|
||||||
|
|
||||||
self.win.present()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
app = YAFI(application_id="au.stevetech.yafi")
|
|
||||||
app.run(sys.argv)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||