diff --git a/hwmonDaemon.py b/hwmonDaemon.py index b0b0e01..0abd46a 100644 --- a/hwmonDaemon.py +++ b/hwmonDaemon.py @@ -116,6 +116,28 @@ class SystemHealthMonitor: } MANUFACTURER_SMART_PROFILES = { + 'Western Digital': { + 'aliases': ['WDC', 'Western Digital', 'HGST', 'Ultrastar'], + 'attributes': { + 'Raw_Read_Error_Rate': { + 'monitor': False, + 'description': 'WD drives use this as operation counter, not error count' + }, + 'Seek_Error_Rate': { + 'monitor': False, + 'description': 'WD drives use this as operation counter, not error count' + } + } + }, + 'Seagate': { + 'aliases': ['Seagate', 'ST'], + 'attributes': { + 'Raw_Read_Error_Rate': { + 'monitor': False, + 'description': 'Seagate drives use this as operation counter' + } + } + }, 'Ridata': { 'aliases': ['Ridata', 'Ritek', 'RIDATA', 'RITEK', 'SSD 512GB'], 'firmware_patterns': ['HT3618B7', 'HT36'], @@ -608,9 +630,9 @@ class SystemHealthMonitor: # Drive-type specific temperature thresholds - ADJUSTED TO BE LESS SENSITIVE if drive_type == 'SSD': - temp_thresholds = {'warning': 70, 'critical': 85, 'optimal_max': 65} # Raised from 60 + temp_thresholds = {'warning': 70, 'critical': 85, 'optimal_max': 65} else: # HDD - temp_thresholds = {'warning': 60, 'critical': 70, 'optimal_max': 55} # Raised from 45/55/65 + temp_thresholds = {'warning': 60, 'critical': 70, 'optimal_max': 55} if temperature >= temp_thresholds['critical']: issues.append(f"CRITICAL: Drive temperature {temperature}°C exceeds safe operating limit for {drive_type}") @@ -1498,10 +1520,56 @@ class SystemHealthMonitor: logger.debug(f"Could not parse SMART value: {raw_value}") return 0 + def _detect_manufacturer(self, model: str, serial: str = None) -> str: + """Enhanced manufacturer detection based on model and serial patterns.""" + if not model: + return 'Unknown' + + model_upper = model.upper() + + # Western Digital patterns (including HGST which WD acquired) + if any(pattern in model_upper for pattern in ['WDC', 'WD-', 'HGST', 'WESTERN DIGITAL']): + return 'Western Digital' + + # Seagate patterns + elif any(pattern in model_upper for pattern in ['ST', 'SEAGATE']): + return 'Seagate' + + # Samsung patterns + elif 'SAMSUNG' in model_upper: + return 'Samsung' + + # Intel patterns + elif any(pattern in model_upper for pattern in ['INTEL', 'SSDSC']): + return 'Intel' + + # Micron/Crucial patterns + elif any(pattern in model_upper for pattern in ['CRUCIAL', 'MICRON', 'CT']): + return 'Micron' + + # Toshiba patterns + elif 'TOSHIBA' in model_upper: + return 'Toshiba' + + # Ridata/Ritek patterns (for your existing special handling) + elif any(pattern in model_upper for pattern in ['RIDATA', 'RITEK']): + return 'Ridata' + + # OOS patterns (for your existing special handling) + elif 'OOS' in model_upper: + return 'OOS' + + return 'Unknown' + def _get_manufacturer_profile(self, model: str, manufacturer: str = None, firmware: str = None) -> Dict[str, Any]: """Get manufacturer-specific SMART profile based on drive model/manufacturer/firmware.""" logger.debug(f"Looking for profile - Model: '{model}', Manufacturer: '{manufacturer}', Firmware: '{firmware}'") + # First, try to detect manufacturer if not provided + if not manufacturer: + manufacturer = self._detect_manufacturer(model) + logger.debug(f"Auto-detected manufacturer: {manufacturer}") + # Check each manufacturer profile for mfg, profile in self.MANUFACTURER_SMART_PROFILES.items(): # Check firmware patterns first (most specific for OEM drives like RiData) @@ -1511,14 +1579,19 @@ class SystemHealthMonitor: logger.debug(f"Matched manufacturer profile: {mfg} for firmware pattern '{pattern}' in '{firmware}'") return profile - # Check model/manufacturer aliases + # Check if detected manufacturer matches this profile + if manufacturer and manufacturer in profile['aliases']: + logger.debug(f"Matched manufacturer profile: {mfg} for detected manufacturer '{manufacturer}'") + return profile + + # Check model/manufacturer aliases (fallback) for alias in profile['aliases']: if alias.lower() in model.lower() or (manufacturer and alias.lower() in manufacturer.lower()): logger.debug(f"Matched manufacturer profile: {mfg} for model alias '{alias}' in '{model}'") return profile # Return generic profile if no match - logger.debug(f"No specific profile found for Model: '{model}', Firmware: '{firmware}', using Generic profile") + logger.debug(f"No specific profile found for Model: '{model}', Manufacturer: '{manufacturer}', Firmware: '{firmware}', using Generic profile") return self.MANUFACTURER_SMART_PROFILES['Generic'] def _should_monitor_attribute(self, attr_name: str, manufacturer_profile: dict) -> bool: