Files
PyFan/pyfan.py
2019-09-03 15:27:49 +02:00

118 lines
3.6 KiB
Python

#!/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)