inital commit

This commit is contained in:
2019-09-03 15:26:39 +02:00
parent 26f4c1295a
commit 8aac0de0e1
3 changed files with 147 additions and 2 deletions

View File

@@ -1,3 +1,20 @@
# PyFan
## PyFAN
Fan control based on hwmon and pir.
This simple python script utilizes PID as base for fan control.
# Usage
Put your config in /etc/pyfan (reference example config) and enable pyfan as a service (service file also available here).
# Config
To know which hwmon is what device and what pwm controls what fan, the following commands can help you:
List all devices + names:
```tail /sys/class/hwmon/hwmon*/name```
Enable control for a specific pwm: ```echo 1 > /sys/class/hwmon/hwmonX/pwmX_enable```
Set fan speed: ```echo [0-255] > /sys/class/hwmon/hwmonX/pwmX```
After you have figured out which fan is controlled by what pwm, you can adjust your config. You can have as many thermal zones as you want, just repeate them like shown.

117
pyfan.py Normal file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python
import sys
import logging
from time import sleep
import yaml
from simple_pid import PID
SYSFS_HWMON_BASE = "/sys/class/hwmon/"
def build_pwm_path(specific):
return SYSFS_HWMON_BASE + specific
def write_sysfs(path, value):
try:
with open(build_pwm_path(path), 'w') as sysfs_f:
sysfs_f.write(str(value))
except OSError as err:
print("WARN:", err.strerror)
def read_sysfs(path):
with open(build_pwm_path(path)) as sysfs_f:
return sysfs_f.readline()
def set_pwm_mode(path, value=1):
write_sysfs(path + "_enable", value)
class ThermalZone:
def __init__(self, temp_source, fans, p, i, d, target, factor, name) -> None:
self.fans = fans
self.temp_source = temp_source
self.pid = PID(p, i, d, setpoint=0)
self.pid.output_limits = (0, 255)
self.factor = 1 / factor
self.name = name
self.target = target
self.setup_pwm()
logging.info("[{zone}] Source={source} Fans={fans} Factor={factor} PID={pid}".format(zone=name,
source=temp_source,
fans=fans,
factor=factor,
pid=(
p, i, d)))
def eval(self):
diff = self.target - self.get_temp()
val = self.pid(diff)
for target_fan in self.fans:
if type(target_fan) is dict:
write_sysfs(list(target_fan.keys())[0], min(int(val), list(target_fan.values())[0]))
else:
write_sysfs(target_fan, int(val))
logging.debug(
"[{name}] {val}% ({diff}C/{temp}C)".format(name=self.name, val=int(val / 255 * 100), diff=diff,
temp=self.get_temp()))
def get_temp(self):
return float(read_sysfs(self.temp_source)) * self.factor
def restore(self):
self.setup_pwm(2)
def setup_pwm(self, value=1):
for target_fan in self.fans:
if type(target_fan) is dict:
set_pwm_mode(list(target_fan.keys())[0], value)
else:
set_pwm_mode(target_fan, value)
class PyFan:
def __init__(self, config="/etc/pyfan") -> None:
self.config = self.__load_config(config)
logging.basicConfig(level=logging.getLevelName(self.config["loglevel"]), style='{')
self.zones = []
for zone in self.config["thermalzones"]:
self.zones.append(
ThermalZone(zone["source"], zone["fan"], zone["pid"]["p"], zone["pid"]["i"], zone["pid"]["d"],
zone["target"],
zone["factor"], zone["name"]))
logging.info("Finished creating %d thermal zones." % len(self.zones))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
for zone in self.zones:
zone.restore()
def eval(self):
for zone in self.zones:
zone.eval()
@staticmethod
def __load_config(path):
with open(path) as cfg_file:
return yaml.safe_load(cfg_file)
if __name__ == "__main__":
with PyFan() as pyfan:
while True:
try:
pyfan.eval()
sleep(1)
except KeyboardInterrupt:
sys.exit(0)

11
pyfan.service Normal file
View File

@@ -0,0 +1,11 @@
[Unit]
Description=Start PyFan fan control
ConditionFileNotEmpty=/etc/pyfan
After=lm_sensors.service
[Service]
Type=simple
ExecStart=/usr/sbin/pyfan.py
[Install]
WantedBy=multi-user.target