Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change path detection for liquidctl.exe #36

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
*.git-credentials
FanControl.Liquidctl.zip
liquidctl/
liquidctl.exe

include/


## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
Expand Down
4 changes: 2 additions & 2 deletions FanControl.Liquidctl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="FanControl.Plugins">
<HintPath>..\..\..\..\Documents\FanControl\FanControl.Plugins.dll</HintPath>
<HintPath>include\FanControl.Plugins.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\..\..\Documents\FanControl\Newtonsoft.Json.dll</HintPath>
<HintPath>include\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
Expand Down
47 changes: 43 additions & 4 deletions LiquidctlCLIWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using Newtonsoft.Json;
using FanControl.Plugins;
using Newtonsoft.Json.Linq;


namespace FanControl.Liquidctl
{
internal static class LiquidctlCLIWrapper
{
public static string liquidctlexe = "Plugins\\liquidctl.exe"; //TODO extract path to executable to config
internal static void Initialize()
public static string liquidctlexe = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "liquidctl.exe"); // This should always resolve to the same directory as the FanControl.Liquidctl.dll
// TODO: extract path to executable to config(?) - Seems to work fine now though
internal static IPluginLogger logger;

internal static void Initialize(IPluginLogger pluginLogger)
{
logger = pluginLogger;

LiquidctlCall($"--json initialize all");
}

internal static List<LiquidctlStatusJSON> ReadStatus()
{
Process process = LiquidctlCall($"--json status");
return JsonConvert.DeserializeObject<List<LiquidctlStatusJSON>>(process.StandardOutput.ReadToEnd());
return ParseStatuses(process.StandardOutput.ReadToEnd());
}

internal static List<LiquidctlStatusJSON> ReadStatus(string address)
{
Process process = LiquidctlCall($"--json --address {address} status");
return JsonConvert.DeserializeObject<List<LiquidctlStatusJSON>>(process.StandardOutput.ReadToEnd());
return ParseStatuses(process.StandardOutput.ReadToEnd());
}

internal static void SetPump(string address, int value)
{
LiquidctlCall($"--address {address} set pump speed {(value)}");
Expand All @@ -33,6 +45,11 @@ internal static void SetFan(string address, int value)
LiquidctlCall($"--address {address} set fan speed {(value)}");
}

internal static void SetFanNumber(string address, int index, int value)
{
LiquidctlCall($"--address {address} set fan{index} speed {(value)}");
}

private static Process LiquidctlCall(string arguments)
{
Process process = new Process();
Expand All @@ -56,5 +73,27 @@ private static Process LiquidctlCall(string arguments)

return process;
}


// Code by akotulu
// See https://github.com/jmarucha/FanControl.Liquidctl/pull/29/commits/145978bdf1c2d1a464b2a036b4fc26f559bb77dc#diff-d7a2c0cf4c270870ed263c55d2cd4fc41258347085a3cded3a78b48e73f78092
private static List<LiquidctlStatusJSON> ParseStatuses(string json) {
JArray statusArray = JArray.Parse(json);
List<LiquidctlStatusJSON> statuses = new List<LiquidctlStatusJSON>();

foreach (JObject statusObject in statusArray) {
try
{
LiquidctlStatusJSON status = statusObject.ToObject<LiquidctlStatusJSON>();
statuses.Add(status);
}
catch (Exception e)
{
logger.Log($"Unable to parse {statusObject}\n{e.Message}");
}
}

return statuses;
}
}
}
151 changes: 146 additions & 5 deletions LiquidctlDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public LiquidTemperature(LiquidctlStatusJSON output)
_name = $"Liquid Temp. - {output.description}";
UpdateFromJSON(output);
}

public void UpdateFromJSON(LiquidctlStatusJSON output)
{
_value = (float)output.status.Single(entry => entry.key == KEY).value;
Expand All @@ -33,6 +34,7 @@ public void UpdateFromJSON(LiquidctlStatusJSON output)
public void Update()
{ } // plugin updates sensors
}

public class PumpSpeed : IPluginSensor
{
public PumpSpeed(LiquidctlStatusJSON output)
Expand All @@ -41,6 +43,7 @@ public PumpSpeed(LiquidctlStatusJSON output)
_name = $"Pump - {output.description}";
UpdateFromJSON(output);
}

public void UpdateFromJSON(LiquidctlStatusJSON output)
{
_value = (float)output.status.Single(entry => entry.key == KEY).value;
Expand All @@ -59,6 +62,7 @@ public void UpdateFromJSON(LiquidctlStatusJSON output)
public void Update()
{ } // plugin updates sensors
}

public class PumpDuty : IPluginControlSensor
{
public PumpDuty(LiquidctlStatusJSON output)
Expand Down Expand Up @@ -87,6 +91,7 @@ public void UpdateFromJSON(LiquidctlStatusJSON output)
{2780, 90}, {2789, 91}, {2798, 92}, {2807, 93}, {2816, 94}, {2825, 95}, {2834, 96}, {2843, 97}, {2852, 98}, {2861, 99},
{MAX_RPM, 100}
};

static readonly int MAX_RPM = 2870;

public string Id => _id;
Expand Down Expand Up @@ -122,6 +127,7 @@ public FanSpeed(LiquidctlStatusJSON output)
_name = $"Fan - {output.description}";
UpdateFromJSON(output);
}

public void UpdateFromJSON(LiquidctlStatusJSON output)
{
_value = (float)output.status.Single(entry => entry.key == KEY).value;
Expand Down Expand Up @@ -150,6 +156,7 @@ public FanControl(LiquidctlStatusJSON output)
_name = $"Fan Control - {output.description}";
UpdateFromJSON(output);
}

// We can only estimate, as it is not provided in any output
public void UpdateFromJSON(LiquidctlStatusJSON output)
{
Expand All @@ -171,6 +178,7 @@ public void UpdateFromJSON(LiquidctlStatusJSON output)
{1530, 90}, {1550, 91}, {1570, 92}, {1590, 93}, {1610, 94}, {1630, 95}, {1650, 96}, {1670, 97}, {1690, 98}, {1720, 99},
{MAX_RPM, 100}
};

static readonly int MAX_RPM = 1980;

public string Id => _id;
Expand All @@ -197,32 +205,145 @@ public void Update()
{ } // plugin updates sensors
}

// Try to get the speeds for multiple fans
public class FanSpeedMultiple : IPluginSensor
{
public FanSpeedMultiple(int index, LiquidctlStatusJSON output)
{
_id = $"{output.address}-fan{index}rpm";
_name = $"Fan {index} - {output.description}";
UpdateFromJSON(index, output);
}

public void UpdateFromJSON(int index, LiquidctlStatusJSON output)
{
string currentKey = KEY.Replace("###", index.ToString());
_value = (float) output.status.Single(entry => entry.key == currentKey).value;
}

public static string KEY = "Fan ### speed";

public string Id => _id;
readonly string _id;

public string Name => _name;
readonly string _name;

public float? Value => _value;
float _value;

public void Update() { } // plugin updates sensors
}

// Try to control multiple fans
public class FanControlMultiple : IPluginControlSensor
{
public FanControlMultiple(int index, LiquidctlStatusJSON output)
{
_address = output.address;
_id = $"{output.address}-fan{index}ctrl";
_name = $"Fan {index} Control - {output.description}";
_index = index;

UpdateFromJSON(index, output);
}

// We can only estimate, as it is not provided in any output
public void UpdateFromJSON(int index, LiquidctlStatusJSON output) {
string currentKey = FanSpeedMultiple.KEY.Replace("###", index.ToString());
float reading = (float)output.status.Single(entry => entry.key == currentKey).value;
//_value = reading > MAX_RPM ? 100.0f : (float)Math.Ceiling(100.0f * reading / MAX_RPM);
_value = RPM_LOOKUP.OrderBy(e => Math.Abs(e.Key - reading)).FirstOrDefault().Value;
}

public static string KEY = "Fan ### speed";
//public static string KEY = $"Fan {_index} speed";

static readonly Dictionary<int, int> RPM_LOOKUP = new Dictionary<int, int>
{ // We can only estimate, as it is not provided in any output. Hence I applied this ugly hack
{520, 20}, {521, 21}, {522, 22}, {523, 23}, {524, 24}, {525, 25}, {526, 26}, {527, 27}, {528, 28}, {529, 29},
{530, 30}, {532, 31}, {534, 32}, {536, 33}, {538, 34}, {540, 35}, {542, 36}, {544, 37}, {546, 38}, {548, 39},
{550, 40}, {571, 41}, {592, 42}, {613, 43}, {634, 44}, {655, 45}, {676, 46}, {697, 47}, {718, 48}, {739, 49},
{760, 50}, {781, 51}, {802, 52}, {823, 53}, {844, 54}, {865, 55}, {886, 56}, {907, 57}, {928, 58}, {949, 59},
{970, 60}, {989, 61}, {1008, 62}, {1027, 63}, {1046, 64}, {1065, 65}, {1084, 66}, {1103, 67}, {1122, 68}, {1141, 69},
{1160, 70}, {1180, 71}, {1200, 72}, {1220, 73}, {1240, 74}, {1260, 75}, {1280, 76}, {1300, 77}, {1320, 78}, {1340, 79},
{1360, 80}, {1377, 81}, {1394, 82}, {1411, 83}, {1428, 84}, {1445, 85}, {1462, 86}, {1479, 87}, {1496, 88}, {1513, 89},
{1530, 90}, {1550, 91}, {1570, 92}, {1590, 93}, {1610, 94}, {1630, 95}, {1650, 96}, {1670, 97}, {1690, 98}, {1720, 99},
{MAX_RPM, 100}
};

static readonly int MAX_RPM = 1980;

public string Id => _id;
readonly string _id;
string _address;

public string Name => _name;
readonly string _name;

public float? Value => _value;
float _value;

public int Index => _index;
int _index;

public void Reset()
{
Set(50.0f);
}

public void Set(float val)
{
LiquidctlCLIWrapper.SetFanNumber(_address, _index, (int) val);
}

public void Update() { } // plugin updates sensors
}

public LiquidctlDevice(LiquidctlStatusJSON output, IPluginLogger pluginLogger)
{
logger = pluginLogger;
address = output.address;

hasPumpSpeed = output.status.Exists(entry => entry.key == PumpSpeed.KEY && !(entry.value is null));
if (hasPumpSpeed)
if (hasPumpSpeed) {
pumpSpeed = new PumpSpeed(output);
}

hasPumpDuty = output.status.Exists(entry => entry.key == PumpDuty.KEY && !(entry.value is null));
if (hasPumpDuty)
if (hasPumpDuty) {
pumpDuty = new PumpDuty(output);
}

hasFanSpeed = output.status.Exists(entry => entry.key == FanSpeed.KEY && !(entry.value is null));
if(hasFanSpeed)
{
if (hasFanSpeed) {
fanSpeed = new FanSpeed(output);
fanControl = new FanControl(output);
}

hasLiquidTemperature = output.status.Exists(entry => entry.key == LiquidTemperature.KEY && !(entry.value is null));
if (hasLiquidTemperature)
if (hasLiquidTemperature) {
liquidTemperature = new LiquidTemperature(output);
}


// Get the info for multiple fans
for (int i=0; i<20; i++) {
int index = i+1;
string currentKey = FanSpeedMultiple.KEY.Replace("###", index.ToString());
hasMultipleFanSpeed[i] = output.status.Exists(entry => entry.key == currentKey && !(entry.value is null));

if (hasMultipleFanSpeed[i]) {
fanSpeedMultiple[i] = new FanSpeedMultiple(index, output);
fanControlMultiple[i] = new FanControlMultiple(index, output);
}
}
}


public readonly bool hasPumpSpeed, hasPumpDuty, hasLiquidTemperature, hasFanSpeed;
public readonly bool[] hasMultipleFanSpeed = new bool[20];


public void UpdateFromJSON(LiquidctlStatusJSON output)
{
Expand All @@ -233,8 +354,16 @@ public void UpdateFromJSON(LiquidctlStatusJSON output)
fanSpeed.UpdateFromJSON(output);
fanControl.UpdateFromJSON(output);
}

for (int i = 0; i<20; i++) {
if (hasMultipleFanSpeed[i]) {
fanSpeedMultiple[i].UpdateFromJSON(i+1, output);
fanControlMultiple[i].UpdateFromJSON(i+1, output);
}
}
}


internal IPluginLogger logger;
public string address;
public LiquidTemperature liquidTemperature;
Expand All @@ -243,6 +372,10 @@ public void UpdateFromJSON(LiquidctlStatusJSON output)
public FanSpeed fanSpeed;
public FanControl fanControl;

public FanSpeedMultiple[] fanSpeedMultiple = new FanSpeedMultiple[20];
public FanControlMultiple[] fanControlMultiple = new FanControlMultiple[20];


public void LoadJSON()
{
try
Expand All @@ -256,12 +389,20 @@ public void LoadJSON()
}
}


public String GetDeviceInfo() {
String ret = $"Device @ {address}";
if (hasLiquidTemperature) ret += $", Liquid @ {liquidTemperature.Value}";
if (hasPumpSpeed) ret += $", Pump @ {pumpSpeed.Value}";
if (hasPumpDuty) ret += $"({pumpDuty.Value})";
if (hasFanSpeed) ret += $", Fan @ {fanSpeed.Value} ({fanControl.Value})";

for (int i = 0; i<20; i++) {
if (hasMultipleFanSpeed[i]) {
ret += $", Fan{i+1} @ {fanSpeedMultiple[i].Value} ({fanControlMultiple[i].Value})";
}
}

return ret;
}
}
Expand Down
Loading