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
-
+
##### Dark Theme
-
+
## 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