273 lines
6.4 KiB
PHP
273 lines
6.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/config/config.php';
|
|
|
|
function local_fan_paths(): array
|
|
{
|
|
$candidates = glob('/sys/devices/platform/cooling_fan/hwmon/hwmon*/pwm1') ?: [];
|
|
$candidates = array_merge(
|
|
$candidates,
|
|
glob('/sys/class/hwmon/hwmon*/pwm1') ?: []
|
|
);
|
|
|
|
foreach ($candidates as $pwm) {
|
|
$dir = dirname($pwm);
|
|
|
|
return [
|
|
'base' => $dir,
|
|
'pwm' => $pwm,
|
|
'enable' => $dir . '/pwm1_enable',
|
|
'rpm' => $dir . '/fan1_input',
|
|
'name' => first_readable([$dir . '/name']) ?: 'cooling_fan',
|
|
];
|
|
}
|
|
|
|
return [
|
|
'base' => '',
|
|
'pwm' => '',
|
|
'enable' => '',
|
|
'rpm' => '',
|
|
'name' => '',
|
|
];
|
|
}
|
|
|
|
function local_temp(): float
|
|
{
|
|
$raw = first_readable([
|
|
'/sys/class/thermal/thermal_zone0/temp',
|
|
'/sys/devices/virtual/thermal/thermal_zone0/temp',
|
|
]);
|
|
|
|
if ($raw !== '' && is_numeric($raw)) {
|
|
return round(((float)$raw) / 1000, 2);
|
|
}
|
|
|
|
$vc = sh(['/usr/bin/vcgencmd', 'measure_temp'], false, 3)['out'];
|
|
|
|
if (preg_match('/([0-9.]+)/', $vc, $m)) {
|
|
return round((float)$m[1], 2);
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
function local_target(float $temp): int
|
|
{
|
|
if ($temp >= 80) {
|
|
return 255;
|
|
}
|
|
|
|
if ($temp <= 50) {
|
|
return 0;
|
|
}
|
|
|
|
$ratio = ($temp - 50) / 30;
|
|
|
|
return max(0, min(255, (int)round($ratio * 255)));
|
|
}
|
|
|
|
function local_ramped_pwm(int $current, int $desired, float $temp): int
|
|
{
|
|
if ($temp >= 80) {
|
|
return $desired;
|
|
}
|
|
|
|
if ($desired > $current) {
|
|
return min($desired, $current + 2);
|
|
}
|
|
|
|
if ($desired < $current) {
|
|
return max($desired, $current - 2);
|
|
}
|
|
|
|
return $desired;
|
|
}
|
|
|
|
function local_mem(): array
|
|
{
|
|
$rows = [];
|
|
|
|
foreach (@file('/proc/meminfo', FILE_IGNORE_NEW_LINES) ?: [] as $line) {
|
|
if (preg_match('/^([^:]+):\s+(\d+)/', $line, $m)) {
|
|
$rows[$m[1]] = (int)$m[2];
|
|
}
|
|
}
|
|
|
|
$total = (int)($rows['MemTotal'] ?? 0);
|
|
$available = (int)($rows['MemAvailable'] ?? 0);
|
|
$used = max(0, $total - $available);
|
|
|
|
return [
|
|
'total_mb' => (int)round($total / 1024),
|
|
'used_mb' => (int)round($used / 1024),
|
|
'free_mb' => (int)round($available / 1024),
|
|
];
|
|
}
|
|
|
|
function local_disk(string $path = '/'): array
|
|
{
|
|
$total = @disk_total_space($path) ?: 0;
|
|
$free = @disk_free_space($path) ?: 0;
|
|
$used = max(0, $total - $free);
|
|
|
|
return [
|
|
'total_kb' => (int)round($total / 1024),
|
|
'used_kb' => (int)round($used / 1024),
|
|
'free_kb' => (int)round($free / 1024),
|
|
];
|
|
}
|
|
|
|
function local_uptime_seconds(): int
|
|
{
|
|
$raw = trim((string)@file_get_contents('/proc/uptime'));
|
|
|
|
if ($raw === '') {
|
|
return 0;
|
|
}
|
|
|
|
return (int)floor((float)explode(' ', $raw)[0]);
|
|
}
|
|
|
|
function read_int_file(string $file): int
|
|
{
|
|
if ($file === '' || !is_readable($file)) {
|
|
return 0;
|
|
}
|
|
|
|
$raw = trim((string)@file_get_contents($file));
|
|
|
|
return is_numeric($raw) ? (int)$raw : 0;
|
|
}
|
|
|
|
function write_sys_value(string $path, int $value): bool
|
|
{
|
|
if ($path === '') {
|
|
return false;
|
|
}
|
|
|
|
return @file_put_contents($path, $value . "\n", LOCK_EX) !== false;
|
|
}
|
|
|
|
function dmesg_log(string $message): void
|
|
{
|
|
$line = '<6>fanpanel: ' . $message . "\n";
|
|
@file_put_contents('/dev/kmsg', $line, FILE_APPEND);
|
|
}
|
|
|
|
function dmesg_log_throttled(string $key, string $message, int $seconds = 30): void
|
|
{
|
|
$file = '/tmp/fanpanel_dmesg_' . preg_replace('/[^a-zA-Z0-9_.-]/', '_', $key) . '.last';
|
|
$now = time();
|
|
$last = is_file($file) ? (int)trim((string)@file_get_contents($file)) : 0;
|
|
|
|
if (($now - $last) < $seconds) {
|
|
return;
|
|
}
|
|
|
|
@file_put_contents($file, (string)$now, LOCK_EX);
|
|
dmesg_log($message);
|
|
}
|
|
|
|
try {
|
|
$state = get_control_state();
|
|
|
|
$mode = (string)($state['mode'] ?? 'auto');
|
|
$manualPwm = max(0, min(255, (int)($state['manual_pwm'] ?? 120)));
|
|
|
|
if (!in_array($mode, ['auto', 'manual', 'off'], true)) {
|
|
$mode = 'auto';
|
|
}
|
|
|
|
$paths = local_fan_paths();
|
|
$temp = local_temp();
|
|
|
|
$currentPwm = read_int_file($paths['pwm']);
|
|
$desired = match ($mode) {
|
|
'manual' => $manualPwm,
|
|
'off' => 0,
|
|
default => local_target($temp),
|
|
};
|
|
$target = $mode === 'auto'
|
|
? local_ramped_pwm($currentPwm, $desired, $temp)
|
|
: $desired;
|
|
|
|
$enableOk = write_sys_value($paths['enable'], $mode === 'off' ? 0 : 1);
|
|
$pwmOk = write_sys_value($paths['pwm'], $target);
|
|
|
|
usleep(80000);
|
|
|
|
$actualPwm = read_int_file($paths['pwm']);
|
|
|
|
$rpm = read_int_file($paths['rpm']);
|
|
|
|
$load = sys_getloadavg() ?: [0, 0, 0];
|
|
$mem = local_mem();
|
|
$disk = local_disk('/');
|
|
$cpuPower = cpu_power_status();
|
|
$battery = battery_status();
|
|
|
|
$ok = $enableOk && $pwmOk;
|
|
|
|
add_sensor_log([
|
|
'cpu_temp_c' => $temp,
|
|
'fan_rpm' => $rpm,
|
|
'fan_efficiency' => fan_efficiency($rpm, $temp, $cpuPower['watts'] ?? null),
|
|
'rp1_temp_c' => rp1_temp_c(),
|
|
'cpu_voltage' => $cpuPower['voltage'],
|
|
'cpu_watts' => $cpuPower['watts'],
|
|
'battery_voltage' => $battery['voltage'],
|
|
'battery_percent' => $battery['percent'],
|
|
|
|
'pwm_value' => $actualPwm,
|
|
'pwm_percent' => round($actualPwm / 255 * 100, 2),
|
|
'pwm_mode' => $mode,
|
|
|
|
'cpu_load_1' => round((float)$load[0], 2),
|
|
'cpu_load_5' => round((float)$load[1], 2),
|
|
'cpu_load_15' => round((float)$load[2], 2),
|
|
|
|
'mem_total_mb' => $mem['total_mb'],
|
|
'mem_used_mb' => $mem['used_mb'],
|
|
'mem_free_mb' => $mem['free_mb'],
|
|
|
|
'disk_total_kb' => $disk['total_kb'],
|
|
'disk_used_kb' => $disk['used_kb'],
|
|
'disk_free_kb' => $disk['free_kb'],
|
|
|
|
'uptime_seconds' => local_uptime_seconds(),
|
|
|
|
'hostname' => gethostname() ?: null,
|
|
]);
|
|
|
|
send_battery_low_push_if_needed($battery);
|
|
|
|
if (!$ok) {
|
|
add_fan_action(
|
|
'apply_policy_failed',
|
|
$mode,
|
|
$target,
|
|
'enable=' . ($enableOk ? 'ok' : 'fail')
|
|
. ', pwm=' . ($pwmOk ? 'ok' : 'fail')
|
|
. ', path=' . ($paths['pwm'] ?: 'N/A'),
|
|
false
|
|
);
|
|
}
|
|
|
|
exit($ok ? 0 : 1);
|
|
} catch (Throwable $e) {
|
|
try {
|
|
add_fan_action(
|
|
'apply_policy_exception',
|
|
null,
|
|
null,
|
|
$e->getMessage(),
|
|
false
|
|
);
|
|
} catch (Throwable) {
|
|
}
|
|
|
|
fwrite(STDERR, $e->getMessage() . PHP_EOL);
|
|
exit(1);
|
|
}
|