Version: 1.0.0
A lightweight Python tool to control a fan based on system temperature, using libgpiod.
- Core service tested in Docker on Raspberry Pi 3.
- No verified results yet for other boards/architectures.
git clone https://github.com/m00sfett/fanctrl.git
cd fanctrl
docker compose up -d --build- Backend: Uses strict
libgpiod(modern Linux GPIO). no legacyRPi.GPIOdependency. - Configurable: Set thresholds, poll intervals, and GPIO lines via TOML config or environment variables.
- Status API: Simple HTTP endpoint to monitor current state.
- Safety: Failsafe defaults if config is invalid or sensor reading fails.
If you are looking for the Home Assistant Add-on, find it here: ha-addon-fanctrl In current Home Assistant versions (2026.2+), add-ons are listed under Apps.
git clone https://github.com/m00sfett/fanctrl.git
cd fanctrl
pip install .- Clone the repo and create a config:
git clone https://github.com/m00sfett/fanctrl.git
cd fanctrl
# Edit config/fanctrl.toml to match your hardware.- Start with Docker Compose:
docker compose up -d --build- Logs:
docker compose logs -f fanctrlNotes
- Ensure GPIO devices are passed through (
/dev/gpiochip*, optionally/dev/gpiomem). - If your Pi exposes GPIO on a different chip, set
gpio_chipin the config. - The status endpoint listens on
9101inside thefanctrlcontainer/network. - With the provided
docker-compose.yml, no host port is published by default.
Run the tool directly:
fanctrl [path/to/config.toml]Or via environment variables:
export FANCTRL_CONFIG="/path/to/config.toml"
fanctrlExample config.toml:
[fan]
# GPIO Settings
gpio_chip = "gpiochip0" # The GPIO character device
gpio_pin = 33 # The line offset (BCM pin number usually, NOT physical pin)
active_high = true # Set to false for active-low fans
# Temperature Thresholds (Celsius)
temp_on_c = 55.0
temp_off_c = 45.0
temp_path = "/sys/class/thermal/thermal_zone0/temp"
# Timing
poll_interval_s = 5.0 # How often to check temp
min_switch_s = 10.0 # Minimum time to stay in one state (hysteresis)
log_each_read = true # Log every poll or just changesImportant: This tool uses libgpiod, which exclusively uses chip line offsets (often matching BCM numbers on RPi), not physical board pin numbers.
- If you used
RPi.GPIO"BOARD" mode pin 33 previously, find the corresponding BCM number (e.g. BCM 13) and use that asgpio_pin.
By default, a status server runs on port 9101 inside the container:
GET http://fanctrl:9101/status (from another container on the same Docker network)
If you publish the port explicitly, host access is possible:
GET http://<host-ip>:9101/status
Returns:
{
"fan_on": true,
"temp_c": 56.2,
"temp_on_c": 55.0,
"temp_off_c": 45.0,
"version": "1.0.0"
}gpiod import failed: ensure the runtime has Pythongpiodbindings installed.GPIO chip not found: verify the correct/dev/gpiochipXdevice is passed through.