Skip to content
Snippets Groups Projects
Config.php 6.07 KiB
Newer Older
Thomas Flori's avatar
Thomas Flori committed
<?php

namespace App\Command;

use App\Application;
use GetOpt\GetOpt;
use Hugga\Console;
use Hugga\Input\Question\Choice;
use Hugga\Input\Question\Confirmation;

class Config extends AbstractCommand
{
    protected $name = 'config';

    protected $description = 'Generate or update fan configuration';

    public function __construct(Application $app, Console $console)
    {
        parent::__construct($app, $console);
    }

    public function handle(GetOpt $getOpt): int
    {
        if (posix_getuid() !== 0) {
            $this->warn('This programm has to run as root');
            return 1;
        }

        if ($this->getOption('quiet')) {
            $this->warn('This command requires user interaction');
            return 1;
        }

        $fanConfig = $this->app->fanConfig;
        if (!$fanConfig->hasFans()) {
            if (!$this->initializeConfig()) {
                return 1;
            }
        }

        $actions = new Choice([
            'edit a fan',
            'add a sensor',
            'edit a sensor',
            'add a rule',
            'edit a rule',
            'save and exit',
            'exit without saving',
        ], 'What next?', 'save and exit');
        do {
            $answer = $this->console->ask($actions);
            switch ($answer) {
                case 'exit without saving':
                    break 2;
                case 'save and exit':
//                    $fanConfig->save();
                    break 2;
                default:
                    $this->info('not implemented');
            }
        } while (true);


        // - edit a fan
        // - add a sensor
        // - edit a sensor
        // - add a rule
        // - edit a rule
        // - save and leave
        // - leave without saving

        return 0;
    }

    protected function initializeConfig()
    {
        $fanConfig = $this->app->fanConfig;

        $this->info('No fans configured. Detecting fans...');
        $fans = $this->detectControllableFans();
        if (count($fans) > 0) {
            // for each fan
            // ask if it should be added
            // ask for a name
            // test min speed
            // ask for a pwm limit
            // add the fan to the config

            $fanConfig->setFans($fans);
            $fanConfig->save();
            return true;
        }
        $this->error('We could not find any fan to control.');
        return false;
    }

    protected function detectControllableFans(): array
    {
        $fans = [];
        $inputs = glob(HWMON_PATH . '/hwmon*/fan*_input');
        $inputs = array_reduce($inputs, function ($inputs, $input) {
            if (!preg_match('~(hwmon(\d+)/(fan\d+))_input$~', $input, $match)) {
                return $inputs;
            }

            $hwmon = trim(file_get_contents(HWMON_PATH . '/hwmon' . $match[2] . '/name'));
            $fan = $match[3];
            $pwm = preg_replace('~fan(\d+)~', 'pwm$1', $fan);
            $name = $hwmon . '/' . $fan;

            $inputs[$name] = [
                'input' => $input,
                'pwm' => preg_replace('~fan(\d+)_input~', 'pwm$1', $input),
                'hwmon' => 'hwmon' . $match[2],
                'name' => $name,
                'type' => 'hwmon',
                'options' => [
                    'hwmon' => $hwmon,
                    'fan' => $fan,
                    'pwm' => $pwm,
                ],
            ];
            return $inputs;
        }, []);

        $unknownInputs = [];
        foreach ($inputs as $name => $input) {
            [$min, $max] = $this->testPwm($input['pwm'], $input['input']);
            if ($max === 0 || $min > $max*0.4) {
                $unknownInputs[$name] = $input;
                continue;
            }
            $this->info(sprintf('%s at full speed: %d; stopped: %d', $name, $max, $min));
            $fans[] = $input;
        }

        if (count($unknownInputs) > 0) {
            $this->info('Some fans could not be assigned to a pwm. Trying remaining pwms...');
            $assignedPwms = array_map(fn($fan) => $fan['pwm'], $fans);
            $pwms = array_reduce(glob(HWMON_PATH . '/hwmon*/pwm*'), function ($pwms, $pwm) use ($assignedPwms) {
                if (!preg_match('~(hwmon(\d+)/pwm(\d+))$~', $pwm, $match) || in_array($pwm, $assignedPwms)) {
                    return $pwms;
                }

                $hwmon = $match[2];
                if (!isset($pwms[$hwmon])) {
                    $pwms[$hwmon] = [];
                }
                $pwms[$hwmon][] = $pwm;

                return $pwms;
            }, []);
            foreach ($unknownInputs as $id => $input) {
                if (!isset($pwms[$input['hwmon']])) {
                    continue;
                }
                foreach ($pwms[$input['hwmon']] as $pwm) {
                    if ($input['pwm'] !== $pwm) {
                        continue;
                    }

                    [$min, $max] = $this->testPwm($pwm, $input['input']);
                    if ($max === 0 || $min > $max*0.4) {
                        continue;
                    }

                    $input['pwm'] = $pwm;
                    $input['options']['pwm'] = basename($pwm);
                    $fans[] = $input;
                    continue 2;
                }
                $this->info($id . ' not connected or not controllable');
            }
        }

        return array_map(function ($fan) {
            unset($fan['input'], $fan['pwm']);
            return $fan;
        }, $fans);
    }

    protected function testPwm($pwm, $input): array
    {
        $currentEnable = file_get_contents($pwm . '_enable');

        file_put_contents($pwm . '_enable', '1');
        file_put_contents($pwm, '255');
        sleep(4);
        $fullSpeed = (int)file_get_contents($input);

        if ($fullSpeed === 0) {
            file_put_contents($pwm . '_enable', $currentEnable);
            return [0, 0];
        }

        file_put_contents($pwm, '0');
        sleep(10);
        $stopSpeed = (int)file_get_contents($input);

        file_put_contents($pwm . '_enable', $currentEnable);
        return [$stopSpeed, $fullSpeed];
    }
}