From dc30944a897257d55f6fe59e49cceb49d3f7d939 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 31 Dec 2023 16:44:44 -0500 Subject: [PATCH] Updating many thermal and status things --- .python-version | 1 + local/bin/i3wrapper.py | 56 ++++++++------- local/bin/thinkfan-confgen | 52 -------------- local/bin/thinkfan_confgen.py | 126 ++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 77 deletions(-) create mode 100644 .python-version delete mode 100755 local/bin/thinkfan-confgen create mode 100755 local/bin/thinkfan_confgen.py diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..92536a9 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12.0 diff --git a/local/bin/i3wrapper.py b/local/bin/i3wrapper.py index b6ca302..93fb346 100755 --- a/local/bin/i3wrapper.py +++ b/local/bin/i3wrapper.py @@ -1,16 +1,18 @@ #!/usr/bin/env python import json -import os +import shutil +import subprocess import sys +import typing __all__ = ["_mem", "_backlight", "_hwtemp"] -_BITS = [] +_BITS: list[tuple[int, str, typing.Callable]] = [] def _bit(idx): def wrapper(func): - _BITS.append([idx, func.__name__.replace("_", ""), func]) + _BITS.append((idx, func.__name__.replace("_", ""), func)) return func return wrapper @@ -49,29 +51,39 @@ def _backlight(dev_dir="/sys/class/backlight/intel_backlight"): return "☼:{}%".format(pct), color -THINKFAN_CONF = "/etc/thinkfan.conf" ACPI_IBM_FAN = "/proc/acpi/ibm/fan" @_bit(7) -def _hwtemp(conf=THINKFAN_CONF, fan=ACPI_IBM_FAN): - if not os.path.exists(conf): +def _hwtemp(fan=ACPI_IBM_FAN): + acpi_exe = shutil.which("acpi") + if acpi_exe is None: return None, None - temps = [] - - with open("/etc/thinkfan.conf") as tfc_fp: - for line in tfc_fp.readlines(): - if not line.startswith("hwmon "): - continue - try: - with open(line.split(" ")[1].strip()) as temp_fp: - temps.append(float(temp_fp.read())) - except (OSError, IOError) as exc: - sys.stderr.write(str(exc) + "\n") + raw_temp = subprocess.run( + [acpi_exe, "-t"], + text=True, + capture_output=True, + check=False, + ) + if raw_temp.returncode != 0 or raw_temp.stdout is None: + return None, None - avg_temp = float(sum(temps)) / len(temps) / 1000.0 - color = None + temps_by_zone = {} + for line in raw_temp.stdout.splitlines(): + zone_id, value = line.split(":") + human_temp = value.split(",")[-1] + temps_by_zone[zone_id.lower().lstrip("thermal ")] = float( + human_temp.lower().strip().rstrip("degrees c") + ) + + temps = list(temps_by_zone.values()) + avg_temp = float(sum(temps)) / len(temps) + color = "#5599ff" + if avg_temp > 75: + color = "#ff0000" + if avg_temp >= 50: + color = "#ffaa00" fan_level = "unset" try: @@ -83,12 +95,6 @@ def _hwtemp(conf=THINKFAN_CONF, fan=ACPI_IBM_FAN): except (OSError, IOError) as exc: sys.stderr.write(str(exc) + "\n") - if avg_temp > 75: - color = "#ff0000" - if avg_temp >= 50: - color = "#ffaa00" - if avg_temp <= 25: - color = "#5599ff" return "T:{:.1f}°C L:{}".format(avg_temp, fan_level), color diff --git a/local/bin/thinkfan-confgen b/local/bin/thinkfan-confgen deleted file mode 100755 index e2d2981..0000000 --- a/local/bin/thinkfan-confgen +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -o errexit -set -o pipefail - -main() { - if [[ -f "${1}" ]]; then - exec 1>"${1}" - shift - fi - - if [[ -f /etc/default/thinkfan-confgen ]]; then - source /etc/default/thinkfan-confgen - fi - - : "${THINKFAN_LOWER_BOUND:=30}" - : "${THINKFAN_STEP:=4}" - - printf '# thinkfan-confgen created %s\n' "$(date -u)" - printf '# THINKFAN_LOWER_BOUND=%s\n' "${THINKFAN_LOWER_BOUND}" - printf '# THINKFAN_STEP=%s\n\n' "${THINKFAN_STEP}" - - find /sys -type f -name 'temp*_input' | while read -r line; do - if [[ "${line}" =~ thinkpad_hwmon ]]; then - continue - fi - printf 'hwmon %s\n' "${line}" - done - - printf '\ntp_fan /proc/acpi/ibm/fan\n\n' - - local halfstep="$((THINKFAN_STEP / 2))" - local cur="${THINKFAN_LOWER_BOUND}" - - for level in 0 1 2 3 4 5 6 7; do - if [[ "${level}" == 0 ]]; then - printf '(0, 0, %s)\n' "${cur}" - continue - fi - - if [[ "${level}" == 7 ]]; then - printf '(7, %s, 32767)\n' "$((cur - halfstep))" - continue - fi - - printf '(%s, %s, %s)\n' \ - "${level}" "$((cur - halfstep))" "$((cur + THINKFAN_STEP))" - - cur="$((cur + THINKFAN_STEP))" - done -} - -main "${@}" diff --git a/local/bin/thinkfan_confgen.py b/local/bin/thinkfan_confgen.py new file mode 100755 index 0000000..bc22ea4 --- /dev/null +++ b/local/bin/thinkfan_confgen.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +import argparse +import datetime +import json +import os +import pathlib +import sys +import typing + + +class ThinkfanConfig(typing.TypedDict): + sensors: list[dict[str, str]] + fans: list[dict[str, str]] + levels: list[dict[str, int | list[int]]] + + +def main(sysargs=sys.argv[:]) -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", + "--lower-bound", + default=int(os.environ.get("THINKFAN_LOWER_BOUND", "30")), + type=int, + help="lower bound of sensor value for min fan speed (THINKFAN_LOWER_BOUND)", + ) + parser.add_argument( + "-u", + "--upper-bound", + default=int(os.environ.get("THINKFAN_UPPER_BOUND", "32767")), + type=int, + help="upper bound of sensor value for max fan speed (THINKFAN_UPPER_BOUND)", + ) + parser.add_argument( + "-s", + "--step", + default=int(os.environ.get("THINKFAN_STEP", "4")), + type=int, + help="step size between fan speed levels (THINKFAN_STEP)", + ) + args = parser.parse_args(sysargs[1:]) + + cfg: ThinkfanConfig = { + "sensors": [], + "fans": [], + "levels": [], + } + + acpi_fan = pathlib.Path("/proc/acpi/ibm/fan") + if acpi_fan.exists(): + cfg["fans"].append({"tpacpi": str(acpi_fan)}) + + acpi_thermal = pathlib.Path("/proc/acpi/ibm/thermal") + + n_sensors: int = 0 + + if acpi_thermal.exists(): + cfg["sensors"].append({"tpacpi": str(acpi_thermal)}) + n_sensors = len( + [ + l + for l in acpi_thermal.read_text().splitlines() + if l.startswith("temperatures:") + ][0] + .split(":")[1] + .strip() + .split() + ) + + else: + for dirpath, dirnames, filenames in pathlib.Path("/sys/devices").walk( + on_error=print, follow_symlinks=False + ): + if "thinkpad_hwmon" in dirnames: + dirnames.remove("thinkpad_hwmon") + + for filename in filenames: + if filename.startswith("temp") and filename.endswith("_input"): + cfg["sensors"].append({"hwmon": str(dirpath.joinpath(filename))}) + + n_sensors += 1 + + cur: int = args.lower_bound + step: int = args.step + halfstep = step // 2 + + for level in range(8): + if level == 0: + cfg["levels"].append( + { + "speed": 0, + "lower_limit": [0] * n_sensors, + "upper_limit": [cur] * n_sensors, + } + ) + + continue + + if level == 7: + cfg["levels"].append( + { + "speed": 7, + "upper_limit": [args.upper_bound] * n_sensors, + "lower_limit": [cur - halfstep] * n_sensors, + } + ) + + continue + + cfg["levels"].append( + { + "speed": level, + "lower_limit": [cur - halfstep] * n_sensors, + "upper_limit": [cur + step] * n_sensors, + }, + ) + + cur += step + + print(f"# thinkfan_confgen.py created {datetime.datetime.now(datetime.UTC)}") + print(json.dumps(cfg)) + + return 0 + + +if __name__ == "__main__": + sys.exit(main())