diff --git a/FanControl/App.config b/FanControl/App.config new file mode 100644 index 0000000..49cc43e --- /dev/null +++ b/FanControl/App.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/FanControl/FanControl.cs b/FanControl/FanControl.cs index e3619be..f903c4a 100644 --- a/FanControl/FanControl.cs +++ b/FanControl/FanControl.cs @@ -1,4 +1,7 @@ -using System; +using LibreHardwareMonitor.Hardware; +using LibreHardwareMonitor.Hardware.CPU; +using Microsoft.VisualBasic.Devices; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Metrics; @@ -9,7 +12,7 @@ using System.Threading.Tasks; namespace FanControl { [TypeConverter(typeof(ExpandableObjectConverter))] - internal class FanControl + internal class FanControl : IDisposable { public enum FanMode { @@ -18,6 +21,79 @@ namespace FanControl Max } + [TypeConverter(typeof(ExpandableObjectConverter))] + internal class FanSensor + { + public string? Name { get; internal set; } + public float? Value { get; internal set; } + public ushort InducedRPM { get; internal set; } + + internal string HardwareName { get; set; } = ""; + internal HardwareType HardwareType { get; set; } + internal string SensorName { get; set; } = ""; + internal SensorType SensorType { get; set; } + + public void Reset() + { + Name = null; + Value = null; + InducedRPM = 0; + } + + public bool Matches(ISensor sensor) + { + return sensor != null && + sensor.Hardware.HardwareType == HardwareType && + sensor.Hardware.Name.StartsWith(HardwareName) && + sensor.SensorType == SensorType && + sensor.Name == SensorName; + } + + public bool Update(ISensor hwSensor) + { + if (!Matches(hwSensor)) + return false; + + System.Diagnostics.Trace.WriteLine(String.Format("{0}: {1} {2}, value: {3}, type: {4}", + hwSensor.Identifier, hwSensor.Hardware.Name, hwSensor.Name, hwSensor.Value, hwSensor.SensorType)); + + return Update( + String.Format("{0} {1}", hwSensor.Hardware.Name, hwSensor.Name), + hwSensor.Value); + } + + public bool Update(string name, float? value) + { + if (!value.HasValue || value <= 0.0) + return false; + + Name = name; + Value = value; + return true; + } + + public String FormattedValue() + { + if (!Value.HasValue) + return ""; + + switch (SensorType) + { + case SensorType.Temperature: + return Value.Value.ToString("F1") + "℃"; + case SensorType.Power: + return Value.Value.ToString("F1") + "W"; + default: + return Value.Value.ToString("F1"); + } + } + + public override string ToString() + { + return Name; + } + } + [CategoryAttribute("Fan")] public FanMode Mode { get; private set; } @@ -27,10 +103,124 @@ namespace FanControl [CategoryAttribute("Fan")] public ushort DesiredRPM { get; private set; } + [CategoryAttribute("Sensor - APU"), DisplayName("Name")] + public String? APUName { get { return allSensors["APU"].Name; } } + [CategoryAttribute("Sensor - APU"), DisplayName("Power")] + public String? APUPower { get { return allSensors["APU"].FormattedValue(); } } + + [CategoryAttribute("Sensor - CPU"), DisplayName("Name")] + public String? CPUName { get { return allSensors["CPU"].Name; } } + [CategoryAttribute("Sensor - CPU"), DisplayName("Temperature")] + public String? CPUTemperature { get { return allSensors["CPU"].FormattedValue(); } } + + [CategoryAttribute("Sensor - GPU"), DisplayName("Name")] + public String? GPUName { get { return allSensors["GPU"].Name; } } + [CategoryAttribute("Sensor - GPU"), DisplayName("Temperature")] + public String? GPUTemperature { get { return allSensors["GPU"].FormattedValue(); } } + + [CategoryAttribute("Sensor - SSD"), DisplayName("Name")] + public String? SSDName { get { return allSensors["SSD"].Name; } } + [CategoryAttribute("Sensor - SSD"), DisplayName("Temperature")] + public String? SSDTemperature { get { return allSensors["SSD"].FormattedValue(); } } + [CategoryAttribute("Sensor - Battery"), DisplayName("Name")] + public String? BatteryName { get { return allSensors["Batt"].Name; } } + [CategoryAttribute("Sensor - Battery"), DisplayName("Temperature")] + public String? BatteryTemperature { get { return allSensors["Batt"].FormattedValue(); } } + + private Dictionary allSensors = new Dictionary + { + { "APU", + new FanSensor() + { + // TODO: Is this correct? + HardwareName = "AMD Custom APU 0405", + HardwareType = HardwareType.Cpu, + SensorName = "Package", + SensorType = SensorType.Power + } }, + { "CPU", + new FanSensor() + { + HardwareName = "AMD Custom APU 0405", + HardwareType = HardwareType.Cpu, + SensorName = "Core (Tctl/Tdie)", + SensorType = SensorType.Temperature + } }, + { "GPU", + new FanSensor() + { + HardwareName = "AMD Custom GPU 0405", + HardwareType = HardwareType.GpuAmd, + SensorName = "GPU Core", + SensorType = SensorType.Temperature + } }, + { "SSD", + new FanSensor() + { + HardwareType = HardwareType.Storage, + SensorName = "Temperature", + SensorType = SensorType.Temperature + } }, + { "Batt", + new FanSensor() + { + HardwareType = HardwareType.Battery, + SensorName = "Temperature", + SensorType = SensorType.Temperature + } } + }; + + LibreHardwareMonitor.Hardware.Computer libreHardwareComputer = new LibreHardwareMonitor.Hardware.Computer + { + IsCpuEnabled = true, + IsGpuEnabled = true, + IsStorageEnabled = true, + IsBatteryEnabled = true + }; + + public FanControl() + { + libreHardwareComputer.Open(); + } + + private void visitHardware(IHardware hardware) + { + Dictionary matched = new Dictionary(); + + foreach (ISensor hwSensor in hardware.Sensors) + { + foreach (var sensor in allSensors.Values) + { + if (sensor.Matches(hwSensor)) + matched[sensor] = hwSensor; + } + } + + if (matched.Any()) + { + hardware.Update(); + foreach (var sensor in matched) + sensor.Key.Update(sensor.Value); + } + + foreach (IHardware subhardware in hardware.SubHardware) + { + visitHardware(subhardware); + } + } + public void Update() { CurrentRPM = Vlv0100.GetFanRPM(); DesiredRPM = Vlv0100.GetFanDesiredRPM(); + + foreach (var sensor in allSensors.Values) + sensor.Reset(); + + foreach(var hardware in libreHardwareComputer.Hardware) + visitHardware(hardware); + + allSensors["Batt"].Update("VLV0100", Vlv0100.GetBattTemperature()); } public void SetMode(FanMode mode) @@ -54,5 +244,10 @@ namespace FanControl this.Mode = mode; } + + public void Dispose() + { + libreHardwareComputer.Close(); + } } } diff --git a/FanControl/FanControl.csproj b/FanControl/FanControl.csproj index 73ca95a..ab2ca79 100644 --- a/FanControl/FanControl.csproj +++ b/FanControl/FanControl.csproj @@ -7,16 +7,29 @@ enable True FanControl.Program + app.manifest + + + True + True + Settings.settings + + + PreserveNewest + + SettingsSingleFileGenerator + Settings.Designer.cs + diff --git a/FanControl/FanControlForm.Designer.cs b/FanControl/FanControlForm.Designer.cs index 9148584..2627524 100644 --- a/FanControl/FanControlForm.Designer.cs +++ b/FanControl/FanControlForm.Designer.cs @@ -30,7 +30,7 @@ { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FanControlForm)); - this.updateTimer = new System.Windows.Forms.Timer(this.components); + this.fanLoopTimer = new System.Windows.Forms.Timer(this.components); this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components); this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); this.fanModeSelectNotifyMenu = new System.Windows.Forms.ToolStripComboBox(); @@ -41,15 +41,16 @@ this.fanModeSelectMenu = new System.Windows.Forms.ToolStripComboBox(); this.controlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.propertyGridUpdateTimer = new System.Windows.Forms.Timer(this.components); this.contextMenu.SuspendLayout(); this.menuStrip1.SuspendLayout(); this.SuspendLayout(); // - // updateTimer + // fanLoopTimer // - this.updateTimer.Enabled = true; - this.updateTimer.Interval = 250; - this.updateTimer.Tick += new System.EventHandler(this.updateTimer_Tick); + this.fanLoopTimer.Enabled = true; + this.fanLoopTimer.Interval = 250; + this.fanLoopTimer.Tick += new System.EventHandler(this.fanLoopTimer_Tick); // // notifyIcon // @@ -98,7 +99,7 @@ this.propertyGrid1.HelpVisible = false; this.propertyGrid1.Location = new System.Drawing.Point(0, 44); this.propertyGrid1.Name = "propertyGrid1"; - this.propertyGrid1.Size = new System.Drawing.Size(712, 599); + this.propertyGrid1.Size = new System.Drawing.Size(712, 885); this.propertyGrid1.TabIndex = 0; this.propertyGrid1.ToolbarVisible = false; // @@ -138,11 +139,17 @@ this.exitToolStripMenuItem.Text = "&Exit"; this.exitToolStripMenuItem.Click += new System.EventHandler(this.formClose_Event); // + // propertyGridUpdateTimer + // + this.propertyGridUpdateTimer.Enabled = true; + this.propertyGridUpdateTimer.Interval = 1000; + this.propertyGridUpdateTimer.Tick += new System.EventHandler(this.propertyGridUpdateTimer_Tick); + // // FanControlForm // this.AutoScaleDimensions = new System.Drawing.SizeF(13F, 32F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(712, 643); + this.ClientSize = new System.Drawing.Size(712, 929); this.Controls.Add(this.propertyGrid1); this.Controls.Add(this.menuStrip1); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); @@ -150,7 +157,6 @@ this.Name = "FanControlForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Steam Deck Fan Control"; - this.WindowState = System.Windows.Forms.FormWindowState.Minimized; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FanControlForm_FormClosing); this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FanControlForm_FormClosed); this.contextMenu.ResumeLayout(false); @@ -163,7 +169,7 @@ #endregion - private System.Windows.Forms.Timer updateTimer; + private System.Windows.Forms.Timer fanLoopTimer; private NotifyIcon notifyIcon; private PropertyGrid propertyGrid1; private ContextMenuStrip contextMenu; @@ -174,5 +180,6 @@ private ToolStripComboBox fanModeSelectMenu; private ToolStripMenuItem controlToolStripMenuItem; private ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.Timer propertyGridUpdateTimer; } } \ No newline at end of file diff --git a/FanControl/FanControlForm.cs b/FanControl/FanControlForm.cs index 80330e3..9de96be 100644 --- a/FanControl/FanControlForm.cs +++ b/FanControl/FanControlForm.cs @@ -21,8 +21,9 @@ namespace FanControl InitializeComponent(); propertyGrid1.SelectedObject = fanControl; + propertyGrid1.ExpandAllGridItems(); - foreach(var item in Enum.GetValues(typeof(FanControl.FanMode))) + foreach (var item in Enum.GetValues(typeof(FanControl.FanMode))) { fanModeSelectMenu.Items.Add(item); fanModeSelectNotifyMenu.Items.Add(item); @@ -55,6 +56,7 @@ namespace FanControl private void formShow_Event(object sender, EventArgs e) { Show(); + propertyGrid1.Refresh(); } private void formClose_Event(object sender, EventArgs e) @@ -69,15 +71,19 @@ namespace FanControl fanControl.SetMode(FanControl.FanMode.Default); } - private void updateTimer_Tick(object sender, EventArgs e) + private void fanLoopTimer_Tick(object sender, EventArgs e) { fanControl.Update(); + } - if (Visible) - { - propertyGrid1.Refresh(); - } + private void propertyGridUpdateTimer_Tick(object sender, EventArgs e) + { + if (!Visible) + return; + var item = propertyGrid1.SelectedGridItem; + propertyGrid1.Refresh(); + propertyGrid1.SelectedGridItem = item; notifyIcon.Text = String.Format("Fan: {0} RPM Mode: {1}", fanControl.CurrentRPM, fanControl.Mode); } } diff --git a/FanControl/FanControlForm.resx b/FanControl/FanControlForm.resx index d02e7fc..87f2860 100644 --- a/FanControl/FanControlForm.resx +++ b/FanControl/FanControlForm.resx @@ -57,14 +57,14 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 17, 17 + + 1188, 17 - 159, 17 + 955, 18 - 353, 17 + 8, 6 @@ -111,6 +111,9 @@ 651, 17 + + 1477, 16 + AAABAAEAAAAAAAEAIAB5CAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAABAAAAAQAgGAAAAqmlx3gAAAAFz diff --git a/FanControl/Program.cs b/FanControl/Program.cs index 5622e47..4a124da 100644 --- a/FanControl/Program.cs +++ b/FanControl/Program.cs @@ -34,15 +34,17 @@ namespace FanControl IsMotherboardEnabled = true, IsControllerEnabled = true, IsNetworkEnabled = true, - IsStorageEnabled = true + IsStorageEnabled = true, + IsPsuEnabled = true, + IsBatteryEnabled = true }; computer.Open(); - computer.Accept(new UpdateVisitor()); + //computer.Accept(new UpdateVisitor()); foreach (IHardware hardware in computer.Hardware) { - Console.WriteLine("Hardware: {0}", hardware.Name); + Console.WriteLine("Hardware: {0}. Type: {1}", hardware.Name, hardware.HardwareType); foreach (IHardware subhardware in hardware.SubHardware) { @@ -50,13 +52,13 @@ namespace FanControl foreach (ISensor sensor in subhardware.Sensors) { - Console.WriteLine("\t\tSensor: {0}, value: {1}", sensor.Name, sensor.Value); + Console.WriteLine("\t\tSensor: {0}, value: {1}, type: {2}", sensor.Name, sensor.Value, sensor.SensorType); } } foreach (ISensor sensor in hardware.Sensors) { - Console.WriteLine("\tSensor: {0}, value: {1}", sensor.Name, sensor.Value); + Console.WriteLine("\tSensor: {0}, value: {1}, type: {2}", sensor.Name, sensor.Value, sensor.SensorType); } } @@ -64,7 +66,7 @@ namespace FanControl } static void ConsoleMain(string[] args) { - // Monitor(); + Monitor(); while (true) { diff --git a/FanControl/Properties/Settings.Designer.cs b/FanControl/Properties/Settings.Designer.cs new file mode 100644 index 0000000..4bb435d --- /dev/null +++ b/FanControl/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace FanControl.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/FanControl/Properties/Settings.settings b/FanControl/Properties/Settings.settings new file mode 100644 index 0000000..049245f --- /dev/null +++ b/FanControl/Properties/Settings.settings @@ -0,0 +1,6 @@ + + + + + + diff --git a/FanControl/Vlv0100.cs b/FanControl/Vlv0100.cs index 7300617..9e68a69 100644 --- a/FanControl/Vlv0100.cs +++ b/FanControl/Vlv0100.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Text; @@ -16,6 +17,7 @@ namespace FanControl static IntPtr FRPR = new IntPtr(0xFE700B00 + 0x97); static IntPtr FNRL_FNRH = new IntPtr(0xFE700300 + 0xB0); static IntPtr FNCK = new IntPtr(0xFE700300 + 0x9F); + static IntPtr BATH_BATL = new IntPtr(0xFE700400 + 0x6E); static ushort IO6C = 0x6C; public const ushort MAX_FAN_RPM = 0x1C84; @@ -54,6 +56,13 @@ namespace FanControl return (data[0] & 0x1) != 0; } + public static float GetBattTemperature() + { + byte[] data = InpOut.ReadMemory(BATH_BATL, 2); + int value = (data[0] << 8) + data[1]; + return (float)(value - 0x0AAC) / 10.0f; + } + private static void SetGain(ushort gain) { byte[] data = BitConverter.GetBytes(gain); diff --git a/FanControl/app.manifest b/FanControl/app.manifest new file mode 100644 index 0000000..9eddb63 --- /dev/null +++ b/FanControl/app.manifest @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + + + +