mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 10:51:36 -04:00
83 lines
2.2 KiB
Python
83 lines
2.2 KiB
Python
"""
|
|
SMART disk health monitoring for WROLPi Controller.
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
# Import pySMART only if available
|
|
try:
|
|
from pySMART import DeviceList, Device
|
|
|
|
SMART_AVAILABLE = True
|
|
except ImportError:
|
|
SMART_AVAILABLE = False
|
|
|
|
from controller.lib.config import is_docker_mode
|
|
|
|
|
|
def is_smart_available() -> bool:
|
|
"""Check if SMART monitoring is available."""
|
|
if is_docker_mode():
|
|
return False
|
|
return SMART_AVAILABLE
|
|
|
|
|
|
def get_all_smart_status() -> list[dict]:
|
|
"""
|
|
Get SMART status for all drives.
|
|
|
|
Returns:
|
|
list of SMART status dicts
|
|
"""
|
|
if not is_smart_available():
|
|
return []
|
|
|
|
try:
|
|
devices = DeviceList()
|
|
results = []
|
|
|
|
for device in devices.devices:
|
|
results.append(_get_device_smart(device))
|
|
|
|
return results
|
|
except Exception:
|
|
return []
|
|
|
|
|
|
def _get_device_smart(device) -> dict:
|
|
"""Extract SMART data from a pySMART device."""
|
|
return {
|
|
"device": device.name,
|
|
"path": f"/dev/{device.name}",
|
|
"model": device.model,
|
|
"serial": device.serial,
|
|
"capacity": device.capacity,
|
|
"assessment": device.assessment, # PASS, FAIL, WARN
|
|
"temperature": _get_temperature(device),
|
|
"power_on_hours": _get_attribute(device, "Power_On_Hours"),
|
|
"reallocated_sectors": _get_attribute(device, "Reallocated_Sector_Ct"),
|
|
"pending_sectors": _get_attribute(device, "Current_Pending_Sector"),
|
|
"smart_enabled": device.smart_enabled,
|
|
}
|
|
|
|
|
|
def _get_temperature(device) -> Optional[int]:
|
|
"""Get drive temperature from SMART attributes."""
|
|
temp = _get_attribute(device, "Temperature_Celsius")
|
|
if temp is None:
|
|
temp = _get_attribute(device, "Airflow_Temperature_Cel")
|
|
return temp
|
|
|
|
|
|
def _get_attribute(device, name: str) -> Optional[int]:
|
|
"""Get a specific SMART attribute value."""
|
|
if not hasattr(device, 'attributes') or device.attributes is None:
|
|
return None
|
|
|
|
for attr in device.attributes:
|
|
if attr and attr.name == name:
|
|
try:
|
|
return int(attr.raw)
|
|
except (ValueError, TypeError):
|
|
return None
|
|
return None
|