23
23
import platform
24
24
from abc import ABC , abstractmethod
25
25
26
+ import ctypes
27
+ import math
28
+ import os
29
+ import sys
30
+ from statistics import mean
31
+ from typing import Tuple
32
+
33
+ import clr # Clr is from pythonnet package. Do not install clr package
34
+ import psutil
35
+ from win32api import *
36
+
37
+ import library .sensors .sensors as sensors
38
+ from library .log import logger
39
+
40
+ # Import LibreHardwareMonitor dll to Python
41
+ lhm_dll = os .getcwd () + '\\ external\\ LibreHardwareMonitor\\ LibreHardwareMonitorLib.dll'
42
+ # noinspection PyUnresolvedReferences
43
+ clr .AddReference (lhm_dll )
44
+ # noinspection PyUnresolvedReferences
45
+ clr .AddReference (os .getcwd () + '\\ external\\ LibreHardwareMonitor\\ HidSharp.dll' )
46
+ # noinspection PyUnresolvedReferences
47
+ from LibreHardwareMonitor import Hardware
48
+
49
+ # Import RTSSSharedMemoryNET dll to Python
50
+ clr .AddReference (os .getcwd () + '\\ external\\ RTSSSharedMemoryNET\\ RTSSSharedMemoryNET.dll' )
51
+ from RTSSSharedMemoryNET import OSD
52
+
53
+
54
+ File_information = GetFileVersionInfo (lhm_dll , "\\ " )
55
+
56
+ ms_file_version = File_information ['FileVersionMS' ]
57
+ ls_file_version = File_information ['FileVersionLS' ]
58
+
59
+ logger .debug ("Found LibreHardwareMonitorLib %s" % "." .join ([str (HIWORD (ms_file_version )), str (LOWORD (ms_file_version )),
60
+ str (HIWORD (ls_file_version )),
61
+ str (LOWORD (ls_file_version ))]))
62
+
63
+ if ctypes .windll .shell32 .IsUserAnAdmin () == 0 :
64
+ logger .error (
65
+ "Program is not running as administrator. Please run with admin rights or choose another HW_SENSORS option in "
66
+ "config.yaml" )
67
+ try :
68
+ sys .exit (0 )
69
+ except :
70
+ os ._exit (0 )
71
+
72
+ handle = Hardware .Computer ()
73
+ handle .IsCpuEnabled = True
74
+ handle .IsGpuEnabled = True
75
+ handle .IsMemoryEnabled = True
76
+ handle .IsMotherboardEnabled = True
77
+ handle .IsControllerEnabled = False
78
+ handle .IsNetworkEnabled = True
79
+ handle .IsStorageEnabled = True
80
+ handle .Open ()
81
+ for hardware in handle .Hardware :
82
+ if hardware .HardwareType == Hardware .HardwareType .Cpu :
83
+ logger .info ("Found CPU: %s" % hardware .Name )
84
+ elif hardware .HardwareType == Hardware .HardwareType .Memory :
85
+ logger .info ("Found Memory: %s" % hardware .Name )
86
+ elif hardware .HardwareType == Hardware .HardwareType .GpuNvidia :
87
+ logger .info ("Found Nvidia GPU: %s" % hardware .Name )
88
+ elif hardware .HardwareType == Hardware .HardwareType .GpuAmd :
89
+ logger .info ("Found AMD GPU: %s" % hardware .Name )
90
+ elif hardware .HardwareType == Hardware .HardwareType .GpuIntel :
91
+ logger .info ("Found Intel GPU: %s" % hardware .Name )
92
+ elif hardware .HardwareType == Hardware .HardwareType .Storage :
93
+ logger .info ("Found Storage: %s" % hardware .Name )
94
+ elif hardware .HardwareType == Hardware .HardwareType .Network :
95
+ logger .info ("Found Network interface: %s" % hardware .Name )
96
+
97
+
98
+ def get_hw_and_update (hwtype : Hardware .HardwareType , name : str = None ) -> Hardware .Hardware :
99
+ for hardware in handle .Hardware :
100
+ if hardware .HardwareType == hwtype :
101
+ if (name and hardware .Name == name ) or not name :
102
+ hardware .Update ()
103
+ return hardware
104
+ return None
105
+
26
106
27
107
# Custom data classes must be implemented in this file, inherit the CustomDataSource and implement its 2 methods
28
108
class CustomDataSource (ABC ):
@@ -40,6 +120,11 @@ def as_string(self) -> str:
40
120
# If this function is empty, the numeric value will be used as string without formatting
41
121
pass
42
122
123
+ @abstractmethod
124
+ def as_histo (self ) -> list [float ]:
125
+ # List of numeric values will be used for plot graph
126
+ # If there is no histo values, keep this function empty
127
+ pass
43
128
44
129
# Example for a custom data class that has numeric and text values
45
130
class ExampleCustomNumericData (CustomDataSource ):
@@ -61,6 +146,9 @@ def as_string(self) -> str:
61
146
# --> return f'{self.as_numeric():>4}%'
62
147
# Otherwise, part of the previous value can stay displayed ("ghosting") after a refresh
63
148
149
+ def as_histo (self ) -> list [float ]:
150
+ pass
151
+
64
152
65
153
# Example for a custom data class that only has text values
66
154
class ExampleCustomTextOnlyData (CustomDataSource ):
@@ -71,3 +159,69 @@ def as_numeric(self) -> float:
71
159
def as_string (self ) -> str :
72
160
# If a custom data class only has text values, it won't be possible to display graph or radial bars
73
161
return "Python version: " + platform .python_version ()
162
+
163
+ def as_histo (self ) -> list [float ]:
164
+ pass
165
+
166
+
167
+ class GpuNvidiaFanPercent (CustomDataSource ):
168
+ def as_numeric (self ) -> float :
169
+ gpu = get_hw_and_update (Hardware .HardwareType .GpuNvidia )
170
+ for sensor in gpu .Sensors :
171
+ if sensor .SensorType == Hardware .SensorType .Control :
172
+ return float (sensor .Value )
173
+ #return float(50)
174
+
175
+ logger .error ("GPU Nvidia fan percent cannot be read" )
176
+ return math .nan
177
+
178
+ def as_string (self ) -> str :
179
+ return f'{ int (self .as_numeric ())} %'
180
+
181
+ def as_histo (self ) -> list [float ]:
182
+ pass
183
+
184
+ class CpuFanPercent (CustomDataSource ):
185
+ def as_numeric (self ) -> float :
186
+ mb = get_hw_and_update (Hardware .HardwareType .Motherboard )
187
+ for sh in mb .SubHardware :
188
+ sh .Update ()
189
+ for sensor in sh .Sensors :
190
+ if sensor .SensorType == Hardware .SensorType .Control and "#2" in str (sensor .Name ):
191
+ return float (sensor .Value )
192
+
193
+ logger .error ("CPU fan percent cannot be read" )
194
+ return math .nan
195
+
196
+ def as_string (self ) -> str :
197
+ return f'{ int (self .as_numeric ())} %'
198
+
199
+ def as_histo (self ) -> list [float ]:
200
+ pass
201
+
202
+ class RTSSFps (CustomDataSource ):
203
+
204
+ histo = [- 1 ] * 100
205
+
206
+ def as_numeric (self ) -> float :
207
+ appEntries = OSD .GetAppEntries ()
208
+ for app in appEntries :
209
+ if app .InstantaneousFrames > 0 :
210
+ return float (app .InstantaneousFrames )
211
+
212
+ return float (0 )
213
+
214
+ def as_string (self ) -> str :
215
+ return f'{ int (self .as_numeric ())} '
216
+
217
+ def as_histo (self ) -> list [float ]:
218
+ appEntries = OSD .GetAppEntries ()
219
+ for app in appEntries :
220
+ if app .InstantaneousFrames > 0 :
221
+ RTSSFps .histo .append (app .InstantaneousFrames )
222
+ RTSSFps .histo .pop (0 )
223
+ return RTSSFps .histo
224
+
225
+ return RTSSFps .histo
226
+
227
+
0 commit comments