From 2047e54ebc2f9ab5a112da1af4843f77296015e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 6 Jul 2024 03:22:04 +0200 Subject: [PATCH 01/35] Add windows installation #30 --- install.bat | 476 ++++++++++++++++++ install.sh | 2 +- post-install.sh | 2 +- pre-uninstall.sh | 2 +- services/{ => linux}/fw-fanctrl.service | 0 .../system-sleep/fw-fanctrl-suspend | 0 services/windows/fw-fanctrl.bat | 3 + services/windows/run-service.bat | 8 + uninstall.bat | 3 + 9 files changed, 493 insertions(+), 3 deletions(-) create mode 100644 install.bat rename services/{ => linux}/fw-fanctrl.service (100%) rename services/{ => linux}/system-sleep/fw-fanctrl-suspend (100%) create mode 100644 services/windows/fw-fanctrl.bat create mode 100644 services/windows/run-service.bat create mode 100644 uninstall.bat diff --git a/install.bat b/install.bat new file mode 100644 index 0000000..4fa1950 --- /dev/null +++ b/install.bat @@ -0,0 +1,476 @@ +@echo off + +setlocal + +for /F "delims=#" %%E in ('"prompt #$E# & for %%E in (1) do rem"') do set "ESC=%%E" + +:: Check if the script is running with administrative privileges +net session > nul 2>&1 +:: If not, relaunch the script with elevated privileges +if %errorLevel% neq 0 ( + echo requesting administrative privileges... + :: Check if there are arguments + if "%~1"=="" ( + PowerShell -Command "Start-Process '%~f0' -Verb runAs" + ) else ( + PowerShell -Command "Start-Process '%~f0' -ArgumentList '%*' -Verb runAs" + ) + exit /b +) + +echo running with administrative privileges... + +set "ARG_remove=" +set "ARG_r=" + +CALL :ARG-PARSER %* + +if defined ARG_r ( + if not defined ARG_remove set "ARG_remove=%ARG_r%" +) + +cd /d "%~dp0" + +if not defined ARG_remove ( + GOTO :ACKNOLEDGEMENT-CHECK +) + +if defined ARG_remove ( + echo: + echo ---------- + CALL :UNINSTALL +) + +GOTO :EOF + +:ACKNOLEDGEMENT-CHECK + set "acknoledgementPhrase=this is dangerous and I know what I am doing" + echo %ESC%[91m + echo ====================================== WARNING ====================================== + echo 1. THIS WINDOWS VERSION REQUIRES THE USE OF AN UNSIGNED DRIVER 'crosec' TO WORK. + echo 2. SECURE BOOT NEED TO BE DISABLED IN ORDER TO USE THE PROGRAMME + echo 3. IF YOU HAVE BITLOCKER ENABLED, YOU WILL NEED YOUR RECOVERY CODE ON BOOT !!!! + echo ====================================== WARNING ====================================== + echo PLEASE, BACK UP YOUR BITLOCKER RECOVERY KEY BEFORE DOING ANYTHING ! + echo YOU CAN BE LOCKED OUT OF YOUR COMPUTER IF YOU ARE NOT CAUTIOUS ENOUGH ! + echo ONLY CONTINUE THE INSTALLATION IF YOU ARE ABSOLUTELY SURE ABOUT WHAT YOU ARE DOING ! + echo ------------------------------------------------------------------------------------- + echo %ESC%[0m + echo to continue the installation, type '%acknoledgementPhrase%'. + echo to stop here, simply press enter. + set "acknoledgement=" + set /p "acknoledgement=> " + if not defined acknoledgement ( + echo goodbye. + exit /b 2 + ) + if not "%acknoledgement%" equ "%acknoledgementPhrase%" ( + echo wrong acknoledgement phrase [%acknoledgement%] not equal to [%acknoledgementPhrase%], stopping here! + exit /b 2 + ) + echo: + echo ---------- + CALL :INSTALL + GOTO :EOF + +:INSTALL + CALL :UNINSTALL + + "%localAppData%\Programs\Python\Python312\python" --version + if %errorLevel% neq 0 ( + echo python 3.12.x is required to use 'fw-fanctrl' + echo please install it from the official website https://www.python.org/ before running this script + GOTO :EOF + ) + + echo installing + + set "addedEnvironmentPaths=" + + echo: + echo ---------- + CALL :install-crosec + if %errorLevel% neq 0 ( + echo failed to install 'crosec' + GOTO UNINSTALL + ) + + echo: + echo ---------- + CALL :install-ectool + if %errorLevel% neq 0 ( + echo failed to install 'ectool' + GOTO UNINSTALL + ) + + echo: + echo ---------- + CALL :install-nssm + if %errorLevel% neq 0 ( + echo failed to install 'nssm' + GOTO UNINSTALL + ) + + echo: + echo ---------- + CALL :install-fw-fanctrl + if %errorLevel% neq 0 ( + echo failed to install 'fw-fanctrl' + GOTO UNINSTALL + ) + + if defined addedEnvironmentPaths ( + echo adding '%addedEnvironmentPaths%' to path + @echo on + powershell -Command "[System.Environment]::SetEnvironmentVariable('Path', $env:Path+'%addedEnvironmentPaths%', [System.EnvironmentVariableTarget]::Machine)" + powershell -Command "[System.Environment]::SetEnvironmentVariable('Path', [System.Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::Machine) + ';' + [System.Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::User), [System.EnvironmentVariableTarget]::Process)" + @echo off + ) + + echo starting 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" start "fw-fanctrl" + "%ProgramFiles%\nssm\nssm" continue "fw-fanctrl" + @echo off + if %errorLevel% neq 0 ( + echo failed to start 'fw-fanctrl' service + ) + + rmdir /s /q ".temp" 2> nul + + GOTO :EOF + + :install-crosec + echo setting up 'crosec' + rmdir /s /q ".temp" 2> nul + mkdir ".temp" + + echo enabling 'bcdedit testsigning' + bcdedit /set {default} testsigning on + + echo downloading 'crosec.zip' + @echo on + curl -s -o ".temp\crosec.zip" -L "https://github.com/DHowett/FrameworkWindowsUtils/releases/download/v0.0.2/CrosEC-0.0.2-4ac038b.zip" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to download 'crosec.zip' + exit /b 1 + ) + + echo extracting 'crosec.zip' + @echo on + tar -xf ".temp\crosec.zip" --strip-components=1 -C ".temp" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to extract 'crosec.zip' + exit /b 2 + ) + + echo installing 'crosec' driver + @echo on + ".temp\installer" install + @echo off + ::if %errorLevel% neq 0 ( + :: echo failed to run the 'crosec' driver installation + :: exit /b 3 + ::) + + echo testing 'crosec' driver + @echo on + ".temp\fauxectool" > ".temp\test-result.txt" + @echo off + + set count=0 + for %%i in (".temp\test-result.txt") do @set count=%%~zi + ::if "%count%" == "0" ( + :: echo 'crosec' driver not installed correctly + :: exit /b 4 + ::) + + rmdir /s /q "%ProgramFiles%\crosec" 2> nul + echo copying '.temp' to '%ProgramFiles%\crosec' + @echo on + xcopy /e /i ".temp" "%ProgramFiles%\crosec" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to copy '.temp' to '%ProgramFiles%\ectool' + exit /b 5 + ) + + GOTO :EOF + + :install-ectool + echo setting up 'ectool' + rmdir /s /q ".temp" 2> nul + mkdir ".temp" + + echo downloading 'artifact.zip' + @echo on + curl -s -o ".temp\artifact.zip" -L "https://gitlab.howett.net/DHowett/ectool/-/jobs/904/artifacts/download?file_type=archive" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to download 'artifact.zip' + exit /b 1 + ) + + echo extracting 'artifact.zip' + @echo on + tar -xf ".temp\artifact.zip" --strip-components=3 -C ".temp" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to extract 'artifact.zip' + exit /b 2 + ) + + rmdir /s /q "%ProgramFiles%\ectool" 2> nul + echo creating directory '%ProgramFiles%\ectool' + @echo on + mkdir "%ProgramFiles%\ectool" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to create directory '%ProgramFiles%\ectool' + exit /b 3 + ) + + @echo %PATH% | findstr /I /C:"%ProgramFiles%\ectool" >nul + if %errorLevel% neq 0 ( + set "addedEnvironmentPaths=%addedEnvironmentPaths%;%ProgramFiles%\ectool" + ) + + echo installing 'ectool.exe' to '%ProgramFiles%\ectool' + @echo on + copy /v ".temp\ectool.exe" "%ProgramFiles%\ectool" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install 'ectool.exe' + exit /b 3 + ) + + GOTO :EOF + + :install-nssm + echo setting up 'nssm' + rmdir /s /q ".temp" 2> nul + mkdir ".temp" + + echo downloading 'nssm.zip' + @echo on + curl -s -o ".temp\nssm.zip" -L "https://nssm.cc/release/nssm-2.24.zip" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to download 'nssm.zip' + exit /b 1 + ) + + echo extracting 'nssm.zip' + @echo on + tar -xf ".temp\nssm.zip" --strip-components=1 -C ".temp" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to extract 'nssm.zip' + exit /b 2 + ) + + echo creating directory '%ProgramFiles%\nssm' + @echo on + mkdir "%ProgramFiles%\nssm" > nul 2> nul + @echo off + + echo installing 'nssm.exe' to '%ProgramFiles%\nssm\' + @echo on + copy /v ".temp\win64\nssm.exe" "%ProgramFiles%\nssm\" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install 'nssm.exe' + exit /b 3 + ) + + GOTO :EOF + + + :install-fw-fanctrl + echo setting up 'fw-fanctrl' + rmdir /s /q "%ProgramFiles%\fw-fanctrl" 2> nul + echo creating directory '%ProgramFiles%\fw-fanctrl' + @echo on + mkdir "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to create directory '%ProgramFiles%\fw-fanctrl' + exit /b 3 + ) + + @echo %PATH% | findstr /I /C:"%ProgramFiles%\fw-fanctrl" >nul + if %errorLevel% neq 0 ( + set "addedEnvironmentPaths=%addedEnvironmentPaths%;%ProgramFiles%\fw-fanctrl" + ) + + echo installing 'fanctrl.py' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\fanctrl.py" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install 'fanctrl.py' + exit /b 3 + ) + + echo installing '.\services\windows\run-service.bat' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\services\windows\run-service.bat" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install '.\services\windows\run-service.bat' + exit /b 4 + ) + + echo installing '.\services\windows\run-service.bat' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\services\windows\run-service.bat" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install '.\services\windows\run-service.bat' + exit /b 4 + ) + + powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\run-service.bat') -replace '####CONFIG_PATH####', '%Appdata%\fw-fanctrl\config.json' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\run-service.bat'" + + echo installing '.\services\windows\fw-fanctrl.bat' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\services\windows\fw-fanctrl.bat" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install '.\services\windows\fw-fanctrl.bat' + exit /b 4 + ) + + powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\fw-fanctrl.bat') -replace '####PYTHON_PATH####', '%localAppData%\Programs\Python\Python312\python' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\fw-fanctrl.bat'" + + echo creating directory '%Appdata%\fw-fanctrl' + if not exist "%Appdata%\fw-fanctrl" mkdir "%Appdata%\fw-fanctrl" + + echo installing 'config.json' in '%Appdata%\fw-fanctrl\config.json' + if not exist "%Appdata%\fw-fanctrl\config.json" echo n | copy /-y ".\config.json" "%Appdata%\fw-fanctrl\config.json" > nul + + echo creating 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" install "fw-fanctrl" "%ProgramFiles%\fw-fanctrl\run-service.bat" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Start "SERVICE_DELAYED_AUTO_START" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" DisplayName "Framework Fanctrl" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Description "A simple systemd service to better control Framework Laptop's fan(s)" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStdout "%ProgramFiles%\fw-fanctrl\out.log" + ::"%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\error.log" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" + @echo off + + GOTO :EOF + GOTO :EOF + +:UNINSTALL + echo uninstalling + + CALL :uninstall-fw-fanctrl + + CALL :uninstall-nssm + + CALL :uninstall-ectool + + CALL :uninstall-crosec + + rmdir /s /q ".temp" 2> nul + + GOTO :EOF + + :uninstall-fw-fanctrl + echo removing 'fw-fanctrl' + + echo stopping 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" stop "fw-fanctrl" + @echo off + + echo removing 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" remove "fw-fanctrl" confirm + @echo off + + echo removing directory '%ProgramFiles%\fw-fanctrl' + rmdir /s /q "%ProgramFiles%\fw-fanctrl" 2> nul + + GOTO :EOF + + :uninstall-nssm + echo removing 'nssm' + + echo removing directory '%ProgramFiles%\nssm' + rmdir /s /q "%ProgramFiles%\nssm" 2> nul + + :uninstall-ectool + echo removing 'ectool' + + echo setting the fan control back to normal + @echo on + "%ProgramFiles%\ectool\ectool" autofanctrl + @echo off + + echo removing directory '%ProgramFiles%\ectool' + rmdir /s /q "%ProgramFiles%\ectool" 2> nul + + GOTO :EOF + + :uninstall-crosec + echo removing 'crosec' + + echo uninstalling 'crosec' driver + @echo on + "%ProgramFiles%\crosec\installer" uninstall + @echo off + + echo removing directory '%ProgramFiles%\crosec' + rmdir /s /q "%ProgramFiles%\crosec" 2> nul + + echo disabling 'bcdedit testsigning' + bcdedit /set {default} testsigning off + + GOTO :EOF + GOTO :EOF + + +:ARG-PARSER + :: Loop until two consecutive empty args + :loopargs + IF "%~1%~2" EQU "" GOTO :EOF + + set "arg1=%~1" + set "arg2=%~2" + shift + + :: Allow either / or - + set "tst1=%arg1:-=/%" + if "%arg1%" NEQ "" ( + set "tst1=%tst1:~0,1%" + ) ELSE ( + set "tst1=" + ) + + set "tst2=%arg2:-=/%" + if "%arg2%" NEQ "" ( + set "tst2=%tst2:~0,1%" + ) ELSE ( + set "tst2=" + ) + + + :: Capture assignments (eg. /foo bar) + IF "%tst1%" EQU "/" IF "%tst2%" NEQ "/" IF "%tst2%" NEQ "" ( + set "ARG_%arg1:~1%=%arg2%" + GOTO loopargs + ) + + :: Capture flags (eg. /foo) + IF "%tst1%" EQU "/" ( + set "ARG_%arg1:~1%=1" + GOTO loopargs + ) + GOTO loopargs + GOTO :EOF + diff --git a/install.sh b/install.sh index 2ae6cfa..c7c9b46 100755 --- a/install.sh +++ b/install.sh @@ -64,7 +64,7 @@ while true; do done # -SERVICES_DIR="./services" +SERVICES_DIR="./services/linux" SERVICE_EXTENSION=".service" SERVICES="$(cd "$SERVICES_DIR" && find . -maxdepth 1 -maxdepth 1 -type f -name "*$SERVICE_EXTENSION" -exec basename {} "$SERVICE_EXTENSION" \;)" diff --git a/post-install.sh b/post-install.sh index c92e74a..d9b4c4b 100755 --- a/post-install.sh +++ b/post-install.sh @@ -42,7 +42,7 @@ while true; do done # -SERVICES_DIR="./services" +SERVICES_DIR="./services/linux" SERVICE_EXTENSION=".service" SERVICES="$(cd "$SERVICES_DIR" && find . -maxdepth 1 -maxdepth 1 -type f -name "*$SERVICE_EXTENSION" -exec basename {} "$SERVICE_EXTENSION" \;)" diff --git a/pre-uninstall.sh b/pre-uninstall.sh index 9ca3953..3047a5d 100755 --- a/pre-uninstall.sh +++ b/pre-uninstall.sh @@ -31,7 +31,7 @@ while true; do done # -SERVICES_DIR="./services" +SERVICES_DIR="./services/linux" SERVICE_EXTENSION=".service" SERVICES="$(cd "$SERVICES_DIR" && find . -maxdepth 1 -maxdepth 1 -type f -name "*$SERVICE_EXTENSION" -exec basename {} "$SERVICE_EXTENSION" \;)" diff --git a/services/fw-fanctrl.service b/services/linux/fw-fanctrl.service similarity index 100% rename from services/fw-fanctrl.service rename to services/linux/fw-fanctrl.service diff --git a/services/system-sleep/fw-fanctrl-suspend b/services/linux/system-sleep/fw-fanctrl-suspend similarity index 100% rename from services/system-sleep/fw-fanctrl-suspend rename to services/linux/system-sleep/fw-fanctrl-suspend diff --git a/services/windows/fw-fanctrl.bat b/services/windows/fw-fanctrl.bat new file mode 100644 index 0000000..bf8b72b --- /dev/null +++ b/services/windows/fw-fanctrl.bat @@ -0,0 +1,3 @@ +@setlocal + +####PYTHON_PATH#### ".\fanctrl.py" %* diff --git a/services/windows/run-service.bat b/services/windows/run-service.bat new file mode 100644 index 0000000..b95d883 --- /dev/null +++ b/services/windows/run-service.bat @@ -0,0 +1,8 @@ +@setlocal + +@cd /d "%~dp0" + +fw-fanctrl --run --config "####CONFIG_PATH####" --no-log & ectool autofanctrl + +@echo "waiting 5 seconds before retrying..." +@timeout 5 > NUL diff --git a/uninstall.bat b/uninstall.bat new file mode 100644 index 0000000..246463a --- /dev/null +++ b/uninstall.bat @@ -0,0 +1,3 @@ +cd /d "%~dp0" + +.\install.bat /r \ No newline at end of file From 0bd5b5436182c1660dc5732bd5e8b9111b7d39e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 6 Jul 2024 03:48:17 +0200 Subject: [PATCH 02/35] fix installation after test with real hardware --- install.bat | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/install.bat b/install.bat index 4fa1950..781ccf4 100644 --- a/install.bat +++ b/install.bat @@ -138,6 +138,7 @@ GOTO :EOF rmdir /s /q ".temp" 2> nul + pause GOTO :EOF :install-crosec @@ -167,13 +168,17 @@ GOTO :EOF ) echo installing 'crosec' driver + + cd /d ".temp" @echo on - ".temp\installer" install + ".\installer" install @echo off - ::if %errorLevel% neq 0 ( - :: echo failed to run the 'crosec' driver installation - :: exit /b 3 - ::) + if %errorLevel% neq 0 ( + cd /d "%~dp0" + echo failed to run the 'crosec' driver installation + exit /b 3 + ) + cd /d "%~dp0" echo testing 'crosec' driver @echo on @@ -182,10 +187,10 @@ GOTO :EOF set count=0 for %%i in (".temp\test-result.txt") do @set count=%%~zi - ::if "%count%" == "0" ( - :: echo 'crosec' driver not installed correctly - :: exit /b 4 - ::) + if "%count%" == "0" ( + echo 'crosec' driver not installed correctly + exit /b 4 + ) rmdir /s /q "%ProgramFiles%\crosec" 2> nul echo copying '.temp' to '%ProgramFiles%\crosec' @@ -358,7 +363,6 @@ GOTO :EOF "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" DisplayName "Framework Fanctrl" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Description "A simple systemd service to better control Framework Laptop's fan(s)" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStdout "%ProgramFiles%\fw-fanctrl\out.log" - ::"%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\error.log" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" @echo off @@ -378,6 +382,8 @@ GOTO :EOF rmdir /s /q ".temp" 2> nul + pause + GOTO :EOF :uninstall-fw-fanctrl From 2b5ef6b6eece194118c4a06d7c2441ed519130be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 6 Jul 2024 04:08:13 +0200 Subject: [PATCH 03/35] fixing typos --- install.bat | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/install.bat b/install.bat index 781ccf4..cc1156a 100644 --- a/install.bat +++ b/install.bat @@ -32,7 +32,7 @@ if defined ARG_r ( cd /d "%~dp0" if not defined ARG_remove ( - GOTO :ACKNOLEDGEMENT-CHECK + GOTO :ACKNOWLEDGEMENT-CHECK ) if defined ARG_remove ( @@ -43,29 +43,29 @@ if defined ARG_remove ( GOTO :EOF -:ACKNOLEDGEMENT-CHECK - set "acknoledgementPhrase=this is dangerous and I know what I am doing" +:ACKNOWLEDGEMENT-CHECK + set "acknowledgementPhrase=this is dangerous and I know what I am doing" echo %ESC%[91m echo ====================================== WARNING ====================================== - echo 1. THIS WINDOWS VERSION REQUIRES THE USE OF AN UNSIGNED DRIVER 'crosec' TO WORK. - echo 2. SECURE BOOT NEED TO BE DISABLED IN ORDER TO USE THE PROGRAMME + echo 1. THIS WINDOWS VERSION REQUIRES THE USE OF AN UNSIGNED 'crosec' DRIVER TO WORK. + echo 2. SECURE BOOT MUST BE DISABLED IN ORDER TO USE THE PROGRAM echo 3. IF YOU HAVE BITLOCKER ENABLED, YOU WILL NEED YOUR RECOVERY CODE ON BOOT !!!! echo ====================================== WARNING ====================================== - echo PLEASE, BACK UP YOUR BITLOCKER RECOVERY KEY BEFORE DOING ANYTHING ! - echo YOU CAN BE LOCKED OUT OF YOUR COMPUTER IF YOU ARE NOT CAUTIOUS ENOUGH ! - echo ONLY CONTINUE THE INSTALLATION IF YOU ARE ABSOLUTELY SURE ABOUT WHAT YOU ARE DOING ! + echo PLEASE MAKE A BACKUP OF YOUR BITLOCKER RECOVERY KEY BEFORE YOU DO ANYTHING ! + echo YOU GET LOCKED OUT OF YOUR COMPUTER IF YOU ARE NOT CAREFUL ENOUGH ! + echo PROCEED WITH THE INSTALLATION IF YOU ARE ABSOLUTELY SURE OF WHAT YOU ARE DOING ! echo ------------------------------------------------------------------------------------- echo %ESC%[0m - echo to continue the installation, type '%acknoledgementPhrase%'. + echo to continue the installation, type '%acknowledgementPhrase%'. echo to stop here, simply press enter. - set "acknoledgement=" - set /p "acknoledgement=> " - if not defined acknoledgement ( + set "acknowledgement=" + set /p "acknowledgement=> " + if not defined acknowledgement ( echo goodbye. exit /b 2 ) - if not "%acknoledgement%" equ "%acknoledgementPhrase%" ( - echo wrong acknoledgement phrase [%acknoledgement%] not equal to [%acknoledgementPhrase%], stopping here! + if not "%acknowledgement%" equ "%acknowledgementPhrase%" ( + echo wrong acknowledgement phrase [%acknowledgement%] not equal to [%acknowledgementPhrase%], stopping here! exit /b 2 ) echo: From cab888bc9e798408dae1538391f886d0e40e276d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sun, 4 Aug 2024 03:33:09 +0200 Subject: [PATCH 04/35] working windows version! --- README.md | 2 +- fanctrl.py | 100 ++++++++++++++++++++++++++++++- services/windows/fw-fanctrl.bat | 2 +- services/windows/run-service.bat | 2 +- 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b7a4bf7..a2f360c 100644 --- a/README.md +++ b/README.md @@ -94,4 +94,4 @@ The strategy active by default is the one specified in the `defaultStrategy` ent | --pause | configure | temporarily disable the service and reset the fans to their default behaviour | | --resume | configure | resume the service | | --hardware-controller, --hc | run | select the hardware controller. choices: ectool | -| --socket-controller, --sc | run & configure | select the socket controller. choices: unix | +| --socket-controller, --sc | run & configure | select the socket controller. choices: unix, win32 | diff --git a/fanctrl.py b/fanctrl.py index 9c75bbd..890c3db 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -15,6 +15,7 @@ DEFAULT_CONFIGURATION_FILE_PATH = "/etc/fw-fanctrl/config.json" SOCKETS_FOLDER_PATH = "/run/fw-fanctrl" COMMANDS_SOCKET_FILE_PATH = os.path.join(SOCKETS_FOLDER_PATH, ".fw-fanctrl.commands.sock") +WINDOWS_SOCKET_PATH = r"\\.\pipe\fw-fanctrl.socket" parser = None @@ -170,6 +171,101 @@ def sendViaClientSocket(self, command): client_socket.close() +class WindowsSocketController(SocketController, ABC): + import ctypes + from ctypes import wintypes, windll + + _ctypes = ctypes + + CreateNamedPipe = windll.kernel32.CreateNamedPipeW + ConnectNamedPipe = windll.kernel32.ConnectNamedPipe + DisconnectNamedPipe = windll.kernel32.DisconnectNamedPipe + CreateFile = windll.kernel32.CreateFileW + ReadFile = windll.kernel32.ReadFile + WriteFile = windll.kernel32.WriteFile + CloseHandle = windll.kernel32.CloseHandle + + LPSECURITY_ATTRIBUTES = ctypes.c_void_p + LPDWORD = ctypes.POINTER(wintypes.DWORD) + LPVOID = ctypes.c_void_p + DWORD = wintypes.DWORD + + server_socket = None + + def startServerSocket(self, commandCallback=None): + if self.server_socket: + raise SocketAlreadyRunningException(self.server_socket) + self.server_socket = self.CreateNamedPipe( + WINDOWS_SOCKET_PATH, + self.ctypes.wintypes.DWORD(3), # PIPE_ACCESS_DUPLEX + self.ctypes.wintypes.DWORD(4), # PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT + 1, # nMaxInstances + 65536, # nOutBufferSize + 65536, # nInBufferSize + 0, # nDefaultTimeOut + None # lpSecurityAttributes + ) + try: + while True: + self.ConnectNamedPipe(self.server_socket, None) + try: + # Receive data from the client + buffer = self.ctypes.create_string_buffer(65536) + bytes_read = self.ctypes.wintypes.DWORD(0) + self.ReadFile(self.server_socket, buffer, 65536, self.ctypes.byref(bytes_read), None) + data = buffer.raw[:bytes_read.value].decode('utf-8') + + args = parser.parse_args(shlex.split(data)) + commandReturn = commandCallback(args) + if not commandReturn: + commandReturn = "Success!" + + commandReturn = commandReturn.encode('utf-8') + bytes_written = self.ctypes.wintypes.DWORD(0) + self.WriteFile(self.server_socket, commandReturn, len(commandReturn), + self.ctypes.byref(bytes_written), None) + except Exception as e: + message = f"[Error] > An error occurred: {e}".encode('utf-8') + bytes_written = self.wintypes.DWORD(0) + self.WriteFile(self.server_socket, message, len(message), self.ctypes.byref(bytes_written), None) + finally: + self.DisconnectNamedPipe(self.server_socket) + finally: + self.stopServerSocket() + + def stopServerSocket(self): + if self.server_socket: + self.CloseHandle(self.server_socket) + self.server_socket = None + + def isServerSocketRunning(self): + return self.server_socket is not None + + def sendViaClientSocket(self, command): + client_socket = self.CreateFile( + WINDOWS_SOCKET_PATH, + self.ctypes.wintypes.DWORD(0x00000003), # GENERIC_READ | GENERIC_WRITE + 0, + None, + self.ctypes.wintypes.DWORD(3), # OPEN_EXISTING + 0, + None + ) + try: + message = command.encode('utf-8') + bytes_written = self.ctypes.wintypes.DWORD(0) + self.WriteFile(client_socket, message, len(message), self.ctypes.byref(bytes_written), None) + buffer = self.ctypes.create_string_buffer(65536) + bytes_read = self.ctypes.wintypes.DWORD(0) + self.ReadFile(client_socket, buffer, 65536, self.ctypes.byref(bytes_read), None) + data = buffer.raw[:bytes_read.value].decode('utf-8') + if data.startswith("[Error] > "): + raise Exception(data) + return data + finally: + self.CloseHandle(client_socket) + + class HardwareController(ABC): @abstractmethod def getTemperature(self): @@ -405,13 +501,15 @@ def main(): commandGroup.add_argument("--hardware-controller", "--hc", help="Select the hardware controller", type=str, choices=["ectool"], default="ectool") commandGroup.add_argument("--socket-controller", "--sc", help="Select the socket controller", type=str, - choices=["unix"], default="unix") + choices=["unix", "win32"], default="unix") args = parser.parse_args() socketController = None if args.socket_controller == "unix": socketController = UnixSocketController() + elif args.socket_controller == "win32": + socketController = WindowsSocketController() if args.run: hardwareController = None diff --git a/services/windows/fw-fanctrl.bat b/services/windows/fw-fanctrl.bat index bf8b72b..57c4910 100644 --- a/services/windows/fw-fanctrl.bat +++ b/services/windows/fw-fanctrl.bat @@ -1,3 +1,3 @@ @setlocal -####PYTHON_PATH#### ".\fanctrl.py" %* +####PYTHON_PATH#### "%ProgramFiles%\fw-fanctrl\fanctrl.py" --socket-controller win32 %* diff --git a/services/windows/run-service.bat b/services/windows/run-service.bat index b95d883..c3b083a 100644 --- a/services/windows/run-service.bat +++ b/services/windows/run-service.bat @@ -2,7 +2,7 @@ @cd /d "%~dp0" -fw-fanctrl --run --config "####CONFIG_PATH####" --no-log & ectool autofanctrl +fw-fanctrl --run --config "####CONFIG_PATH####" --no-log --socket-controller win32 & ectool autofanctrl @echo "waiting 5 seconds before retrying..." @timeout 5 > NUL From a774785855517f8e25e6d871ffed6545fbc554ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:01:32 +0200 Subject: [PATCH 05/35] README.md documentation update --- README.md | 206 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 159 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index a2f360c..e83479f 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,203 @@ # fw-fanctrl -This is a simple Python service for Linux that drives Framework Laptop's fan(s) speed according to a configurable speed/temp curve. -Its default configuration targets very silent fan operation, but it's easy to configure it for a different comfort/performance trade-off. -Its possible to specify two separate fan curves depending on whether the Laptop is charging/discharging. -Under the hood, it uses [ectool](https://gitlab.howett.net/DHowett/ectool) to change parameters in Framework's embedded controller (EC). +![Static Badge](https://img.shields.io/badge/Linux%2FGlobal-FCC624?style=flat&logo=linux&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fmain) +![Static Badge](https://img.shields.io/badge/no%20binary%20blobs-30363D?style=flat&logo=GitHub-Sponsors&logoColor=4dff61) -It is compatible with all kinds of 13" and 16" models, both AMD/Intel CPUs, with or without a discrete GPU. +![Static Badge](https://img.shields.io/badge/Python__3.12-FFDE57?style=flat&label=Requirement&link=https%3A%2F%2Fwww.python.org%2Fdownloads) + +## Additional platforms: + +![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix) + +## Description + +Fw-fanctrl is a simple Python CLI service that controls Framework Laptop's fan(s) +speed according to a configurable speed/temperature curve. + +Its default strategy aims for very quiet fan operation, but you can choose amongst the other provided strategies, or +easily configure your own for a different comfort/performance trade-off. + +It also is possible to assign separate strategies depending on whether the laptop is charging or discharging. + +Under the hood, it uses [ectool](https://gitlab.howett.net/DHowett/ectool) +to change parameters in Framework's embedded controller (EC). + +It is compatible with all 13" and 16" models, both AMD/Intel CPUs, with or without a discrete GPU. If the service is paused or stopped, the fans will revert to their default behaviour. -# Install +## Installation + +### Requirements + +| name | version | url | +|--------|---------|----------------------------------------------------------------------| +| Python | 3.12.x | [https://www.python.org/downloads](https://www.python.org/downloads) | -## Dependencies +### Dependencies -To communicate with the embedded controller the `ectool` is required. -You can either let the script download it from the [gitlab repository](https://gitlab.howett.net/DHowett/ectool) artifacts, -or disable its installation (`--no-ectool`) and install your own. +Dependencies are downloaded and installed automatically, but can be excluded from the installation script if you wish to +do this manually. -You also need to disable secure boot of your device for `ectool` to work (more details about why [here](https://www.howett.net/posts/2021-12-framework-ec/#using-fw-ectool)) +| name | version | url | exclusion argument | +|----------------|-----------|--------------------------------------------------------------------------------------|--------------------| +| DHowett@ectool | build#899 | [https://gitlab.howett.net/DHowett/ectool](https://gitlab.howett.net/DHowett/ectool) | `--no-ectool` | + +### Instructions + +First, make sure that you have disabled secure boot in your BIOS/UEFI settings. +(more details on why [here](https://www.howett.net/posts/2021-12-framework-ec/#using-fw-ectool)) + +[Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip) and extract it manually, or +download/clone it with the appropriate tools: + +```shell +git clone "https://github.com/TamtamHero/fw-fanctrl.git" +``` + +```shell +curl -L "https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip" -o "./fw-fanctrl.zip" && unzip "./fw-fanctrl.zip" -d "./fw-fanctrl" && rm -rf "./fw-fanctrl.zip" +``` + +Then run the installation script with administrator privileges -Then run: ```bash sudo ./install.sh ``` -This bash script will to create and activate a service that runs this repo's main script, `fanctrl.py`. -It will copy `fanctrl.py` (to an executable file `fw-fanctrl`), download the ectool to `[dest-dir(/)]/bin` and create a config file -in `[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` +You can add a number of arguments to the installation command to suit your needs + +| argument | description | +|---------------------------------------------------------------------------------|----------------------------------------------------| +| `--dest-dir ` | specify an installation destination directory | +| `--prefix-dir ` | specify an installation prefix directory | +| `--sysconf-dir ` | specify a default configuration directory | +| `--no-ectool` | disable ectool installation and service activation | +| `--no-post-install` | disable post-install process | +| `--no-pre-uninstall` | disable pre-uninstall process | -this script also includes options to: -- specify an installation destination directory (`--dest-dir `). -- specify an installation prefix directory (`--prefix-dir `). -- specify a default configuration directory (`--sysconf-dir `). -- disable ectool installation and service activation (`--no-ectool`) -- disable post-install process (`--no-post-install`) -- disable pre-uninstall process (`--no-pre-uninstall`) +## Update -# Update +To update, you can download or pull the appropriate branch from this repository, and run the installation script again. -To install an update, you can pull the latest commit on the `main` branch of this repository, and run the install script again. +## Uninstall + +To uninstall, run the installation script with the `--remove` argument, as well as other +corresponding [arguments if necessary](#instructions) -# Uninstall ```bash sudo ./install.sh --remove ``` -# Configuration +## Configuration -There is a single `config.json` file located at `[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json`. +After installation, you will find the configuration file in the following location: -(You will need to reload the configuration with) -```bash -fw-fanctrl --reload +`/etc/fw-fanctrl/config.json` + +If you have modified the `dest-dir` or `sysconf-dir`, here is the corresponding pattern + +`[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` + +It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging +strategies. + +For example, one could use a lower fan speed strategy on discharging to optimise battery life (- noise, + heat), +and a high fan speed strategy on AC (+ noise, - heat). + +You can add or edit strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this +repo :) + +### Default strategy + +The default strategy is the one used when the service is started. + +It can be changed by replacing the value of the `defaultStrategy` field with one of the strategies present in the +configuration. + +```json +"defaultStrategy": "[STRATEGY NAME]" ``` -It contains different strategies, ranked from the most silent to the noisiest. It is possible to specify two different strategies for charging/discharging allowing for different optimization goals. -On discharging one could have fan curve optimized for low fan speeds in order to save power while accepting a bit more heat. -On charging one could have a fan curve that focuses on keeping the CPU from throttling and the system cool, at the expense of fan noise. -You can add new strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this repo :) +### Charging/Discharging strategies -Strategies can be configured with the following parameters: +The discharging strategy is the one that will be used when the laptop is not on AC, +Otherwise the default strategy is used. -- **SpeedCurve**: +It can be changed by replacing the value of the `strategyOnDischarging` field with one of the strategies present in the +configuration. - This is the curve points for `f(temperature) = fan speed` +```json +"strategyOnDischarging": "[STRATEGY NAME]" +``` - `fw-fanctrl` measures the CPU temperature, compute a moving average of it, and then find an appropriate `fan speed` value by interpolation on the curve. +This is optional and can be left empty to have the same strategy at all times. -- **FanSpeedUpdateFrequency**: +### Editing strategies - Time interval between every update to the fan's speed. `fw-fanctrl` measures temperature every second and add it to its moving average, but the actual update to fan speed is controlled using this configuration. This is for comfort, otherwise the speed is changed too often and it is noticeable and annoying, especially at low speed. - For a more reactive fan, you can lower this setting. **Defaults to 5 seconds.** +Strategies can be configured with the following parameters: -- **MovingAverageInterval**: +> **SpeedCurve**: +> +> It is represented by the curve points for `f(temperature) = fan(s) speed`. +> +> ```json +> "speedCurve": [ +> { "temp": [TEMPERATURE POINT], "speed": [PERCENTAGE SPEED] }, +> ... +> ] +> ``` +> +> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an appropriate `fan speed` +> value by interpolating on the curve. + +> **FanSpeedUpdateFrequency**: +> +> It is the interval in seconds between fan speed calculations. +> +> ```json +> "fanSpeedUpdateFrequency": [UPDATE FREQUENCY] +> ``` +> +> This is for comfort, otherwise the speed will change too often, which is noticeable and annoying, especially at low +> speed. +> +> For a more responsive fan, you can reduce this setting. +> +> **Defaults to 5 seconds.** (minimum 1) + +> **MovingAverageInterval**: +> +> It is the number of seconds over which the moving average of temperature is calculated. +> +> ```json +> "movingAverageInterval": [AVERAGING INTERVAL] +> ``` +> +> Increase it, and the fan speed changes more gradually. Lower it, and it becomes more responsive. +> +> **Defaults to 20 seconds.** (minimum 1) + +--- + +Once the configuration has been changed, you must reload it with the following command - Number of seconds on which the moving average of temperature is computed. Increase it, and the fan speed will change more gradually. Lower it, and it will gain in reactivity. **Defaults to 20 seconds.** +```bash +fw-fanctrl --reload +``` -## Charging/Discharging strategies +## Commands -The strategy active by default is the one specified in the `defaultStrategy` entry. Optionally a separate strategy only active during discharge can be defined, using the `strategyOnDischarging` entry. By default no extra strategy for discharging is provided, the default strategy is active during all times. +Here is a list of commands used to interact with the service. -# Commands +The commands in the `run` context are used launch the service manually. +If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will +never need them. | Option | Context | Description | |-----------------------------|-----------------|-------------------------------------------------------------------------------| | \ | run & configure | the name of the strategy to use | -| --run | run | run the service | +| --run | run | run the service manually | | --config | run | specify the configuration path | | --no-log | run | disable state logging | | --query, -q | configure | print the current strategy name | From 0616bf89b51826c57b9a8a65301b7f4f9feb6af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:05:31 +0200 Subject: [PATCH 06/35] change log format on fatal crash --- fanctrl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fanctrl.py b/fanctrl.py index 71d0c87..4e83edc 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -456,8 +456,10 @@ def run(self, debug=True): else: sleep(5) except InvalidStrategyException as e: - print("Error: missing strategy, exiting for safety reasons: " + e.args[0]) - exit(1) + print(f"Missing strategy, exiting for safety reasons: {e.args[0]}") + except Exception as e: + print(f"Critical error, exiting for safety reasons: {e}") + exit(1) def main(): From 46e19cb682a1f4ea7be511d31244a42f6fdf6582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:11:18 +0200 Subject: [PATCH 07/35] fix badges links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e83479f..a18f37c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # fw-fanctrl -![Static Badge](https://img.shields.io/badge/Linux%2FGlobal-FCC624?style=flat&logo=linux&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fmain) +[![Static Badge](https://img.shields.io/badge/Linux%2FGlobal-FCC624?style=flat&logo=linux&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fmain)](https://github.com/TamtamHero/fw-fanctrl/tree/main) ![Static Badge](https://img.shields.io/badge/no%20binary%20blobs-30363D?style=flat&logo=GitHub-Sponsors&logoColor=4dff61) -![Static Badge](https://img.shields.io/badge/Python__3.12-FFDE57?style=flat&label=Requirement&link=https%3A%2F%2Fwww.python.org%2Fdownloads) +[![Static Badge](https://img.shields.io/badge/Python__3.12-FFDE57?style=flat&label=Requirement&link=https%3A%2F%2Fwww.python.org%2Fdownloads)](https://www.python.org/downloads) ## Additional platforms: -![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix) +[![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix)](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) ## Description From 0dc997cff2bdd39272976b0dc75e3a241d3c4a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:36:25 +0200 Subject: [PATCH 08/35] README.md update with windows documentation --- README.md | 66 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index a18f37c..ee89d28 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # fw-fanctrl -[![Static Badge](https://img.shields.io/badge/Linux%2FGlobal-FCC624?style=flat&logo=linux&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fmain)](https://github.com/TamtamHero/fw-fanctrl/tree/main) -![Static Badge](https://img.shields.io/badge/no%20binary%20blobs-30363D?style=flat&logo=GitHub-Sponsors&logoColor=4dff61) +[![Static Badge](https://img.shields.io/badge/Windows-0078D6?style=flat&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fwindows)](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/windows) [![Static Badge](https://img.shields.io/badge/Python__3.12-FFDE57?style=flat&label=Requirement&link=https%3A%2F%2Fwww.python.org%2Fdownloads)](https://www.python.org/downloads) ## Additional platforms: +[![Static Badge](https://img.shields.io/badge/Linux%2FGlobal-FCC624?style=flat&logo=linux&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fmain)](https://github.com/TamtamHero/fw-fanctrl/tree/main) + [![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix)](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) ## Description @@ -36,68 +37,64 @@ If the service is paused or stopped, the fans will revert to their default behav ### Dependencies -Dependencies are downloaded and installed automatically, but can be excluded from the installation script if you wish to -do this manually. +Dependencies are downloaded and installed automatically. -| name | version | url | exclusion argument | -|----------------|-----------|--------------------------------------------------------------------------------------|--------------------| -| DHowett@ectool | build#899 | [https://gitlab.howett.net/DHowett/ectool](https://gitlab.howett.net/DHowett/ectool) | `--no-ectool` | +| name | version | url | +|----------------|--------------|------------------------------------------------------------------------------------------------------| +| DHowett@crosec | v0.0.2 | [https://github.com/DHowett/FrameworkWindowsUtils](https://github.com/DHowett/FrameworkWindowsUtils) | +| DHowett@ectool | artifact#904 | [https://gitlab.howett.net/DHowett/ectool](https://gitlab.howett.net/DHowett/ectool) | +| nssm | 2.24 | [https://nssm.cc](https://nssm.cc) | ### Instructions +Please note that the windows version of this service uses an unsigned experimental [crosec](https://github.com/DHowett/FrameworkWindowsUtils) driver that may be unstable. +We are not responsible for any damage or data loss that this may cause. + First, make sure that you have disabled secure boot in your BIOS/UEFI settings. (more details on why [here](https://www.howett.net/posts/2021-12-framework-ec/#using-fw-ectool)) +``` +============================================================================ +IF YOU HAVE BITLOCKER ENABLED, YOU WILL NEED YOUR RECOVERY CODE ON BOOT !!!! +PLEASE MAKE A BACKUP OF YOUR BITLOCKER RECOVERY KEY BEFORE YOU DO ANYTHING ! +YOU GET LOCKED OUT OF YOUR COMPUTER IF YOU ARE NOT CAREFUL ENOUGH ! +============================================================================ +``` + [Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip) and extract it manually, or download/clone it with the appropriate tools: ```shell -git clone "https://github.com/TamtamHero/fw-fanctrl.git" +git clone --branch "packaging/windows" "https://github.com/TamtamHero/fw-fanctrl.git" ``` ```shell -curl -L "https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip" -o "./fw-fanctrl.zip" && unzip "./fw-fanctrl.zip" -d "./fw-fanctrl" && rm -rf "./fw-fanctrl.zip" +curl -L "https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/packaging/windows.zip" -o "./fw-fanctrl.zip" && unzip "./fw-fanctrl.zip" -d "./fw-fanctrl" && rm -rf "./fw-fanctrl.zip" ``` -Then run the installation script with administrator privileges +Then run the installation script with administrator privileges (by double clicking it, or with the following command) -```bash -sudo ./install.sh +```shell +install.bat ``` -You can add a number of arguments to the installation command to suit your needs - -| argument | description | -|---------------------------------------------------------------------------------|----------------------------------------------------| -| `--dest-dir ` | specify an installation destination directory | -| `--prefix-dir ` | specify an installation prefix directory | -| `--sysconf-dir ` | specify a default configuration directory | -| `--no-ectool` | disable ectool installation and service activation | -| `--no-post-install` | disable post-install process | -| `--no-pre-uninstall` | disable pre-uninstall process | - ## Update To update, you can download or pull the appropriate branch from this repository, and run the installation script again. ## Uninstall -To uninstall, run the installation script with the `--remove` argument, as well as other -corresponding [arguments if necessary](#instructions) +To uninstall, run the uninstallation script `uninstall.bat` (by double clicking it, or with the following command) -```bash -sudo ./install.sh --remove +```shell +install.bat ``` ## Configuration After installation, you will find the configuration file in the following location: -`/etc/fw-fanctrl/config.json` - -If you have modified the `dest-dir` or `sysconf-dir`, here is the corresponding pattern - -`[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` +`%Appdata%\fw-fanctrl\config.json` It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging strategies. @@ -148,7 +145,8 @@ Strategies can be configured with the following parameters: > ] > ``` > -> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an appropriate `fan speed` +> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an +> appropriate `fan speed` > value by interpolating on the curve. > **FanSpeedUpdateFrequency**: @@ -206,4 +204,4 @@ never need them. | --pause | configure | temporarily disable the service and reset the fans to their default behaviour | | --resume | configure | resume the service | | --hardware-controller, --hc | run | select the hardware controller. choices: ectool | -| --socket-controller, --sc | run & configure | select the socket controller. choices: unix, win32 | +| --socket-controller, --sc | run & configure | select the socket controller. choices: win32 | From 3fe74bb5a1b9a92dca1662b8bb0cc58fabfc0f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:39:35 +0200 Subject: [PATCH 09/35] removing linux specific files and socket controllers --- fanctrl.py | 71 +----- fetch/ectool/LICENSE | 27 --- fetch/ectool/linux/gitlab_job_id | 1 - fetch/ectool/linux/hash.sha256 | 1 - install.sh | 221 ------------------ post-install.sh | 74 ------ pre-uninstall.sh | 55 ----- services/linux/fw-fanctrl.service | 10 - .../linux/system-sleep/fw-fanctrl-suspend | 6 - 9 files changed, 2 insertions(+), 464 deletions(-) delete mode 100644 fetch/ectool/LICENSE delete mode 100644 fetch/ectool/linux/gitlab_job_id delete mode 100644 fetch/ectool/linux/hash.sha256 delete mode 100755 install.sh delete mode 100755 post-install.sh delete mode 100755 pre-uninstall.sh delete mode 100644 services/linux/fw-fanctrl.service delete mode 100644 services/linux/system-sleep/fw-fanctrl-suspend diff --git a/fanctrl.py b/fanctrl.py index 4e83edc..adbee8f 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -13,8 +13,6 @@ from abc import ABC, abstractmethod DEFAULT_CONFIGURATION_FILE_PATH = "/etc/fw-fanctrl/config.json" -SOCKETS_FOLDER_PATH = "/run/fw-fanctrl" -COMMANDS_SOCKET_FILE_PATH = os.path.join(SOCKETS_FOLDER_PATH, ".fw-fanctrl.commands.sock") WINDOWS_SOCKET_PATH = r"\\.\pipe\fw-fanctrl.socket" parser = None @@ -108,69 +106,6 @@ def sendViaClientSocket(self, command): raise UnimplementedException() -class UnixSocketController(SocketController, ABC): - server_socket = None - - def startServerSocket(self, commandCallback=None): - if self.server_socket: - raise SocketAlreadyRunningException(self.server_socket) - self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - if os.path.exists(COMMANDS_SOCKET_FILE_PATH): - os.remove(COMMANDS_SOCKET_FILE_PATH) - try: - if not os.path.exists(SOCKETS_FOLDER_PATH): - os.makedirs(SOCKETS_FOLDER_PATH) - self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.server_socket.bind(COMMANDS_SOCKET_FILE_PATH) - os.chmod(COMMANDS_SOCKET_FILE_PATH, 0o777) - self.server_socket.listen(1) - while True: - client_socket, _ = self.server_socket.accept() - try: - # Receive data from the client - data = client_socket.recv(4096).decode() - args = parser.parse_args(shlex.split(data)) - commandReturn = commandCallback(args) - if not commandReturn: - commandReturn = "Success!" - client_socket.sendall(commandReturn.encode('utf-8')) - except Exception as e: - client_socket.sendall(f"[Error] > An error occurred: {e}".encode('utf-8')) - finally: - client_socket.shutdown(socket.SHUT_WR) - client_socket.close() - finally: - self.stopServerSocket() - - def stopServerSocket(self): - if self.server_socket: - self.server_socket.close() - self.server_socket = None - - def isServerSocketRunning(self): - return self.server_socket is not None - - def sendViaClientSocket(self, command): - client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - client_socket.connect(COMMANDS_SOCKET_FILE_PATH) - client_socket.sendall(command.encode('utf-8')) - received_data = b"" - while True: - data_chunk = client_socket.recv(1024) - if not data_chunk: - break - received_data += data_chunk - # Receive data from the server - data = received_data.decode() - if data.startswith("[Error] > "): - raise Exception(data) - return data - finally: - if client_socket: - client_socket.close() - - class WindowsSocketController(SocketController, ABC): import ctypes from ctypes import wintypes, windll @@ -503,14 +438,12 @@ def main(): commandGroup.add_argument("--hardware-controller", "--hc", help="Select the hardware controller", type=str, choices=["ectool"], default="ectool") commandGroup.add_argument("--socket-controller", "--sc", help="Select the socket controller", type=str, - choices=["unix", "win32"], default="unix") + choices=["win32"], default="win32") args = parser.parse_args() socketController = None - if args.socket_controller == "unix": - socketController = UnixSocketController() - elif args.socket_controller == "win32": + if args.socket_controller == "win32": socketController = WindowsSocketController() if args.run: diff --git a/fetch/ectool/LICENSE b/fetch/ectool/LICENSE deleted file mode 100644 index 71d7f5c..0000000 --- a/fetch/ectool/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2010 The Chromium OS Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/fetch/ectool/linux/gitlab_job_id b/fetch/ectool/linux/gitlab_job_id deleted file mode 100644 index 2a0f68f..0000000 --- a/fetch/ectool/linux/gitlab_job_id +++ /dev/null @@ -1 +0,0 @@ -899 \ No newline at end of file diff --git a/fetch/ectool/linux/hash.sha256 b/fetch/ectool/linux/hash.sha256 deleted file mode 100644 index a926f8b..0000000 --- a/fetch/ectool/linux/hash.sha256 +++ /dev/null @@ -1 +0,0 @@ -ab94a1e9a33f592d5482dbfd4f42ad351ef91227ee3b3707333c0107d7f2b1b0 \ No newline at end of file diff --git a/install.sh b/install.sh deleted file mode 100755 index c7c9b46..0000000 --- a/install.sh +++ /dev/null @@ -1,221 +0,0 @@ -#!/bin/bash -set -e - -if [ "$EUID" -ne 0 ] - then echo "This program requires root permissions" - exit 1 -fi - -# Argument parsing -SHORT=r,d:,p:,s:,h -LONG=remove,dest-dir:,prefix-dir:,sysconf-dir:,no-ectool,no-pre-uninstall,no-post-install,help -VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") -if [[ $? -ne 0 ]]; then - exit 1; -fi - -TEMP_FOLDER='./.temp' -trap 'rm -rf $TEMP_FOLDER' EXIT - -PREFIX_DIR="/usr" -DEST_DIR="" -SYSCONF_DIR="/etc" -SHOULD_INSTALL_ECTOOL=true -SHOULD_PRE_UNINSTALL=true -SHOULD_POST_INSTALL=true -SHOULD_REMOVE=false - -eval set -- "$VALID_ARGS" -while true; do - case "$1" in - '--remove' | '-r') - SHOULD_REMOVE=true - ;; - '--prefix-dir' | '-p') - PREFIX_DIR=$2 - shift - ;; - '--dest-dir' | '-d') - DEST_DIR=$2 - shift - ;; - '--sysconf-dir' | '-s') - SYSCONF_DIR=$2 - shift - ;; - '--no-ectool') - SHOULD_INSTALL_ECTOOL=false - ;; - '--no-pre-uninstall') - SHOULD_PRE_UNINSTALL=false - ;; - '--no-post-install') - SHOULD_POST_INSTALL=false - ;; - '--help' | '-h') - echo "Usage: $0 [--remove,-r] [--dest-dir,-d ] [--prefix-dir,-p ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)] [--no-ectool] [--no-post-install] [--no-pre-uninstall]" 1>&2 - exit 0 - ;; - --) - break - ;; - esac - shift -done -# - -SERVICES_DIR="./services/linux" -SERVICE_EXTENSION=".service" - -SERVICES="$(cd "$SERVICES_DIR" && find . -maxdepth 1 -maxdepth 1 -type f -name "*$SERVICE_EXTENSION" -exec basename {} "$SERVICE_EXTENSION" \;)" -SERVICES_SUBCONFIGS="$(cd "$SERVICES_DIR" && find . -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)" - -function sanitizePath() { - local SANITIZED_PATH="$1" - local SANITIZED_PATH=${SANITIZED_PATH//..\//} - local SANITIZED_PATH=${SANITIZED_PATH#./} - local SANITIZED_PATH=${SANITIZED_PATH#/} - echo "$SANITIZED_PATH" -} - -# remove remaining legacy files -function uninstall_legacy() { - echo "removing legacy files" - rm "/usr/local/bin/fw-fanctrl" 2> "/dev/null" || true - rm "/usr/local/bin/ectool" 2> "/dev/null" || true - rm "/usr/local/bin/fanctrl.py" 2> "/dev/null" || true - rm "/etc/systemd/system/fw-fanctrl.service" 2> "/dev/null" || true -} - -function uninstall() { - if [ "$SHOULD_PRE_UNINSTALL" = true ]; then - ./pre-uninstall.sh - fi - # remove program services based on the services present in the './services' folder - echo "removing services" - for SERVICE in $SERVICES ; do - SERVICE=$(sanitizePath "$SERVICE") - # be EXTRA CAREFUL about the validity of the paths (dont wanna delete something important, right?... O_O) - rm -rf "$DEST_DIR$PREFIX_DIR/lib/systemd/system/$SERVICE$SERVICE_EXTENSION" - done - - # remove program services sub-configurations based on the sub-configurations present in the './services' folder - echo "removing services sub-configurations" - for SERVICE in $SERVICES_SUBCONFIGS ; do - SERVICE=$(sanitizePath "$SERVICE") - echo "removing sub-configurations for [$SERVICE]" - SUBCONFIGS="$(cd "$SERVICES_DIR/$SERVICE" && find . -mindepth 1 -type f)" - for SUBCONFIG in $SUBCONFIGS ; do - SUBCONFIG=$(sanitizePath "$SUBCONFIG") - echo "removing '$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG'" - rm -rf "$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG" 2> "/dev/null" || true - done - done - - rm "$DEST_DIR$PREFIX_DIR/bin/fw-fanctrl" 2> "/dev/null" || true - ectool autofanctrl 2> "/dev/null" || true # restore default fan manager - if [ "$SHOULD_INSTALL_ECTOOL" = true ]; then - rm "$DEST_DIR$PREFIX_DIR/bin/ectool" 2> "/dev/null" || true - fi - rm -rf "$DEST_DIR$SYSCONF_DIR/fw-fanctrl" 2> "/dev/null" || true - rm -rf "/run/fw-fanctrl" 2> "/dev/null" || true - - uninstall_legacy -} - -function install() { - uninstall_legacy - - rm -rf "$TEMP_FOLDER" - mkdir -p "$DEST_DIR$PREFIX_DIR/bin" - if [ "$SHOULD_INSTALL_ECTOOL" = true ]; then - mkdir "$TEMP_FOLDER" - installEctool "$TEMP_FOLDER" || (echo "an error occurred when installing ectool." && echo "please check your internet connection or consider installing it manually and using --no-ectool on the installation script." && exit 1) - rm -rf "$TEMP_FOLDER" - fi - mkdir -p "$DEST_DIR$SYSCONF_DIR/fw-fanctrl" - cp "./fanctrl.py" "$DEST_DIR$PREFIX_DIR/bin/fw-fanctrl" - chmod +x "$DEST_DIR$PREFIX_DIR/bin/fw-fanctrl" - - cp -n "./config.json" "$DEST_DIR$SYSCONF_DIR/fw-fanctrl" 2> "/dev/null" || true - - # create program services based on the services present in the './services' folder - echo "creating '$DEST_DIR$PREFIX_DIR/lib/systemd/system'" - mkdir -p "$DEST_DIR$PREFIX_DIR/lib/systemd/system" - echo "creating services" - for SERVICE in $SERVICES ; do - SERVICE=$(sanitizePath "$SERVICE") - if [ "$SHOULD_PRE_UNINSTALL" = true ] && [ "$(systemctl is-active "$SERVICE")" == "active" ]; then - echo "stopping [$SERVICE]" - systemctl stop "$SERVICE" - fi - echo "creating '$DEST_DIR$PREFIX_DIR/lib/systemd/system/$SERVICE$SERVICE_EXTENSION'" - cat "$SERVICES_DIR/$SERVICE$SERVICE_EXTENSION" | sed -e "s/%PREFIX_DIRECTORY%/${PREFIX_DIR//\//\\/}/" | sed -e "s/%SYSCONF_DIRECTORY%/${SYSCONF_DIR//\//\\/}/" | tee "$DEST_DIR$PREFIX_DIR/lib/systemd/system/$SERVICE$SERVICE_EXTENSION" > "/dev/null" - done - - # add program services sub-configurations based on the sub-configurations present in the './services' folder - echo "adding services sub-configurations" - for SERVICE in $SERVICES_SUBCONFIGS ; do - SERVICE=$(sanitizePath "$SERVICE") - echo "adding sub-configurations for [$SERVICE]" - SUBCONFIG_FOLDERS="$(cd "$SERVICES_DIR/$SERVICE" && find . -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)" - # ensure folders exists - mkdir -p "$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE" - for SUBCONFIG_FOLDER in $SUBCONFIG_FOLDERS ; do - SUBCONFIG_FOLDER=$(sanitizePath "$SUBCONFIG_FOLDER") - echo "creating '$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG_FOLDER'" - mkdir -p "$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG_FOLDER" - done - SUBCONFIGS="$(cd "$SERVICES_DIR/$SERVICE" && find . -mindepth 1 -type f)" - # add sub-configurations - for SUBCONFIG in $SUBCONFIGS ; do - SUBCONFIG=$(sanitizePath "$SUBCONFIG") - echo "adding '$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG'" - cat "$SERVICES_DIR/$SERVICE/$SUBCONFIG" | sed -e "s/%PREFIX_DIRECTORY%/${PREFIX_DIR//\//\\/}/" | tee "$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG" > "/dev/null" - chmod +x "$DEST_DIR$PREFIX_DIR/lib/systemd/$SERVICE/$SUBCONFIG" - done - done - if [ "$SHOULD_POST_INSTALL" = true ]; then - ./post-install.sh --dest-dir "$DEST_DIR" --sysconf-dir "$SYSCONF_DIR" - fi -} - -function installEctool() { - workingDirectory=$1 - echo "installing ectool" - - ectoolDestPath="$DEST_DIR$PREFIX_DIR/bin/ectool" - - ectoolJobId="$(cat './fetch/ectool/linux/gitlab_job_id')" - ectoolSha256Hash="$(cat './fetch/ectool/linux/hash.sha256')" - - artifactsZipFile="$workingDirectory/artifact.zip" - - echo "downloading artifact from gitlab" - curl -s -S -o "$artifactsZipFile" -L "https://gitlab.howett.net/DHowett/ectool/-/jobs/${ectoolJobId}/artifacts/download?file_type=archive" || (echo "failed to download the artifact." && return 1) - if [[ $? -ne 0 ]]; then return 1; fi - - echo "checking artifact sha256 sum" - actualEctoolSha256Hash=$(sha256sum "$artifactsZipFile" | cut -d ' ' -f 1) - if [[ "$actualEctoolSha256Hash" != "$ectoolSha256Hash" ]]; then - echo "Incorrect sha256 sum for ectool gitlab artifact '$ectoolJobId' : '$ectoolSha256Hash' != '$actualEctoolSha256Hash'" - return 1 - fi - - echo "extracting artifact" - { - unzip -q -j "$artifactsZipFile" '_build/src/ectool' -d "$workingDirectory" && - cp "$workingDirectory/ectool" "$ectoolDestPath" && - chmod +x "$ectoolDestPath" - } || (echo "failed to extract the artifact to its designated location." && return 1) - if [[ $? -ne 0 ]]; then return 1; fi - - echo "ectool installed" -} - -if [ "$SHOULD_REMOVE" = true ]; then - uninstall -else - install -fi -exit 0 diff --git a/post-install.sh b/post-install.sh deleted file mode 100755 index d9b4c4b..0000000 --- a/post-install.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -set -e - -if [ "$EUID" -ne 0 ] - then echo "This program requires root permissions" - exit 1 -fi - -HOME_DIR="$(eval echo "~$(logname)")" - -# Argument parsing -SHORT=d:,s:,h -LONG=dest-dir:,sysconf-dir:,help -VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") -if [[ $? -ne 0 ]]; then - exit 1; -fi - -DEST_DIR="/usr" -SYSCONF_DIR="/etc" - -eval set -- "$VALID_ARGS" -while true; do - case "$1" in - '--dest-dir' | '-d') - DEST_DIR=$2 - shift - ;; - '--sysconf-dir' | '-s') - SYSCONF_DIR=$2 - shift - ;; - '--help' | '-h') - echo "Usage: $0 [--dest-dir,-d ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)]" 1>&2 - exit 0 - ;; - --) - break - ;; - esac - shift -done -# - -SERVICES_DIR="./services/linux" -SERVICE_EXTENSION=".service" - -SERVICES="$(cd "$SERVICES_DIR" && find . -maxdepth 1 -maxdepth 1 -type f -name "*$SERVICE_EXTENSION" -exec basename {} "$SERVICE_EXTENSION" \;)" - -function sanitizePath() { - local SANITIZED_PATH="$1" - local SANITIZED_PATH=${SANITIZED_PATH//..\//} - local SANITIZED_PATH=${SANITIZED_PATH#./} - local SANITIZED_PATH=${SANITIZED_PATH#/} - echo "$SANITIZED_PATH" -} - -# move remaining legacy files -function move_legacy() { - echo "moving legacy files to their new destination" - (cp "$HOME_DIR/.config/fw-fanctrl"/* "$DEST_DIR$SYSCONF_DIR/fw-fanctrl/" && rm -rf "$HOME_DIR/.config/fw-fanctrl") 2> "/dev/null" || true -} - -move_legacy - -echo "enabling services" -systemctl daemon-reload -for SERVICE in $SERVICES ; do - SERVICE=$(sanitizePath "$SERVICE") - echo "enabling [$SERVICE]" - systemctl enable "$SERVICE" - echo "starting [$SERVICE]" - systemctl start "$SERVICE" -done diff --git a/pre-uninstall.sh b/pre-uninstall.sh deleted file mode 100755 index 3047a5d..0000000 --- a/pre-uninstall.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -set -e - -if [ "$EUID" -ne 0 ] - then echo "This program requires root permissions" - exit 1 -fi - -HOME_DIR="$(eval echo "~$(logname)")" - -# Argument parsing -SHORT=h -LONG=help -VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") -if [[ $? -ne 0 ]]; then - exit 1; -fi - -eval set -- "$VALID_ARGS" -while true; do - case "$1" in - '--help' | '-h') - echo "Usage: $0" 1>&2 - exit 0 - ;; - --) - break - ;; - esac - shift -done -# - -SERVICES_DIR="./services/linux" -SERVICE_EXTENSION=".service" - -SERVICES="$(cd "$SERVICES_DIR" && find . -maxdepth 1 -maxdepth 1 -type f -name "*$SERVICE_EXTENSION" -exec basename {} "$SERVICE_EXTENSION" \;)" - -function sanitizePath() { - local SANITIZED_PATH="$1" - local SANITIZED_PATH=${SANITIZED_PATH//..\//} - local SANITIZED_PATH=${SANITIZED_PATH#./} - local SANITIZED_PATH=${SANITIZED_PATH#/} - echo "$SANITIZED_PATH" -} - -echo "disabling services" -systemctl daemon-reload -for SERVICE in $SERVICES ; do - SERVICE=$(sanitizePath "$SERVICE") - echo "stopping [$SERVICE]" - systemctl stop "$SERVICE" 2> "/dev/null" || true - echo "disabling [$SERVICE]" - systemctl disable "$SERVICE" 2> "/dev/null" || true -done diff --git a/services/linux/fw-fanctrl.service b/services/linux/fw-fanctrl.service deleted file mode 100644 index 7f70d58..0000000 --- a/services/linux/fw-fanctrl.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Framework Fan Controller -After=multi-user.target -[Service] -Type=simple -Restart=always -ExecStart=/usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" --run --config "%SYSCONF_DIRECTORY%/fw-fanctrl/config.json" --no-log -ExecStopPost=/bin/sh -c "ectool autofanctrl" -[Install] -WantedBy=multi-user.target diff --git a/services/linux/system-sleep/fw-fanctrl-suspend b/services/linux/system-sleep/fw-fanctrl-suspend deleted file mode 100644 index 1fb3952..0000000 --- a/services/linux/system-sleep/fw-fanctrl-suspend +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -case $1 in - pre) /usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" --pause ;; - post) /usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" --resume ;; -esac From 624fed42f479918d450e37129973e314da286722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:43:02 +0200 Subject: [PATCH 10/35] woops, putting the LICENSE back... --- fetch/ectool/LICENSE | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 fetch/ectool/LICENSE diff --git a/fetch/ectool/LICENSE b/fetch/ectool/LICENSE new file mode 100644 index 0000000..71d7f5c --- /dev/null +++ b/fetch/ectool/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2010 The Chromium OS Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From cf3e36afec0b0f5d01a4f96923e4138a2d2e1175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 01:58:34 +0200 Subject: [PATCH 11/35] remove linux specific part from the README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee89d28..cb32bf4 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ fw-fanctrl --reload Here is a list of commands used to interact with the service. The commands in the `run` context are used launch the service manually. -If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will +If you have installed it correctly, the windows `fw-fanctrl` service will do this for you, so you probably will never need them. | Option | Context | Description | From 480f86796889273631dfd1ea2c021150c19b2f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 18:24:43 +0200 Subject: [PATCH 12/35] fix error on main rebase, and make the windows name pipe (socket) available to non-administrator users (fix bug in #60) --- fanctrl.py | 32 ++++++++++++++++++++++++++++++-- install.bat | 23 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/fanctrl.py b/fanctrl.py index adbee8f..1618cf7 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -9,6 +9,7 @@ import subprocess import sys import threading +import traceback from time import sleep from abc import ABC, abstractmethod @@ -53,7 +54,7 @@ def __init__(self, name, parameters): class Configuration: path = None - data: None + data = None def __init__(self, path): self.path = path @@ -125,6 +126,32 @@ class WindowsSocketController(SocketController, ABC): LPVOID = ctypes.c_void_p DWORD = wintypes.DWORD + kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + advapi32 = ctypes.WinDLL("advapi32", use_last_error=True) + + ConvertStringSecurityDescriptorToSecurityDescriptorW = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW + ConvertStringSecurityDescriptorToSecurityDescriptorW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.DWORD)] + ConvertStringSecurityDescriptorToSecurityDescriptorW.restype = wintypes.HANDLE + + sddl_string = "D:P(A;;GA;;;WD)" + + security_descriptor = wintypes.LPVOID() + ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl_string, 1, ctypes.byref(security_descriptor), None) + + class SECURITY_ATTRIBUTES(ctypes.Structure): + from ctypes import wintypes + + _fields_ = [ + ("nLength", wintypes.DWORD), + ("lpSecurityDescriptor", wintypes.LPVOID), + ("nLength", wintypes.BOOL), + ] + + security_attributes = SECURITY_ATTRIBUTES() + security_attributes.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES) + security_attributes.lpSecurityDescriptor = security_descriptor + security_attributes.bInheritHandle = False + server_socket = None def startServerSocket(self, commandCallback=None): @@ -138,7 +165,7 @@ def startServerSocket(self, commandCallback=None): 65536, # nOutBufferSize 65536, # nInBufferSize 0, # nDefaultTimeOut - None # lpSecurityAttributes + self.ctypes.byref(self.security_attributes) # lpSecurityAttributes ) try: while True: @@ -394,6 +421,7 @@ def run(self, debug=True): print(f"Missing strategy, exiting for safety reasons: {e.args[0]}") except Exception as e: print(f"Critical error, exiting for safety reasons: {e}") + traceback.print_exc() exit(1) diff --git a/install.bat b/install.bat index cc1156a..2a9e571 100644 --- a/install.bat +++ b/install.bat @@ -62,10 +62,12 @@ GOTO :EOF set /p "acknowledgement=> " if not defined acknowledgement ( echo goodbye. + pause exit /b 2 ) if not "%acknowledgement%" equ "%acknowledgementPhrase%" ( echo wrong acknowledgement phrase [%acknowledgement%] not equal to [%acknowledgementPhrase%], stopping here! + pause exit /b 2 ) echo: @@ -155,6 +157,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo failed to download 'crosec.zip' + pause exit /b 1 ) @@ -164,6 +167,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo failed to extract 'crosec.zip' + pause exit /b 2 ) @@ -176,6 +180,7 @@ GOTO :EOF if %errorLevel% neq 0 ( cd /d "%~dp0" echo failed to run the 'crosec' driver installation + pause exit /b 3 ) cd /d "%~dp0" @@ -189,6 +194,7 @@ GOTO :EOF for %%i in (".temp\test-result.txt") do @set count=%%~zi if "%count%" == "0" ( echo 'crosec' driver not installed correctly + pause exit /b 4 ) @@ -199,6 +205,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to copy '.temp' to '%ProgramFiles%\ectool' + pause exit /b 5 ) @@ -215,6 +222,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo failed to download 'artifact.zip' + pause exit /b 1 ) @@ -224,6 +232,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo failed to extract 'artifact.zip' + pause exit /b 2 ) @@ -234,6 +243,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to create directory '%ProgramFiles%\ectool' + pause exit /b 3 ) @@ -248,6 +258,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to install 'ectool.exe' + pause exit /b 3 ) @@ -264,6 +275,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo failed to download 'nssm.zip' + pause exit /b 1 ) @@ -273,6 +285,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo failed to extract 'nssm.zip' + pause exit /b 2 ) @@ -287,6 +300,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to install 'nssm.exe' + pause exit /b 3 ) @@ -302,6 +316,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to create directory '%ProgramFiles%\fw-fanctrl' + pause exit /b 3 ) @@ -316,6 +331,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to install 'fanctrl.py' + pause exit /b 3 ) @@ -325,6 +341,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to install '.\services\windows\run-service.bat' + pause exit /b 4 ) @@ -334,6 +351,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to install '.\services\windows\run-service.bat' + pause exit /b 4 ) @@ -345,6 +363,7 @@ GOTO :EOF @echo off if %errorLevel% neq 0 ( echo unable to install '.\services\windows\fw-fanctrl.bat' + pause exit /b 4 ) @@ -362,6 +381,7 @@ GOTO :EOF "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Start "SERVICE_DELAYED_AUTO_START" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" DisplayName "Framework Fanctrl" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Description "A simple systemd service to better control Framework Laptop's fan(s)" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppExit "%ProgramFiles%\ectool\ectool autofanctrl" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStdout "%ProgramFiles%\fw-fanctrl\out.log" "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" @echo off @@ -410,6 +430,8 @@ GOTO :EOF echo removing directory '%ProgramFiles%\nssm' rmdir /s /q "%ProgramFiles%\nssm" 2> nul + GOTO :EOF + :uninstall-ectool echo removing 'ectool' @@ -438,6 +460,7 @@ GOTO :EOF bcdedit /set {default} testsigning off GOTO :EOF + GOTO :EOF From 06d69c90e9a92c92b15213b15c394d8899fdb607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sat, 10 Aug 2024 18:31:49 +0200 Subject: [PATCH 13/35] fix mishaps in the documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cb32bf4..c534aa8 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ YOU GET LOCKED OUT OF YOUR COMPUTER IF YOU ARE NOT CAREFUL ENOUGH ! ============================================================================ ``` -[Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip) and extract it manually, or +[Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/packaging/windows.zip) and extract it manually, or download/clone it with the appropriate tools: ```shell @@ -87,7 +87,7 @@ To update, you can download or pull the appropriate branch from this repository, To uninstall, run the uninstallation script `uninstall.bat` (by double clicking it, or with the following command) ```shell -install.bat +uninstall.bat ``` ## Configuration From 2ef31c13099ba72224173ae3b6b5b87b15d552f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 20:00:18 +0200 Subject: [PATCH 14/35] README.md documentation update (#65) * forcing utf-8 encoding for socket messages and usage of stopServerSocket method instead of manual closing, as well as updating error detection pattern * README.md documentation update * change log format on fatal crash * fix badges links * adding windows platform badge and issue template * fix `:` instead of `=` * disabling part of the README.md while waiting for merge --- .../bug-report---packaging-windows.md | 37 ++++ README.md | 206 ++++++++++++++---- fanctrl.py | 8 +- 3 files changed, 201 insertions(+), 50 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug-report---packaging-windows.md diff --git a/.github/ISSUE_TEMPLATE/bug-report---packaging-windows.md b/.github/ISSUE_TEMPLATE/bug-report---packaging-windows.md new file mode 100644 index 0000000..fe63495 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report---packaging-windows.md @@ -0,0 +1,37 @@ +--- +name: Bug report | packaging/windows +about: Report a bug involving the windows packaging +title: "[BUG] [packaging/windows]" +labels: bug, packaging/windows +assignees: 'leopoldhub' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Error message** +```txt +If applicable, add the full error message. +``` + +**Environment (please complete the following information):** + - OS: [e.g. Windows 11] + - Version [e.g. commit 176d34b] + - Configuration file [config.json] + +**Additional context** +Add any other context about the problem here. diff --git a/README.md b/README.md index b7a4bf7..da51389 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,203 @@ # fw-fanctrl -This is a simple Python service for Linux that drives Framework Laptop's fan(s) speed according to a configurable speed/temp curve. -Its default configuration targets very silent fan operation, but it's easy to configure it for a different comfort/performance trade-off. -Its possible to specify two separate fan curves depending on whether the Laptop is charging/discharging. -Under the hood, it uses [ectool](https://gitlab.howett.net/DHowett/ectool) to change parameters in Framework's embedded controller (EC). +[![Static Badge](https://img.shields.io/badge/Linux%2FGlobal-FCC624?style=flat&logo=linux&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fmain)](https://github.com/TamtamHero/fw-fanctrl/tree/main) +![Static Badge](https://img.shields.io/badge/no%20binary%20blobs-30363D?style=flat&logo=GitHub-Sponsors&logoColor=4dff61) -It is compatible with all kinds of 13" and 16" models, both AMD/Intel CPUs, with or without a discrete GPU. +[![Static Badge](https://img.shields.io/badge/Python__3.12-FFDE57?style=flat&label=Requirement&link=https%3A%2F%2Fwww.python.org%2Fdownloads)](https://www.python.org/downloads) + +## Additional platforms: + +[![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix)](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) + +## Description + +Fw-fanctrl is a simple Python CLI service that controls Framework Laptop's fan(s) +speed according to a configurable speed/temperature curve. + +Its default strategy aims for very quiet fan operation, but you can choose amongst the other provided strategies, or +easily configure your own for a different comfort/performance trade-off. + +It also is possible to assign separate strategies depending on whether the laptop is charging or discharging. + +Under the hood, it uses [ectool](https://gitlab.howett.net/DHowett/ectool) +to change parameters in Framework's embedded controller (EC). + +It is compatible with all 13" and 16" models, both AMD/Intel CPUs, with or without a discrete GPU. If the service is paused or stopped, the fans will revert to their default behaviour. -# Install +## Installation + +### Requirements + +| name | version | url | +|--------|---------|----------------------------------------------------------------------| +| Python | 3.12.x | [https://www.python.org/downloads](https://www.python.org/downloads) | -## Dependencies +### Dependencies -To communicate with the embedded controller the `ectool` is required. -You can either let the script download it from the [gitlab repository](https://gitlab.howett.net/DHowett/ectool) artifacts, -or disable its installation (`--no-ectool`) and install your own. +Dependencies are downloaded and installed automatically, but can be excluded from the installation script if you wish to +do this manually. -You also need to disable secure boot of your device for `ectool` to work (more details about why [here](https://www.howett.net/posts/2021-12-framework-ec/#using-fw-ectool)) +| name | version | url | exclusion argument | +|----------------|-----------|--------------------------------------------------------------------------------------|--------------------| +| DHowett@ectool | build#899 | [https://gitlab.howett.net/DHowett/ectool](https://gitlab.howett.net/DHowett/ectool) | `--no-ectool` | + +### Instructions + +First, make sure that you have disabled secure boot in your BIOS/UEFI settings. +(more details on why [here](https://www.howett.net/posts/2021-12-framework-ec/#using-fw-ectool)) + +[Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip) and extract it manually, or +download/clone it with the appropriate tools: + +```shell +git clone "https://github.com/TamtamHero/fw-fanctrl.git" +``` + +```shell +curl -L "https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/main.zip" -o "./fw-fanctrl.zip" && unzip "./fw-fanctrl.zip" -d "./fw-fanctrl" && rm -rf "./fw-fanctrl.zip" +``` + +Then run the installation script with administrator privileges -Then run: ```bash sudo ./install.sh ``` -This bash script will to create and activate a service that runs this repo's main script, `fanctrl.py`. -It will copy `fanctrl.py` (to an executable file `fw-fanctrl`), download the ectool to `[dest-dir(/)]/bin` and create a config file -in `[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` +You can add a number of arguments to the installation command to suit your needs + +| argument | description | +|---------------------------------------------------------------------------------|----------------------------------------------------| +| `--dest-dir ` | specify an installation destination directory | +| `--prefix-dir ` | specify an installation prefix directory | +| `--sysconf-dir ` | specify a default configuration directory | +| `--no-ectool` | disable ectool installation and service activation | +| `--no-post-install` | disable post-install process | +| `--no-pre-uninstall` | disable pre-uninstall process | -this script also includes options to: -- specify an installation destination directory (`--dest-dir `). -- specify an installation prefix directory (`--prefix-dir `). -- specify a default configuration directory (`--sysconf-dir `). -- disable ectool installation and service activation (`--no-ectool`) -- disable post-install process (`--no-post-install`) -- disable pre-uninstall process (`--no-pre-uninstall`) +## Update -# Update +To update, you can download or pull the appropriate branch from this repository, and run the installation script again. -To install an update, you can pull the latest commit on the `main` branch of this repository, and run the install script again. +## Uninstall + +To uninstall, run the installation script with the `--remove` argument, as well as other +corresponding [arguments if necessary](#instructions) -# Uninstall ```bash sudo ./install.sh --remove ``` -# Configuration +## Configuration -There is a single `config.json` file located at `[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json`. +After installation, you will find the configuration file in the following location: -(You will need to reload the configuration with) -```bash -fw-fanctrl --reload +`/etc/fw-fanctrl/config.json` + +If you have modified the `dest-dir` or `sysconf-dir`, here is the corresponding pattern + +`[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` + +It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging +strategies. + +For example, one could use a lower fan speed strategy on discharging to optimise battery life (- noise, + heat), +and a high fan speed strategy on AC (+ noise, - heat). + +You can add or edit strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this +repo :) + +### Default strategy + +The default strategy is the one used when the service is started. + +It can be changed by replacing the value of the `defaultStrategy` field with one of the strategies present in the +configuration. + +```json +"defaultStrategy": "[STRATEGY NAME]" ``` -It contains different strategies, ranked from the most silent to the noisiest. It is possible to specify two different strategies for charging/discharging allowing for different optimization goals. -On discharging one could have fan curve optimized for low fan speeds in order to save power while accepting a bit more heat. -On charging one could have a fan curve that focuses on keeping the CPU from throttling and the system cool, at the expense of fan noise. -You can add new strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this repo :) +### Charging/Discharging strategies -Strategies can be configured with the following parameters: +The discharging strategy is the one that will be used when the laptop is not on AC, +Otherwise the default strategy is used. -- **SpeedCurve**: +It can be changed by replacing the value of the `strategyOnDischarging` field with one of the strategies present in the +configuration. - This is the curve points for `f(temperature) = fan speed` +```json +"strategyOnDischarging": "[STRATEGY NAME]" +``` - `fw-fanctrl` measures the CPU temperature, compute a moving average of it, and then find an appropriate `fan speed` value by interpolation on the curve. +This is optional and can be left empty to have the same strategy at all times. -- **FanSpeedUpdateFrequency**: +### Editing strategies - Time interval between every update to the fan's speed. `fw-fanctrl` measures temperature every second and add it to its moving average, but the actual update to fan speed is controlled using this configuration. This is for comfort, otherwise the speed is changed too often and it is noticeable and annoying, especially at low speed. - For a more reactive fan, you can lower this setting. **Defaults to 5 seconds.** +Strategies can be configured with the following parameters: -- **MovingAverageInterval**: +> **SpeedCurve**: +> +> It is represented by the curve points for `f(temperature) = fan(s) speed`. +> +> ```json +> "speedCurve": [ +> { "temp": [TEMPERATURE POINT], "speed": [PERCENTAGE SPEED] }, +> ... +> ] +> ``` +> +> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an appropriate `fan speed` +> value by interpolating on the curve. + +> **FanSpeedUpdateFrequency**: +> +> It is the interval in seconds between fan speed calculations. +> +> ```json +> "fanSpeedUpdateFrequency": [UPDATE FREQUENCY] +> ``` +> +> This is for comfort, otherwise the speed will change too often, which is noticeable and annoying, especially at low +> speed. +> +> For a more responsive fan, you can reduce this setting. +> +> **Defaults to 5 seconds.** (minimum 1) + +> **MovingAverageInterval**: +> +> It is the number of seconds over which the moving average of temperature is calculated. +> +> ```json +> "movingAverageInterval": [AVERAGING INTERVAL] +> ``` +> +> Increase it, and the fan speed changes more gradually. Lower it, and it becomes more responsive. +> +> **Defaults to 20 seconds.** (minimum 1) + +--- + +Once the configuration has been changed, you must reload it with the following command - Number of seconds on which the moving average of temperature is computed. Increase it, and the fan speed will change more gradually. Lower it, and it will gain in reactivity. **Defaults to 20 seconds.** +```bash +fw-fanctrl --reload +``` -## Charging/Discharging strategies +## Commands -The strategy active by default is the one specified in the `defaultStrategy` entry. Optionally a separate strategy only active during discharge can be defined, using the `strategyOnDischarging` entry. By default no extra strategy for discharging is provided, the default strategy is active during all times. +Here is a list of commands used to interact with the service. -# Commands +The commands in the `run` context are used launch the service manually. +If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will +never need them. | Option | Context | Description | |-----------------------------|-----------------|-------------------------------------------------------------------------------| | \ | run & configure | the name of the strategy to use | -| --run | run | run the service | +| --run | run | run the service manually | | --config | run | specify the configuration path | | --no-log | run | disable state logging | | --query, -q | configure | print the current strategy name | diff --git a/fanctrl.py b/fanctrl.py index 9ac7bd4..820569b 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -54,7 +54,7 @@ def __init__(self, name, parameters): class Configuration: path = None - data: None + data = None def __init__(self, path): self.path = path @@ -360,8 +360,10 @@ def run(self, debug=True): else: sleep(5) except InvalidStrategyException as e: - print("Error: missing strategy, exiting for safety reasons: " + e.args[0]) - exit(1) + print(f"Missing strategy, exiting for safety reasons: {e.args[0]}") + except Exception as e: + print(f"Critical error, exiting for safety reasons: {e}") + exit(1) def main(): From 871566fed874bd9f730f088a97d4d9d0213bde35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 20:11:19 +0200 Subject: [PATCH 15/35] Command arguments refactoring Pt.1 (#66) * forcing utf-8 encoding for socket messages and usage of stopServerSocket method instead of manual closing, as well as updating error detection pattern * README.md documentation update * change log format on fatal crash * fix badges links * adding windows platform badge and issue template * fix `:` instead of `=` * first part of the command argument refactoring. the old argument format is deprecated but still usable. improved feedback when executing commands. #31 * trim blank lines in README.md --- README.md | 77 +++-- fanctrl.py | 339 ++++++++++++++++++----- services/fw-fanctrl.service | 2 +- services/system-sleep/fw-fanctrl-suspend | 4 +- 4 files changed, 328 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index da51389..6ec08ca 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,8 @@ Strategies can be configured with the following parameters: > ] > ``` > -> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an appropriate `fan speed` +> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an +> appropriate `fan speed` > value by interpolating on the curve. > **FanSpeedUpdateFrequency**: @@ -188,22 +189,62 @@ fw-fanctrl --reload ## Commands -Here is a list of commands used to interact with the service. +Here is a list of commands and options used to interact with the service. + +the base of all commands is the following + +```shell +fw-fanctrl [commands and options] +``` + +First, the global options + +| Option | Optional | choices | Default | Description | +|---------------------------|----------|---------|---------|--------------------------------------------------------------------------------| +| --socket-controller, --sc | yes | unix | unix | the socket controller to use for communication between the cli and the service | + +**run** + +run the service manually -The commands in the `run` context are used launch the service manually. If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will -never need them. - -| Option | Context | Description | -|-----------------------------|-----------------|-------------------------------------------------------------------------------| -| \ | run & configure | the name of the strategy to use | -| --run | run | run the service manually | -| --config | run | specify the configuration path | -| --no-log | run | disable state logging | -| --query, -q | configure | print the current strategy name | -| --list-strategies | configure | print the available strategies | -| --reload, -r | configure | reload the configuration file | -| --pause | configure | temporarily disable the service and reset the fans to their default behaviour | -| --resume | configure | resume the service | -| --hardware-controller, --hc | run | select the hardware controller. choices: ectool | -| --socket-controller, --sc | run & configure | select the socket controller. choices: unix | +never need those. + +| Option | Optional | choices | Default | Description | +|-----------------------------|----------|----------------|----------------------|-----------------------------------------------------------------------------------| +| \ | yes | | the default strategy | the name of the strategy to use | +| --config | yes | \[CONFIG_PATH] | | the configuration file path | +| --silent, -s | yes | | | disable printing speed/temp status to stdout | +| --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | + +**use** + +change the current strategy + +| Option | Optional | Description | +|-------------|----------|---------------------------------| +| \ | no | the name of the strategy to use | + +**reset** + +reset to the default strategy + +**reload** + +reload the configuration file + +**pause** + +pause the service + +**resume** + +resume the service + +**print** + +print the selected information + +| Option | Optional | choices | Default | Description | +|--------------------|----------|---------------|---------|------------------------| +| \ | yes | current, list | current | what should be printed | diff --git a/fanctrl.py b/fanctrl.py index 820569b..30c695a 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -1,6 +1,7 @@ #! /usr/bin/python3 import argparse import collections +import io import json import os import re @@ -11,12 +12,232 @@ import threading from time import sleep from abc import ABC, abstractmethod +import textwrap DEFAULT_CONFIGURATION_FILE_PATH = "/etc/fw-fanctrl/config.json" SOCKETS_FOLDER_PATH = "/run/fw-fanctrl" COMMANDS_SOCKET_FILE_PATH = os.path.join(SOCKETS_FOLDER_PATH, ".fw-fanctrl.commands.sock") -parser = None + +class CommandParser: + isRemote = True + + legacyParser = None + parser = None + + def __init__(self, isRemote=False): + self.isRemote = isRemote + self.initParser() + self.initLegacyParser() + + def initParser(self): + self.parser = argparse.ArgumentParser( + prog="fw-fanctrl", + description="control Framework's laptop fan(s) with a speed curve", + epilog=textwrap.dedent( + "obtain more help about a command or subcommand using `fw-fanctrl [subcommand...] -h/--help`"), + formatter_class=argparse.RawTextHelpFormatter + ) + self.parser.add_argument( + "--socket-controller", + "--sc", + help="the socket controller to use for communication between the cli and the service", + type=str, + choices=["unix"], + default="unix" + ) + + commandsSubParser = self.parser.add_subparsers(dest="command") + + if not self.isRemote: + runCommand = commandsSubParser.add_parser( + "run", + description="run the service", + formatter_class=argparse.RawTextHelpFormatter + ) + runCommand.add_argument( + "strategy", + help='name of the strategy to use e.g: "lazy" (use `print strategies` to list available strategies)', + nargs=argparse.OPTIONAL + ) + runCommand.add_argument( + "--config", + "-c", + help=f"the configuration file path (default: {DEFAULT_CONFIGURATION_FILE_PATH})", + type=str, + default=DEFAULT_CONFIGURATION_FILE_PATH + ) + runCommand.add_argument( + "--silent", + "-s", + help="disable printing speed/temp status to stdout", + action="store_true" + ) + runCommand.add_argument( + "--hardware-controller", + "--hc", + help="the hardware controller to use for fetching and setting the temp and fan(s) speed", + type=str, + choices=["ectool"], + default="ectool" + ) + + useCommand = commandsSubParser.add_parser( + "use", + description="change the current strategy" + ) + useCommand.add_argument( + "strategy", + help='name of the strategy to use e.g: "lazy". (use `print strategies` to list available strategies)' + ) + + commandsSubParser.add_parser( + "reset", + description="reset to the default strategy" + ) + commandsSubParser.add_parser( + "reload", + description="reload the configuration file" + ) + commandsSubParser.add_parser( + "pause", + description="pause the service" + ) + commandsSubParser.add_parser( + "resume", + description="resume the service" + ) + + printCommand = commandsSubParser.add_parser( + "print", + description="print the selected information" + ) + printCommand.add_argument( + "print_selection", + help="what should be printed", + nargs="?", + type=str, + choices=["current", + "list"], + default="current" + ) + + def initLegacyParser(self): + self.legacyParser = argparse.ArgumentParser(add_help=False) + + # avoid collision with the new parser commands + def excludedPositionalArguments(value): + if value in ["run", "use", "reload", "reset", "pause", "resume", "print"]: + raise argparse.ArgumentTypeError("%s is an excluded value" % value) + return value + + bothGroup = self.legacyParser.add_argument_group("both") + bothGroup.add_argument( + "_strategy", + nargs="?", + type=excludedPositionalArguments + ) + bothGroup.add_argument( + "--strategy", + nargs="?" + ) + + runGroup = self.legacyParser.add_argument_group("run") + runGroup.add_argument( + "--run", + action="store_true" + ) + runGroup.add_argument( + "--config", + type=str, + default=DEFAULT_CONFIGURATION_FILE_PATH + ) + runGroup.add_argument( + "--no-log", + action="store_true" + ) + commandGroup = self.legacyParser.add_argument_group("configure") + commandGroup.add_argument( + "--query", + "-q", + action="store_true" + ) + commandGroup.add_argument( + "--list-strategies", + action="store_true" + ) + commandGroup.add_argument( + "--reload", + "-r", + action="store_true" + ) + commandGroup.add_argument( + "--pause", + action="store_true" + ) + commandGroup.add_argument( + "--resume", + action="store_true" + ) + commandGroup.add_argument( + "--hardware-controller", + "--hc", + type=str, + choices=["ectool"], + default="ectool" + ) + commandGroup.add_argument( + "--socket-controller", + "--sc", + type=str, + choices=["unix"], + default="unix" + ) + + def parseArgs(self, args=None): + values = None + original_stderr = sys.stderr + # silencing legacy parser output + sys.stderr = open(os.devnull, 'w') + try: + legacy_values = self.legacyParser.parse_args(args) + if legacy_values.strategy is None: + legacy_values.strategy = legacy_values._strategy + # converting legacy values into new ones + values = argparse.Namespace() + values.socket_controller = legacy_values.socket_controller + if legacy_values.query: + values.command = "print" + values.print_selection = "current" + if legacy_values.list_strategies: + values.command = "print" + values.print_selection = "list" + if legacy_values.resume: + values.command = "resume" + if legacy_values.pause: + values.command = "pause" + if legacy_values.reload: + values.command = "reload" + if legacy_values.run: + values.command = "run" + values.silent = legacy_values.no_log + values.hardware_controller = legacy_values.hardware_controller + values.config = legacy_values.config + values.strategy = legacy_values.strategy + if not hasattr(values, "command") and legacy_values.strategy is not None: + values.command = "use" + values.strategy = legacy_values.strategy + if not hasattr(values, "command"): + raise Exception("not a valid legacy command") + if self.isRemote or values.command == "run": + print( + "[Warning] > this command is deprecated and will be removed soon, please use the new command format instead ('fw-fanctrl -h' for more details).") + except (SystemExit, Exception): + sys.stderr = original_stderr + values = self.parser.parse_args(args) + finally: + sys.stderr = original_stderr + return values class JSONException(Exception): @@ -125,15 +346,30 @@ def startServerSocket(self, commandCallback=None): self.server_socket.listen(1) while True: client_socket, _ = self.server_socket.accept() + parsePrintCapture = io.StringIO() try: # Receive data from the client data = client_socket.recv(4096).decode() - args = parser.parse_args(shlex.split(data)) + original_stderr = sys.stderr + original_stdout = sys.stdout + # capture parsing std outputs for the client + sys.stderr = parsePrintCapture + sys.stdout = parsePrintCapture + try: + args = CommandParser(True).parseArgs(shlex.split(data)) + finally: + sys.stderr = original_stderr + sys.stdout = original_stdout commandReturn = commandCallback(args) if not commandReturn: commandReturn = "Success!" + if parsePrintCapture.getvalue().strip(): + commandReturn = parsePrintCapture.getvalue() + commandReturn client_socket.sendall(commandReturn.encode('utf-8')) + except SystemExit: + client_socket.sendall(f"{parsePrintCapture.getvalue()}".encode('utf-8')) except Exception as e: + print(f"[Error] > An error occurred while treating a socket command: {e}", file=sys.stderr) client_socket.sendall(f"[Error] > An error occurred: {e}".encode('utf-8')) finally: client_socket.shutdown(socket.SHUT_WR) @@ -273,33 +509,35 @@ def getCurrentStrategy(self): return self.configuration.getDefaultStrategy() return self.configuration.getDischargingStrategy() - def commandManager(self, command): - if command.strategy or command._strategy: - strategy = command.strategy - if strategy is None: - strategy = command._strategy + def commandManager(self, args): + if args.command == "reset" or (args.command == "use" and args.strategy == "defaultStrategy"): + self.clearOverwrittenStrategy() + return + elif args.command == "use": try: - if strategy == "defaultStrategy": - self.clearOverwrittenStrategy() - else: - self.overwriteStrategy(strategy) + self.overwriteStrategy(args.strategy) return self.getCurrentStrategy().name except InvalidStrategyException: - raise InvalidStrategyException(f"The specified strategy is invalid: {strategy}") - elif command.pause: - self.pause() - elif command.resume: - self.resume() - elif command.query: - return self.getCurrentStrategy().name - elif command.list_strategies: - return '\n'.join(self.configuration.getStrategies()) - elif command.reload: + raise InvalidStrategyException(f"The specified strategy is invalid: {args.strategy}") + elif args.command == "reload": if self.configuration.reload(): if self.overwrittenStrategy is not None: self.overwriteStrategy(self.overwrittenStrategy.name) else: raise JSONException("Config file could not be parsed due to JSON Error") + return + elif args.command == "pause": + self.pause() + return + elif args.command == "resume": + self.resume() + return + elif args.command == "print": + if args.print_selection == "current": + return self.getCurrentStrategy().name + elif args.print_selection == "list": + return '\n'.join(self.configuration.getStrategies()) + return "Unknown command, unexpected." # return mean temperature over a given time interval (in seconds) def getMovingAverageTemperature(self, timeInterval): @@ -360,72 +598,27 @@ def run(self, debug=True): else: sleep(5) except InvalidStrategyException as e: - print(f"Missing strategy, exiting for safety reasons: {e.args[0]}") + print(f"[Error] > Missing strategy, exiting for safety reasons: {e.args[0]}", file=sys.stderr) except Exception as e: - print(f"Critical error, exiting for safety reasons: {e}") + print(f"[Error] > Critical error, exiting for safety reasons: {e}", file=sys.stderr) exit(1) def main(): - global parser - parser = argparse.ArgumentParser( - description="Control Framework's laptop fan with a speed curve", - ) - - bothGroup = parser.add_argument_group("both") - bothGroup.add_argument( - "_strategy", - nargs="?", - help='Name of the strategy to use e.g: "lazy" (check config.json for others). Use "defaultStrategy" to go ' - 'back to the default strategy', - ) - bothGroup.add_argument( - "--strategy", - nargs="?", - help='Name of the strategy to use e.g: "lazy" (check config.json for others). Use "defaultStrategy" to go ' - 'back to the default strategy', - ) - - runGroup = parser.add_argument_group("run") - runGroup.add_argument("--run", help="run the service", action="store_true") - runGroup.add_argument("--config", type=str, help="Path to config file", default=DEFAULT_CONFIGURATION_FILE_PATH) - runGroup.add_argument( - "--no-log", help="Disable print speed/meanTemp to stdout", action="store_true" - ) - commandGroup = parser.add_argument_group("configure") - commandGroup.add_argument( - "--query", "-q", help="Query the currently active strategy", action="store_true" - ) - commandGroup.add_argument( - "--list-strategies", help="List the available strategies", action="store_true" - ) - commandGroup.add_argument( - "--reload", "-r", help="Reload the configuration from file", action="store_true" - ) - commandGroup.add_argument("--pause", help="Pause the program", action="store_true") - commandGroup.add_argument("--resume", help="Resume the program", action="store_true") - commandGroup.add_argument("--hardware-controller", "--hc", help="Select the hardware controller", type=str, - choices=["ectool"], default="ectool") - commandGroup.add_argument("--socket-controller", "--sc", help="Select the socket controller", type=str, - choices=["unix"], default="unix") - - args = parser.parse_args() + args = CommandParser().parseArgs() - socketController = None + socketController = UnixSocketController() if args.socket_controller == "unix": socketController = UnixSocketController() - if args.run: - hardwareController = None + if args.command == "run": + hardwareController = EctoolHardwareController() if args.hardware_controller == "ectool": hardwareController = EctoolHardwareController() - strategy = args.strategy - if strategy is None: - strategy = args._strategy fan = FanController(hardwareController=hardwareController, socketController=socketController, configPath=args.config, strategyName=args.strategy) - fan.run(debug=not args.no_log) + fan.run(debug=not args.silent) else: try: commandResult = socketController.sendViaClientSocket(' '.join(sys.argv[1:])) diff --git a/services/fw-fanctrl.service b/services/fw-fanctrl.service index 7f70d58..0bcac31 100644 --- a/services/fw-fanctrl.service +++ b/services/fw-fanctrl.service @@ -4,7 +4,7 @@ After=multi-user.target [Service] Type=simple Restart=always -ExecStart=/usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" --run --config "%SYSCONF_DIRECTORY%/fw-fanctrl/config.json" --no-log +ExecStart=/usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" run --config "%SYSCONF_DIRECTORY%/fw-fanctrl/config.json" --silent ExecStopPost=/bin/sh -c "ectool autofanctrl" [Install] WantedBy=multi-user.target diff --git a/services/system-sleep/fw-fanctrl-suspend b/services/system-sleep/fw-fanctrl-suspend index 1fb3952..3cf2697 100644 --- a/services/system-sleep/fw-fanctrl-suspend +++ b/services/system-sleep/fw-fanctrl-suspend @@ -1,6 +1,6 @@ #!/bin/sh case $1 in - pre) /usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" --pause ;; - post) /usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" --resume ;; + pre) /usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" pause ;; + post) /usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" resume ;; esac From b4f87b6c772fc5703486c6c229c7897c26e4fc1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 20:35:59 +0200 Subject: [PATCH 16/35] finishing touches (#67) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6ec08ca..de38c65 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ Strategies can be configured with the following parameters: Once the configuration has been changed, you must reload it with the following command ```bash -fw-fanctrl --reload +fw-fanctrl reload ``` ## Commands @@ -199,7 +199,7 @@ fw-fanctrl [commands and options] First, the global options -| Option | Optional | choices | Default | Description | +| Option | Optional | Choices | Default | Description | |---------------------------|----------|---------|---------|--------------------------------------------------------------------------------| | --socket-controller, --sc | yes | unix | unix | the socket controller to use for communication between the cli and the service | @@ -210,7 +210,7 @@ run the service manually If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will never need those. -| Option | Optional | choices | Default | Description | +| Option | Optional | Choices | Default | Description | |-----------------------------|----------|----------------|----------------------|-----------------------------------------------------------------------------------| | \ | yes | | the default strategy | the name of the strategy to use | | --config | yes | \[CONFIG_PATH] | | the configuration file path | @@ -245,6 +245,6 @@ resume the service print the selected information -| Option | Optional | choices | Default | Description | +| Option | Optional | Choices | Default | Description | |--------------------|----------|---------------|---------|------------------------| | \ | yes | current, list | current | what should be printed | From 00cfec481065bd7343e0dca80e415d51093d7da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 20:43:51 +0200 Subject: [PATCH 17/35] adding back stacktrace on fatal fail --- fanctrl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fanctrl.py b/fanctrl.py index d8f38e8..e4536a8 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -542,6 +542,7 @@ def run(self, debug=True): print(f"[Error] > Missing strategy, exiting for safety reasons: {e.args[0]}", file=sys.stderr) except Exception as e: print(f"[Error] > Critical error, exiting for safety reasons: {e}", file=sys.stderr) + traceback.print_exc() exit(1) From 11969991fbc89221746d5c5c5aa5f016a8137948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 20:44:22 +0200 Subject: [PATCH 18/35] removing unused dependencies --- fanctrl.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/fanctrl.py b/fanctrl.py index e4536a8..cc076cf 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -3,10 +3,8 @@ import collections import io import json -import os import re import shlex -import socket import subprocess import sys import threading From 3b1779d2598105ac82e35c79b26615ce9efc0e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 22:22:47 +0200 Subject: [PATCH 19/35] update windows "one-line" download command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c74119..d69d8a7 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ git clone --branch "packaging/windows" "https://github.com/TamtamHero/fw-fanctrl ``` ```shell -curl -L "https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/packaging/windows.zip" -o "./fw-fanctrl.zip" && unzip "./fw-fanctrl.zip" -d "./fw-fanctrl" && rm -rf "./fw-fanctrl.zip" +curl -L "https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/packaging/windows.zip" -o "./fw-fanctrl.zip" && tar -xf "./fw-fanctrl.zip" && del "./fw-fanctrl.zip" ``` Then run the installation script with administrator privileges (by double clicking it, or with the following command) From e4d64410c2a474340a712c7a45e78fc74283ea9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 22:39:24 +0200 Subject: [PATCH 20/35] windows batch are a bit capricious... changing indentation to fix various weird behaviours --- install.bat | 504 ++++++++++++++++++++++++++-------------------------- 1 file changed, 255 insertions(+), 249 deletions(-) diff --git a/install.bat b/install.bat index 2a9e571..a0eb117 100644 --- a/install.bat +++ b/install.bat @@ -74,6 +74,7 @@ GOTO :EOF echo ---------- CALL :INSTALL GOTO :EOF +GOTO :EOF :INSTALL CALL :UNINSTALL @@ -142,252 +143,255 @@ GOTO :EOF pause GOTO :EOF +GOTO :EOF - :install-crosec - echo setting up 'crosec' - rmdir /s /q ".temp" 2> nul - mkdir ".temp" +:install-crosec + echo setting up 'crosec' + rmdir /s /q ".temp" 2> nul + mkdir ".temp" - echo enabling 'bcdedit testsigning' - bcdedit /set {default} testsigning on + echo enabling 'bcdedit testsigning' + bcdedit /set {default} testsigning on - echo downloading 'crosec.zip' - @echo on - curl -s -o ".temp\crosec.zip" -L "https://github.com/DHowett/FrameworkWindowsUtils/releases/download/v0.0.2/CrosEC-0.0.2-4ac038b.zip" > nul - @echo off - if %errorLevel% neq 0 ( - echo failed to download 'crosec.zip' - pause - exit /b 1 - ) + echo downloading 'crosec.zip' + @echo on + curl -s -o ".temp\crosec.zip" -L "https://github.com/DHowett/FrameworkWindowsUtils/releases/download/v0.0.2/CrosEC-0.0.2-4ac038b.zip" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to download 'crosec.zip' + pause + exit /b 1 + ) - echo extracting 'crosec.zip' - @echo on - tar -xf ".temp\crosec.zip" --strip-components=1 -C ".temp" > nul - @echo off - if %errorLevel% neq 0 ( - echo failed to extract 'crosec.zip' - pause - exit /b 2 - ) + echo extracting 'crosec.zip' + @echo on + tar -xf ".temp\crosec.zip" --strip-components=1 -C ".temp" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to extract 'crosec.zip' + pause + exit /b 2 + ) - echo installing 'crosec' driver + echo installing 'crosec' driver - cd /d ".temp" - @echo on - ".\installer" install - @echo off - if %errorLevel% neq 0 ( - cd /d "%~dp0" - echo failed to run the 'crosec' driver installation - pause - exit /b 3 - ) + cd /d ".temp" + @echo on + ".\installer" install + @echo off + if %errorLevel% neq 0 ( cd /d "%~dp0" + echo failed to run the 'crosec' driver installation + pause + exit /b 3 + ) + cd /d "%~dp0" - echo testing 'crosec' driver - @echo on - ".temp\fauxectool" > ".temp\test-result.txt" - @echo off - - set count=0 - for %%i in (".temp\test-result.txt") do @set count=%%~zi - if "%count%" == "0" ( - echo 'crosec' driver not installed correctly - pause - exit /b 4 - ) + echo testing 'crosec' driver + @echo on + ".temp\fauxectool" > ".temp\test-result.txt" + @echo off - rmdir /s /q "%ProgramFiles%\crosec" 2> nul - echo copying '.temp' to '%ProgramFiles%\crosec' - @echo on - xcopy /e /i ".temp" "%ProgramFiles%\crosec" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to copy '.temp' to '%ProgramFiles%\ectool' - pause - exit /b 5 - ) + set count=0 + for %%i in (".temp\test-result.txt") do @set count=%%~zi + if "%count%" == "0" ( + echo 'crosec' driver not installed correctly + pause + exit /b 4 + ) - GOTO :EOF + rmdir /s /q "%ProgramFiles%\crosec" 2> nul + echo copying '.temp' to '%ProgramFiles%\crosec' + @echo on + xcopy /e /i ".temp" "%ProgramFiles%\crosec" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to copy '.temp' to '%ProgramFiles%\ectool' + pause + exit /b 5 + ) - :install-ectool - echo setting up 'ectool' - rmdir /s /q ".temp" 2> nul - mkdir ".temp" + GOTO :EOF +GOTO :EOF - echo downloading 'artifact.zip' - @echo on - curl -s -o ".temp\artifact.zip" -L "https://gitlab.howett.net/DHowett/ectool/-/jobs/904/artifacts/download?file_type=archive" > nul - @echo off - if %errorLevel% neq 0 ( - echo failed to download 'artifact.zip' - pause - exit /b 1 - ) +:install-ectool + echo setting up 'ectool' + rmdir /s /q ".temp" 2> nul + mkdir ".temp" - echo extracting 'artifact.zip' - @echo on - tar -xf ".temp\artifact.zip" --strip-components=3 -C ".temp" > nul - @echo off - if %errorLevel% neq 0 ( - echo failed to extract 'artifact.zip' - pause - exit /b 2 - ) + echo downloading 'artifact.zip' + @echo on + curl -s -o ".temp\artifact.zip" -L "https://gitlab.howett.net/DHowett/ectool/-/jobs/904/artifacts/download?file_type=archive" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to download 'artifact.zip' + pause + exit /b 1 + ) - rmdir /s /q "%ProgramFiles%\ectool" 2> nul - echo creating directory '%ProgramFiles%\ectool' - @echo on - mkdir "%ProgramFiles%\ectool" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to create directory '%ProgramFiles%\ectool' - pause - exit /b 3 - ) + echo extracting 'artifact.zip' + @echo on + tar -xf ".temp\artifact.zip" --strip-components=3 -C ".temp" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to extract 'artifact.zip' + pause + exit /b 2 + ) - @echo %PATH% | findstr /I /C:"%ProgramFiles%\ectool" >nul - if %errorLevel% neq 0 ( - set "addedEnvironmentPaths=%addedEnvironmentPaths%;%ProgramFiles%\ectool" - ) + rmdir /s /q "%ProgramFiles%\ectool" 2> nul + echo creating directory '%ProgramFiles%\ectool' + @echo on + mkdir "%ProgramFiles%\ectool" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to create directory '%ProgramFiles%\ectool' + pause + exit /b 3 + ) - echo installing 'ectool.exe' to '%ProgramFiles%\ectool' - @echo on - copy /v ".temp\ectool.exe" "%ProgramFiles%\ectool" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to install 'ectool.exe' - pause - exit /b 3 - ) + @echo %PATH% | findstr /I /C:"%ProgramFiles%\ectool" >nul + if %errorLevel% neq 0 ( + set "addedEnvironmentPaths=%addedEnvironmentPaths%;%ProgramFiles%\ectool" + ) - GOTO :EOF + echo installing 'ectool.exe' to '%ProgramFiles%\ectool' + @echo on + copy /v ".temp\ectool.exe" "%ProgramFiles%\ectool" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install 'ectool.exe' + pause + exit /b 3 + ) - :install-nssm - echo setting up 'nssm' - rmdir /s /q ".temp" 2> nul - mkdir ".temp" + GOTO :EOF +GOTO :EOF - echo downloading 'nssm.zip' - @echo on - curl -s -o ".temp\nssm.zip" -L "https://nssm.cc/release/nssm-2.24.zip" > nul - @echo off - if %errorLevel% neq 0 ( - echo failed to download 'nssm.zip' - pause - exit /b 1 - ) +:install-nssm + echo setting up 'nssm' + rmdir /s /q ".temp" 2> nul + mkdir ".temp" - echo extracting 'nssm.zip' - @echo on - tar -xf ".temp\nssm.zip" --strip-components=1 -C ".temp" > nul - @echo off - if %errorLevel% neq 0 ( - echo failed to extract 'nssm.zip' - pause - exit /b 2 - ) + echo downloading 'nssm.zip' + @echo on + curl -s -o ".temp\nssm.zip" -L "https://nssm.cc/release/nssm-2.24.zip" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to download 'nssm.zip' + pause + exit /b 1 + ) - echo creating directory '%ProgramFiles%\nssm' - @echo on - mkdir "%ProgramFiles%\nssm" > nul 2> nul - @echo off + echo extracting 'nssm.zip' + @echo on + tar -xf ".temp\nssm.zip" --strip-components=1 -C ".temp" > nul + @echo off + if %errorLevel% neq 0 ( + echo failed to extract 'nssm.zip' + pause + exit /b 2 + ) - echo installing 'nssm.exe' to '%ProgramFiles%\nssm\' - @echo on - copy /v ".temp\win64\nssm.exe" "%ProgramFiles%\nssm\" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to install 'nssm.exe' - pause - exit /b 3 - ) + echo creating directory '%ProgramFiles%\nssm' + @echo on + mkdir "%ProgramFiles%\nssm" > nul 2> nul + @echo off - GOTO :EOF + echo installing 'nssm.exe' to '%ProgramFiles%\nssm\' + @echo on + copy /v ".temp\win64\nssm.exe" "%ProgramFiles%\nssm\" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install 'nssm.exe' + pause + exit /b 3 + ) + GOTO :EOF +GOTO :EOF - :install-fw-fanctrl - echo setting up 'fw-fanctrl' - rmdir /s /q "%ProgramFiles%\fw-fanctrl" 2> nul - echo creating directory '%ProgramFiles%\fw-fanctrl' - @echo on - mkdir "%ProgramFiles%\fw-fanctrl" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to create directory '%ProgramFiles%\fw-fanctrl' - pause - exit /b 3 - ) +:install-fw-fanctrl + echo setting up 'fw-fanctrl' + rmdir /s /q "%ProgramFiles%\fw-fanctrl" 2> nul + echo creating directory '%ProgramFiles%\fw-fanctrl' + @echo on + mkdir "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to create directory '%ProgramFiles%\fw-fanctrl' + pause + exit /b 3 + ) - @echo %PATH% | findstr /I /C:"%ProgramFiles%\fw-fanctrl" >nul - if %errorLevel% neq 0 ( - set "addedEnvironmentPaths=%addedEnvironmentPaths%;%ProgramFiles%\fw-fanctrl" - ) + @echo %PATH% | findstr /I /C:"%ProgramFiles%\fw-fanctrl" >nul + if %errorLevel% neq 0 ( + set "addedEnvironmentPaths=%addedEnvironmentPaths%;%ProgramFiles%\fw-fanctrl" + ) - echo installing 'fanctrl.py' to '%ProgramFiles%\fw-fanctrl' - @echo on - copy /v ".\fanctrl.py" "%ProgramFiles%\fw-fanctrl" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to install 'fanctrl.py' - pause - exit /b 3 - ) + echo installing 'fanctrl.py' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\fanctrl.py" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install 'fanctrl.py' + pause + exit /b 3 + ) - echo installing '.\services\windows\run-service.bat' to '%ProgramFiles%\fw-fanctrl' - @echo on - copy /v ".\services\windows\run-service.bat" "%ProgramFiles%\fw-fanctrl" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to install '.\services\windows\run-service.bat' - pause - exit /b 4 - ) + echo installing '.\services\windows\run-service.bat' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\services\windows\run-service.bat" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install '.\services\windows\run-service.bat' + pause + exit /b 4 + ) - echo installing '.\services\windows\run-service.bat' to '%ProgramFiles%\fw-fanctrl' - @echo on - copy /v ".\services\windows\run-service.bat" "%ProgramFiles%\fw-fanctrl" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to install '.\services\windows\run-service.bat' - pause - exit /b 4 - ) + echo installing '.\services\windows\run-service.bat' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\services\windows\run-service.bat" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install '.\services\windows\run-service.bat' + pause + exit /b 4 + ) - powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\run-service.bat') -replace '####CONFIG_PATH####', '%Appdata%\fw-fanctrl\config.json' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\run-service.bat'" + powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\run-service.bat') -replace '####CONFIG_PATH####', '%Appdata%\fw-fanctrl\config.json' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\run-service.bat'" - echo installing '.\services\windows\fw-fanctrl.bat' to '%ProgramFiles%\fw-fanctrl' - @echo on - copy /v ".\services\windows\fw-fanctrl.bat" "%ProgramFiles%\fw-fanctrl" > nul - @echo off - if %errorLevel% neq 0 ( - echo unable to install '.\services\windows\fw-fanctrl.bat' - pause - exit /b 4 - ) + echo installing '.\services\windows\fw-fanctrl.bat' to '%ProgramFiles%\fw-fanctrl' + @echo on + copy /v ".\services\windows\fw-fanctrl.bat" "%ProgramFiles%\fw-fanctrl" > nul + @echo off + if %errorLevel% neq 0 ( + echo unable to install '.\services\windows\fw-fanctrl.bat' + pause + exit /b 4 + ) - powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\fw-fanctrl.bat') -replace '####PYTHON_PATH####', '%localAppData%\Programs\Python\Python312\python' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\fw-fanctrl.bat'" + powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\fw-fanctrl.bat') -replace '####PYTHON_PATH####', '%localAppData%\Programs\Python\Python312\python' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\fw-fanctrl.bat'" - echo creating directory '%Appdata%\fw-fanctrl' - if not exist "%Appdata%\fw-fanctrl" mkdir "%Appdata%\fw-fanctrl" + echo creating directory '%Appdata%\fw-fanctrl' + if not exist "%Appdata%\fw-fanctrl" mkdir "%Appdata%\fw-fanctrl" - echo installing 'config.json' in '%Appdata%\fw-fanctrl\config.json' - if not exist "%Appdata%\fw-fanctrl\config.json" echo n | copy /-y ".\config.json" "%Appdata%\fw-fanctrl\config.json" > nul + echo installing 'config.json' in '%Appdata%\fw-fanctrl\config.json' + if not exist "%Appdata%\fw-fanctrl\config.json" echo n | copy /-y ".\config.json" "%Appdata%\fw-fanctrl\config.json" > nul - echo creating 'fw-fanctrl' service - @echo on - "%ProgramFiles%\nssm\nssm" install "fw-fanctrl" "%ProgramFiles%\fw-fanctrl\run-service.bat" - "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Start "SERVICE_DELAYED_AUTO_START" - "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" DisplayName "Framework Fanctrl" - "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Description "A simple systemd service to better control Framework Laptop's fan(s)" - "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppExit "%ProgramFiles%\ectool\ectool autofanctrl" - "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStdout "%ProgramFiles%\fw-fanctrl\out.log" - "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" - @echo off + echo creating 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" install "fw-fanctrl" "%ProgramFiles%\fw-fanctrl\run-service.bat" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Start "SERVICE_DELAYED_AUTO_START" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" DisplayName "Framework Fanctrl" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" Description "A simple systemd service to better control Framework Laptop's fan(s)" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppExit "%ProgramFiles%\ectool\ectool autofanctrl" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStdout "%ProgramFiles%\fw-fanctrl\out.log" + "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" + @echo off - GOTO :EOF GOTO :EOF +GOTO :EOF :UNINSTALL echo uninstalling @@ -405,64 +409,66 @@ GOTO :EOF pause GOTO :EOF +GOTO :EOF - :uninstall-fw-fanctrl - echo removing 'fw-fanctrl' +:uninstall-fw-fanctrl + echo removing 'fw-fanctrl' - echo stopping 'fw-fanctrl' service - @echo on - "%ProgramFiles%\nssm\nssm" stop "fw-fanctrl" - @echo off + echo stopping 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" stop "fw-fanctrl" + @echo off - echo removing 'fw-fanctrl' service - @echo on - "%ProgramFiles%\nssm\nssm" remove "fw-fanctrl" confirm - @echo off + echo removing 'fw-fanctrl' service + @echo on + "%ProgramFiles%\nssm\nssm" remove "fw-fanctrl" confirm + @echo off - echo removing directory '%ProgramFiles%\fw-fanctrl' - rmdir /s /q "%ProgramFiles%\fw-fanctrl" 2> nul + echo removing directory '%ProgramFiles%\fw-fanctrl' + rmdir /s /q "%ProgramFiles%\fw-fanctrl" 2> nul - GOTO :EOF + GOTO :EOF +GOTO :EOF - :uninstall-nssm - echo removing 'nssm' +:uninstall-nssm + echo removing 'nssm' - echo removing directory '%ProgramFiles%\nssm' - rmdir /s /q "%ProgramFiles%\nssm" 2> nul + echo removing directory '%ProgramFiles%\nssm' + rmdir /s /q "%ProgramFiles%\nssm" 2> nul - GOTO :EOF + GOTO :EOF +GOTO :EOF - :uninstall-ectool - echo removing 'ectool' +:uninstall-ectool + echo removing 'ectool' - echo setting the fan control back to normal - @echo on - "%ProgramFiles%\ectool\ectool" autofanctrl - @echo off + echo setting the fan control back to normal + @echo on + "%ProgramFiles%\ectool\ectool" autofanctrl + @echo off - echo removing directory '%ProgramFiles%\ectool' - rmdir /s /q "%ProgramFiles%\ectool" 2> nul + echo removing directory '%ProgramFiles%\ectool' + rmdir /s /q "%ProgramFiles%\ectool" 2> nul - GOTO :EOF + GOTO :EOF +GOTO :EOF - :uninstall-crosec - echo removing 'crosec' - - echo uninstalling 'crosec' driver - @echo on - "%ProgramFiles%\crosec\installer" uninstall - @echo off +:uninstall-crosec + echo removing 'crosec' - echo removing directory '%ProgramFiles%\crosec' - rmdir /s /q "%ProgramFiles%\crosec" 2> nul + echo uninstalling 'crosec' driver + @echo on + "%ProgramFiles%\crosec\installer" uninstall + @echo off - echo disabling 'bcdedit testsigning' - bcdedit /set {default} testsigning off + echo removing directory '%ProgramFiles%\crosec' + rmdir /s /q "%ProgramFiles%\crosec" 2> nul - GOTO :EOF + echo disabling 'bcdedit testsigning' + bcdedit /set {default} testsigning off GOTO :EOF - +GOTO :EOF :ARG-PARSER :: Loop until two consecutive empty args From 7f174432e0605c4e29d88518fd259c87f7452bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Tue, 13 Aug 2024 23:12:56 +0200 Subject: [PATCH 21/35] small fixes after merge --- fanctrl.py | 3 +-- services/fw-fanctrl.service | 0 services/windows/run-service.bat | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 services/fw-fanctrl.service diff --git a/fanctrl.py b/fanctrl.py index cc076cf..f94509c 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -13,7 +13,7 @@ from time import sleep from abc import ABC, abstractmethod -DEFAULT_CONFIGURATION_FILE_PATH = "/etc/fw-fanctrl/config.json" +DEFAULT_CONFIGURATION_FILE_PATH = "%%appdata%%/fw-fanctrl/config.json" WINDOWS_SOCKET_PATH = r"\\.\pipe\fw-fanctrl.socket" @@ -26,7 +26,6 @@ class CommandParser: def __init__(self, isRemote=False): self.isRemote = isRemote self.initParser() - self.initLegacyParser() def initParser(self): self.parser = argparse.ArgumentParser( diff --git a/services/fw-fanctrl.service b/services/fw-fanctrl.service deleted file mode 100644 index e69de29..0000000 diff --git a/services/windows/run-service.bat b/services/windows/run-service.bat index c3b083a..d01a925 100644 --- a/services/windows/run-service.bat +++ b/services/windows/run-service.bat @@ -2,7 +2,7 @@ @cd /d "%~dp0" -fw-fanctrl --run --config "####CONFIG_PATH####" --no-log --socket-controller win32 & ectool autofanctrl +fw-fanctrl run --config "####CONFIG_PATH####" --silent & ectool autofanctrl @echo "waiting 5 seconds before retrying..." @timeout 5 > NUL From 80a239dfae6ff11c1e7dfc2c72d823254e114c96 Mon Sep 17 00:00:00 2001 From: Oli Thornton <50975890+saizo80@users.noreply.github.com> Date: Sun, 18 Aug 2024 14:14:02 -0500 Subject: [PATCH 22/35] add no battery mode for mainboards without battery (#69) * add configuration for no battery mode in hardware controller * fix wrong line getting noBatteryMode * dynamically fetching battery sensor on init/reload * add --no-battery flag for install * update readme with --no-battery flag * rework no battery config to come from service args * change sensors to be ectool specific - reword the argument to be more clear about battery sensors - move `noBatteryMode` and `nonBatterySensors` to EctoolHardwareController - update `getNonBatterySensors` to be able to handle more than one sensor - update installer and readme accordingly * update grep command for checking existing `--no-battery-sensors` * combine getTemperature functions to one * add documentation for run option `--no-battery-sensors` * rename variable `NO_BATTERY` to `NO_BATTERY_SENSOR` * update the installer to use existing placeholder format * rename noBatterySensorMode variables and functions for clarity * rename placeholder to `NO_BATTERY_SENSOR_OPTION` for clarity * update comments in installer to reflect new argument name --- README.md | 2 ++ fanctrl.py | 33 ++++++++++++++++++++++++++++++--- install.sh | 13 +++++++++++-- services/fw-fanctrl.service | 2 +- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index de38c65..b275cfc 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ You can add a number of arguments to the installation command to suit your needs | `--no-ectool` | disable ectool installation and service activation | | `--no-post-install` | disable post-install process | | `--no-pre-uninstall` | disable pre-uninstall process | +| `--no-battery-sensors` | disable checking battery tempurature sensors | ## Update @@ -216,6 +217,7 @@ never need those. | --config | yes | \[CONFIG_PATH] | | the configuration file path | | --silent, -s | yes | | | disable printing speed/temp status to stdout | | --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | +| --no-battery-sensors | yes | | | disable checking battery tempurature sensors (for mainboards without batteries) | **use** diff --git a/fanctrl.py b/fanctrl.py index 30c695a..1c07c6e 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -81,6 +81,11 @@ def initParser(self): choices=["ectool"], default="ectool" ) + runCommand.add_argument( + "--no-battery-sensors", + help="disable checking battery tempurature sensors", + action="store_true", + ) useCommand = commandsSubParser.add_parser( "use", @@ -429,9 +434,31 @@ def isOnAC(self): class EctoolHardwareController(HardwareController, ABC): + noBatterySensorMode = False + nonBatterySensors = None + + def __init__(self, noBatterySensorMode=False): + if noBatterySensorMode: + self.noBatterySensorMode = True + self.populateNonBatterySensors() + + def populateNonBatterySensors(self): + self.nonBatterySensors = [] + rawOut = subprocess.run("ectool tempsinfo all", stdout=subprocess.PIPE, shell=True, text=True).stdout + batterySensorsRaw = re.findall(r"\d+ Battery", rawOut, re.MULTILINE) + batterySensors = [x.split(" ")[0] for x in batterySensorsRaw] + for x in re.findall(r"^\d+", rawOut, re.MULTILINE): + if x not in batterySensors: + self.nonBatterySensors.append(x) def getTemperature(self): - rawOut = subprocess.run("ectool temps all", stdout=subprocess.PIPE, shell=True, text=True).stdout + if self.noBatterySensorMode: + rawOut = "".join([ + subprocess.run("ectool temps " + x, stdout=subprocess.PIPE, shell=True, text=True).stdout + for x in self.nonBatterySensors + ]) + else: + rawOut = subprocess.run("ectool temps all", stdout=subprocess.PIPE, shell=True, text=True).stdout rawTemps = re.findall(r'\(= (\d+) C\)', rawOut) temps = sorted([x for x in [int(x) for x in rawTemps] if x > 0], reverse=True) # safety fallback to avoid damaging hardware @@ -612,9 +639,9 @@ def main(): socketController = UnixSocketController() if args.command == "run": - hardwareController = EctoolHardwareController() + hardwareController = EctoolHardwareController(noBatterySensorMode=args.no_battery_sensors) if args.hardware_controller == "ectool": - hardwareController = EctoolHardwareController() + hardwareController = EctoolHardwareController(noBatterySensorMode=args.no_battery_sensors) fan = FanController(hardwareController=hardwareController, socketController=socketController, configPath=args.config, strategyName=args.strategy) diff --git a/install.sh b/install.sh index 2ae6cfa..3280d96 100755 --- a/install.sh +++ b/install.sh @@ -8,7 +8,7 @@ fi # Argument parsing SHORT=r,d:,p:,s:,h -LONG=remove,dest-dir:,prefix-dir:,sysconf-dir:,no-ectool,no-pre-uninstall,no-post-install,help +LONG=remove,dest-dir:,prefix-dir:,sysconf-dir:,no-ectool,no-pre-uninstall,no-post-install,no-battery-sensors,help VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") if [[ $? -ne 0 ]]; then exit 1; @@ -24,6 +24,7 @@ SHOULD_INSTALL_ECTOOL=true SHOULD_PRE_UNINSTALL=true SHOULD_POST_INSTALL=true SHOULD_REMOVE=false +NO_BATTERY_SENSOR=false eval set -- "$VALID_ARGS" while true; do @@ -52,6 +53,9 @@ while true; do '--no-post-install') SHOULD_POST_INSTALL=false ;; + '--no-battery-sensors') + NO_BATTERY_SENSOR=true + ;; '--help' | '-h') echo "Usage: $0 [--remove,-r] [--dest-dir,-d ] [--prefix-dir,-p ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)] [--no-ectool] [--no-post-install] [--no-pre-uninstall]" 1>&2 exit 0 @@ -139,6 +143,11 @@ function install() { cp -n "./config.json" "$DEST_DIR$SYSCONF_DIR/fw-fanctrl" 2> "/dev/null" || true + # add --no-battery-sensors flag to the fanctrl service if specified + if [ "$NO_BATTERY_SENSOR" = true ]; then + NO_BATTERY_SENSOR_OPTION="--no-battery-sensors" + fi + # create program services based on the services present in the './services' folder echo "creating '$DEST_DIR$PREFIX_DIR/lib/systemd/system'" mkdir -p "$DEST_DIR$PREFIX_DIR/lib/systemd/system" @@ -150,7 +159,7 @@ function install() { systemctl stop "$SERVICE" fi echo "creating '$DEST_DIR$PREFIX_DIR/lib/systemd/system/$SERVICE$SERVICE_EXTENSION'" - cat "$SERVICES_DIR/$SERVICE$SERVICE_EXTENSION" | sed -e "s/%PREFIX_DIRECTORY%/${PREFIX_DIR//\//\\/}/" | sed -e "s/%SYSCONF_DIRECTORY%/${SYSCONF_DIR//\//\\/}/" | tee "$DEST_DIR$PREFIX_DIR/lib/systemd/system/$SERVICE$SERVICE_EXTENSION" > "/dev/null" + cat "$SERVICES_DIR/$SERVICE$SERVICE_EXTENSION" | sed -e "s/%PREFIX_DIRECTORY%/${PREFIX_DIR//\//\\/}/" | sed -e "s/%SYSCONF_DIRECTORY%/${SYSCONF_DIR//\//\\/}/" | sed -e "s/%NO_BATTERY_SENSOR_OPTION%/${NO_BATTERY_SENSOR_OPTION}/" | tee "$DEST_DIR$PREFIX_DIR/lib/systemd/system/$SERVICE$SERVICE_EXTENSION" > "/dev/null" done # add program services sub-configurations based on the sub-configurations present in the './services' folder diff --git a/services/fw-fanctrl.service b/services/fw-fanctrl.service index 0bcac31..ec7d46c 100644 --- a/services/fw-fanctrl.service +++ b/services/fw-fanctrl.service @@ -4,7 +4,7 @@ After=multi-user.target [Service] Type=simple Restart=always -ExecStart=/usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" run --config "%SYSCONF_DIRECTORY%/fw-fanctrl/config.json" --silent +ExecStart=/usr/bin/python3 "%PREFIX_DIRECTORY%/bin/fw-fanctrl" run --config "%SYSCONF_DIRECTORY%/fw-fanctrl/config.json" --silent %NO_BATTERY_SENSOR_OPTION% ExecStopPost=/bin/sh -c "ectool autofanctrl" [Install] WantedBy=multi-user.target From cc65f267759d983d24701dd1b4758748e3feb483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sun, 18 Aug 2024 22:48:14 +0200 Subject: [PATCH 23/35] adding ectool sub-dependency to documentation (#70) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b275cfc..3daa5f8 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ If the service is paused or stopped, the fans will revert to their default behav Dependencies are downloaded and installed automatically, but can be excluded from the installation script if you wish to do this manually. -| name | version | url | exclusion argument | -|----------------|-----------|--------------------------------------------------------------------------------------|--------------------| -| DHowett@ectool | build#899 | [https://gitlab.howett.net/DHowett/ectool](https://gitlab.howett.net/DHowett/ectool) | `--no-ectool` | +| name | version | url | sub-dependencies | exclusion argument | +|----------------|-----------|--------------------------------------------------------------------------------------|------------------|--------------------| +| DHowett@ectool | build#899 | [https://gitlab.howett.net/DHowett/ectool](https://gitlab.howett.net/DHowett/ectool) | libftdi | `--no-ectool` | ### Instructions From 88590e3416f4ff0bf148c20bd9c5860bb859e8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sun, 18 Aug 2024 23:27:39 +0200 Subject: [PATCH 24/35] typo "tempurature" => "temperature" (#71) typo "tempurature" => "temperature" --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3daa5f8..8f79b48 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ You can add a number of arguments to the installation command to suit your needs | `--no-ectool` | disable ectool installation and service activation | | `--no-post-install` | disable post-install process | | `--no-pre-uninstall` | disable pre-uninstall process | -| `--no-battery-sensors` | disable checking battery tempurature sensors | +| `--no-battery-sensors` | disable checking battery temperature sensors | ## Update @@ -217,7 +217,7 @@ never need those. | --config | yes | \[CONFIG_PATH] | | the configuration file path | | --silent, -s | yes | | | disable printing speed/temp status to stdout | | --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | -| --no-battery-sensors | yes | | | disable checking battery tempurature sensors (for mainboards without batteries) | +| --no-battery-sensors | yes | | | disable checking battery temperature sensors (for mainboards without batteries) | **use** From 6d2d3de8151b82d3ca207f0ed6a0198e16762f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sun, 18 Aug 2024 23:28:57 +0200 Subject: [PATCH 25/35] typo "tempurature" => "temperature" (#72) typo "tempurature" => "temperature" --- fanctrl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fanctrl.py b/fanctrl.py index 1c07c6e..93b6a39 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -83,7 +83,7 @@ def initParser(self): ) runCommand.add_argument( "--no-battery-sensors", - help="disable checking battery tempurature sensors", + help="disable checking battery temperature sensors", action="store_true", ) From 6681c6bce7940ca12c09b2c1493022625b8ceb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Sun, 18 Aug 2024 23:30:20 +0200 Subject: [PATCH 26/35] add "/no-battery-sensors" installation argument --- README.md | 6 ++++++ install.bat | 9 ++++++++- services/windows/run-service.bat | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6b8548..bbcfae6 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,12 @@ Then run the installation script with administrator privileges (by double clicki install.bat ``` +You can add a number of arguments to the installation command to suit your needs + +| argument | description | +|-----------------------|----------------------------------------------| +| `/no-battery-sensors` | disable checking battery temperature sensors | + ## Update To update, you can download or pull the appropriate branch from this repository, and run the installation script again. diff --git a/install.bat b/install.bat index a0eb117..0af45d4 100644 --- a/install.bat +++ b/install.bat @@ -22,6 +22,7 @@ echo running with administrative privileges... set "ARG_remove=" set "ARG_r=" +set "ARG_no-battery-sensor=" CALL :ARG-PARSER %* @@ -359,7 +360,13 @@ GOTO :EOF exit /b 4 ) - powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\run-service.bat') -replace '####CONFIG_PATH####', '%Appdata%\fw-fanctrl\config.json' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\run-service.bat'" + set "no-battery-sensor-option=" + + if defined ARG_no-battery-sensor ( + set "no-battery-sensor-option=--no-battery-sensors" + ) + + powershell -Command "(gc '%ProgramFiles%\fw-fanctrl\run-service.bat') -replace '####CONFIG_PATH####', '%Appdata%\fw-fanctrl\config.json' -replace '####NO_BATTERY_SENSOR_OPTION####', '%no-battery-sensor-option%' | Out-File -encoding ASCII '%ProgramFiles%\fw-fanctrl\run-service.bat'" echo installing '.\services\windows\fw-fanctrl.bat' to '%ProgramFiles%\fw-fanctrl' @echo on diff --git a/services/windows/run-service.bat b/services/windows/run-service.bat index d01a925..c408ba4 100644 --- a/services/windows/run-service.bat +++ b/services/windows/run-service.bat @@ -2,7 +2,7 @@ @cd /d "%~dp0" -fw-fanctrl run --config "####CONFIG_PATH####" --silent & ectool autofanctrl +fw-fanctrl run --config "####CONFIG_PATH####" --silent ####NO_BATTERY_SENSOR_OPTION#### & ectool autofanctrl @echo "waiting 5 seconds before retrying..." @timeout 5 > NUL From a961cfae40dc34431829a75569a2bb02fba5265c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Thu, 22 Aug 2024 01:31:13 +0200 Subject: [PATCH 27/35] adding service pause on sleep and resume on wake --- install.bat | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/install.bat b/install.bat index 0af45d4..654158e 100644 --- a/install.bat +++ b/install.bat @@ -397,6 +397,12 @@ GOTO :EOF "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" @echo off + echo creating service pause/resume on sleep/wake tasks + @echo on + schtasks /create /tn "fw-fanctrl_pauseOnSleep" /tr "powershell.exe -Command 'fw-fanctrl pause'" /sc onevent /ec System /mo *[System[Provider[@Name='Kernel-Power'] and (EventID=42 or EventID=40)]] /ru SYSTEM /RL HIGHEST + schtasks /create /tn "fw-fanctrl_resumeOnWake" /tr "powershell.exe -Command 'fw-fanctrl resume'" /sc onevent /ec System /mo *[System[Provider[@Name='Power-Troubleshooter'] and (EventID=1)]] /ru SYSTEM /RL HIGHEST + @echo off + GOTO :EOF GOTO :EOF @@ -421,6 +427,12 @@ GOTO :EOF :uninstall-fw-fanctrl echo removing 'fw-fanctrl' + echo removing service pause/resume on sleep/wake tasks + @echo on + schtasks /delete /tn "fw-fanctrl_pauseOnSleep" /f + schtasks /delete /tn "fw-fanctrl_resumeOnWake" /f + @echo off + echo stopping 'fw-fanctrl' service @echo on "%ProgramFiles%\nssm\nssm" stop "fw-fanctrl" From 23d53edc7fe02bc35b43c6771a577026f30915e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Thu, 22 Aug 2024 01:31:13 +0200 Subject: [PATCH 28/35] adding service pause on sleep and resume on wake --- install.bat | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/install.bat b/install.bat index 0af45d4..e8c380a 100644 --- a/install.bat +++ b/install.bat @@ -397,6 +397,12 @@ GOTO :EOF "%ProgramFiles%\nssm\nssm" set "fw-fanctrl" AppStderr "%ProgramFiles%\fw-fanctrl\out.log" @echo off + echo creating service pause/resume on sleep/wake tasks + @echo on + schtasks /create /tn "fw-fanctrl_pauseOnSleep" /tr "powershell.exe -Command 'fw-fanctrl pause'" /sc onevent /ec System /mo "*[System[Provider[@Name='Kernel-Power'] and (EventID=42 or EventID=40)]]" /ru SYSTEM /RL HIGHEST + schtasks /create /tn "fw-fanctrl_resumeOnWake" /tr "powershell.exe -Command 'fw-fanctrl resume'" /sc onevent /ec System /mo "*[System[Provider[@Name='Power-Troubleshooter'] and (EventID=1)]]" /ru SYSTEM /RL HIGHEST + @echo off + GOTO :EOF GOTO :EOF @@ -421,6 +427,12 @@ GOTO :EOF :uninstall-fw-fanctrl echo removing 'fw-fanctrl' + echo removing service pause/resume on sleep/wake tasks + @echo on + schtasks /delete /tn "fw-fanctrl_pauseOnSleep" /f + schtasks /delete /tn "fw-fanctrl_resumeOnWake" /f + @echo off + echo stopping 'fw-fanctrl' service @echo on "%ProgramFiles%\nssm\nssm" stop "fw-fanctrl" From ec9a9ae939515b6da534f5a2b63427f2dcc3b1ea Mon Sep 17 00:00:00 2001 From: Svenum <43136984+Svenum@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:18:41 +0200 Subject: [PATCH 29/35] Add ToC + link to NixOS Documentation (#75) * add doc folder * update nix link * add toc * add link * add missin # * add doc * fix link * add new line under titles --- README.md | 181 +++++-------------------------------------- doc/README.md | 6 ++ doc/commands.md | 62 +++++++++++++++ doc/configuration.md | 104 +++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 161 deletions(-) create mode 100644 doc/README.md create mode 100644 doc/commands.md create mode 100644 doc/configuration.md diff --git a/README.md b/README.md index 8f79b48..013bc4a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## Additional platforms: -[![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix)](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) +[![Static Badge](https://img.shields.io/badge/NixOS-5277C3?style=flat&logo=nixos&logoColor=FFFFFF&label=Platform&link=https%3A%2F%2Fgithub.com%2FTamtamHero%2Ffw-fanctrl%2Ftree%2Fpackaging%2Fnix)](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix/doc/nix-flake.md) ## Description @@ -26,8 +26,27 @@ It is compatible with all 13" and 16" models, both AMD/Intel CPUs, with or witho If the service is paused or stopped, the fans will revert to their default behaviour. +## Table of Content + +- [Documentation](#documentation) +- [Installation](#installation) + * [Requirements](#requirements) + * [Dependencies](#dependencies) + * [Instructions](#instructions) +- [Update](#update) +- [Uninstall](#uninstall) + +## Documentation + +More documentation could be found [here](./doc/README.md). + ## Installation +### Other Platforms +| name | branch | documentation | +|-------|---------------|---------------| +| NixOS | [packaging/nix](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) | [packaging/nix/doc/nix-flake](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix/doc/nix-flake.md) | + ### Requirements | name | version | url | @@ -90,163 +109,3 @@ corresponding [arguments if necessary](#instructions) sudo ./install.sh --remove ``` -## Configuration - -After installation, you will find the configuration file in the following location: - -`/etc/fw-fanctrl/config.json` - -If you have modified the `dest-dir` or `sysconf-dir`, here is the corresponding pattern - -`[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` - -It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging -strategies. - -For example, one could use a lower fan speed strategy on discharging to optimise battery life (- noise, + heat), -and a high fan speed strategy on AC (+ noise, - heat). - -You can add or edit strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this -repo :) - -### Default strategy - -The default strategy is the one used when the service is started. - -It can be changed by replacing the value of the `defaultStrategy` field with one of the strategies present in the -configuration. - -```json -"defaultStrategy": "[STRATEGY NAME]" -``` - -### Charging/Discharging strategies - -The discharging strategy is the one that will be used when the laptop is not on AC, -Otherwise the default strategy is used. - -It can be changed by replacing the value of the `strategyOnDischarging` field with one of the strategies present in the -configuration. - -```json -"strategyOnDischarging": "[STRATEGY NAME]" -``` - -This is optional and can be left empty to have the same strategy at all times. - -### Editing strategies - -Strategies can be configured with the following parameters: - -> **SpeedCurve**: -> -> It is represented by the curve points for `f(temperature) = fan(s) speed`. -> -> ```json -> "speedCurve": [ -> { "temp": [TEMPERATURE POINT], "speed": [PERCENTAGE SPEED] }, -> ... -> ] -> ``` -> -> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an -> appropriate `fan speed` -> value by interpolating on the curve. - -> **FanSpeedUpdateFrequency**: -> -> It is the interval in seconds between fan speed calculations. -> -> ```json -> "fanSpeedUpdateFrequency": [UPDATE FREQUENCY] -> ``` -> -> This is for comfort, otherwise the speed will change too often, which is noticeable and annoying, especially at low -> speed. -> -> For a more responsive fan, you can reduce this setting. -> -> **Defaults to 5 seconds.** (minimum 1) - -> **MovingAverageInterval**: -> -> It is the number of seconds over which the moving average of temperature is calculated. -> -> ```json -> "movingAverageInterval": [AVERAGING INTERVAL] -> ``` -> -> Increase it, and the fan speed changes more gradually. Lower it, and it becomes more responsive. -> -> **Defaults to 20 seconds.** (minimum 1) - ---- - -Once the configuration has been changed, you must reload it with the following command - -```bash -fw-fanctrl reload -``` - -## Commands - -Here is a list of commands and options used to interact with the service. - -the base of all commands is the following - -```shell -fw-fanctrl [commands and options] -``` - -First, the global options - -| Option | Optional | Choices | Default | Description | -|---------------------------|----------|---------|---------|--------------------------------------------------------------------------------| -| --socket-controller, --sc | yes | unix | unix | the socket controller to use for communication between the cli and the service | - -**run** - -run the service manually - -If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will -never need those. - -| Option | Optional | Choices | Default | Description | -|-----------------------------|----------|----------------|----------------------|-----------------------------------------------------------------------------------| -| \ | yes | | the default strategy | the name of the strategy to use | -| --config | yes | \[CONFIG_PATH] | | the configuration file path | -| --silent, -s | yes | | | disable printing speed/temp status to stdout | -| --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | -| --no-battery-sensors | yes | | | disable checking battery temperature sensors (for mainboards without batteries) | - -**use** - -change the current strategy - -| Option | Optional | Description | -|-------------|----------|---------------------------------| -| \ | no | the name of the strategy to use | - -**reset** - -reset to the default strategy - -**reload** - -reload the configuration file - -**pause** - -pause the service - -**resume** - -resume the service - -**print** - -print the selected information - -| Option | Optional | Choices | Default | Description | -|--------------------|----------|---------------|---------|------------------------| -| \ | yes | current, list | current | what should be printed | diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..823fbff --- /dev/null +++ b/doc/README.md @@ -0,0 +1,6 @@ +# Table of Content + +- [Default Installation](../README.md#installation) +- [NixOS Flake](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix/doc/nix-flake.md) +- [Commands](./commands.md) +- [Configuration](./configuration.md) diff --git a/doc/commands.md b/doc/commands.md new file mode 100644 index 0000000..91b2fda --- /dev/null +++ b/doc/commands.md @@ -0,0 +1,62 @@ +# Commands + +Here is a list of commands and options used to interact with the service. + +the base of all commands is the following + +```shell +fw-fanctrl [commands and options] +``` + +First, the global options + +| Option | Optional | Choices | Default | Description | +|---------------------------|----------|---------|---------|--------------------------------------------------------------------------------| +| --socket-controller, --sc | yes | unix | unix | the socket controller to use for communication between the cli and the service | + +**run** + +run the service manually + +If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will +never need those. + +| Option | Optional | Choices | Default | Description | +|-----------------------------|----------|----------------|----------------------|-----------------------------------------------------------------------------------| +| \ | yes | | the default strategy | the name of the strategy to use | +| --config | yes | \[CONFIG_PATH] | | the configuration file path | +| --silent, -s | yes | | | disable printing speed/temp status to stdout | +| --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | +| --no-battery-sensors | yes | | | disable checking battery temperature sensors (for mainboards without batteries) | + +**use** + +change the current strategy + +| Option | Optional | Description | +|-------------|----------|---------------------------------| +| \ | no | the name of the strategy to use | + +**reset** + +reset to the default strategy + +**reload** + +reload the configuration file + +**pause** + +pause the service + +**resume** + +resume the service + +**print** + +print the selected information + +| Option | Optional | Choices | Default | Description | +|--------------------|----------|---------------|---------|------------------------| +| \ | yes | current, list | current | what should be printed | diff --git a/doc/configuration.md b/doc/configuration.md new file mode 100644 index 0000000..6f381fb --- /dev/null +++ b/doc/configuration.md @@ -0,0 +1,104 @@ +# Table of Content + +- [Configuration](#configuration) + * [Default strategy](#default-strategy) + * [Charging/Discharging strategies](#chargingdischarging-strategies) + * [Editing strategies](#editing-strategies) + +# Configuration + +After installation, you will find the configuration file in the following location: + +`/etc/fw-fanctrl/config.json` + +If you have modified the `dest-dir` or `sysconf-dir`, here is the corresponding pattern + +`[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` + +It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging +strategies. + +For example, one could use a lower fan speed strategy on discharging to optimise battery life (- noise, + heat), +and a high fan speed strategy on AC (+ noise, - heat). + +You can add or edit strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this +repo :) + +## Default strategy + +The default strategy is the one used when the service is started. + +It can be changed by replacing the value of the `defaultStrategy` field with one of the strategies present in the +configuration. + +```json +"defaultStrategy": "[STRATEGY NAME]" +``` + +## Charging/Discharging strategies + +The discharging strategy is the one that will be used when the laptop is not on AC, +Otherwise the default strategy is used. + +It can be changed by replacing the value of the `strategyOnDischarging` field with one of the strategies present in the +configuration. + +```json +"strategyOnDischarging": "[STRATEGY NAME]" +``` + +This is optional and can be left empty to have the same strategy at all times. + +## Editing strategies + +Strategies can be configured with the following parameters: + +> **SpeedCurve**: +> +> It is represented by the curve points for `f(temperature) = fan(s) speed`. +> +> ```json +> "speedCurve": [ +> { "temp": [TEMPERATURE POINT], "speed": [PERCENTAGE SPEED] }, +> ... +> ] +> ``` +> +> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an +> appropriate `fan speed` +> value by interpolating on the curve. + +> **FanSpeedUpdateFrequency**: +> +> It is the interval in seconds between fan speed calculations. +> +> ```json +> "fanSpeedUpdateFrequency": [UPDATE FREQUENCY] +> ``` +> +> This is for comfort, otherwise the speed will change too often, which is noticeable and annoying, especially at low +> speed. +> +> For a more responsive fan, you can reduce this setting. +> +> **Defaults to 5 seconds.** (minimum 1) + +> **MovingAverageInterval**: +> +> It is the number of seconds over which the moving average of temperature is calculated. +> +> ```json +> "movingAverageInterval": [AVERAGING INTERVAL] +> ``` +> +> Increase it, and the fan speed changes more gradually. Lower it, and it becomes more responsive. +> +> **Defaults to 20 seconds.** (minimum 1) + +--- + +Once the configuration has been changed, you must reload it with the following command + +```bash +fw-fanctrl reload +``` From fe98b44168e7fbc05adcdd6a0c4ef896085f8058 Mon Sep 17 00:00:00 2001 From: Svenum <43136984+Svenum@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:56:35 +0200 Subject: [PATCH 30/35] add --no-sudo option (#76) --- install.sh | 24 ++++++++++++++---------- post-install.sh | 20 ++++++++++++-------- pre-uninstall.sh | 19 +++++++++++-------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/install.sh b/install.sh index 3280d96..4a63e78 100755 --- a/install.sh +++ b/install.sh @@ -1,14 +1,9 @@ #!/bin/bash set -e -if [ "$EUID" -ne 0 ] - then echo "This program requires root permissions" - exit 1 -fi - # Argument parsing SHORT=r,d:,p:,s:,h -LONG=remove,dest-dir:,prefix-dir:,sysconf-dir:,no-ectool,no-pre-uninstall,no-post-install,no-battery-sensors,help +LONG=remove,dest-dir:,prefix-dir:,sysconf-dir:,no-ectool,no-pre-uninstall,no-post-install,no-battery-sensors,no-sudo,help VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") if [[ $? -ne 0 ]]; then exit 1; @@ -25,6 +20,7 @@ SHOULD_PRE_UNINSTALL=true SHOULD_POST_INSTALL=true SHOULD_REMOVE=false NO_BATTERY_SENSOR=false +NO_SUDO=false eval set -- "$VALID_ARGS" while true; do @@ -56,8 +52,11 @@ while true; do '--no-battery-sensors') NO_BATTERY_SENSOR=true ;; + '--no-sudo') + NO_SUDO=true + ;; '--help' | '-h') - echo "Usage: $0 [--remove,-r] [--dest-dir,-d ] [--prefix-dir,-p ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)] [--no-ectool] [--no-post-install] [--no-pre-uninstall]" 1>&2 + echo "Usage: $0 [--remove,-r] [--dest-dir,-d ] [--prefix-dir,-p ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)] [--no-ectool] [--no-post-install] [--no-pre-uninstall] [--no-sudo]" 1>&2 exit 0 ;; --) @@ -66,7 +65,12 @@ while true; do esac shift done -# + +# Root check +if [ "$EUID" -ne 0 ] && [ "$NO_SUDO" = false ] + then echo "This program requires root permissions ore use the '--no-sudo' option" + exit 1 +fi SERVICES_DIR="./services" SERVICE_EXTENSION=".service" @@ -93,7 +97,7 @@ function uninstall_legacy() { function uninstall() { if [ "$SHOULD_PRE_UNINSTALL" = true ]; then - ./pre-uninstall.sh + ./pre-uninstall.sh "$([ "$NO_SUDO" = true ] && echo "--no-sudo")" fi # remove program services based on the services present in the './services' folder echo "removing services" @@ -185,7 +189,7 @@ function install() { done done if [ "$SHOULD_POST_INSTALL" = true ]; then - ./post-install.sh --dest-dir "$DEST_DIR" --sysconf-dir "$SYSCONF_DIR" + ./post-install.sh --dest-dir "$DEST_DIR" --sysconf-dir "$SYSCONF_DIR" "$([ "$NO_SUDO" = true ] && echo "--no-sudo")" fi } diff --git a/post-install.sh b/post-install.sh index c92e74a..0a2f252 100755 --- a/post-install.sh +++ b/post-install.sh @@ -1,16 +1,12 @@ #!/bin/bash set -e -if [ "$EUID" -ne 0 ] - then echo "This program requires root permissions" - exit 1 -fi - HOME_DIR="$(eval echo "~$(logname)")" # Argument parsing +NO_SUDO=false SHORT=d:,s:,h -LONG=dest-dir:,sysconf-dir:,help +LONG=dest-dir:,sysconf-dir:,no-sudo,help VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") if [[ $? -ne 0 ]]; then exit 1; @@ -30,8 +26,11 @@ while true; do SYSCONF_DIR=$2 shift ;; + '--no-sudo') + NO_SUDO=true + ;; '--help' | '-h') - echo "Usage: $0 [--dest-dir,-d ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)]" 1>&2 + echo "Usage: $0 [--dest-dir,-d ] [--sysconf-dir,-s system configuration destination directory (defaults to $SYSCONF_DIR)] [--no-sudo]" 1>&2 exit 0 ;; --) @@ -40,7 +39,12 @@ while true; do esac shift done -# + +# Root check +if [ "$EUID" -ne 0 ] && [ "$NO_SUDO" = false ] + then echo "This program requires root permissions ore use the '--no-sudo' option" + exit 1 +fi SERVICES_DIR="./services" SERVICE_EXTENSION=".service" diff --git a/pre-uninstall.sh b/pre-uninstall.sh index 9ca3953..cc08cc9 100755 --- a/pre-uninstall.sh +++ b/pre-uninstall.sh @@ -1,16 +1,12 @@ #!/bin/bash set -e -if [ "$EUID" -ne 0 ] - then echo "This program requires root permissions" - exit 1 -fi - HOME_DIR="$(eval echo "~$(logname)")" # Argument parsing +NO_SUDO=false SHORT=h -LONG=help +LONG=no-sudo,help VALID_ARGS=$(getopt -a --options $SHORT --longoptions $LONG -- "$@") if [[ $? -ne 0 ]]; then exit 1; @@ -19,8 +15,11 @@ fi eval set -- "$VALID_ARGS" while true; do case "$1" in + '--no-sudo') + NO_SUDO=true + ;; '--help' | '-h') - echo "Usage: $0" 1>&2 + echo "Usage: $0 [--no-sudo]" 1>&2 exit 0 ;; --) @@ -29,7 +28,11 @@ while true; do esac shift done -# + +if [ "$EUID" -ne 0 ] + then echo "This program requires root permissions ore use the '--no-sudo' option" + exit 1 +fi SERVICES_DIR="./services" SERVICE_EXTENSION=".service" From 4a8a3a089cc6613700277a4071f29a0288c7b955 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 24 Aug 2024 16:55:51 -0400 Subject: [PATCH 31/35] Add choice to print fan speed percentage (#78) * Add option to print current speed percentage * Update README.md * Update commands.md * Add print choice descriptions to help text --- doc/commands.md | 12 +++++++++--- fanctrl.py | 10 +++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/doc/commands.md b/doc/commands.md index 91b2fda..5ef3294 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -57,6 +57,12 @@ resume the service print the selected information -| Option | Optional | Choices | Default | Description | -|--------------------|----------|---------------|---------|------------------------| -| \ | yes | current, list | current | what should be printed | +| Option | Optional | Choices | Default | Description | +|--------------------|----------|----------------------|---------|------------------------| +| \ | yes | current, list, speed | current | what should be printed | + +| Choice | Description | +|---------|----------------------------------| +| current | The current strategy being used | +| list | List available strategies | +| speed | The current fan speed percentage | diff --git a/fanctrl.py b/fanctrl.py index 93b6a39..703e465 100644 --- a/fanctrl.py +++ b/fanctrl.py @@ -115,15 +115,17 @@ def initParser(self): printCommand = commandsSubParser.add_parser( "print", - description="print the selected information" + description="print the selected information", + formatter_class=argparse.RawTextHelpFormatter ) printCommand.add_argument( "print_selection", - help="what should be printed", + help="current - The current strategy\nlist - List available strategies\nspeed - The current fan speed percentage", nargs="?", type=str, choices=["current", - "list"], + "list", + "speed"], default="current" ) @@ -564,6 +566,8 @@ def commandManager(self, args): return self.getCurrentStrategy().name elif args.print_selection == "list": return '\n'.join(self.configuration.getStrategies()) + elif args.print_selection == "speed": + return str(self.speed) + '%' return "Unknown command, unexpected." # return mean temperature over a given time interval (in seconds) From 2a9828f91620c0e8746ecb6875887b34da7888e3 Mon Sep 17 00:00:00 2001 From: Sven Ziegler <43136984+Svenum@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:10:38 +0200 Subject: [PATCH 32/35] add missing no_sudo check (#79) --- .gitignore | 3 +++ pre-uninstall.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c28b5ba..7bb0dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Nix Build dir +/result + ### Intellij template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 diff --git a/pre-uninstall.sh b/pre-uninstall.sh index cc08cc9..8607db6 100755 --- a/pre-uninstall.sh +++ b/pre-uninstall.sh @@ -29,7 +29,7 @@ while true; do shift done -if [ "$EUID" -ne 0 ] +if [ "$EUID" -ne 0 ] && [ "$NO_SUDO" = false ] then echo "This program requires root permissions ore use the '--no-sudo' option" exit 1 fi From b6a70a971214a86f39d84f95d09855fa48ffb7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Fri, 1 Nov 2024 16:48:55 +0100 Subject: [PATCH 33/35] fix typo (#84) --- install.sh | 2 +- post-install.sh | 2 +- pre-uninstall.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install.sh b/install.sh index 4a63e78..fd6843e 100755 --- a/install.sh +++ b/install.sh @@ -68,7 +68,7 @@ done # Root check if [ "$EUID" -ne 0 ] && [ "$NO_SUDO" = false ] - then echo "This program requires root permissions ore use the '--no-sudo' option" + then echo "This program requires root permissions or use the '--no-sudo' option" exit 1 fi diff --git a/post-install.sh b/post-install.sh index 0a2f252..b528406 100755 --- a/post-install.sh +++ b/post-install.sh @@ -42,7 +42,7 @@ done # Root check if [ "$EUID" -ne 0 ] && [ "$NO_SUDO" = false ] - then echo "This program requires root permissions ore use the '--no-sudo' option" + then echo "This program requires root permissions or use the '--no-sudo' option" exit 1 fi diff --git a/pre-uninstall.sh b/pre-uninstall.sh index 8607db6..66c771c 100755 --- a/pre-uninstall.sh +++ b/pre-uninstall.sh @@ -30,7 +30,7 @@ while true; do done if [ "$EUID" -ne 0 ] && [ "$NO_SUDO" = false ] - then echo "This program requires root permissions ore use the '--no-sudo' option" + then echo "This program requires root permissions or use the '--no-sudo' option" exit 1 fi From 2e03bc2aca5c5d6b096c61faf531e4b8df541b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Fri, 1 Nov 2024 20:10:46 +0100 Subject: [PATCH 34/35] updated TOC and opted for an autogenerated one (will be easier to keep up to date) (#89) --- README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 013bc4a..bcb14de 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,20 @@ If the service is paused or stopped, the fans will revert to their default behav ## Table of Content -- [Documentation](#documentation) -- [Installation](#installation) - * [Requirements](#requirements) - * [Dependencies](#dependencies) - * [Instructions](#instructions) -- [Update](#update) -- [Uninstall](#uninstall) + +* [fw-fanctrl](#fw-fanctrl) + * [Additional platforms:](#additional-platforms) + * [Description](#description) + * [Table of Content](#table-of-content) + * [Documentation](#documentation) + * [Installation](#installation) + * [Other Platforms](#other-platforms) + * [Requirements](#requirements) + * [Dependencies](#dependencies) + * [Instructions](#instructions) + * [Update](#update) + * [Uninstall](#uninstall) + ## Documentation From 4ba02b2f9dbdf1d965d295dc1f351b93092b1f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9opold=20Hubert?= Date: Fri, 1 Nov 2024 20:30:05 +0100 Subject: [PATCH 35/35] merged main --- README.md | 172 +++---------------------------------------- doc/README.md | 3 +- doc/commands.md | 30 ++++++-- doc/configuration.md | 11 ++- 4 files changed, 38 insertions(+), 178 deletions(-) diff --git a/README.md b/README.md index 6ceadd1..3bb9ca5 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,11 @@ More documentation could be found [here](./doc/README.md). ## Installation ### Other Platforms -| name | branch | documentation | -|-------|---------------|---------------| -| NixOS | [packaging/nix](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) | [packaging/nix/doc/nix-flake](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix/doc/nix-flake.md) | + +| name | branch | documentation | +|--------------|------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| +| Linux/Global | [main](https://github.com/TamtamHero/fw-fanctrl/tree/main) | [main/doc](https://github.com/TamtamHero/fw-fanctrl/tree/main/doc/README.md) | +| NixOS | [packaging/nix](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix) | [packaging/nix/doc/nix-flake](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix/doc/nix-flake.md) | ### Requirements @@ -73,7 +75,8 @@ Dependencies are downloaded and installed automatically. ### Instructions -Please note that the windows version of this service uses an unsigned experimental [crosec](https://github.com/DHowett/FrameworkWindowsUtils) driver that may be unstable. +Please note that the windows version of this service uses an unsigned +experimental [crosec](https://github.com/DHowett/FrameworkWindowsUtils) driver that may be unstable. We are not responsible for any damage or data loss that this may cause. First, make sure that you have disabled secure boot in your BIOS/UEFI settings. @@ -87,8 +90,8 @@ YOU GET LOCKED OUT OF YOUR COMPUTER IF YOU ARE NOT CAREFUL ENOUGH ! ============================================================================ ``` -[Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/packaging/windows.zip) and extract it manually, or -download/clone it with the appropriate tools: +[Download the repo](https://github.com/TamtamHero/fw-fanctrl/archive/refs/heads/packaging/windows.zip) and extract it +manually, or download/clone it with the appropriate tools: ```shell git clone --branch "packaging/windows" "https://github.com/TamtamHero/fw-fanctrl.git" @@ -121,160 +124,3 @@ To uninstall, run the uninstallation script `uninstall.bat` (by double clicking ```shell uninstall.bat ``` - -## Configuration - -After installation, you will find the configuration file in the following location: - -`%Appdata%\fw-fanctrl\config.json` - -It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging -strategies. - -For example, one could use a lower fan speed strategy on discharging to optimise battery life (- noise, + heat), -and a high fan speed strategy on AC (+ noise, - heat). - -You can add or edit strategies, and if you think you have one that deserves to be shared, feel free to make a PR to this -repo :) - -### Default strategy - -The default strategy is the one used when the service is started. - -It can be changed by replacing the value of the `defaultStrategy` field with one of the strategies present in the -configuration. - -```json -"defaultStrategy": "[STRATEGY NAME]" -``` - -### Charging/Discharging strategies - -The discharging strategy is the one that will be used when the laptop is not on AC, -Otherwise the default strategy is used. - -It can be changed by replacing the value of the `strategyOnDischarging` field with one of the strategies present in the -configuration. - -```json -"strategyOnDischarging": "[STRATEGY NAME]" -``` - -This is optional and can be left empty to have the same strategy at all times. - -### Editing strategies - -Strategies can be configured with the following parameters: - -> **SpeedCurve**: -> -> It is represented by the curve points for `f(temperature) = fan(s) speed`. -> -> ```json -> "speedCurve": [ -> { "temp": [TEMPERATURE POINT], "speed": [PERCENTAGE SPEED] }, -> ... -> ] -> ``` -> -> `fw-fanctrl` measures the CPU temperature, calculates a moving average of it, and then finds an -> appropriate `fan speed` -> value by interpolating on the curve. - -> **FanSpeedUpdateFrequency**: -> -> It is the interval in seconds between fan speed calculations. -> -> ```json -> "fanSpeedUpdateFrequency": [UPDATE FREQUENCY] -> ``` -> -> This is for comfort, otherwise the speed will change too often, which is noticeable and annoying, especially at low -> speed. -> -> For a more responsive fan, you can reduce this setting. -> -> **Defaults to 5 seconds.** (minimum 1) - -> **MovingAverageInterval**: -> -> It is the number of seconds over which the moving average of temperature is calculated. -> -> ```json -> "movingAverageInterval": [AVERAGING INTERVAL] -> ``` -> -> Increase it, and the fan speed changes more gradually. Lower it, and it becomes more responsive. -> -> **Defaults to 20 seconds.** (minimum 1) - ---- - -Once the configuration has been changed, you must reload it with the following command - -```bash -fw-fanctrl reload -``` - -## Commands - -Here is a list of commands and options used to interact with the service. - -the base of all commands is the following - -```shell -fw-fanctrl [commands and options] -``` - -First, the global options - -| Option | Optional | Choices | Default | Description | -|---------------------------|----------|---------|---------|--------------------------------------------------------------------------------| -| --socket-controller, --sc | yes | win32 | win32 | the socket controller to use for communication between the cli and the service | - -**run** - -run the service manually - -If you have installed it correctly, the systemd `fw-fanctrl.service` service will do this for you, so you probably will -never need those. - -| Option | Optional | Choices | Default | Description | -|-----------------------------|----------|----------------|----------------------|-----------------------------------------------------------------------------------| -| \ | yes | | the default strategy | the name of the strategy to use | -| --config | yes | \[CONFIG_PATH] | | the configuration file path | -| --silent, -s | yes | | | disable printing speed/temp status to stdout | -| --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | -| --no-battery-sensors | yes | | | disable checking battery temperature sensors (for mainboards without batteries) | - -**use** - -change the current strategy - -| Option | Optional | Description | -|-------------|----------|---------------------------------| -| \ | no | the name of the strategy to use | - -**reset** - -reset to the default strategy - -**reload** - -reload the configuration file - -**pause** - -pause the service - -**resume** - -resume the service - -**print** - -print the selected information - -| Option | Optional | Choices | Default | Description | -|--------------------|----------|---------------|---------|------------------------| -| \ | yes | current, list | current | what should be printed | \ No newline at end of file diff --git a/doc/README.md b/doc/README.md index 823fbff..bb83560 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,6 +1,7 @@ # Table of Content -- [Default Installation](../README.md#installation) +- [Windows Installation](../README.md#installation) +- [Linux/Global](https://github.com/TamtamHero/fw-fanctrl/tree/main#installation) - [NixOS Flake](https://github.com/TamtamHero/fw-fanctrl/tree/packaging/nix/doc/nix-flake.md) - [Commands](./commands.md) - [Configuration](./configuration.md) diff --git a/doc/commands.md b/doc/commands.md index 5ef3294..a0be05d 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -1,3 +1,17 @@ +# Table of Content + + +* [Table of Content](#table-of-content) +* [Commands](#commands) + * [run](#run) + * [use](#use) + * [reset](#reset) + * [reload](#reload) + * [pause](#pause) + * [resume](#resume) + * [print](#print) + + # Commands Here is a list of commands and options used to interact with the service. @@ -12,9 +26,9 @@ First, the global options | Option | Optional | Choices | Default | Description | |---------------------------|----------|---------|---------|--------------------------------------------------------------------------------| -| --socket-controller, --sc | yes | unix | unix | the socket controller to use for communication between the cli and the service | +| --socket-controller, --sc | yes | win32 | win32 | the socket controller to use for communication between the cli and the service | -**run** +## run run the service manually @@ -29,7 +43,7 @@ never need those. | --hardware-controller, --hc | yes | ectool | ectool | the hardware controller to use for fetching and setting the temp and fan(s) speed | | --no-battery-sensors | yes | | | disable checking battery temperature sensors (for mainboards without batteries) | -**use** +## use change the current strategy @@ -37,23 +51,23 @@ change the current strategy |-------------|----------|---------------------------------| | \ | no | the name of the strategy to use | -**reset** +## reset reset to the default strategy -**reload** +## reload reload the configuration file -**pause** +## pause pause the service -**resume** +## resume resume the service -**print** +## print print the selected information diff --git a/doc/configuration.md b/doc/configuration.md index 6f381fb..241ef39 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -1,19 +1,18 @@ # Table of Content -- [Configuration](#configuration) + +* [Table of Content](#table-of-content) +* [Configuration](#configuration) * [Default strategy](#default-strategy) * [Charging/Discharging strategies](#chargingdischarging-strategies) * [Editing strategies](#editing-strategies) + # Configuration After installation, you will find the configuration file in the following location: -`/etc/fw-fanctrl/config.json` - -If you have modified the `dest-dir` or `sysconf-dir`, here is the corresponding pattern - -`[dest-dir(/)][sysconf-dir(/etc)]/fw-fanctrl/config.json` +`%Appdata%\fw-fanctrl\config.json` It contains a list of strategies, ranked from the quietest to loudest, as well as the default and discharging strategies.