{"id":47356678,"url":"https://github.com/iceteaSA/unifi-fan-control","last_synced_at":"2026-04-01T07:00:58.909Z","repository":{"id":275217095,"uuid":"925428996","full_name":"iceteaSA/unifi-fan-control","owner":"iceteaSA","description":"Intelligent adaptive fan controller for UniFi OS devices. Multi-fan auto-detection, quadratic response curve, learning PWM optimization. Works on UDM-SE, UDM-Pro-Max, UCG-Max, UCG-Fibre, UXG-Fibre, UDR7, UNVR.","archived":false,"fork":false,"pushed_at":"2026-03-30T20:40:21.000Z","size":124,"stargazers_count":196,"open_issues_count":1,"forks_count":17,"subscribers_count":18,"default_branch":"main","last_synced_at":"2026-03-30T22:17:08.180Z","etag":null,"topics":["adaptive-fan-control","bash","fan-control","pwm","systemd","temperature-monitoring","thermal-management","ubiquiti","udm","udm-pro-max","udm-se","udr7","unifi","unvr","uxg-fibre"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iceteaSA.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"ko_fi":"H2H719VB0U"}},"created_at":"2025-01-31T21:32:13.000Z","updated_at":"2026-03-30T20:40:25.000Z","dependencies_parsed_at":"2025-05-31T22:38:13.584Z","dependency_job_id":"84710477-49b7-4717-a401-2c7fd48d2b5a","html_url":"https://github.com/iceteaSA/unifi-fan-control","commit_stats":null,"previous_names":["iceteasa/ucg-max-fan-control"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/iceteaSA/unifi-fan-control","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iceteaSA%2Funifi-fan-control","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iceteaSA%2Funifi-fan-control/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iceteaSA%2Funifi-fan-control/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iceteaSA%2Funifi-fan-control/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iceteaSA","download_url":"https://codeload.github.com/iceteaSA/unifi-fan-control/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iceteaSA%2Funifi-fan-control/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31269194,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T06:57:45.811Z","status":"ssl_error","status_checked_at":"2026-04-01T06:57:42.389Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adaptive-fan-control","bash","fan-control","pwm","systemd","temperature-monitoring","thermal-management","ubiquiti","udm","udm-pro-max","udm-se","udr7","unifi","unvr","uxg-fibre"],"created_at":"2026-03-18T03:00:18.694Z","updated_at":"2026-04-01T07:00:58.899Z","avatar_url":"https://github.com/iceteaSA.png","language":"Shell","funding_links":["https://ko-fi.com/H2H719VB0U"],"categories":["Dream Machine Utilities"],"sub_categories":["Other Platforms"],"readme":"# UniFi Intelligent Fan Control\n\nAdvanced temperature management for Ubiquiti UniFi OS devices with fan control.\n\nConfirmed working on: UCG-Max, UCG-Fibre, UXG-Fibre, UDM-SE, UDM-Pro-Max, UDR7, UNVR\n\n\u003e This project is built and maintained independently. If it keeps your UniFi gear cool and quiet, [consider supporting it](https://ko-fi.com/H2H719VB0U).\n\n## Features\n- 🎛️ **Four Operational States**: \n  - **OFF**: Fan disabled (temp \u003c activation threshold)\n  - **TAPER**: Post-cooling minimum speed period\n  - **ACTIVE**: Quadratic response curve (temp ≥ activation threshold)\n  - **EMERGENCY**: Immediate full speed (255 PWM) (critical temps)\n- 🚨 **Emergency Override**: Instant full speed at critical temps with hysteresis for stable transitions\n- 📈 **Quadratic Response**: Progressive cooling curve for optimal noise/performance\n- 🧠 **Enhanced Adaptive Learning**: Intelligent PWM optimization with temperature trend analysis\n- 📉 **Exponential Smoothing**: Noise-resistant temperature tracking\n- 🛡️ **Robust Safety Systems**: \n  - Speed limits and thermal protection\n  - Hardware validation\n  - Sensor failure detection and recovery\n  - Configuration validation\n- 🔄 **State Transition Hysteresis**: Prevents rapid state oscillation\n- 🔍 **Multi-Fan Auto-Detection**: Automatically discovers and controls all active fan channels\n  - Searches hwmon class directories first (UCG-Max, UNVR)\n  - Falls back to raw sysfs device paths when needed (UDM-SE)\n  - Identifies active fans by RPM reading and write-tests each channel\n  - All detected fans receive the same PWM value\n\n## Installation\n```bash\ncurl -sSL https://raw.githubusercontent.com/iceteaSA/unifi-fan-control/main/install.sh | sudo bash\n```\n\n### Using a Different Branch\nIf you want to install from a specific branch (e.g., for testing new features):\n\n**Method 1: Direct URL**\n```bash\n# Replace 'dev' with your desired branch name\ncurl -sSL https://raw.githubusercontent.com/iceteaSA/unifi-fan-control/dev/install.sh | sudo bash\n```\n\n**Method 2: Environment Variable**\n```bash\n# Set the branch name via environment variable\nFAN_CONTROL_BRANCH=dev curl -sSL https://raw.githubusercontent.com/iceteaSA/unifi-fan-control/main/install.sh | sudo bash\n```\n\n### Manual Installation\nIf you prefer to inspect the code before installation:\n```bash\n# Clone the repository\ngit clone https://github.com/iceteaSA/unifi-fan-control.git\ncd unifi-fan-control\n\n# Optionally checkout a specific branch\n# git checkout dev\n\n# Run the installer (you can also use FAN_CONTROL_BRANCH to override the branch)\nsudo ./install.sh\n# Or with a specific branch:\n# sudo FAN_CONTROL_BRANCH=dev ./install.sh\n```\n\n## Configuration\nEdit `/data/fan-control/config`:\n```bash\n# Core Thresholds\nMIN_TEMP=60            # Base threshold (°C)\nMAX_TEMP=85            # Critical temperature (°C)\nHYSTERESIS=5           # Temperature buffer (°C)\n\n# Fan Behavior\nMIN_PWM=91        # Minimum active speed (0-255)\nMAX_PWM=255       # Maximum speed (0-255)\nMAX_PWM_STEP=25   # Maximum speed change per adjustment\n                  # Note: Due to hardware limitations, actual PWM values may vary slightly from requested values\n\n# Advanced Tuning\nALPHA=20          # Smoothing factor, lower values make the smoothed temp follow raw temp more closely (0-100 raw→smooth)\nDEADBAND=1        # Temperature stability threshold (°C)\nLEARNING_RATE=5   # Hourly PWM optimization step size\nTAPER_MINS=90     # Cool-down duration (minutes)\nCHECK_INTERVAL=15 # Temperature check frequency (seconds)\n\n# Auto-detects all active fan channels by default (recommended)\n# Set to false to use FAN_PWM_DEVICE as a single manual override instead\nFAN_PWM_AUTODETECT=true\n# Only used when FAN_PWM_AUTODETECT=false\nFAN_PWM_DEVICE=\"/sys/class/hwmon/hwmon0/pwm1\"\nOPTIMAL_PWM_FILE=\"/data/fan-control/optimal_pwm\"\n```\n\n\u003e **Note**: The script automatically checks for missing configuration parameters and adds them with default values if they're not present in the config file. This ensures that all required parameters are always available, even if you've edited the config file manually.\n\nApply changes:\n```bash\nsystemctl restart fan-control.service\n```\n\n## Operational Overview\n| State       | Trigger Condition          | Exit Condition                   | Behavior                          |\n|-------------|----------------------------|----------------------------------|-----------------------------------|\n| **OFF**     | \u003c65°C (60+5)               | Temp ≥ 65°C                      | Fan disabled                      |\n| **TAPER**   | Temp ≤ 60°C from ACTIVE    | Temp ≥ 67°C or timer elapsed     | Minimum speed for configured mins |\n| **ACTIVE**  | 65°C - 85°C                | Temp ≤ 60°C or Temp ≥ 85°C       | Quadratic speed response          |\n| **EMERGENCY**| ≥85°C                     | Temp ≤ 80°C (with hysteresis)    | Immediate full speed (255 PWM)    |\n\n### State Transitions\n- **OFF → ACTIVE**: Temperature rises above activation threshold (65°C)\n- **ACTIVE → TAPER**: Temperature drops below minimum threshold (60°C)\n- **ACTIVE → EMERGENCY**: Temperature reaches critical level (85°C)\n- **TAPER → OFF**: Cool-down period (default: 90 minutes) completes\n- **TAPER → ACTIVE**: Temperature rises significantly above activation threshold (67°C, with 2°C buffer)\n- **EMERGENCY → ACTIVE**: Temperature drops significantly below critical level (80°C, with 5°C hysteresis)\n\n## Monitoring \u0026 Logging\nKey operational signals:\n```log\n# Temperature Monitoring\nTEMP: RAW=68℃ | SMOOTH=65℃ | DELTA=-3℃\n\n# Speed Calculations\nCALC: temp_diff=5℃ | range=20℃ | speed=100pwm\n\n# State Transitions\nSTATE: OFF→ACTIVE (67℃ ≥ 65℃)\nSTATE: ACTIVE→TAPER (59℃ ≤ 60℃)\nSTATE: →EMERGENCY (86℃ ≥ 85℃)\nSTATE: EMERGENCY→ACTIVE (79℃ ≤ 80℃)\nSTATE: TAPER→ACTIVE (67℃ ≥ 67℃)\n\n# Speed Changes\nSET: 55→80pwm | Reason: Ramp-up limited: 55→80pwm\nSET: 120→255pwm | Reason: EMERGENCY: Temp 86℃ ≥ 85℃\n\n# Enhanced Learning System\nLEARNING: 80→85pwm (+5 (rising temp 2℃)) [Rate=7]\nLEARNING: 95→90pwm (-5 (stable below threshold)) [Rate=5]\nLEARNING: 100→99pwm (-1 (efficiency optimization)) [Rate=5]\n\n# Error Handling\nERROR: Failed to read temperature (attempt 1)\nALERT: Multiple temperature read failures - using last known temperature\nSAFETY: Activating emergency mode due to sensor failure\n\n# Configuration Validation\nCONFIG: Invalid MIN_TEMP value: 25 (should be between 30 and 80), using default: 60\nCONFIG: Updating configuration file with corrected values\n\n# Configuration Management\nCONFIG: Missing parameter detected: CHECK_INTERVAL\nCONFIG: Updating configuration file with 1 missing parameters\nCONFIG: Configuration file updated successfully\n\n# System Status\nSTATUS: State=ACTIVE | PWM=120 | Temp=72℃\nSTATUS: State=EMERGENCY | PWM=255 | Temp=86℃\n```\n\nView logs with:\n```bash\njournalctl -u fan-control.service -f          # Live monitoring\njournalctl -u fan-control.service --since \"10 minutes ago\"  # Recent history\n```\n\n## Technical Implementation\n- **Quadratic Response Curve**:\n\n\u003cbr\u003e\n\n$$\nPWM = MIN_{PWM} + \\frac{(temp_{diff}^2 \\times (MAX_{PWM} - MIN_{PWM}))}{temp_{range}^2}\n$$\n\nWhere:  \n`temp_diff = current_temp - activation_temp`  \n`temp_range = MAX_TEMP - activation_temp`\n\n\n- **Exponential Smoothing**:\n\n\u003cbr\u003e\n\n$$\nsmoothed_{temp} = \\frac{\\alpha \\times previous_{smooth} + (100 - \\alpha) \\times raw_{temp}}{100}\n$$\n\n(α configured via ALPHA parameter)\n\n\u003cbr\u003e\n\n\n- **Enhanced Adaptive Learning**:\n  - Adjusts optimal PWM based on thermal performance every 30 minutes (configurable)\n  - Uses adaptive learning rate based on temperature stability\n  - Implements three learning strategies:\n    1. Proactive PWM increase when temperature is rising\n    2. PWM reduction when temperature is stable below threshold\n    3. Efficiency optimization when running faster than necessary with stable temperatures\n\n\n- **Robust Error Handling**:\n  - Tracks consecutive temperature reading failures\n  - Implements safety measures after multiple failures\n  - Uses last known temperature when readings fail\n  - Activates fans proactively during sensor uncertainty\n\n- **Configuration Validation**:\n  - Validates all parameters against reasonable ranges\n  - Automatically corrects invalid settings\n  - Prevents misconfiguration issues\n\n- **Hardware PWM Limitations**:  \n  Due to device hardware limitations, the actual PWM values applied may differ from the requested values\n  (e.g., setting 50 might result in ~48, or 100 might result in ~92)\n\n## Maintenance\n```bash\n# Service Management\nsystemctl status fan-control.service   # Current state\nsystemctl restart fan-control.service  # Apply config changes\n\n# Full Removal\n/data/fan-control/uninstall.sh\n```\n\n## Project Structure\n- **fan-control.sh**: The main script that monitors temperature and controls fan speed\n- **install.sh**: Installation script that copies files and sets up the systemd service\n  - Supports installation from different branches via the `FAN_CONTROL_BRANCH` environment variable\n  - Automatically downloads required files if not found locally\n- **uninstall.sh**: Script to remove the fan control system\n- **fan-control.service**: Systemd service configuration\n\n## Credits \u0026 Acknowledgments\n- **Thermal Research**: [UCG-Max Thermal Thread](https://www.reddit.com/r/Ubiquiti/comments/1fr8xyt/)\n- **System Integration**: SierraSoftworks service patterns\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H719VB0U)\n\n---\n\n**Disclaimer**: Community project - Not affiliated with Ubiquiti Inc.  \n**Compatibility**: Verified on UniFi OS 4.0.0+ | UCG-Max, UCG-Fibre, UXG-Fibre, UDM-SE, UDM-Pro-Max, UDR7, UNVR  \n**License**: MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FiceteaSA%2Funifi-fan-control","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FiceteaSA%2Funifi-fan-control","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FiceteaSA%2Funifi-fan-control/lists"}