diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/config-example.yaml b/config-example.yaml index 6bc803c..5124091 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,26 +1,26 @@ loglevel: DEBUG thermalzones: - - - name: GPU+SYSTEM - source: hwmon3/temp1_input - factor: 1000 - fan: - - hwmon2/pwm2 - - hwmon3/pwm1: 170 - target: 60 - pid: - p: 1 - i: 1.5 - d: 2 - - - name: CPU - source: hwmon1/temp1_input - factor: 1000 - fan: - - hwmon2/pwm1 - target: 50 - pid: - p: 1 - i: 1.5 - d: 2 \ No newline at end of file + - name: GPU+SYSTEM + hwmon: amdgpu + source: temp1_input + factor: 1000 + fan: + - it8686/pwm2 + - amdgpu/pwm1: 170 + target: 60 + pid: + p: 1 + i: 1 + d: 1.5 + - name: CPU + hwmon: it8686 + source: coretemp/temp1_input + factor: 1000 + fan: + - it8686/pwm1 + target: 50 + pid: + p: 1 + i: 1 + d: 1.5 \ No newline at end of file diff --git a/pyfan.py b/pyfan.py index e096863..82d9bdc 100644 --- a/pyfan.py +++ b/pyfan.py @@ -1,6 +1,7 @@ #!/usr/bin/env python - +import glob import logging +import re import sys from time import sleep @@ -10,42 +11,28 @@ 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): - with open(build_pwm_path(path), 'w') as sysfs_f: - sysfs_f.write(str(value)) - - -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) + def __init__(self, config, hwmon_map) -> None: + self.fans = config["fan"] + self.temp_source = config["source"] + self.pid = PID(config["pid"]["p"], config["pid"]["i"], config["pid"]["d"], setpoint=0) self.pid.output_limits = (0, 255) - self.factor = 1 / factor - self.name = name - self.target = target + self.factor = 1 / config["factor"] + self.name = config["name"] + self.target = config["target"] + self.hwname = config["hwmon"] + self.hwmap = hwmon_map + self.alias_replace = re.compile(r'\b(' + '|'.join(re.escape(key) for key in self.hwmap.keys()) + r')\b') self.setup_pwm() logging.getLogger("pyfan").info( - "[{zone}] Source={source} Fans={fans} Factor={factor} PID={pid}".format(zone=name, - source=temp_source, - fans=fans, - factor=factor, + "[{zone}] Source={source} Fans={fans} Factor={factor} PID={pid}".format(zone=self.name, + source=self.temp_source, + fans=self.fans, + factor=self.factor, pid=( - p, i, d))) + self.pid.Kp, self.pid.Ki, + self.pid.Kd))) def eval(self): diff = self.target - self.get_temp() @@ -54,9 +41,9 @@ class ThermalZone: try: 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])) + self.write_sysfs(list(target_fan.keys())[0], min(int(val), list(target_fan.values())[0])) else: - write_sysfs(target_fan, int(val)) + self.write_sysfs(target_fan, int(val)) except OSError as err: logging.getLogger("pyfan").warning( "[%s] Failed to set pwm, trying to reset it. (%s)" % (self.name, err.strerror)) @@ -71,7 +58,7 @@ class ThermalZone: d=int(d))) def get_temp(self): - return float(read_sysfs(self.temp_source)) * self.factor + return float(self.read_sysfs(self.temp_source)) * self.factor def restore(self): self.setup_pwm(2) @@ -80,12 +67,31 @@ class ThermalZone: for target_fan in self.fans: try: if type(target_fan) is dict: - set_pwm_mode(list(target_fan.keys())[0], value) + self.set_pwm_mode(list(target_fan.keys())[0], value) else: - set_pwm_mode(target_fan, value) + self.set_pwm_mode(target_fan, value) except FileNotFoundError as err: - logging.getLogger("pyfan").warning("[%s] pwm not found. Not ready yet or wrong path? (%s)" % self.name, - err.strerror) + logging.getLogger("pyfan").warning("[%s] pwm not found. Not ready yet or wrong path? (%s)" % (self.name, + err.strerror)) + + def replace_alias(self, path): + replaced = self.alias_replace.sub(lambda x: self.hwmap[x.group()], path) + logging.getLogger("pyfan").debug("[ALIAS] %s -> %s" % (path, replaced)) + return replaced + + def build_pwm_path(self, specific): + return self.replace_alias(SYSFS_HWMON_BASE + specific) + + def write_sysfs(self, path, value): + with open(self.build_pwm_path(path), 'w') as sysfs_f: + sysfs_f.write(str(value)) + + def read_sysfs(self, path): + with open(self.build_pwm_path(path)) as sysfs_f: + return sysfs_f.readline() + + def set_pwm_mode(self, path, value=1): + self.write_sysfs(path + "_enable", value) class PyFan: @@ -93,12 +99,11 @@ class PyFan: self.config = self.__load_config(config) logging.basicConfig(level=logging.getLevelName(self.config["loglevel"])) self.zones = [] + self.hwmon_map = {} + self.gen_hwmon_map() 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"])) + self.zones.append(ThermalZone(zone, self.hwmon_map)) logging.getLogger("pyfan").info("Finished creating %d thermal zones." % len(self.zones)) @@ -118,6 +123,15 @@ class PyFan: with open(path) as cfg_file: return yaml.safe_load(cfg_file) + def gen_hwmon_map(self): + names = glob.glob(SYSFS_HWMON_BASE + "hwmon*/name") + + for name in names: + hwmon = name.split("/")[-2] + with open(name) as file: + hwname = file.read() + self.hwmon_map[hwname] = hwmon + if __name__ == "__main__": with PyFan() as pyfan: