Skip to content

Commit 7062c20

Browse files
committed
Initial commit
0 parents  commit 7062c20

29 files changed

+2493
-0
lines changed

.golangci.toml

Whitespace-only changes.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Ivan Safonov
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# ⚠️ Warning ⚠️
2+
3+
Think twice before use it. You use on your own risk.
4+
Overheating can destroy your hardware.
5+
6+
# 💡 About
7+
8+
That's a small service to control fan speed. It reads temperature sensors and current power profile and maps it to fan level. Mapping is set by configuration file. There is no user interface and there is no plan to make it.
9+
10+
Main features:
11+
12+
* Multiple fans and sensors.
13+
* Power profile support.
14+
* Delay before changing fan speed.
15+
16+
Originally created for Thinkpad T14 gen 4 (Intel). There is thinkfan but it does not support power profiles.
17+
18+
There are other fan [speed control tools](https://wiki.archlinux.org/title/fan_speed_control).
19+
20+
Adding new drivers and other improvements are welcome.
21+
22+
# ⚙️ Supported drivers
23+
24+
## 💻 Thinkpad ACPI Fan
25+
26+
❗Fan control is disabled by default. You need to enable it.
27+
28+
```bash
29+
echo "options thinkpad_acpi fan_control=1" | sudo tee -a /usr/lib/modprobe.d/thinkpad_acpi.conf
30+
sudo modprobe thinkpad_acpi
31+
```
32+
33+
### Links
34+
35+
* [Arch wiki](https://wiki.archlinux.org/title/fan_speed_control#ThinkPad_laptops)
36+
* [Thinkpad acpi documentation](https://www.kernel.org/doc/Documentation/laptops/thinkpad-acpi.txt)
37+
38+
## 🌡️ Hwmon sensors
39+
40+
It should work on every device but you need to find sensor name and label.
41+
42+
```bash
43+
tail -n 1 $(ls /sys/class/hwmon/hwmon*/{name,temp*_label,temp*_input} | sort)
44+
```
45+
46+
### Links
47+
48+
* [Hwmon kernel documentation](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface)
49+
50+
## 🚀 Profile platform
51+
52+
There is a file `/sys/firmware/acpi/platform_profile` which contains current power profile. In KDE and GNOME you can control current profile from the user interface.
53+
54+
```bash
55+
# To see available profiles
56+
cat /sys/firmware/acpi/platform_profile_choices
57+
```
58+
59+
# Testing config
60+
61+
Binary file has `-d` flag which enables debug logs. And `-c` flag to pass config file path.
62+
63+
```bash
64+
sudo fanctl -d -c ./conf/fanctl.yaml
65+
```
66+
67+
# Install
68+
69+
## Manual
70+
71+
Install [latest version of go](https://go.dev/doc/install)
72+
73+
```bash
74+
cd ./fanctl
75+
go build -o fanctl ./cmd/fanctl
76+
sudo cp ./fanctl /usr/sbin/fanctl
77+
sudo cp ./systemd/* /lib/systemd/system/
78+
sudo cp ./conf/fanctl.yaml /etc/
79+
# Change /etc/fanctl.yaml according to your hardware
80+
sudo systemctl enable fanctl.service fanctl-wakeup.service
81+
sudo systemctl start fanctl
82+
# Check service status
83+
sudo systemctl status fanctl
84+
```
85+
86+
## Deb package
87+

build-deb.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
set -e -x
2+
3+
NAME=fanctl
4+
VERSION=0.01
5+
6+
TMP_PACKAGE_DIR=.debian
7+
TMP_PACKAGE_DEB_DIR=$TMP_PACKAGE_DIR/DEBIAN
8+
9+
rm -rf $TMP_PACKAGE_DIR
10+
mkdir -p $TMP_PACKAGE_DEB_DIR
11+
12+
CONTROL=$TMP_PACKAGE_DEB_DIR/control
13+
CONFFILES=$TMP_PACKAGE_DEB_DIR/conffiles
14+
15+
ARCH=$(dpkg-architecture -qDEB_BUILD_ARCH)
16+
PACKAGE_FILENAME=${NAME}_${VERSION}_${ARCH}.deb
17+
18+
echo "Package: $NAME" >> $CONTROL
19+
echo "Version: $VERSION" >> $CONTROL
20+
echo "Priority: optional" >> $CONTROL
21+
echo "Architecture: $ARCH" >> $CONTROL
22+
echo "Maintainer: Ivan Safonov <[email protected]>" >> $CONTROL
23+
echo "Description: Fan control service" >> $CONTROL
24+
echo "Homepage: https://github.com/IvanSafonov/fanctl" >> $CONTROL
25+
26+
mkdir -p $TMP_PACKAGE_DIR/usr/sbin
27+
go build -o $TMP_PACKAGE_DIR/usr/sbin/fanctl ./cmd/fanctl
28+
strip $TMP_PACKAGE_DIR/usr/sbin/fanctl
29+
30+
mkdir -p $TMP_PACKAGE_DIR/etc
31+
cp ./conf/fanctl.yaml $TMP_PACKAGE_DIR/etc/fanctl.yaml
32+
echo "/etc/fanctl.yaml" >> $CONFFILES
33+
34+
mkdir -p $TMP_PACKAGE_DIR/lib/systemd/system
35+
cp ./systemd/*.service $TMP_PACKAGE_DIR/lib/systemd/system/
36+
37+
echo -n "Installed-Size: " >> $CONTROL
38+
du -sx --exclude DEBIAN $TMP_PACKAGE_DIR | grep -o -E ^[0-9]+ >> $CONTROL
39+
40+
dpkg-deb --build $TMP_PACKAGE_DIR $PACKAGE_FILENAME

cmd/fanctl/main.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"log/slog"
7+
"os"
8+
"os/signal"
9+
"runtime/pprof"
10+
11+
"github.com/IvanSafonov/fanctl/internal/config"
12+
"github.com/IvanSafonov/fanctl/internal/service"
13+
)
14+
15+
func main() {
16+
logLevel := new(slog.LevelVar)
17+
logLevel.Set(slog.LevelInfo)
18+
log := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
19+
Level: logLevel,
20+
}))
21+
slog.SetDefault(log)
22+
23+
var (
24+
confPath string
25+
debug bool
26+
cpuprofile string
27+
)
28+
29+
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
30+
flag.StringVar(&confPath, "c", "/etc/fanctl.yaml", "configuraion file path")
31+
flag.BoolVar(&debug, "d", false, "print debug messages")
32+
flag.Parse()
33+
34+
if debug {
35+
logLevel.Set(slog.LevelDebug)
36+
}
37+
38+
if cpuprofile != "" {
39+
f, err := os.Create(cpuprofile)
40+
if err != nil {
41+
log.Error("failed to create cpuprofile file", "err", err)
42+
} else {
43+
if err := pprof.StartCPUProfile(f); err != nil {
44+
log.Error("failed to start cpu profile", "err", err)
45+
}
46+
defer pprof.StopCPUProfile()
47+
}
48+
}
49+
50+
conf, err := config.Load(confPath)
51+
if err != nil {
52+
slog.Error("config load error", "err", err)
53+
return
54+
}
55+
56+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
57+
defer stop()
58+
59+
srv := service.New(conf)
60+
61+
err = srv.Init()
62+
if err != nil {
63+
slog.Error("service init error", "err", err)
64+
return
65+
}
66+
67+
err = srv.Run(ctx)
68+
if err != nil {
69+
slog.Error("service run error", "err", err)
70+
return
71+
}
72+
}

conf/fanctl.yaml

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Time in seconds between reading sensors.
2+
# 1 second by default.
3+
# period: 1
4+
5+
# Time in seconds between setting the same level.
6+
# When user space fan software doesn't set fan level for a while
7+
# driver can switch to auto mode by itself. Thinkpad acpi driver
8+
# does this after 2 minutes.
9+
# 60 seconds by default.
10+
# repeat: 60
11+
12+
# All controlled fans.
13+
# Has to be at least one fan.
14+
fans:
15+
# Fan driver type
16+
# Available types: thinkpad
17+
# Required
18+
- type: thinkpad
19+
20+
# Name for logs.
21+
# name: cpu
22+
23+
# Default fan level.
24+
# Default value provided from driver.
25+
# level: level auto
26+
27+
# Time in seconds before switching to another level.
28+
# 0 by default.
29+
# delay: 0
30+
31+
# List of sensor names from sensors section which are used to select.
32+
# current level.
33+
# By default uses all sensors.
34+
# sensors:
35+
# - cpu1
36+
# - cpu2
37+
38+
# Multiple sensors select algorithm.
39+
# Available values: min, max, average.
40+
# max by default.
41+
# select: max
42+
43+
# Driver system file path.
44+
# path:
45+
46+
# Levels for power profiles.
47+
# Profile levels have priority over fan levels.
48+
# profiles:
49+
# Power profile name.
50+
# Required.
51+
# - name: low-power
52+
# Time in seconds before switching to another level.
53+
# 0 by default.
54+
# delay: 0
55+
56+
# Sensor value to fan level mapping.
57+
# Order matters. First matching will be used.
58+
# If there is no matching level default fan level is used.
59+
# levels:
60+
# Fan level.
61+
# Required.
62+
# - level: level 1
63+
64+
# Minimal sensor value.
65+
# Required if max is not set.
66+
# -∞ by default.
67+
# min: 10
68+
69+
# Maximal sensor value.
70+
# Required if min is not set.
71+
# +∞ by default.
72+
# max: 55
73+
74+
# Time in seconds before switching to another level.
75+
# 0 by default.
76+
# delay: 0
77+
78+
# Sensor value to fan level mapping.
79+
# Order matters. First matching will be used.
80+
# If there is no matching level default fan level is used.
81+
# levels:
82+
# Fan level.
83+
# Required.
84+
# - level: level 1
85+
86+
# Minimal sensor value.
87+
# Required if max is not set.
88+
# -∞ by default.
89+
# min: 10
90+
91+
# Maximal sensor value.
92+
# Required if min is not set.
93+
# +∞ by default.
94+
# max: 55
95+
96+
# Time in seconds before switching to another level.
97+
# 0 by default.
98+
# delay: 0
99+
100+
# All sensors.
101+
# Has to be at least one sensor.
102+
sensors:
103+
# Fan driver type
104+
# Available types: thinkpad
105+
# Required
106+
- type: hwmon
107+
108+
# Sensor name.
109+
# name: cpu1
110+
111+
# Sensor value multiplier.
112+
# Default value provided from driver.
113+
# factor: 0.001
114+
115+
# Sensor value constant.
116+
# Default value provided from driver.
117+
# add: 0
118+
119+
# Multiple system files select algorithm.
120+
# hwmon has multiple per each cpu core.
121+
# Available values: min, max, average.
122+
# max by default.
123+
# select: max
124+
125+
# Sensor name.
126+
# sensor: coretemp
127+
128+
# Sensor label.
129+
# label: Package
130+
131+
# Profile settings.
132+
# Have to be set if fan profiles are used.
133+
# profile:
134+
# Profile driver type.
135+
# Available types: platform.
136+
# Required.
137+
# type: platform
138+
139+
# Profile system file path.
140+
# path: /sys/profile

0 commit comments

Comments
 (0)