Compare commits
No commits in common. "dc30944a897257d55f6fe59e49cceb49d3f7d939" and "f54f8bd5a8992f5f8eebdcf77ceec816c0d7bab3" have entirely different histories.
dc30944a89
...
f54f8bd5a8
@ -1 +0,0 @@
|
|||||||
3.12.0
|
|
@ -1,105 +1,97 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import json
|
import json
|
||||||
import shutil
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import typing
|
|
||||||
|
|
||||||
__all__ = ["_mem", "_backlight", "_hwtemp"]
|
_BITS = []
|
||||||
|
|
||||||
_BITS: list[tuple[int, str, typing.Callable]] = []
|
|
||||||
|
|
||||||
|
|
||||||
def _bit(idx):
|
def _bit(idx):
|
||||||
def wrapper(func):
|
def wrapper(func):
|
||||||
_BITS.append((idx, func.__name__.replace("_", ""), func))
|
_BITS.append([idx, func.__name__.replace('_', ''), func])
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@_bit(6)
|
@_bit(6)
|
||||||
def _mem():
|
def _mem():
|
||||||
with open("/proc/meminfo") as meminfo_fp:
|
with open('/proc/meminfo') as meminfo_fp:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
for line in meminfo_fp.readlines():
|
for line in meminfo_fp.readlines():
|
||||||
parts = line.split(":", 2)
|
parts = line.split(':', 2)
|
||||||
attrs[parts[0].strip()] = parts[1].strip()
|
attrs[parts[0].strip()] = parts[1].strip()
|
||||||
avail = int(attrs["MemAvailable"].split()[0])
|
avail = int(attrs['MemAvailable'].split()[0])
|
||||||
total = int(attrs["MemTotal"].split()[0])
|
total = int(attrs['MemTotal'].split()[0])
|
||||||
used = int(((total - avail) / total) * 100)
|
used = int(((total - avail) / total) * 100)
|
||||||
color = None
|
color = None
|
||||||
if used > 75:
|
if used > 75:
|
||||||
color = "#ff0000"
|
color = '#ff0000'
|
||||||
if used < 25:
|
if used < 25:
|
||||||
color = "#00ff00"
|
color = '#00ff00'
|
||||||
return "M:{}%".format(used), color
|
return 'M:{}%'.format(used), color
|
||||||
|
|
||||||
|
|
||||||
@_bit(0)
|
@_bit(0)
|
||||||
def _backlight(dev_dir="/sys/class/backlight/intel_backlight"):
|
def _backlight(dev_dir='/sys/class/backlight/intel_backlight'):
|
||||||
with open("{}/brightness".format(dev_dir)) as br_fp:
|
with open('{}/brightness'.format(dev_dir)) as br_fp:
|
||||||
with open("{}/max_brightness".format(dev_dir)) as mb_fp:
|
with open('{}/max_brightness'.format(dev_dir)) as mb_fp:
|
||||||
value = int(br_fp.read().strip())
|
value = int(br_fp.read().strip())
|
||||||
max_value = int(mb_fp.read().strip())
|
max_value = int(mb_fp.read().strip())
|
||||||
pct = int(float(float(value) / float(max_value)) * 100)
|
pct = int(float(float(value) / float(max_value)) * 100)
|
||||||
color = None
|
color = None
|
||||||
if pct >= 75:
|
if pct >= 75:
|
||||||
color = "#ffaa00"
|
color = '#ffaa00'
|
||||||
if pct <= 25:
|
if pct <= 25:
|
||||||
color = "#5599ff"
|
color = '#5599ff'
|
||||||
return "☼:{}%".format(pct), color
|
return '☼:{}%'.format(pct), color
|
||||||
|
|
||||||
|
|
||||||
ACPI_IBM_FAN = "/proc/acpi/ibm/fan"
|
THINKFAN_CONF = '/etc/thinkfan.conf'
|
||||||
|
ACPI_IBM_FAN = '/proc/acpi/ibm/fan'
|
||||||
|
|
||||||
|
|
||||||
@_bit(7)
|
@_bit(7)
|
||||||
def _hwtemp(fan=ACPI_IBM_FAN):
|
def _hwtemp(conf=THINKFAN_CONF, fan=ACPI_IBM_FAN):
|
||||||
acpi_exe = shutil.which("acpi")
|
if not os.path.exists(conf):
|
||||||
if acpi_exe is None:
|
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
raw_temp = subprocess.run(
|
temps = []
|
||||||
[acpi_exe, "-t"],
|
|
||||||
text=True,
|
|
||||||
capture_output=True,
|
|
||||||
check=False,
|
|
||||||
)
|
|
||||||
if raw_temp.returncode != 0 or raw_temp.stdout is None:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
temps_by_zone = {}
|
with open('/etc/thinkfan.conf') as tfc_fp:
|
||||||
for line in raw_temp.stdout.splitlines():
|
for line in tfc_fp.readlines():
|
||||||
zone_id, value = line.split(":")
|
if not line.startswith('hwmon '):
|
||||||
human_temp = value.split(",")[-1]
|
continue
|
||||||
temps_by_zone[zone_id.lower().lstrip("thermal ")] = float(
|
try:
|
||||||
human_temp.lower().strip().rstrip("degrees c")
|
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')
|
||||||
|
|
||||||
temps = list(temps_by_zone.values())
|
avg_temp = float(sum(temps)) / len(temps) / 1000.0
|
||||||
avg_temp = float(sum(temps)) / len(temps)
|
color = None
|
||||||
color = "#5599ff"
|
|
||||||
if avg_temp > 75:
|
|
||||||
color = "#ff0000"
|
|
||||||
if avg_temp >= 50:
|
|
||||||
color = "#ffaa00"
|
|
||||||
|
|
||||||
fan_level = "unset"
|
fan_level = 'unset'
|
||||||
try:
|
try:
|
||||||
with open(fan) as fan_fp:
|
with open(fan) as fan_fp:
|
||||||
for line in fan_fp.readlines():
|
for line in fan_fp.readlines():
|
||||||
if not line.startswith("level:\t\t"):
|
if not line.startswith('level:\t\t'):
|
||||||
continue
|
continue
|
||||||
fan_level = int(line.replace("level:\t\t", ""))
|
fan_level = int(line.replace('level:\t\t', ''))
|
||||||
except (OSError, IOError) as exc:
|
except (OSError, IOError) as exc:
|
||||||
sys.stderr.write(str(exc) + "\n")
|
sys.stderr.write(str(exc) + '\n')
|
||||||
|
|
||||||
return "T:{:.1f}°C L:{}".format(avg_temp, fan_level), color
|
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
|
||||||
|
|
||||||
|
|
||||||
def _print_line(message):
|
def _print_line(message):
|
||||||
sys.stdout.write(message + "\n")
|
sys.stdout.write(message + '\n')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
@ -118,9 +110,9 @@ def main(bits=_BITS):
|
|||||||
_print_line(_read_line())
|
_print_line(_read_line())
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
line, prefix = _read_line(), ""
|
line, prefix = _read_line(), ''
|
||||||
if line.startswith(","):
|
if line.startswith(','):
|
||||||
line, prefix = line[1:], ","
|
line, prefix = line[1:], ','
|
||||||
|
|
||||||
loaded = json.loads(line)
|
loaded = json.loads(line)
|
||||||
for idx, name, func in bits:
|
for idx, name, func in bits:
|
||||||
@ -135,11 +127,11 @@ def main(bits=_BITS):
|
|||||||
|
|
||||||
loaded.insert(idx, record)
|
loaded.insert(idx, record)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
sys.stderr.write(str(exc) + "\n")
|
sys.stderr.write(str(exc) + '\n')
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
_print_line(prefix + json.dumps(loaded))
|
_print_line(prefix+json.dumps(loaded))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
52
local/bin/thinkfan-confgen
Executable file
52
local/bin/thinkfan-confgen
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#!/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 "${@}"
|
@ -1,126 +0,0 @@
|
|||||||
#!/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())
|
|
Loading…
x
Reference in New Issue
Block a user