feat: fan RPM monitoring + FanSpeed alerts#2032
Open
maxmaxme wants to merge 4 commits into
Open
Conversation
Mirrors temperature collection: walks /sys/class/hwmon/*, reads every fan*_input it finds, keys entries by "<chip>_<label-or-fan-idx>", skips 0 RPM (unpopulated headers). Linux-only via hwmonRoot const that's empty on other platforms. New field Stats.Fans (CBOR tag 36, JSON "f") — wire-compatible with older hubs thanks to omitempty / CBOR's forward compatibility. Addresses henrygd#1918.
Mirrors TemperatureChart for the new Stats.Fans field: - $fanFilter store + types update (stats.f) - FanChart component beneath Temperature, RPM units - Wires into both the legacy single-tab grid and the new tabbed view Verified end-to-end on a Raspberry Pi 5 with pwmfan_fan1 graphing ~3760-3770 RPM live alongside cpu_thermal/rp1_adc temperature. Locale .po updates intentionally omitted — they belong in a separate "update translations" commit per the upstream pattern.
Triggers when the highest fan RPM across all collected sensors goes above a user-set threshold. Mirrors the Temperature alert path at every layer: - system.Info.DashboardFan (CBOR tag 24, JSON "df") — max RPM across Stats.Fans, computed in the agent each tick. Used ONLY by the alert for its single-threshold compare; the FanChart in the UI keeps reading the full Stats.Fans map and draws every sensor separately. - alerts_system.go: new "FanSpeed" case in both the one-minute fast path and the multi-minute aggregation. Uses the same per-sensor mapSums pattern as Temperature so the descriptor names the loudest fan (e.g. "Highest fan pwmfan_fan1") in notifications. - SystemAlertStats gains a Fans map[string]float32 mirror of Temperatures. - New sequential migration 1_add_fanspeed_alert.go extends the existing alerts.name select with "FanSpeed" (idempotent, with a down-migration that strips it). Slots FanSpeed next to Temperature so the dropdown order matches the chart ordering on the system page. - Tests: one-minute + multi-minute FanSpeed alert cases added beside the existing Temperature ones.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📃 Description
Adds fan RPM monitoring as a peer to the existing temperature collection, addressing #1918. Four commits, each layer parallel to Temperature:
/sys/class/hwmon/*/fan*_input, skips 0 RPM (unpopulated headers), populates a newStats.Fansmap. Linux-only via a build-taggedhwmonRootconst; other platforms get an empty string and skip collection entirely. gopsutil has no equivalent for fans, hence the small custom sysfs walker per the issue's maintainer guidance.$fanFilterfilter bar. Readsstats.fand renders one line per sensor, mirroringTemperatureChart.FanSpeedalert type. Fires when the highest fan RPM across all sensors goes above the user's threshold. Same per-sensormapSumsaggregation as Temperature so multi-minute alerts name the loudest fan in the notification descriptor (e.g."Highest fan pwmfan_fan1").beszel-docsfor the same change on the website (see below).Verified end-to-end on a Raspberry Pi 5 (Active Cooler): agent reading
pwmfan_fan1 = 3782 RPM, hub persisting"f":{"pwmfan_fan1":3766}next to"t":{...}insystem_stats.stats, UI rendering the chart live, alert tests passing in both one-minute and multi-minute paths.Notes for the reviewer
FanSpeedoption is added via a sequential migration (1_add_fanspeed_alert.go) — applies on existing installs, idempotent, with a working down-migration. Verified on a real DB snapshot pulled from a live Pi install: apply → re-apply → revert, all clean..poupdates intentionally omitted to match the upstream "update translations" cadence — until then the new msgids (Fans,Fan speeds of system sensors) fall back to English in every locale, same as any other newly-introduced string.Info.DashboardFan(max RPM across sensors) exists solely to give the alert path a single number to compare against the threshold. The chart keeps reading the fullStats.Fansmap so every sensor is drawn separately — no aggregation in the visualisation.📖 Documentation
Companion PR: henrygd/beszel-docs#61 — two-line update to
en/guide/what-is-beszel.md(Supported metrics + Alerts feature line).🪵 Changelog
➕ Added
/sys/class/hwmon/*/fan*_inputon Linux hosts.Stats.Fanswire field (CBOR36,keyasint,omitempty, JSONf,omitempty). Omitted entirely when no fan reports a non-zero RPM.Info.DashboardFan— max RPM across sensors, used by the new alert.FanCharton the system detail page mirroringTemperatureChart(RPM units,$fanFilterfilter bar, sortable legend).FanSpeedalert type with multi-minute aggregation and per-sensor descriptors.1_add_fanspeed_alert.goextending the alerts collection's name-select withFanSpeed(idempotent, with down-migration).FanSpeedalert paths.✏️ Changed
agent/system.gocallsupdateFans()immediately afterupdateTemperatures().readme.md— fan speed added to the supported-metrics list and alerts feature line (mirrored in companion docs PR).📷 Screenshots
The new Fans card sits under Temperature on the system detail page:
