Add process to allow modified smartctl commands
This commit is contained in:
@@ -22,10 +22,10 @@ class ConfigClass:
|
|||||||
occasionally.
|
occasionally.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if not self.readFile:
|
try:
|
||||||
_result = self.config.read( self.filename )
|
_result = self.config.read( self.filename )
|
||||||
if len(_result) > 0:
|
except Exception as error:
|
||||||
self.readFile = True
|
print( f"{error}" )
|
||||||
|
|
||||||
def getValue( self, section : str, key : str, default="" ) -> str:
|
def getValue( self, section : str, key : str, default="" ) -> str:
|
||||||
'''
|
'''
|
||||||
@@ -41,7 +41,6 @@ class ConfigClass:
|
|||||||
The value of the key from the section read.
|
The value of the key from the section read.
|
||||||
'''
|
'''
|
||||||
value = default
|
value = default
|
||||||
self._openConfig()
|
|
||||||
try:
|
try:
|
||||||
value = self.config[section][key].replace('"','').strip()
|
value = self.config[section][key].replace('"','').strip()
|
||||||
except:
|
except:
|
||||||
@@ -62,7 +61,6 @@ class ConfigClass:
|
|||||||
a List of items
|
a List of items
|
||||||
'''
|
'''
|
||||||
value = default
|
value = default
|
||||||
self._openConfig()
|
|
||||||
try:
|
try:
|
||||||
temp = self.config[section][name]
|
temp = self.config[section][name]
|
||||||
value = [ n.replace('"','').strip() for n in temp.split(",")]
|
value = [ n.replace('"','').strip() for n in temp.split(",")]
|
||||||
@@ -77,6 +75,9 @@ if __name__ == "__main__":
|
|||||||
print( f"Value = {cfg.getValue( 'performance', 'ignore' )}" )
|
print( f"Value = {cfg.getValue( 'performance', 'ignore' )}" )
|
||||||
print( f"Value = {cfg.getValueAsList( 'performance', 'ignore' )}" )
|
print( f"Value = {cfg.getValueAsList( 'performance', 'ignore' )}" )
|
||||||
|
|
||||||
|
drive = cfg.getValue( 'smartctl', 'sda' )
|
||||||
|
print( f"drive = {drive}" )
|
||||||
|
|
||||||
cfg = ConfigClass( "missingfile.ini" )
|
cfg = ConfigClass( "missingfile.ini" )
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ Requires: PyQt5 (including QtCharts)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from systemsupport import CPUInfo, CPULoad, multiDriveStat
|
from systemsupport import CPUInfo, CPULoad, multiDriveStat, CaseFan
|
||||||
from configfile import ConfigClass
|
from configfile import ConfigClass
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Globals
|
# Globals
|
||||||
# --------------------------
|
# --------------------------
|
||||||
|
|
||||||
|
MIN_WIDTH = 1000
|
||||||
|
MIN_HEIGHT = 800
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# UI
|
# UI
|
||||||
# --------------------------
|
# --------------------------
|
||||||
@@ -267,10 +270,12 @@ class MonitorWindow(QMainWindow):
|
|||||||
# Get supporting objects
|
# Get supporting objects
|
||||||
self.cpuinfo = CPUInfo()
|
self.cpuinfo = CPUInfo()
|
||||||
self.cpuload = CPULoad()
|
self.cpuload = CPULoad()
|
||||||
|
self.casefan = CaseFan()
|
||||||
|
self.caseFanPin = None
|
||||||
self.multiDrive = multiDriveStat()
|
self.multiDrive = multiDriveStat()
|
||||||
|
|
||||||
self.setWindowTitle("System Monitor")
|
self.setWindowTitle("System Monitor")
|
||||||
self.setMinimumSize(900, 900)
|
self.setMinimumSize(MIN_WIDTH, MIN_HEIGHT)
|
||||||
|
|
||||||
central = QWidget(self)
|
central = QWidget(self)
|
||||||
grid = QGridLayout(central)
|
grid = QGridLayout(central)
|
||||||
@@ -299,11 +304,13 @@ class MonitorWindow(QMainWindow):
|
|||||||
window=window
|
window=window
|
||||||
)
|
)
|
||||||
|
|
||||||
casefan = self.config.getValue( "cooling", "casefan", None )
|
if self.cpuinfo.model == 5:
|
||||||
if casefan is None:
|
self.caseFanPin = self.config.getValue( "cooling", "casefan", None )
|
||||||
series = [("RPM",None)]
|
if self.caseFanPin is None:
|
||||||
|
series = [("CPU",None)]
|
||||||
else:
|
else:
|
||||||
series = [("CPU", None),("CaseFan",None)]
|
self.casefan.setTACHPin( self.caseFanPin)
|
||||||
|
series = [("CPU",None),("CaseFan",None)]
|
||||||
|
|
||||||
self.fan_chart = RollingChart(
|
self.fan_chart = RollingChart(
|
||||||
title="Fan Speed",
|
title="Fan Speed",
|
||||||
@@ -311,6 +318,8 @@ class MonitorWindow(QMainWindow):
|
|||||||
y_min=0,y_max=6000,
|
y_min=0,y_max=6000,
|
||||||
window=window
|
window=window
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.fan_chart = None
|
||||||
|
|
||||||
series = []
|
series = []
|
||||||
for name in self.multiDrive.drives:
|
for name in self.multiDrive.drives:
|
||||||
@@ -328,8 +337,11 @@ class MonitorWindow(QMainWindow):
|
|||||||
# Layout: 2x2 grid (CPU, NVMe on top; IO full width bottom)
|
# Layout: 2x2 grid (CPU, NVMe on top; IO full width bottom)
|
||||||
grid.addWidget(self.use_chart, 0, 0, 1, 2 )
|
grid.addWidget(self.use_chart, 0, 0, 1, 2 )
|
||||||
grid.addWidget(self.io_chart, 1, 0, 1, 2 )
|
grid.addWidget(self.io_chart, 1, 0, 1, 2 )
|
||||||
|
if self.fan_chart:
|
||||||
grid.addWidget(self.cpu_chart, 2, 0, 1, 1 )
|
grid.addWidget(self.cpu_chart, 2, 0, 1, 1 )
|
||||||
grid.addWidget(self.fan_chart, 2, 1, 1, 1 )
|
grid.addWidget(self.fan_chart, 2, 1, 1, 1 )
|
||||||
|
else:
|
||||||
|
grid.addWidget(self.cpu_chart, 2, 0, 1, 2 )
|
||||||
|
|
||||||
# Get the initial information from the syste
|
# Get the initial information from the syste
|
||||||
self.refresh_metrics()
|
self.refresh_metrics()
|
||||||
@@ -350,11 +362,14 @@ class MonitorWindow(QMainWindow):
|
|||||||
# Obtain the current fan speed
|
# Obtain the current fan speed
|
||||||
if self.cpuinfo.model == 5:
|
if self.cpuinfo.model == 5:
|
||||||
try:
|
try:
|
||||||
fan_speed = self.cpuinfo.CPUFanSpeed
|
if self.caseFanPin:
|
||||||
except Exception:
|
fan_speed = [self.cpuinfo.CPUFanSpeed,self.casefan.speed]
|
||||||
fan_speed = None
|
|
||||||
else:
|
else:
|
||||||
fan_speed = None
|
fan_speed = [self.cpuinfo.CPUFanSpeed]
|
||||||
|
except Exception:
|
||||||
|
fan_speed = [None,None]
|
||||||
|
else:
|
||||||
|
fan_speed = [None,None]
|
||||||
|
|
||||||
# Setup the temperature for the CPU and Drives
|
# Setup the temperature for the CPU and Drives
|
||||||
temperatures = []
|
temperatures = []
|
||||||
@@ -392,7 +407,8 @@ class MonitorWindow(QMainWindow):
|
|||||||
|
|
||||||
# Append to charts
|
# Append to charts
|
||||||
self.cpu_chart.append( temperatures )
|
self.cpu_chart.append( temperatures )
|
||||||
self.fan_chart.append([fan_speed])
|
if self.fan_chart:
|
||||||
|
self.fan_chart.append( fan_speed )
|
||||||
self.io_chart.append( rwData )
|
self.io_chart.append( rwData )
|
||||||
self.use_chart.append( values )
|
self.use_chart.append( values )
|
||||||
|
|
||||||
@@ -404,4 +420,3 @@ def main():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class DriveStats:
|
|||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._device
|
return self._device
|
||||||
|
|
||||||
|
@property
|
||||||
def readAllStats( self ) -> list[int]:
|
def readAllStats( self ) -> list[int]:
|
||||||
'''
|
'''
|
||||||
read all of the drive statisics from sysfs for the device.
|
read all of the drive statisics from sysfs for the device.
|
||||||
@@ -93,19 +94,24 @@ class DriveStats:
|
|||||||
'''
|
'''
|
||||||
return self._getStats()
|
return self._getStats()
|
||||||
|
|
||||||
|
@property
|
||||||
def readSectors( self )-> int:
|
def readSectors( self )-> int:
|
||||||
return self._getStats()[DriveStats.READ_SECTORS]
|
return self._getStats()[DriveStats.READ_SECTORS]
|
||||||
|
|
||||||
|
@property
|
||||||
def writeSectors( self ) -> int:
|
def writeSectors( self ) -> int:
|
||||||
return self._getStats()[DriveStats.WRITE_SECTORS]
|
return self._getStats()[DriveStats.WRITE_SECTORS]
|
||||||
|
|
||||||
|
@property
|
||||||
def discardSectors( self ) -> int:
|
def discardSectors( self ) -> int:
|
||||||
return self._getStats()[DriveStats.DISCARD_SECTORS]
|
return self._getStats()[DriveStats.DISCARD_SECTORS]
|
||||||
|
|
||||||
|
@property
|
||||||
def readWriteSectors( self ) -> tuple[int,int]:
|
def readWriteSectors( self ) -> tuple[int,int]:
|
||||||
curData = self._getStats()
|
curData = self._getStats()
|
||||||
return (curData[DriveStats.READ_SECTORS],curData[DriveStats.WRITE_SECTORS])
|
return (curData[DriveStats.READ_SECTORS],curData[DriveStats.WRITE_SECTORS])
|
||||||
|
|
||||||
|
@property
|
||||||
def readWriteBytes( self ) -> tuple[int,int]:
|
def readWriteBytes( self ) -> tuple[int,int]:
|
||||||
curData = self._getStats()
|
curData = self._getStats()
|
||||||
return (curData[DriveStats.READ_SECTORS]*512,curData[DriveStats.WRITE_SECTORS]*512)
|
return (curData[DriveStats.READ_SECTORS]*512,curData[DriveStats.WRITE_SECTORS]*512)
|
||||||
@@ -168,6 +174,19 @@ class multiDriveStat():
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
def driveTemp(self,_drive:str, extracmd = None) -> float:
|
def driveTemp(self,_drive:str, extracmd = None) -> float:
|
||||||
|
'''
|
||||||
|
Get the drive temperature using smart data. There are three basic temperature
|
||||||
|
settings we can read, smart ID 194, 190 and the Temperature: value. These are
|
||||||
|
depenent on the drive, so look for all of them, and depending on the result, we
|
||||||
|
get the value.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
_drive : The device we wish to scan
|
||||||
|
extraCmd : An optional additional command to send to the device.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The temperature as a float, or zero if there is an error.
|
||||||
|
'''
|
||||||
smartOutRaw = ""
|
smartOutRaw = ""
|
||||||
if extracmd is None:
|
if extracmd is None:
|
||||||
cmd = f'sudo smartctl -A /dev/{_drive}'
|
cmd = f'sudo smartctl -A /dev/{_drive}'
|
||||||
@@ -208,7 +227,7 @@ class multiDriveStat():
|
|||||||
'''
|
'''
|
||||||
curData = {}
|
curData = {}
|
||||||
for _ in self._stats:
|
for _ in self._stats:
|
||||||
curData[_.name] = _.readWriteSectors()
|
curData[_.name] = _.readWriteSectors
|
||||||
return curData
|
return curData
|
||||||
|
|
||||||
def readWriteBytes( self ) -> dict[str,tuple[int,int]]:
|
def readWriteBytes( self ) -> dict[str,tuple[int,int]]:
|
||||||
@@ -218,7 +237,7 @@ class multiDriveStat():
|
|||||||
'''
|
'''
|
||||||
curData = {}
|
curData = {}
|
||||||
for _ in self._stats:
|
for _ in self._stats:
|
||||||
curData[_.name] = _.readWriteBytes()
|
curData[_.name] = _.readWriteBytes
|
||||||
return curData
|
return curData
|
||||||
|
|
||||||
class CPUInfo:
|
class CPUInfo:
|
||||||
@@ -230,14 +249,26 @@ class CPUInfo:
|
|||||||
self._cputemp = CPUTemperature()
|
self._cputemp = CPUTemperature()
|
||||||
|
|
||||||
def _cpuModel( self ) -> int:
|
def _cpuModel( self ) -> int:
|
||||||
|
'''
|
||||||
|
Check for the cpu model. Scan cpuinfo to see if we can locate a string that
|
||||||
|
matches something we are looking for.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
Model of the Raspberry PI. This treats the Comput Modules the same as
|
||||||
|
standard model B's
|
||||||
|
'''
|
||||||
with os.popen( "grep Model /proc/cpuinfo" ) as command:
|
with os.popen( "grep Model /proc/cpuinfo" ) as command:
|
||||||
data = command.read()
|
data = command.read()
|
||||||
if "Compute Module 5" in data:
|
if "Compute Module 5" in data:
|
||||||
return 5
|
return 5
|
||||||
elif "Raspberry Pi 4" in data:
|
|
||||||
return 4
|
|
||||||
elif "Raspberry Pi 5" in data:
|
elif "Raspberry Pi 5" in data:
|
||||||
return 5
|
return 5
|
||||||
|
elif "Raspberry Pi 4" in data:
|
||||||
|
return 4
|
||||||
|
elif "Compute Module 4" in data:
|
||||||
|
return 4
|
||||||
|
elif "Raspberry Pi 3" in data:
|
||||||
|
return 3
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -382,6 +413,20 @@ class CPULoad:
|
|||||||
'''
|
'''
|
||||||
return len(self._previousData)
|
return len(self._previousData)
|
||||||
|
|
||||||
|
class CaseFan:
|
||||||
|
'''
|
||||||
|
Class used to monitor a TACH from a case fan.
|
||||||
|
'''
|
||||||
|
def __init__( self, pin=None ):
|
||||||
|
self.tach = pin
|
||||||
|
self.rpm = 0
|
||||||
|
|
||||||
|
def setTACHPin( self, pin = int):
|
||||||
|
self.tach = pin
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speed( self ) -> float:
|
||||||
|
return self.rpm
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@@ -399,6 +444,11 @@ if __name__ == "__main__":
|
|||||||
print( f"CPU Fan Speed = {cpuinfo.CPUFanSpeed}" )
|
print( f"CPU Fan Speed = {cpuinfo.CPUFanSpeed}" )
|
||||||
print( f"CPU Model = {cpuinfo.model}" )
|
print( f"CPU Model = {cpuinfo.model}" )
|
||||||
|
|
||||||
|
caseFan = CaseFan( 18 )
|
||||||
|
print( f"RPM = {caseFan.speed}" )
|
||||||
|
time.sleep(1)
|
||||||
|
print( f"RPM = {caseFan.speed}" )
|
||||||
|
|
||||||
test = multiDriveStat()
|
test = multiDriveStat()
|
||||||
print( test.drives )
|
print( test.drives )
|
||||||
for drive in test.drives:
|
for drive in test.drives:
|
||||||
|
|||||||
@@ -3,3 +3,8 @@
|
|||||||
|
|
||||||
[performance]
|
[performance]
|
||||||
ignore = "mmcblk0"
|
ignore = "mmcblk0"
|
||||||
|
|
||||||
|
[smartctl]
|
||||||
|
sda="foobar"
|
||||||
|
sda="duplicate"
|
||||||
|
sdb='hello'
|
||||||
|
|||||||
Reference in New Issue
Block a user