diff --git a/Build/Build.csproj.DotSettings b/Build/Build.csproj.DotSettings index c8947fc..7bc2848 100644 --- a/Build/Build.csproj.DotSettings +++ b/Build/Build.csproj.DotSettings @@ -1,4 +1,4 @@ - + DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW @@ -20,6 +20,7 @@ True True True + True True True True diff --git a/Plugins/Controllers/TTController.Plugin.ToughfanController/TTController.Plugin.ToughfanController.csproj b/Plugins/Controllers/TTController.Plugin.ToughfanController/TTController.Plugin.ToughfanController.csproj new file mode 100644 index 0000000..8a7a211 --- /dev/null +++ b/Plugins/Controllers/TTController.Plugin.ToughfanController/TTController.Plugin.ToughfanController.csproj @@ -0,0 +1,13 @@ + + + net48 + TTController.Plugin.ToughfanController + TTController.Plugin.ToughfanController + Copyright © 2023 + false + false + + + + + \ No newline at end of file diff --git a/Plugins/Controllers/TTController.Plugin.ToughfanController/ToughfanControllerDefinition.cs b/Plugins/Controllers/TTController.Plugin.ToughfanController/ToughfanControllerDefinition.cs new file mode 100644 index 0000000..af19370 --- /dev/null +++ b/Plugins/Controllers/TTController.Plugin.ToughfanController/ToughfanControllerDefinition.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TTController.Common.Plugin; + +namespace TTController.Plugin.ToughfanController +{ + public class ToughfanControllerDefinition : IControllerDefinition + { + public string Name => "Toughfan"; + + public int VendorId => 0x264a; + + public IEnumerable ProductIds => Enumerable.Range(0, 16).Select(x => 0x232b + x); + public int PortCount => 5; + public Type ControllerProxyType => typeof(ToughfanControllerProxy); + } +} \ No newline at end of file diff --git a/Plugins/Controllers/TTController.Plugin.ToughfanController/ToughfanControllerProxy.cs b/Plugins/Controllers/TTController.Plugin.ToughfanController/ToughfanControllerProxy.cs new file mode 100644 index 0000000..20180b0 --- /dev/null +++ b/Plugins/Controllers/TTController.Plugin.ToughfanController/ToughfanControllerProxy.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TTController.Common; +using TTController.Common.Plugin; + +namespace TTController.Plugin.ToughfanController +{ + public class ToughfanControllerProxy : AbstractControllerProxy + { + private readonly IReadOnlyDictionary _availableEffects; + + public ToughfanControllerProxy(IHidDeviceProxy device, IControllerDefinition definition) + : base(device, definition) + { + var effectModes = new Dictionary() + { + ["Flow"] = 0x00, + ["Spectrum"] = 0x04, + ["Ripple"] = 0x08, + ["Blink"] = 0x0c, + ["Pulse"] = 0x10, + ["Wave"] = 0x14, + }; + + var effectSpeeds = new Dictionary() + { + ["Extreme"] = 0x00, + ["Fast"] = 0x01, + ["Normal"] = 0x02, + ["Slow"] = 0x03 + }; + + var result = new Dictionary(); + foreach (var mkv in effectModes) + foreach (var skv in effectSpeeds) + result.Add($"{mkv.Key}_{skv.Key}", (byte)(mkv.Value + skv.Value)); + + result.Add("PerLed", 0x18); + result.Add("Full", 0x19); + + _availableEffects = result; + } + + public override Version Version + { + get + { + var bytes = Device.WriteReadBytes(0x33, 0x50); + if (bytes == null) + return new Version(); + + return new Version(bytes[3], bytes[4], bytes[5]); + } + } + + public override IEnumerable Ports => Enumerable.Range(1, Definition.PortCount) + .Select(x => new PortIdentifier(Device.VendorId, Device.ProductId, (byte)x)); + + public override IEnumerable EffectTypes => _availableEffects.Keys; + + public override bool SetRgb(byte port, string effectType, IEnumerable colors) + { + if (!_availableEffects.TryGetValue(effectType, out var mode)) + return false; + + var bytes = new List { 0x32, 0x52, port, 0x24 }; + var color = colors.First(); + for (int i = 0; i < 24; i++) + { + bytes.Add(color.G); + bytes.Add(color.R); + bytes.Add(color.B); + } + + // var bytes = new byte[] + // { + // 0x32, 0x52, port, 0x24, + // 0x00, 0x00, 0xff, // 1 + // 0x00, 0x00, 0xff, // 2 + // 0x00, 0x00, 0xff, // 3 + // 0x00, 0x00, 0xff, // 4 + // 0x00, 0x00, 0xff, // 5 + // 0x00, 0x00, 0xff, // 6 + // 0x00, 0x00, 0xff, // 7 + // 0x00, 0x00, 0xff, // 8 + // 0x00, 0x00, 0xff, // 9 + // 0x00, 0x00, 0xff, // 10 + // 0x00, 0x00, 0xff, // 11 + // 0x00, 0x00, 0xff, // 12 + // 0x00, 0x00, 0xff, // 13 + // 0x00, 0x00, 0xff, // 14 + // 0x00, 0x00, 0xff, // 15 + // 0x00, 0x00, 0xff, // 16 + // 0x00, 0x00, 0xff, // 17 + // 0x00, 0x00, 0xff, // 18 + // 0x00, 0x00, 0xff, // 19 + // 0x00, 0x00, 0xff, // 20 + // 0x00, 0x00, 0xff, // 21 + // 0x00, 0x00, 0xff, // 22 + // 0x00, 0x00, 0xff, // 23 + // 0x00, 0x00, 0xff, // 24 + var endBytes = new byte[] + { + 0x7f, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, 0x7f, 0x00, 0xff, 0xff, + 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x7f, 0x00, 0xff, 0xff, 0x00, 0x7f, 0xff, 0x00, + 0x00, 0xff, 0x7f, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0xff, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x7f, 0x00, 0xff, 0xff, 0x00, 0x7f, + 0xff, 0x00, 0x00, 0xff, 0x7f, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, + 0x7f, 0x00, 0xff, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x7f, 0x00, 0xff, 0xff, + 0x00, 0x7f, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + bytes.AddRange(endBytes); + var result = Device.WriteReadBytes(bytes); + return result?[3] == 0xfc; + } + + public override bool SetSpeed(byte port, byte speed) => + Device.WriteReadBytes(0x32, 0x51, port, 0x01, speed)?[3] == 0xfc; + + public override PortData GetPortData(byte port) + { + var result = Device.WriteReadBytes(0x33, 0x51, port); + if (result == null) + return null; + + if (result[3] == 0xfe) + return null; + + var data = new PortData + { + PortId = result[3], + Speed = result[5], + Rpm = (result[7] << 8) + result[6], + ["Unknown"] = result[4] + }; + + return data; + } + + public override void SaveProfile() => + Device.WriteReadBytes(0x32, 0x53); + + public override bool Init() => + Device.WriteReadBytes(0xfe, 0x33)?[3] == 0xfc; + + public override bool IsValidPort(PortIdentifier port) => + port.ControllerProductId == Device.ProductId + && port.ControllerVendorId == Device.VendorId + && port.Id >= 1 + && port.Id <= Definition.PortCount; + } +} \ No newline at end of file diff --git a/Plugins/Devices/Toughfan.json b/Plugins/Devices/Toughfan.json new file mode 100644 index 0000000..a142688 --- /dev/null +++ b/Plugins/Devices/Toughfan.json @@ -0,0 +1,5 @@ +{ + "Name": "Toughfan", + "LedCount": 24, + "Zones": [ 24 ] +} \ No newline at end of file diff --git a/Plugins/Effects/TTController.Plugin.PingPongEffect/PingPongEffect.cs b/Plugins/Effects/TTController.Plugin.PingPongEffect/PingPongEffect.cs index 80a078c..7ba484d 100644 --- a/Plugins/Effects/TTController.Plugin.PingPongEffect/PingPongEffect.cs +++ b/Plugins/Effects/TTController.Plugin.PingPongEffect/PingPongEffect.cs @@ -67,6 +67,11 @@ public override IDictionary> GenerateColors(List< colors.AddRange(GenerateColors(12, portStart, portEnd)); colors.AddRange(GenerateColors(6, portStart, portEnd, radius: 0.33, oddDivide: false)); break; + case "Toughfan": + colors.AddRange(GenerateColors(12, portStart, portEnd)); + colors.AddRange(colors.ToList()); + colors.AddRange(GenerateColors(6, portStart, portEnd, radius: 0.33, oddDivide: false)); + break; case "PurePlus": colors.AddRange(GenerateColors(9, portStart, portEnd, radius: 0.33)); break; diff --git a/Plugins/Effects/TTController.Plugin.StaticColorEffect2/CurvePoint.cs b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/CurvePoint.cs new file mode 100644 index 0000000..9088a03 --- /dev/null +++ b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/CurvePoint.cs @@ -0,0 +1,22 @@ +using System.Drawing; + +namespace TTController.Plugin.StaticColorEffect2 +{ + public struct CurvePoint + { + public int Value { get; } + public byte Red { get; } + public byte Green { get; } + public byte Blue { get; } + + public CurvePoint(int value, byte red, byte green, byte blue) + { + Value = value; + Red = red; + Green = green; + Blue = blue; + } + + public override string ToString() => $"[Value: {Value}, Color: ({Red}, {Green}, {Blue})]"; + } +} \ No newline at end of file diff --git a/Plugins/Effects/TTController.Plugin.StaticColorEffect2/CurvePointConverter.cs b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/CurvePointConverter.cs new file mode 100644 index 0000000..5f59cfc --- /dev/null +++ b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/CurvePointConverter.cs @@ -0,0 +1,21 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace TTController.Plugin.StaticColorEffect2 +{ + public class CurvePointConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, CurvePoint value, JsonSerializer serializer) + { + var array = new JArray { value.Value, value.Red, value.Green, value.Blue }; + writer.WriteRawValue(JsonConvert.SerializeObject(array, Formatting.None)); + } + + public override CurvePoint ReadJson(JsonReader reader, Type objectType, CurvePoint existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var array = JArray.Load(reader); + return new CurvePoint(array[0].Value(), array[1].Value(), array[2].Value(), array[3].Value()); + } + } +} \ No newline at end of file diff --git a/Plugins/Effects/TTController.Plugin.StaticColorEffect2/StaticColorEffect2.cs b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/StaticColorEffect2.cs new file mode 100644 index 0000000..4477156 --- /dev/null +++ b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/StaticColorEffect2.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using LibreHardwareMonitor.Hardware; +using TTController.Common; +using TTController.Common.Plugin; + +namespace TTController.Plugin.StaticColorEffect2 +{ + public class StaticColorEffect2Config : EffectConfigBase + { + public List CurvePoints { get; internal set; } = new List(); + public List Sensors { get; internal set; } = new List(); + + [DefaultValue(SensorMixFunction.Maximum)] + public SensorMixFunction SensorMixFunction { get; internal set; } = SensorMixFunction.Maximum; + + [DefaultValue(4)] public int MinimumChange { get; internal set; } = 4; + [DefaultValue(8)] public int MaximumChange { get; internal set; } = 8; + } + + public class StaticColorEffect2 : EffectBase + { + public StaticColorEffect2(StaticColorEffect2Config config) : base(config) + { + } + + public override string EffectType => "PerLed"; + + private Color InterpolateColor(double temperature) + { + var curvePoints = Config.CurvePoints.OrderBy(p => p.Value).ToArray(); + var n = curvePoints.Length; + + if (temperature <= curvePoints[0].Value) + { + return CreateColorFromCurvePoint(curvePoints[0]); + } + + if (temperature >= curvePoints[n - 1].Value) + { + return CreateColorFromCurvePoint(curvePoints[n - 1]); + } + + for (var i = 0; i < n - 1; i++) + { + if (IsInRange(temperature, curvePoints[i].Value, curvePoints[i + 1].Value)) + { + var t = CalculateInterpolationParameter(temperature, curvePoints[i].Value, + curvePoints[i + 1].Value); + + var r = InterpolateLinear(curvePoints[i].Red, curvePoints[i + 1].Red, t); + var g = InterpolateLinear(curvePoints[i].Green, curvePoints[i + 1].Green, t); + var b = InterpolateLinear(curvePoints[i].Blue, curvePoints[i + 1].Blue, t); + + return CreateColorFromRgbValues(r, g, b); + } + } + + return Color.White; // Handle cases outside the defined range + } + + private Color CreateColorFromCurvePoint(CurvePoint curvePoint) + { + return Color.FromArgb(curvePoint.Red, curvePoint.Green, curvePoint.Blue); + } + + private bool IsInRange(double value, int minValue, int maxValue) + { + return value >= minValue && value <= maxValue; + } + + private double CalculateInterpolationParameter(double value, int minValue, int maxValue) + { + return (double)(value - minValue) / (maxValue - minValue); + } + + private double InterpolateLinear(int startValue, int endValue, double t) + { + return startValue + t * (endValue - startValue); + } + + private Color CreateColorFromRgbValues(double r, double g, double b) + { + return Color.FromArgb(Clamp((int)r, 0, 255), Clamp((int)g, 0, 255), Clamp((int)b, 0, 255)); + } + + private int Clamp(int value, int min, int max) + { + if (value < min) + return min; + if (value > max) + return max; + return value; + } + + public override IDictionary> GenerateColors(List ports, + ICacheProvider cache) + { + var values = Config.Sensors.Select(cache.GetSensorValue); + var value = float.NaN; + if (Config.SensorMixFunction == SensorMixFunction.Average) + value = values.Average(); + else if (Config.SensorMixFunction == SensorMixFunction.Minimum) + value = values.Min(); + else if (Config.SensorMixFunction == SensorMixFunction.Maximum) + value = values.Max(); + + if (float.IsNaN(value)) + return ports.ToDictionary(p => p, _ => new List { new LedColor(255, 255, 255) }); + + var curveTargetColor = InterpolateColor(value); + + return ports.ToDictionary(p => p, _ => new List + { new LedColor(curveTargetColor.R, curveTargetColor.G, curveTargetColor.B) } + ); + } + + + public override List GenerateColors(int count, ICacheProvider cache) + { + return null; + } + } +} \ No newline at end of file diff --git a/Plugins/Effects/TTController.Plugin.StaticColorEffect2/TTController.Plugin.StaticColorEffect2.csproj b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/TTController.Plugin.StaticColorEffect2.csproj new file mode 100644 index 0000000..c6f7ce9 --- /dev/null +++ b/Plugins/Effects/TTController.Plugin.StaticColorEffect2/TTController.Plugin.StaticColorEffect2.csproj @@ -0,0 +1,16 @@ + + + net48 + TTController.Plugin.StaticColorEffect + TTController.Plugin.StaticColorEffect + Copyright © 2020 + false + false + + + + + + + + \ No newline at end of file diff --git a/Source/TTController.Common/Enums.cs b/Source/TTController.Common/Enums.cs index e395fa9..66d9e4c 100644 --- a/Source/TTController.Common/Enums.cs +++ b/Source/TTController.Common/Enums.cs @@ -19,6 +19,7 @@ public enum DeviceType RiingTrio, RiingDuo, FloeRiing, - PurePlus + PurePlus, + Toughfan, } } diff --git a/TTController.Plugin.StaticColorEffect2.csproj b/TTController.Plugin.StaticColorEffect2.csproj new file mode 100644 index 0000000..c42f9d6 --- /dev/null +++ b/TTController.Plugin.StaticColorEffect2.csproj @@ -0,0 +1,13 @@ + + + net48 + TTController.Plugin.StaticColorEffect2 + TTController.Plugin.StaticColorEffect2 + Copyright © 2020 + false + false + + + + + \ No newline at end of file diff --git a/TTController.sln b/TTController.sln index 45f871d..a07e038 100644 --- a/TTController.sln +++ b/TTController.sln @@ -82,6 +82,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Devices", "Devices", "{A1AD Plugins\Devices\RiingDuo.json = Plugins\Devices\RiingDuo.json Plugins\Devices\RiingQuad.json = Plugins\Devices\RiingQuad.json Plugins\Devices\RiingTrio.json = Plugins\Devices\RiingTrio.json + Plugins\Devices\Toughfan.json = Plugins\Devices\Toughfan.json EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TTController.Plugin.CopyColorEffect", "Plugins\Effects\TTController.Plugin.CopyColorEffect\TTController.Plugin.CopyColorEffect.csproj", "{7CD00102-32DA-4664-83EB-7FDDCE5B9129}" @@ -118,6 +119,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TTController.Plugin.RotateL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TTController.Plugin.ReverseLedColorModifier", "Plugins\Modifiers\TTController.Plugin.ReverseLedColorModifier\TTController.Plugin.ReverseLedColorModifier.csproj", "{BEA975C9-DADC-4EE3-9B38-D4AD3A72D4C0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TTController.Plugin.ToughfanController", "Plugins\Controllers\TTController.Plugin.ToughfanController\TTController.Plugin.ToughfanController.csproj", "{6B41EC5A-23B0-4705-937E-345B2812B408}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TTController.Plugin.StaticColorEffect2", "Plugins\Effects\TTController.Plugin.StaticColorEffect2\TTController.Plugin.StaticColorEffect2.csproj", "{4912FE98-A9D3-4947-842B-9B5330FEAF1A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -298,6 +303,14 @@ Global {BEA975C9-DADC-4EE3-9B38-D4AD3A72D4C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEA975C9-DADC-4EE3-9B38-D4AD3A72D4C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEA975C9-DADC-4EE3-9B38-D4AD3A72D4C0}.Release|Any CPU.Build.0 = Release|Any CPU + {6B41EC5A-23B0-4705-937E-345B2812B408}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B41EC5A-23B0-4705-937E-345B2812B408}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B41EC5A-23B0-4705-937E-345B2812B408}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B41EC5A-23B0-4705-937E-345B2812B408}.Release|Any CPU.Build.0 = Release|Any CPU + {4912FE98-A9D3-4947-842B-9B5330FEAF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4912FE98-A9D3-4947-842B-9B5330FEAF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4912FE98-A9D3-4947-842B-9B5330FEAF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4912FE98-A9D3-4947-842B-9B5330FEAF1A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -353,6 +366,8 @@ Global {194E027F-FD95-449F-9D17-729036DD9CE6} = {EA09CF17-E583-4C9C-B68A-05764654BCE3} {42E8E8E9-E4BB-4D8F-81DC-63880A84A970} = {EA09CF17-E583-4C9C-B68A-05764654BCE3} {BEA975C9-DADC-4EE3-9B38-D4AD3A72D4C0} = {EA09CF17-E583-4C9C-B68A-05764654BCE3} + {6B41EC5A-23B0-4705-937E-345B2812B408} = {72415EA8-F57C-4F2C-9B6C-167BF94FE3F8} + {4912FE98-A9D3-4947-842B-9B5330FEAF1A} = {521264D1-73FB-44B3-8243-CB3C95AFB179} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9B8A6C39-77D8-476B-B89B-C39D2AF3D3B9}