feat: add min/max temperature tracking for nodes
Track minimum and maximum CPU temperatures since monitoring started. This provides better insight into temperature trends and cooling adequacy over time. Changes: - Backend: Add CPUMin, CPUMaxRecord, MinRecorded, MaxRecorded fields to Temperature model - Backend: Implement min/max tracking logic in monitoring cycle that preserves values across polling cycles - Backend: Initialize min/max on first reading, update on extremes - Frontend: Update Temperature TypeScript interface with new fields - Frontend: Display min/max range in NodeCard tooltip (e.g., "52°C (48-67°C since monitoring started)") - Frontend: Rebuild dist assets Temperature display now shows: - Current temperature with color coding (green/yellow/red) - Tooltip with full min-max range and context - Min/max tracked in-memory (resets on Pulse restart) Example tooltip: "CPU: 52°C (48-67°C since monitoring started)" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
27db397f62
commit
97b9c6739c
7 changed files with 71 additions and 245 deletions
File diff suppressed because one or more lines are too long
217
frontend-modern/dist/assets/index-cdElc049.js
vendored
217
frontend-modern/dist/assets/index-cdElc049.js
vendored
File diff suppressed because one or more lines are too long
2
frontend-modern/dist/index.html
vendored
2
frontend-modern/dist/index.html
vendored
|
|
@ -6,7 +6,7 @@
|
|||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<title>Pulse</title>
|
||||
<script type="module" crossorigin src="/assets/index-cdElc049.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BFQdFd5E.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-VFB0BYLW.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -133,6 +133,16 @@ const NodeCard: Component<NodeCardProps> = (props) => {
|
|||
if (value === null) {
|
||||
return 'CPU sensor unavailable';
|
||||
}
|
||||
|
||||
const temp = props.node.temperature;
|
||||
const hasMinMax = temp && temp.cpuMin > 0 && temp.cpuMaxRecord > 0;
|
||||
|
||||
if (hasMinMax) {
|
||||
const min = Math.round(temp.cpuMin);
|
||||
const max = Math.round(temp.cpuMaxRecord);
|
||||
return `CPU: ${value}°C (${min}-${max}°C since monitoring started)`;
|
||||
}
|
||||
|
||||
return `CPU: ${value}°C`;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -512,6 +512,10 @@ export interface CPUInfo {
|
|||
export interface Temperature {
|
||||
cpuPackage?: number; // CPU package temperature (primary metric)
|
||||
cpuMax?: number; // Highest core temperature
|
||||
cpuMin?: number; // Minimum recorded CPU temperature (since monitoring started)
|
||||
cpuMaxRecord?: number; // Maximum recorded CPU temperature (since monitoring started)
|
||||
minRecorded?: string; // When minimum temperature was recorded
|
||||
maxRecorded?: string; // When maximum temperature was recorded
|
||||
cores?: CoreTemp[]; // Individual core temperatures
|
||||
nvme?: NVMeTemp[]; // NVMe drive temperatures
|
||||
available: boolean; // Whether any temperature data is available
|
||||
|
|
|
|||
|
|
@ -569,14 +569,18 @@ type CPUInfo struct {
|
|||
|
||||
// Temperature represents temperature sensors data
|
||||
type Temperature struct {
|
||||
CPUPackage float64 `json:"cpuPackage,omitempty"` // CPU package temperature (primary metric)
|
||||
CPUMax float64 `json:"cpuMax,omitempty"` // Highest core temperature
|
||||
Cores []CoreTemp `json:"cores,omitempty"` // Individual core temperatures
|
||||
NVMe []NVMeTemp `json:"nvme,omitempty"` // NVMe drive temperatures
|
||||
Available bool `json:"available"` // Whether any temperature data is available
|
||||
HasCPU bool `json:"hasCPU"` // Whether CPU temperature data is available
|
||||
HasNVMe bool `json:"hasNVMe"` // Whether NVMe temperature data is available
|
||||
LastUpdate time.Time `json:"lastUpdate"` // When this data was collected
|
||||
CPUPackage float64 `json:"cpuPackage,omitempty"` // CPU package temperature (primary metric)
|
||||
CPUMax float64 `json:"cpuMax,omitempty"` // Highest core temperature
|
||||
CPUMin float64 `json:"cpuMin,omitempty"` // Minimum recorded CPU temperature (since monitoring started)
|
||||
CPUMaxRecord float64 `json:"cpuMaxRecord,omitempty"` // Maximum recorded CPU temperature (since monitoring started)
|
||||
MinRecorded time.Time `json:"minRecorded,omitempty"` // When minimum temperature was recorded
|
||||
MaxRecorded time.Time `json:"maxRecorded,omitempty"` // When maximum temperature was recorded
|
||||
Cores []CoreTemp `json:"cores,omitempty"` // Individual core temperatures
|
||||
NVMe []NVMeTemp `json:"nvme,omitempty"` // NVMe drive temperatures
|
||||
Available bool `json:"available"` // Whether any temperature data is available
|
||||
HasCPU bool `json:"hasCPU"` // Whether CPU temperature data is available
|
||||
HasNVMe bool `json:"hasNVMe"` // Whether NVMe temperature data is available
|
||||
LastUpdate time.Time `json:"lastUpdate"` // When this data was collected
|
||||
}
|
||||
|
||||
// CoreTemp represents a CPU core temperature
|
||||
|
|
|
|||
|
|
@ -2590,12 +2590,56 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie
|
|||
tempCancel()
|
||||
|
||||
if err == nil && temp != nil && temp.Available {
|
||||
// Get the current CPU temperature (prefer package, fall back to max)
|
||||
currentTemp := temp.CPUPackage
|
||||
if currentTemp == 0 && temp.CPUMax > 0 {
|
||||
currentTemp = temp.CPUMax
|
||||
}
|
||||
|
||||
// Find previous temperature data for this node to preserve min/max
|
||||
var prevTemp *models.Temperature
|
||||
for _, prevNode := range prevInstanceNodes {
|
||||
if prevNode.ID == modelNode.ID && prevNode.Temperature != nil {
|
||||
prevTemp = prevNode.Temperature
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize or update min/max tracking
|
||||
if prevTemp != nil && prevTemp.CPUMin > 0 {
|
||||
// Preserve existing min/max and update if necessary
|
||||
temp.CPUMin = prevTemp.CPUMin
|
||||
temp.CPUMaxRecord = prevTemp.CPUMaxRecord
|
||||
temp.MinRecorded = prevTemp.MinRecorded
|
||||
temp.MaxRecorded = prevTemp.MaxRecorded
|
||||
|
||||
// Update min if current is lower
|
||||
if currentTemp > 0 && currentTemp < temp.CPUMin {
|
||||
temp.CPUMin = currentTemp
|
||||
temp.MinRecorded = time.Now()
|
||||
}
|
||||
|
||||
// Update max if current is higher
|
||||
if currentTemp > temp.CPUMaxRecord {
|
||||
temp.CPUMaxRecord = currentTemp
|
||||
temp.MaxRecorded = time.Now()
|
||||
}
|
||||
} else if currentTemp > 0 {
|
||||
// First reading - initialize min/max to current value
|
||||
temp.CPUMin = currentTemp
|
||||
temp.CPUMaxRecord = currentTemp
|
||||
temp.MinRecorded = time.Now()
|
||||
temp.MaxRecorded = time.Now()
|
||||
}
|
||||
|
||||
modelNode.Temperature = temp
|
||||
log.Debug().
|
||||
Str("node", node.Node).
|
||||
Str("sshHost", sshHost).
|
||||
Float64("cpuPackage", temp.CPUPackage).
|
||||
Float64("cpuMax", temp.CPUMax).
|
||||
Float64("cpuMin", temp.CPUMin).
|
||||
Float64("cpuMaxRecord", temp.CPUMaxRecord).
|
||||
Int("nvmeCount", len(temp.NVMe)).
|
||||
Msg("Collected temperature data")
|
||||
} else if err != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue