Skip to content

Commit

Permalink
Add same robustness for Python sensors
Browse files Browse the repository at this point in the history
  • Loading branch information
mathoudebine committed Apr 26, 2024
1 parent 1f5abca commit bacd047
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 58 deletions.
57 changes: 38 additions & 19 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") and sensor.Value is not None:
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 @@ -190,7 +191,8 @@ def frequency() -> float:
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:
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:
Expand All @@ -213,19 +215,23 @@ def temperature() -> float:
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:
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:
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:
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:
if sensor.SensorType == Hardware.SensorType.Temperature and str(sensor.Name).startswith(
"Core") and sensor.Value is not None:
return float(sensor.Value)
except:
pass
Expand Down Expand Up @@ -280,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") and sensor.Value is not None:
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) 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") and sensor.Value is not None:
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) and sensor.Value is not None:
"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") and sensor.Value is not None:
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") and sensor.Value is not None:
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 @@ -310,7 +321,8 @@ def fps(cls) -> int:

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 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)
Expand Down Expand Up @@ -375,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") and sensor.Value is not None:
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") and sensor.Value is not None:
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") 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") and sensor.Value is not None:
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 @@ -401,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") and sensor.Value is not None:
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 @@ -410,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") and sensor.Value is not None:
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 @@ -419,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") and sensor.Value is not None:
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 @@ -455,7 +473,8 @@ 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") and sensor.Value is not None:
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") and sensor.Value is not None:
Expand Down
110 changes: 71 additions & 39 deletions library/sensors/sensors_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,24 @@ def sensors_fans_percent():
class Cpu(sensors.Cpu):
@staticmethod
def percentage(interval: float) -> float:
return psutil.cpu_percent(interval=interval)
try:
return psutil.cpu_percent(interval=interval)
except:
return math.nan

@staticmethod
def frequency() -> float:
return psutil.cpu_freq().current
try:
return psutil.cpu_freq().current
except:
return math.nan

@staticmethod
def load() -> Tuple[float, float, float]: # 1 / 5 / 15min avg (%):
return psutil.getloadavg()
try:
return psutil.getloadavg()
except:
return math.nan, math.nan, math.nan

@staticmethod
def temperature() -> float:
Expand Down Expand Up @@ -369,65 +378,88 @@ def is_available() -> bool:
class Memory(sensors.Memory):
@staticmethod
def swap_percent() -> float:
return psutil.swap_memory().percent
try:
return psutil.swap_memory().percent
except:
return math.nan

@staticmethod
def virtual_percent() -> float:
return psutil.virtual_memory().percent
try:
return psutil.virtual_memory().percent
except:
return math.nan

@staticmethod
def virtual_used() -> int: # In bytes
# Do not use psutil.virtual_memory().used: from https://psutil.readthedocs.io/en/latest/#memory
# "It is calculated differently depending on the platform and designed for informational purposes only"
return psutil.virtual_memory().total - psutil.virtual_memory().available
try:
# Do not use psutil.virtual_memory().used: from https://psutil.readthedocs.io/en/latest/#memory
# "It is calculated differently depending on the platform and designed for informational purposes only"
return psutil.virtual_memory().total - psutil.virtual_memory().available
except:
return -1

@staticmethod
def virtual_free() -> int: # In bytes
# Do not use psutil.virtual_memory().free: from https://psutil.readthedocs.io/en/latest/#memory
# "note that this doesn’t reflect the actual memory available (use available instead)."
return psutil.virtual_memory().available

try:
# Do not use psutil.virtual_memory().free: from https://psutil.readthedocs.io/en/latest/#memory
# "note that this doesn’t reflect the actual memory available (use available instead)."
return psutil.virtual_memory().available
except:
return -1

class Disk(sensors.Disk):
@staticmethod
def disk_usage_percent() -> float:
return psutil.disk_usage("/").percent
try:
return psutil.disk_usage("/").percent
except:
return math.nan

@staticmethod
def disk_used() -> int: # In bytes
return psutil.disk_usage("/").used
try:
return psutil.disk_usage("/").used
except:
return -1

@staticmethod
def disk_free() -> int: # In bytes
return psutil.disk_usage("/").free
try:
return psutil.disk_usage("/").free
except:
return -1


class Net(sensors.Net):
@staticmethod
def stats(if_name, interval) -> Tuple[
int, int, int, int]: # up rate (B/s), uploaded (B), dl rate (B/s), downloaded (B)
global PNIC_BEFORE
# Get current counters
pnic_after = psutil.net_io_counters(pernic=True)

upload_rate = 0
uploaded = 0
download_rate = 0
downloaded = 0

if if_name != "":
if if_name in pnic_after:
try:
upload_rate = (pnic_after[if_name].bytes_sent - PNIC_BEFORE[if_name].bytes_sent) / interval
uploaded = pnic_after[if_name].bytes_sent
download_rate = (pnic_after[if_name].bytes_recv - PNIC_BEFORE[if_name].bytes_recv) / interval
downloaded = pnic_after[if_name].bytes_recv
except:
# Interface might not be in PNIC_BEFORE for now
pass

PNIC_BEFORE.update({if_name: pnic_after[if_name]})
else:
logger.warning("Network interface '%s' not found. Check names in config.yaml." % if_name)

return upload_rate, uploaded, download_rate, downloaded
try:
# Get current counters
pnic_after = psutil.net_io_counters(pernic=True)

upload_rate = 0
uploaded = 0
download_rate = 0
downloaded = 0

if if_name != "":
if if_name in pnic_after:
try:
upload_rate = (pnic_after[if_name].bytes_sent - PNIC_BEFORE[if_name].bytes_sent) / interval
uploaded = pnic_after[if_name].bytes_sent
download_rate = (pnic_after[if_name].bytes_recv - PNIC_BEFORE[if_name].bytes_recv) / interval
downloaded = pnic_after[if_name].bytes_recv
except:
# Interface might not be in PNIC_BEFORE for now
pass

PNIC_BEFORE.update({if_name: pnic_after[if_name]})
else:
logger.warning("Network interface '%s' not found. Check names in config.yaml." % if_name)

return upload_rate, uploaded, download_rate, downloaded
except:
return -1, -1, -1, -1

0 comments on commit bacd047

Please sign in to comment.