All notable changes to the UCG Max Fan Control project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.2.2 (2026-08-09)
- keep fans stopped at startup under control and cut steady-state log volume (#47) (a3e310b), closes #42 #46
1.2.1 (2026-08-09)
- diagnose install failures precisely and harden the tar header walk (#44) (a014711)
- drive floor follows the hottest drive across all readable drives (#43) (8859849)
- Diagnose failed verified-release downloads by curl failure class. DNS failures now name the failing host and explain GitHub's release-asset redirect.
- Add a tag-scoped
FAN_CONTROL_ALLOW_UNVERIFIEDfallback for a verified download that cannot complete. The fallback is explicit and still validates its payload.
1.2.0 (2026-08-09)
1.1.1 (2026-08-09)
- restrict install and config permissions (d8c6f5d)
1.1.0 (2026-08-09)
First tagged release. The code has been running on people's routers for months
— what's new is that it now has a version number, verified downloads, and a
test suite. Everything below is relative to installing from main before today.
- Fan sped up as the device cooled (#26) —
calculate_speedsquared a signedtemp_diff, mirroring the response curve below the activation temperature. BetweenMIN_TEMPandFAN_ACTIVATION_TEMPthe fan got louder as the router got cooler, and 60 °C produced the same PWM as 70 °C. A reporter measured 603 PWM writes in 21 hours on an idle UCG-Fiber. - Sensor fail-safe never fired (#18) —
get_smoothed_tempwas called through$(...), so its failure counter and smoothed temperature died in the subshell. The 3-strike fail-safe that forcesMAX_PWMon sensor failure was unreachable code, and temperature smoothing never accumulated. - Cleanup trap fired at startup (#17) — the trap and
flockwere registered inside a subshell that exited immediately, so the PID file was deleted at launch, exit cleanup never ran, and the single-instance guard did nothing. - Stale temperature reused on hot restart (#22) — the guard on the persisted temperature stripped the minus sign from each operand instead of computing
|saved - raw|, so it only protected one direction. A hot boot with a stale low value kept the fan off for a full check interval.
- Releases are tagged, and each one ships a tarball plus
SHA256SUMS. install.shdownloads, verifies the checksum, validates every archive entry, and syntax-checks the scripts before anything is written to/data. Archive entries are checked by raw tar header, not just filename, so an archive carrying a symlink under a legitimate name is rejected.- Pin a version with
FAN_CONTROL_VERSION=v1.0.0; omit it to get the latest release. - Previously the installer fetched loose files from
main, and a 404 could be written to disk and executed as root, becausecurlwas called without-f.
The daemon logs CONFIG: fan-control vX.Y.Z starting, and VERSION is installed alongside it. Previously there was no way to tell which build a device was running short of hashing the script against git history.
- 11 sandboxed tests that need no device and no root, including a regression test per bug above.
- CI runs them on bash 4.4, 5.1, 5.2 and native Ubuntu, all under mawk — matching the UCG-Max, which runs bash 5.1.4 and mawk 1.3.4.
- shellcheck and shfmt are enforced at zero findings.
- Tagged release automation with verified runtime tarballs and
SHA256SUMS. - Verified release installation with latest-release resolution and
FAN_CONTROL_VERSIONpinning. - Archive allowlist, checksum, syntax, and rollback validation before installer files replace the running deployment.
VERSIONidentity file and startup logging for the deployed daemon version.- Test suite (
tests/) — sandboxed, no-root, dependency-free bash tests covering config bootstrap, PWM detection, state machine, and regression tests for #17 and #18. - Env-var seams for testability:
FAN_CONTROL_CONFIG_FILE,FAN_CONTROL_TEMP_STATE_FILE,FAN_CONTROL_PID_FILE,FAN_CONTROL_OPTIMAL_PWM_FILE,FAN_CONTROL_HWMON_BASE. - CONTRIBUTING.md with contribution guidelines
- CHANGELOG.md for tracking version history
- TROUBLESHOOTING.md for common issues and solutions
- SECURITY.md for security policy
- CODE_OF_CONDUCT.md for community guidelines
- GitHub issue templates for bug reports and feature requests
- GitHub pull request template
- #17: Lock and cleanup trap were registered in a subshell that exited immediately. Moved
flockandtrapto the parent shell so the lock is held for the daemon's lifetime, cleanup runs on actual exit, and single-instance guard is authoritative. - #18:
get_smoothed_tempwas called via$(...), losingTEMP_READ_FAILURESandSMOOTHED_TEMPmutations in subshells. Rewrote to communicate via globals; added a sensor fail-safe inupdate_fan_statethat forcesMAX_PWMafter 3 consecutive read failures, bypassing state-machine and ramp limits. - Fan speed no longer increases as the device cools below the activation temperature; the quadratic curve now clamps sub-activation
temp_diffto zero (#26). - Saved-temp bootstrap used
(( ${saved_temp#-} - ${raw_temp#-} < 15 ))to guard against re-initialising to a stale persisted smoothed temp. The${var#-}form strips a leading minus from each operand independently and does not compute|saved - raw|, so only thesaved > rawdirection was guarded. On a hot restart with a stale low saved temp (raw > saved), the difference was negative, always< 15, andSMOOTHED_TEMPwas re-initialised to the stale low value — leaving the coldstart fan-OFF decision at line 774 to run against a too-low temp and keeping the fan OFF for one fullCHECK_INTERVAL. Replaced with a real absolute difference; addedtests/test_regression_saved_temp_bootstrap.sh.
- Removed locale settings and enhanced temperature smoothing precision
- Refactored temperature smoothing and PWM logic for better precision and reliability
- Improved error handling with atomic writes and logging
- Ensured fan PWM is set to 0 during uninstallation to prevent unintended behavior
- Four operational states: OFF, TAPER, ACTIVE, EMERGENCY
- Quadratic response curve for progressive cooling
- Exponential smoothing for noise-resistant temperature tracking
- State transition hysteresis to prevent rapid oscillation
- Emergency override for critical temperatures
- Speed limits and thermal protection
- Hardware validation on startup
- Sensor failure detection and recovery
- Configuration validation with automatic correction
- Enhanced adaptive learning system
- Intelligent PWM optimization
- Temperature trend analysis
- Efficiency optimization strategies
- User-configurable temperature thresholds
- Adjustable fan speed ranges
- Customizable smoothing factors
- Flexible check intervals and taper duration
- One-line installation command
- Support for branch-specific installations
- Smart service management (fresh install or hot update)
- Automatic configuration file creation with defaults
- Comprehensive logging system
- Real-time status updates
- Temperature delta tracking
- Learning activity logs
- Configuration change notifications
Future releases will follow this format:
- New features
- Changes in existing functionality
- Soon-to-be removed features
- Removed features
- Bug fixes
- Security improvements or fixes
Note: This changelog was created on 2025-11-13. Previous changes were reconstructed from git commit history and README documentation.