Support showing current temperatures

This commit is contained in:
Kamil Trzciński 2022-11-12 10:14:09 +01:00
parent d073ab0ff6
commit e25e284e10
11 changed files with 357 additions and 27 deletions

3
FanControl/App.config Normal file
View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

View file

@ -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<string, FanSensor> allSensors = new Dictionary<string, FanSensor>
{
{ "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<FanSensor, ISensor> matched = new Dictionary<FanSensor, ISensor>();
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();
}
}
}

View file

@ -7,16 +7,29 @@
<Nullable>enable</Nullable>
<UseWindowsForms>True</UseWindowsForms>
<StartupObject>FanControl.Program</StartupObject>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LibreHardwareMonitorLib" Version="0.9.1" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="inpoutx64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -57,14 +57,14 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="updateTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
<metadata name="fanLoopTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1188, 17</value>
</metadata>
<metadata name="notifyIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>159, 17</value>
<value>955, 18</value>
</metadata>
<metadata name="contextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>353, 17</value>
<value>8, 6</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="notifyIcon.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
@ -111,6 +111,9 @@
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>651, 17</value>
</metadata>
<metadata name="propertyGridUpdateTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1477, 16</value>
</metadata>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAAAAAAAEAIAB5CAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAABAAAAAQAgGAAAAqmlx3gAAAAFz

View file

@ -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)
{

View file

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
</SettingsFile>

View file

@ -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);

60
FanControl/app.manifest Normal file
View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>