OK, so I have set up a water cooler for my CPU.. I picked up a Kraken X72.
I got liquidctl set up and at
@CaseySJ recommendation I also checked out Yoda.
I am trying to get Yoda to run as a service, similar to the instructions for setting up liquidctl to launch at boot.
I am having a heck of a time getting it to run this way though.
If I run my script manually, it launches OK.
however, if I run it with a LaunchAgent via launchctrl I get a weird error. IT just cant seem to parse the script correctly.
I suspect there is weirdness with variations in Python libraries between my user account and the root user.
Maybe something else is going on.
Has anyone else used Yoda (and maybe launching it at boot?) to manage their cpu cooler? I
I might be able to help with this.
Initially I also ran into this problem because by design, Yoda has to be running continuously given that it has to check for changes in temperature to adjust the fan and pump speeds.
This is in contrast to liquidctl, which dispatches its fan and pump profiles to the actual device, hence the script can quit once the data has been written. Of course that makes it very simple to run as a launch agent since it only has to run once and then quit.
With yoda, we have to take a slightly more advanced approach to run it as some kind of background service.
As a preface, I'm not an expert on how macOS operates and while my solution may work, it might not be optimal. Nevertheless, here we go.
Overall, I ran into two problems while trying to get Yoda to run as a background service:
1. Yoda has to run continuously
2. liquidctl may raise an
OSError: Failed to open if we access the Kraken or the Smart Device too quickly
To circumvent 1. I made use a of a very useful little utility from Linux:
screen
This command-line tool will allow us to create multiple "headless" terminal sessions in which we can run commands programmatically without having to bother with i.e. Terminal.app. I'm using this to dispatch my yoda commands into their individual shell environments where they can keep running indefinitely.
An added bonus is that we can attach to any of these sessions later on to check the output of the script, for example.
Tackling 2. is rather simple: We use the
sleep command to wait a little between each command we issue to our cooling devices to prevent hammering the tiny embedded processors in them too much.
Wrapping this knowledge up in a script nets us something like this:
Bash:
#!/bin/bash
# Init the Smart Device
liquidctl initialize -d 0
sleep 1
# Init the Kraken
liquidctl initialize -d 1
sleep 1
# Configure lighting (all off in this case)
liquidctl -d 0 set led color off
liquidctl -d 1 set ring color off
liquidctl -d 1 set logo color off
sleep 1
# Start Yoda for Kraken
screen -d -m -S yoda-kraken bash -c 'yoda --match "Kraken" control pump with "(60,40),(90,90)" on istats.cpu and fan with "(60,20),(90,50)" on istats.cpu'
sleep 1
# Start Yoda for Smart Device
screen -d -m -S yoda-smart-device bash -c 'yoda --match "Smart Device" control fan2 with "(60,30),(90,85)" on istats.cpu and fan3 with "(60,30),(90,50)" on istats.cpu'
Please take care to adjust the CPU temperatures and fan values to your system!
The ones shown here are exemplary and will likely only work well with my specific cooling setup and ambient temperature.
Save this script somewhere, i.e. in
/Users/Shared/yodactl.sh, similar to what was done with the original liquidctl script.
Please note that this script will supersede liquidctl.sh as it handles the initialisation as well as the fan control!
We can now register a new
LaunchAgent in
/Library/LaunchAgents/.
Before doing this, please
remove the original LaunchAgent for
liquidtcl.sh, we won't need it anymore.
After this is done, we can register the new agent as follows:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>liquidctl.sh</string>
<key>ProgramArguments</key>
<array>
<string>/Users/Shared/yodactl.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<false/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>
Note that I add the
PATH environment variable to point to my Python installation. By default this should not be necessary, however if you have installed multiple versions of Python, i.e. using Homebrew, Pyenv, Conda or what have you, then you should tweak this path to primarily point to the location of the installation that actually has liquidctl installed.
In my case I am using Conda, so my path would have the first component:
/usr/local/Caskroom/miniconda/base/envs/<conda_evironment_name>/bin
After setting everything up as described above, your cooling devices should be initialized a few seconds after login and yoda should be running and handling fan control.
You can verify this with two methods:
- Open
Terminal.app and run:
screen -ls
This should show you two screens with the names from the script and the status
Detached
- Run a benchmarking tool like
Cinebench to load the system and listen for the fans spinning up.
If they do, everything is working.
If they don't, immediately abort the program to avoid overheating.
As a general note, the timing of the
sleep calls could probably be shortened with some trial and error, but I find that these values work alright in most cases, so I didn't bother tuning them down to the minimum delay possible. Given that it's likely to differ from system to system, sticking with slightly higher sleep values seems the safer choice in my eyes.