Skip to content

Commit

Permalink
Merge pull request #507 from mathoudebine/fix/add-lhm-robustness-for-…
Browse files Browse the repository at this point in the history
…none-values
  • Loading branch information
mathoudebine authored Apr 28, 2024
2 parents 20ef2d3 + bacd047 commit 925ac9b
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 97 deletions.
141 changes: 83 additions & 58 deletions library/sensors/sensors_librehardwaremonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ class Cpu(sensors.Cpu):
def percentage(interval: float) -> float:
cpu = get_hw_and_update(Hardware.HardwareType.Cpu)
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith("CPU Total"):
if sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith(
"CPU Total") and sensor.Value is not None:
return float(sensor.Value)

logger.error("CPU load cannot be read")
Expand All @@ -186,19 +187,22 @@ def percentage(interval: float) -> float:
def frequency() -> float:
frequencies = []
cpu = get_hw_and_update(Hardware.HardwareType.Cpu)
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Clock:
# Keep only real core clocks, ignore effective core clocks
if "Core #" in str(sensor.Name) and "Effective" not in str(sensor.Name):
if sensor.Value:
try:
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Clock:
# Keep only real core clocks, ignore effective core clocks
if "Core #" in str(sensor.Name) and "Effective" not in str(
sensor.Name) and sensor.Value is not None:
frequencies.append(float(sensor.Value))

if frequencies:
# Take mean of all core clock as "CPU clock" (as it is done in Windows Task Manager Performance tab)
return mean(frequencies)
else:
# Frequencies reading is not supported on this CPU
return math.nan
if frequencies:
# Take mean of all core clock as "CPU clock" (as it is done in Windows Task Manager Performance tab)
return mean(frequencies)
except:
pass

# Frequencies reading is not supported on this CPU
return math.nan

@staticmethod
def load() -> Tuple[float, float, float]: # 1 / 5 / 15min avg (%):
Expand All @@ -208,22 +212,29 @@ def load() -> Tuple[float, float, float]: # 1 / 5 / 15min avg (%):
@staticmethod
def temperature() -> float:
cpu = get_hw_and_update(Hardware.HardwareType.Cpu)
# By default, the average temperature of all CPU cores will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith("Core Average"):
return float(sensor.Value)
# If not available, the max core temperature will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith("Core Max"):
return float(sensor.Value)
# If not available, the CPU Package temperature (usually same as max core temperature) will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith("CPU Package"):
return float(sensor.Value)
# Otherwise any sensor named "Core..." will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith("Core"):
return float(sensor.Value)
try:
# By default, the average temperature of all CPU cores will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith(
"Core Average") and sensor.Value is not None:
return float(sensor.Value)
# If not available, the max core temperature will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith(
"Core Max") and sensor.Value is not None:
return float(sensor.Value)
# If not available, the CPU Package temperature (usually same as max core temperature) will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith(
"CPU Package") and sensor.Value is not None:
return float(sensor.Value)
# Otherwise any sensor named "Core..." will be used
for sensor in cpu.Sensors:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith(
"Core") and sensor.Value is not None:
return float(sensor.Value)
except:
pass

return math.nan

Expand All @@ -235,7 +246,7 @@ def fan_percent() -> float:
sh.Update()
for sensor in sh.Sensors:
if sensor.SensorType == Hardware.SensorType.Control and "#2" in str(
sensor.Name): # Is Motherboard #2 Fan always the CPU Fan ?
sensor.Name) and sensor.Value is not None: # Is Motherboard #2 Fan always the CPU Fan ?
return float(sensor.Value)
except:
pass
Expand Down Expand Up @@ -275,23 +286,28 @@ def stats(cls) -> Tuple[float, float, float, float]: # load (%) / used mem (%)
temp = math.nan

for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith("GPU Core"):
if sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith(
"GPU Core") and sensor.Value is not None:
load = float(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith("D3D 3D") and math.isnan(
load):
load) and sensor.Value is not None:
# Only use D3D usage if global "GPU Core" sensor is not available, because it is less
# precise and does not cover the entire GPU: https://www.hwinfo.com/forum/threads/what-is-d3d-usage.759/
load = float(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.SmallData and str(sensor.Name).startswith("GPU Memory Used"):
elif sensor.SensorType == Hardware.SensorType.SmallData and str(sensor.Name).startswith(
"GPU Memory Used") and sensor.Value is not None:
used_mem = float(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.SmallData and str(sensor.Name).startswith(
"D3D") and str(sensor.Name).endswith("Memory Used") and math.isnan(used_mem):
"D3D") and str(sensor.Name).endswith("Memory Used") and math.isnan(
used_mem) and sensor.Value is not None:
# Only use D3D memory usage if global "GPU Memory Used" sensor is not available, because it is less
# precise and does not cover the entire GPU: https://www.hwinfo.com/forum/threads/what-is-d3d-usage.759/
used_mem = float(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.SmallData and str(sensor.Name).startswith("GPU Memory Total"):
elif sensor.SensorType == Hardware.SensorType.SmallData and str(sensor.Name).startswith(
"GPU Memory Total") and sensor.Value is not None:
total_mem = float(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith("GPU Core"):
elif sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith(
"GPU Core") and sensor.Value is not None:
temp = float(sensor.Value)

return load, (used_mem / total_mem * 100.0), used_mem, temp
Expand All @@ -303,12 +319,16 @@ def fps(cls) -> int:
# GPU not supported
return -1

for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Factor and "FPS" in str(sensor.Name):
# If a reading returns a value <= 0, returns old value instead
if int(sensor.Value) > 0:
cls.prev_fps = int(sensor.Value)
return cls.prev_fps
try:
for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Factor and "FPS" in str(
sensor.Name) and sensor.Value is not None:
# If a reading returns a value <= 0, returns old value instead
if int(sensor.Value) > 0:
cls.prev_fps = int(sensor.Value)
return cls.prev_fps
except:
pass

# No FPS sensor for this GPU model
return -1
Expand All @@ -322,9 +342,8 @@ def fan_percent(cls) -> float:

try:
for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Control:
if sensor.Value:
return float(sensor.Value)
if sensor.SensorType == Hardware.SensorType.Control and sensor.Value is not None:
return float(sensor.Value)
except:
pass

Expand All @@ -342,9 +361,8 @@ def frequency(cls) -> float:
for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Clock:
# Keep only real core clocks, ignore effective core clocks
if "Core" in str(sensor.Name) and "Effective" not in str(sensor.Name):
if sensor.Value:
return float(sensor.Value)
if "Core" in str(sensor.Name) and "Effective" not in str(sensor.Name) and sensor.Value is not None:
return float(sensor.Value)
except:
pass

Expand All @@ -369,14 +387,17 @@ def swap_percent() -> float:

# Get virtual / physical memory stats
for sensor in memory.Sensors:
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith("Virtual Memory Used"):
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Virtual Memory Used") and sensor.Value is not None:
virtual_mem_used = int(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith("Memory Used"):
elif sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Memory Used") and sensor.Value is not None:
mem_used = int(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Virtual Memory Available"):
"Virtual Memory Available") and sensor.Value is not None:
virtual_mem_available = int(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith("Memory Available"):
elif sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Memory Available") and sensor.Value is not None:
mem_available = int(sensor.Value)

# Compute swap stats from virtual / physical memory stats
Expand All @@ -395,7 +416,8 @@ def swap_percent() -> float:
def virtual_percent() -> float:
memory = get_hw_and_update(Hardware.HardwareType.Memory)
for sensor in memory.Sensors:
if sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith("Memory"):
if sensor.SensorType == Hardware.SensorType.Load and str(sensor.Name).startswith(
"Memory") and sensor.Value is not None:
return float(sensor.Value)

return math.nan
Expand All @@ -404,7 +426,8 @@ def virtual_percent() -> float:
def virtual_used() -> int: # In bytes
memory = get_hw_and_update(Hardware.HardwareType.Memory)
for sensor in memory.Sensors:
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith("Memory Used"):
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Memory Used") and sensor.Value is not None:
return int(sensor.Value * 1000000000.0)

return 0
Expand All @@ -413,7 +436,8 @@ def virtual_used() -> int: # In bytes
def virtual_free() -> int: # In bytes
memory = get_hw_and_update(Hardware.HardwareType.Memory)
for sensor in memory.Sensors:
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith("Memory Available"):
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Memory Available") and sensor.Value is not None:
return int(sensor.Value * 1000000000.0)

return 0
Expand Down Expand Up @@ -449,16 +473,17 @@ def stats(if_name, interval) -> Tuple[
net_if = get_net_interface_and_update(if_name)
if net_if is not None:
for sensor in net_if.Sensors:
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith("Data Uploaded"):
if sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Data Uploaded") and sensor.Value is not None:
uploaded = int(sensor.Value * 1000000000.0)
elif sensor.SensorType == Hardware.SensorType.Data and str(sensor.Name).startswith(
"Data Downloaded"):
"Data Downloaded") and sensor.Value is not None:
downloaded = int(sensor.Value * 1000000000.0)
elif sensor.SensorType == Hardware.SensorType.Throughput and str(sensor.Name).startswith(
"Upload Speed"):
"Upload Speed") and sensor.Value is not None:
upload_rate = int(sensor.Value)
elif sensor.SensorType == Hardware.SensorType.Throughput and str(sensor.Name).startswith(
"Download Speed"):
"Download Speed") and sensor.Value is not None:
download_rate = int(sensor.Value)

return upload_rate, uploaded, download_rate, downloaded
Loading

0 comments on commit 925ac9b

Please sign in to comment.