diff --git a/.github/screenshot_dark_small.png b/.github/screenshot_dark_small.png new file mode 100644 index 0000000..78991cd Binary files /dev/null and b/.github/screenshot_dark_small.png differ diff --git a/.github/screenshot_light_small.png b/.github/screenshot_light_small.png new file mode 100644 index 0000000..5fec886 Binary files /dev/null and b/.github/screenshot_light_small.png differ diff --git a/README.md b/README.md index 69f4e33..767796e 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ # Commander Fan Control (cfancontrol) -A manager for the Corsair Commander fan controller for Linux written in Python with a GUI based on the QT framework. Manage the speeds of all connected fans individually and based on different temperature probes. +A manager for the Corsair Commander Pro fan controller for Linux written in Python with a GUI based on the QT framework. Manage the speeds of all connected fans individually and based on different temperature probes. -A driver for the Corsair Commander was added to the kernel in version 5.9, so this will be the minimal Linux version required. +A driver for the Corsair Commander Pro was added to the kernel in version 5.9, so this will be the minimal Linux version required. ## Aknowledgements -Initially inspired by [afancontrol](https://github.com/KostyaEsmukov/afancontrol) and using the wonderful [liquidctl](https://github.com/liquidctl/liquidctl) for the fan control and also to access temperature sensors in all-in-on liquid coolers (AIOs). Other temperature sensors in the system are detected via the [lm-sensors](https://hwmon.wiki.kernel.org/lm_sensors) package (and the [PySensors](https://pypi.org/project/PySensors/) module). The widget to manage the fan curves is based on the one in [qt-amdgpu-fan-ctl](https://github.com/wepiha/qt-amdgpu-fan-ctl). +Initially inspired by [afancontrol](https://github.com/KostyaEsmukov/afancontrol) and using the wonderful [liquidctl](https://github.com/liquidctl/liquidctl) for the fan control and as wells as to access temperature sensors in all-in-on liquid coolers (AIOs). Other temperature sensors in the system are detected via the [lm-sensors](https://hwmon.wiki.kernel.org/lm_sensors) package (and the [PySensors](https://pypi.org/project/PySensors/) module). The widget to manage the fan curves is based on the one in [qt-amdgpu-fan-ctl](https://github.com/wepiha/qt-amdgpu-fan-ctl). ## Screenshots ##### Light Theme -Light Screenshot +Light Screenshot ##### Dark Theme -Dark Screenshot +Dark Screenshot ## Requirements @@ -34,7 +34,7 @@ cfancontrol itself requires the following additional Python libraries in their r - PyQt5~=5.12.3 - pyqtgraph~=0.12.4 -- liquidctl~=1.6.1 +- liquidctl~=1.8.1 - numpy~=1.17.4 - PyYAML~=5.3.1 - PySensors~=0.0.4 diff --git a/cfancontrol/fancontroller.py b/cfancontrol/fancontroller.py index 99d8872..fe5b686 100644 --- a/cfancontrol/fancontroller.py +++ b/cfancontrol/fancontroller.py @@ -4,12 +4,49 @@ import liquidctl.driver.commander_pro from liquidctl import find_liquidctl_devices +# from liquidctl.driver.commander_pro import CommanderPro from .log import LogManager class FanController(ContextManager): + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + def detect_channels(self) -> List[str]: + return [] + + def get_channel_speed(self, channel: str) -> int: + raise NotImplementedError() + + def set_channel_speed(self, channel: str, new_pwm: int, current_percent: int, new_percent: int, temperature: float) -> bool: + raise NotImplementedError() + + def stop_channel(self, channel: str, current_percent: int) -> bool: + raise NotImplementedError() + + +class ControllerManager(object): + fan_controller: List[FanController] = [] + + @staticmethod + def identify_fan_controllers(): + devices = find_liquidctl_devices() + for dev in devices: + if type(dev) == liquidctl.driver.commander_pro.CommanderPro: + LogManager.logger.info(f"Fan controller '{dev.description}' found") + ControllerManager.fan_controller.append(CommanderPro(dev)) + + +class CommanderPro(FanController, ContextManager): + RPM_STEP: int = 10 RPM_INTERVAL: float = 0.1 @@ -17,15 +54,18 @@ class FanController(ContextManager): def get_commander(cls) -> Optional[liquidctl.driver.commander_pro.CommanderPro]: devices = find_liquidctl_devices() for dev in devices: - if 'Commander' in dev.description: - LogManager.logger.info("Fan controller 'Commander Pro' found") + # if 'Commander' in dev.description: + if type(dev) == CommanderPro: + LogManager.logger.info(f"Fan controller '{dev.description}' found") return dev return None - def __init__(self) -> None: + def __init__(self, device: liquidctl.driver.commander_pro.CommanderPro) -> None: + super().__init__() self.is_valid = False self._lock = threading.Lock() - self.commander = self.get_commander() + # self.commander = self.get_commander() + self.commander = device if self.commander: try: self.commander.connect() diff --git a/cfancontrol/fanmanager.py b/cfancontrol/fanmanager.py index fb6a189..0da9de8 100644 --- a/cfancontrol/fanmanager.py +++ b/cfancontrol/fanmanager.py @@ -6,7 +6,7 @@ from .log import LogManager from .settings import Environment, Config -from .fancontroller import FanController +from .fancontroller import ControllerManager, FanController, CommanderPro from .fancurve import FanCurve, FanMode from .pwmfan import PWMFan from .sensor import Sensor, DummySensor @@ -18,7 +18,7 @@ class FanManager: _is_running: bool - _controller: FanController + _controller: Optional[FanController] _interval: float manager_thread: threading.Thread _channels: Dict[str, PWMFan] @@ -45,9 +45,18 @@ def __init__(self): ProfileManager.enum_profiles(Environment.settings_path) # get active channels from controller - self._controller: FanController = FanController() - self._controller_channels = self._controller.detect_channels() - self.reset_channels(self._controller_channels) + # self._controller: CommanderPro = CommanderPro() + # self._controller_channels = self._controller.detect_channels() + # self.reset_channels(self._controller_channels) + ControllerManager.identify_fan_controllers() + if ControllerManager.fan_controller: + self._controller = ControllerManager.fan_controller[0] + self._controller_channels = self._controller.detect_channels() + self.reset_channels(self._controller_channels) + else: + self._controller = FanController() + self._controller_channels = self._controller.detect_channels() + self.reset_channels(self._controller_channels) def __enter__(self): # reusable self._stack = ExitStack() @@ -55,7 +64,8 @@ def __enter__(self): # reusable for sensor in self._sensors: if isinstance(sensor, AIODeviceSensor): self._stack.enter_context(sensor) - self._stack.enter_context(self._controller) + if self._controller: + self._stack.enter_context(self._controller) except Exception: self._stack.close() raise