Skip to content

Commit

Permalink
Properly detect 'Commander Pro' only
Browse files Browse the repository at this point in the history
  • Loading branch information
maclarsson committed Jul 24, 2022
1 parent 240185a commit c2bd9cc
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 16 deletions.
Binary file added .github/screenshot_dark_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/screenshot_light_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

<img src=".github/screenshot_light.png" alt="Light Screenshot" style="zoom:67%;" />
<img src=".github/screenshot_light_small.png" alt="Light Screenshot" />

##### Dark Theme

<img src=".github/screenshot_dark.png" alt="Dark Screenshot" style="zoom:67%;" />
<img src=".github/screenshot_dark_small.png" alt="Dark Screenshot"/>

## Requirements

Expand All @@ -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
Expand Down
48 changes: 44 additions & 4 deletions cfancontrol/fancontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,68 @@

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

@classmethod
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()
Expand Down
22 changes: 16 additions & 6 deletions cfancontrol/fanmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,7 +18,7 @@
class FanManager:

_is_running: bool
_controller: FanController
_controller: Optional[FanController]
_interval: float
manager_thread: threading.Thread
_channels: Dict[str, PWMFan]
Expand All @@ -45,17 +45,27 @@ 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()
try:
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
Expand Down

0 comments on commit c2bd9cc

Please sign in to comment.