Add comprehensive documentation, README, sensor server script, and systemd service
This commit is contained in:
parent
96abc9bff9
commit
8cf9957518
472
README.md
472
README.md
|
|
@ -1,156 +1,406 @@
|
|||
# IPMI Controller
|
||||
|
||||
Advanced IPMI fan control for Dell servers with web interface, HTTP sensor support, multiple fan curves, and persistent configuration.
|
||||
Advanced web-based fan control for Dell servers with IPMI support. Automatically adjust fan speeds based on temperature readings from IPMI sensors and optional HTTP lm-sensors endpoint.
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Author:** ImpulsiveFPS
|
||||
**License:** MIT
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- 🌡️ **Dual Sensor Support** - IPMI + HTTP (lm-sensors from Proxmox/host)
|
||||
- 🌬️ **Smart Fan Control** - Automatic curves, manual control, panic mode
|
||||
- 📊 **3 Preset Curves** - Balanced (default), Silent, Performance
|
||||
- 👥 **Fan Groups** - Organize and control fans individually or in groups
|
||||
- 🔍 **Fan Identify** - Visual fan identification
|
||||
- 🎨 **Themes** - Dark and Light mode
|
||||
- 📱 **Responsive Web UI** - Works on desktop and mobile
|
||||
- 🔌 **Public API** - For external integrations
|
||||
- 💾 **Persistent Settings** - Survives restarts and updates
|
||||
- 🌡️ **Temperature Monitoring** - Real-time CPU, inlet, exhaust, and PCIe temperature monitoring
|
||||
- 🌀 **Automatic Fan Control** - Dynamic fan speed adjustment based on customizable temperature curves
|
||||
- 📊 **Fan Groups** - Group fans together for unified control
|
||||
- 📈 **Custom Curves** - Create custom fan curves and assign them to specific fan groups
|
||||
- 🖥️ **HTTP Sensors** - Optional integration with lm-sensors for additional temperature data
|
||||
- 🎨 **Dark/Light Theme** - Choose your preferred visual style
|
||||
- 🔒 **Secure** - Built-in authentication and session management
|
||||
- 🚀 **Auto-Start** - Automatically resumes operation after system restart
|
||||
|
||||
## Quick Start
|
||||
---
|
||||
|
||||
### Automated Install (Recommended)
|
||||
## Table of Contents
|
||||
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Installation](#installation)
|
||||
- [IPMI Setup](#ipmi-setup)
|
||||
- [HTTP Sensors Setup (Optional)](#http-sensors-setup-optional)
|
||||
- [First Run](#first-run)
|
||||
- [Configuration](#configuration)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Support](#support)
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Hardware Requirements
|
||||
|
||||
- Dell server with IPMI support (iDRAC)
|
||||
- Network connectivity to the server's IPMI interface
|
||||
- A machine to run the IPMI Controller (can be the same server or a separate management host)
|
||||
|
||||
### Software Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- `ipmitool` (for IPMI communication)
|
||||
- Linux-based system (tested on Ubuntu/Debian)
|
||||
|
||||
### Installing ipmitool
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yourusername/ipmi-controller.git
|
||||
cd ipmi-controller
|
||||
chmod +x install.sh
|
||||
sudo ./install.sh
|
||||
# Ubuntu/Debian
|
||||
sudo apt update
|
||||
sudo apt install ipmitool
|
||||
|
||||
# Verify installation
|
||||
ipmitool -V
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install all dependencies
|
||||
- Create systemd service for auto-start
|
||||
- Set up persistent data directory
|
||||
- Start the controller on boot
|
||||
---
|
||||
|
||||
### Manual Install
|
||||
## Installation
|
||||
|
||||
### 1. Clone the Repository
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
sudo apt-get install -y ipmitool python3-pip
|
||||
pip3 install -r requirements.txt
|
||||
git clone https://github.com/ImpulsiveFPS/IPMI-Controller.git
|
||||
cd IPMI-Controller
|
||||
```
|
||||
|
||||
# Run
|
||||
### 2. Install Python Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Required packages:
|
||||
- fastapi
|
||||
- uvicorn
|
||||
- pydantic
|
||||
- requests
|
||||
|
||||
### 3. Start the Application
|
||||
|
||||
```bash
|
||||
python3 web_server.py
|
||||
```
|
||||
|
||||
Access at `http://your-server:8000`
|
||||
The web interface will be available at `http://localhost:8000`
|
||||
|
||||
## Initial Setup
|
||||
### 4. (Optional) Systemd Service
|
||||
|
||||
1. Complete the setup wizard (create admin + IPMI config)
|
||||
2. Login with your admin credentials
|
||||
3. (Optional) Set up HTTP sensor on your Proxmox host:
|
||||
```bash
|
||||
# On Proxmox server
|
||||
curl -O https://raw.githubusercontent.com/yourusername/ipmi-controller/main/setup-sensors-server.sh
|
||||
sudo ./setup-sensors-server.sh
|
||||
```
|
||||
4. Enable auto control and enjoy automatic fan management!
|
||||
|
||||
## Persistence
|
||||
|
||||
All your settings are automatically saved to `data/config.json`:
|
||||
|
||||
✅ IPMI configuration
|
||||
✅ HTTP sensor settings
|
||||
✅ Fan curves (Balanced, Silent, Performance)
|
||||
✅ User accounts
|
||||
✅ Theme preference
|
||||
✅ All control settings
|
||||
|
||||
**Backups:**
|
||||
```bash
|
||||
./backup.sh backup # Create backup
|
||||
./backup.sh list # List backups
|
||||
./backup.sh restore [file] # Restore from backup
|
||||
```
|
||||
|
||||
**Auto-backup via cron:**
|
||||
```bash
|
||||
# Add to crontab (keeps 30 days of backups)
|
||||
0 2 * * * /opt/ipmi-controller/backup.sh auto
|
||||
```
|
||||
|
||||
## Updating
|
||||
To run the controller as a system service:
|
||||
|
||||
```bash
|
||||
cd ipmi-controller
|
||||
git pull
|
||||
|
||||
# Settings are preserved automatically
|
||||
sudo systemctl restart ipmi-controller
|
||||
sudo cp ipmi-controller.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable ipmi-controller
|
||||
sudo systemctl start ipmi-controller
|
||||
```
|
||||
|
||||
## Fan Curves
|
||||
---
|
||||
|
||||
**Balanced** (Default) - Best for most users:
|
||||
```
|
||||
30°C → 10% | 40°C → 15% | 50°C → 30% | 60°C → 55% | 70°C → 85% | 80°C → 100%
|
||||
```
|
||||
## IPMI Setup
|
||||
|
||||
**Silent** - Noise-sensitive environments:
|
||||
```
|
||||
30°C → 5% | 40°C → 10% | 50°C → 15% | 60°C → 35% | 70°C → 70% | 80°C → 100%
|
||||
```
|
||||
### Step 1: Configure iDRAC/IPMI Network Settings
|
||||
|
||||
**Performance** - Heavy workloads:
|
||||
```
|
||||
30°C → 20% | 40°C → 35% | 50°C → 55% | 60°C → 85% | 70°C → 100%
|
||||
```
|
||||
1. Boot into your Dell server's BIOS/F2 setup
|
||||
2. Navigate to **iDRAC Settings** → **Network**
|
||||
3. Configure a static IP address for iDRAC (e.g., `192.168.5.191`)
|
||||
4. Save and exit
|
||||
|
||||
## Documentation
|
||||
Alternatively, configure via iDRAC web interface:
|
||||
1. Access iDRAC at its current IP
|
||||
2. Go to **iDRAC Settings** → **Network** → **IPV4 Settings**
|
||||
3. Set static IP, subnet mask, and gateway
|
||||
4. Apply changes
|
||||
|
||||
- [Setup Guide](SETUP.md) - Full installation instructions
|
||||
- [Persistence Guide](PERSISTENCE.md) - Backup and restore
|
||||
- [API Reference](API.md) - Public API documentation
|
||||
### Step 2: Create IPMI User
|
||||
|
||||
## Docker
|
||||
#### Method 1: Via iDRAC Web Interface (Recommended)
|
||||
|
||||
1. Log into iDRAC web interface
|
||||
2. Go to **iDRAC Settings** → **User Authentication** → **Local Users**
|
||||
3. Click **Add User** or edit an existing user
|
||||
4. Configure:
|
||||
- **User Name:** `root` (or your preferred username)
|
||||
- **Password:** Strong password
|
||||
- **IPMI LAN Privilege:** Administrator
|
||||
- **Enable IPMI over LAN:** ✓ Checked
|
||||
5. Save changes
|
||||
|
||||
#### Method 2: Via ipmitool (Local Access Required)
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
# List current users
|
||||
sudo ipmitool user list 1
|
||||
|
||||
# Create new user (ID 3)
|
||||
sudo ipmitool user set name 3 root
|
||||
sudo ipmitool user set password 3 YOUR_PASSWORD
|
||||
sudo ipmitool channel setaccess 1 3 callin=on ipmi=on link=on privilege=4
|
||||
sudo ipmitool user enable 3
|
||||
|
||||
# Verify
|
||||
sudo ipmitool user list 1
|
||||
```
|
||||
|
||||
Data persists in `./data` directory.
|
||||
|
||||
## Management Commands
|
||||
### Step 3: Enable IPMI over LAN
|
||||
|
||||
```bash
|
||||
# Status
|
||||
sudo systemctl status ipmi-controller
|
||||
# Enable IPMI over LAN
|
||||
sudo ipmitool lan set 1 ipsrc static
|
||||
sudo ipmitool lan set 1 ipaddr 192.168.5.191
|
||||
sudo ipmitool lan set 1 netmask 255.255.255.0
|
||||
sudo ipmitool lan set 1 defgw ipaddr 192.168.5.1
|
||||
sudo ipmitool lan set 1 access on
|
||||
|
||||
# Logs
|
||||
sudo journalctl -u ipmi-controller -f
|
||||
|
||||
# Restart
|
||||
sudo systemctl restart ipmi-controller
|
||||
|
||||
# Stop
|
||||
sudo systemctl stop ipmi-controller
|
||||
# Verify settings
|
||||
sudo ipmitool lan print 1
|
||||
```
|
||||
|
||||
### Step 4: Test IPMI Connection
|
||||
|
||||
From another machine on the network:
|
||||
|
||||
```bash
|
||||
ipmitool -I lanplus -H 192.168.5.191 -U root -P YOUR_PASSWORD chassis status
|
||||
```
|
||||
|
||||
If successful, you'll see server power status and other information.
|
||||
|
||||
---
|
||||
|
||||
## HTTP Sensors Setup (Optional)
|
||||
|
||||
HTTP sensors allow you to integrate additional temperature readings from lm-sensors running on your Proxmox host or other systems. This provides more granular CPU core temperatures.
|
||||
|
||||
### Step 1: Install lm-sensors on Remote Host
|
||||
|
||||
```bash
|
||||
# On Proxmox or target host
|
||||
sudo apt update
|
||||
sudo apt install lm-sensors
|
||||
|
||||
# Detect sensors
|
||||
sudo sensors-detect
|
||||
|
||||
# Test
|
||||
sensors
|
||||
```
|
||||
|
||||
### Step 2: Create HTTP Sensor Server Script
|
||||
|
||||
Create `sensor-server.py` on the remote host:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Simple HTTP server for lm-sensors data"""
|
||||
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
class SensorHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
if self.path == '/sensors':
|
||||
try:
|
||||
result = subprocess.run(['sensors', '-j'],
|
||||
capture_output=True, text=True)
|
||||
data = json.loads(result.stdout)
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(data).encode())
|
||||
except Exception as e:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'error': str(e)}).encode())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
pass # Suppress logs
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = HTTPServer(('0.0.0.0', 8888), SensorHandler)
|
||||
print("Sensor server running on port 8888")
|
||||
server.serve_forever()
|
||||
```
|
||||
|
||||
### Step 3: Run Sensor Server
|
||||
|
||||
```bash
|
||||
python3 sensor-server.py
|
||||
```
|
||||
|
||||
Or create a systemd service:
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/sensor-server.service
|
||||
[Unit]
|
||||
Description=LM Sensors HTTP Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /path/to/sensor-server.py
|
||||
Restart=always
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl enable sensor-server
|
||||
sudo systemctl start sensor-server
|
||||
```
|
||||
|
||||
### Step 4: Test HTTP Endpoint
|
||||
|
||||
```bash
|
||||
curl http://192.168.5.200:8888/sensors
|
||||
```
|
||||
|
||||
You should see JSON output with sensor readings.
|
||||
|
||||
### Step 5: Configure in IPMI Controller
|
||||
|
||||
1. During setup wizard (Step 3), check "Enable HTTP Sensor"
|
||||
2. Enter URL: `http://192.168.5.200:8888/sensors`
|
||||
3. Click "Test HTTP Sensor" to verify connection
|
||||
4. Complete setup
|
||||
|
||||
---
|
||||
|
||||
## First Run
|
||||
|
||||
### Initial Setup Wizard
|
||||
|
||||
1. **Step 1: Create Admin Account**
|
||||
- Username: Choose your admin username
|
||||
- Password: Minimum 6 characters
|
||||
- Confirm password
|
||||
|
||||
2. **Step 2: IPMI Configuration**
|
||||
- Host/IP: Your iDRAC IP (e.g., `192.168.5.191`)
|
||||
- Port: Usually `623`
|
||||
- Username: IPMI username (e.g., `root`)
|
||||
- Password: IPMI password
|
||||
- Click "Test Connection" to verify
|
||||
|
||||
3. **Step 3: HTTP Sensor (Optional)**
|
||||
- Enable HTTP Sensor: Check if using lm-sensors
|
||||
- URL: `http://your-server:8888/sensors`
|
||||
- Click "Test HTTP Sensor" to verify
|
||||
|
||||
4. **Enable Auto Fan Control**
|
||||
- Check "Enable Auto Fan Control" to start automatic control immediately
|
||||
- This activates the default "Balanced" fan curve
|
||||
|
||||
5. **Complete Setup**
|
||||
- Click "Complete Setup" to finish
|
||||
- You'll be logged in automatically
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Fan Curves
|
||||
|
||||
Fan curves define how fan speed responds to temperature:
|
||||
|
||||
1. Go to **Curves** tab
|
||||
2. Click **+ Add Curve**
|
||||
3. Configure:
|
||||
- **Curve Name:** e.g., "Silent", "Performance"
|
||||
- **Group:** (Optional) Assign to specific fan group
|
||||
- **Points:** Add temperature → speed mappings
|
||||
- Example: `30°C → 20%`, `50°C → 50%`, `70°C → 100%`
|
||||
4. Click **Save Curve**
|
||||
5. Click **Activate** to apply the curve
|
||||
|
||||
### Fan Groups
|
||||
|
||||
Groups allow unified control of multiple fans:
|
||||
|
||||
1. Go to **Fan Groups** tab
|
||||
2. Click **+ Create First Group**
|
||||
3. Enter group name
|
||||
4. Select fans to include
|
||||
5. Click **Save Group**
|
||||
6. Use **Set Speed** to control all fans in group
|
||||
|
||||
### Quick Controls
|
||||
|
||||
- **Start Auto:** Enable automatic fan control
|
||||
- **Stop Auto:** Return to manual/BIOS control
|
||||
- **Manual Speed Slider:** Set all fans to specific speed
|
||||
- **Identify Fan:** Flash individual fan to 100% for identification
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**IPMI Connection Failed:**
|
||||
- Verify IPMI IP, username, password
|
||||
- Test: `ipmitool -I lanplus -H <ip> -U <user> -P <pass> mc info`
|
||||
### Connection Issues
|
||||
|
||||
**No Temperature Data:**
|
||||
- Check HTTP sensor: `curl http://proxmox-ip:8888`
|
||||
- Verify `sensor_preference` is set to "auto" or "http"
|
||||
**"Not connected" status:**
|
||||
- Verify IPMI IP address is correct
|
||||
- Check network connectivity: `ping 192.168.5.191`
|
||||
- Test with ipmitool: `ipmitool -I lanplus -H 192.168.5.191 -U root chassis status`
|
||||
- Ensure IPMI user has Administrator privileges
|
||||
- Verify IPMI over LAN is enabled
|
||||
|
||||
**Settings Lost After Update:**
|
||||
- Ensure `data/` directory is not deleted
|
||||
- Check file permissions: `ls -la data/`
|
||||
**"Connection timeout":**
|
||||
- Check firewall rules on IPMI network
|
||||
- Verify port 623 is open
|
||||
- Try increasing timeout in settings
|
||||
|
||||
## License
|
||||
### Fan Control Not Working
|
||||
|
||||
MIT License
|
||||
**Fans not responding to speed changes:**
|
||||
- Some Dell servers require manual fan control to be enabled first
|
||||
- Check IPMI logs for errors
|
||||
- Verify fan IDs are correct
|
||||
- Try using individual fan controls to test
|
||||
|
||||
**"Manual fan control not supported":**
|
||||
- Some server models don't support external fan control
|
||||
- Check Dell documentation for your specific model
|
||||
- Try updating iDRAC firmware
|
||||
|
||||
### HTTP Sensor Issues
|
||||
|
||||
**"HTTP Sensor not working":**
|
||||
- Verify sensor server is running: `curl http://ip:8888/sensors`
|
||||
- Check firewall on sensor host
|
||||
- Ensure lm-sensors is properly configured
|
||||
- Verify URL format includes `http://` prefix
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- 🐛 **Report Bugs:** https://github.com/ImpulsiveFPS/IPMI-Controller/issues
|
||||
- 📁 **GitHub Repo:** https://github.com/ImpulsiveFPS/IPMI-Controller
|
||||
- ☕ **Support on Ko-fi:** https://ko-fi.com/impulsivefps
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Built with:
|
||||
- [FastAPI](https://fastapi.tiangolo.com/) - Web framework
|
||||
- [Heroicons](https://heroicons.com/) - Icons
|
||||
- [Lucide](https://lucide.dev/) - Additional icons
|
||||
|
||||
---
|
||||
|
||||
**IPMI Controller v1.0.0 - Built by ImpulsiveFPS**
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=IPMI Controller - Advanced Fan Control
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/ipmi-controller
|
||||
ExecStart=/usr/bin/python3 /opt/ipmi-controller/web_server.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
fastapi==0.109.0
|
||||
uvicorn[standard]==0.27.0
|
||||
pydantic==2.5.3
|
||||
pydantic-settings==2.1.0
|
||||
python-multipart==0.0.6
|
||||
paramiko==3.4.0
|
||||
fastapi>=0.100.0
|
||||
uvicorn[standard]>=0.23.0
|
||||
pydantic>=2.0.0
|
||||
requests>=2.31.0
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple HTTP server for lm-sensors data
|
||||
Run this on your Proxmox host or server with lm-sensors installed
|
||||
"""
|
||||
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
class SensorHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
if self.path == '/sensors':
|
||||
try:
|
||||
result = subprocess.run(['sensors', '-j'],
|
||||
capture_output=True, text=True)
|
||||
data = json.loads(result.stdout)
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(data).encode())
|
||||
except Exception as e:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'error': str(e)}).encode())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
pass # Suppress logs
|
||||
|
||||
if __name__ == '__main__':
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8888
|
||||
server = HTTPServer(('0.0.0.0', port), SensorHandler)
|
||||
print(f"Sensor server running on http://0.0.0.0:{port}/sensors")
|
||||
print("Press Ctrl+C to stop")
|
||||
try:
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
print("\nShutting down...")
|
||||
server.shutdown()
|
||||
208
server.log
208
server.log
|
|
@ -1,84 +1,134 @@
|
|||
INFO: Started server process [102941]
|
||||
INFO: Started server process [103738]
|
||||
INFO: Waiting for application startup.
|
||||
2026-02-20 22:26:01,858 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||
2026-02-20 22:26:01,859 - __main__ - INFO - Auto-starting fan control (enabled in config)
|
||||
2026-02-20 22:26:02,047 - fan_controller - INFO - Connected to IPMI at 192.168.5.191
|
||||
2026-02-20 22:26:02,048 - fan_controller - INFO - HTTP sensor client initialized for http://192.168.5.200:8888
|
||||
2026-02-20 22:26:02,049 - fan_controller - INFO - IPMI Controller service started
|
||||
2026-02-20 22:29:23,172 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||
2026-02-20 22:29:23,173 - __main__ - INFO - Auto-starting fan control (enabled in config)
|
||||
2026-02-20 22:29:23,328 - fan_controller - INFO - Connected to IPMI at 192.168.5.191
|
||||
2026-02-20 22:29:23,329 - fan_controller - INFO - HTTP sensor client initialized for http://192.168.5.200:8888
|
||||
2026-02-20 22:29:23,330 - fan_controller - INFO - IPMI Controller service started
|
||||
INFO: Application startup complete.
|
||||
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
|
||||
2026-02-20 22:26:02,254 - fan_controller - INFO - Manual fan control enabled
|
||||
INFO: 192.168.5.30:57456 - "GET /api/status HTTP/1.1" 401 Unauthorized
|
||||
INFO: 192.168.5.30:57456 - "GET /login HTTP/1.1" 200 OK
|
||||
2026-02-20 22:26:09,026 - fan_controller - INFO - Fan 0xff speed set to 14%
|
||||
2026-02-20 22:26:09,026 - fan_controller - INFO - All fans set to 14% (Temp 38.0°C)
|
||||
INFO: 192.168.5.30:55757 - "GET /login HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /favicon.ico HTTP/1.1" 200 OK
|
||||
2026-02-20 22:29:23,495 - fan_controller - INFO - Manual fan control enabled
|
||||
INFO: 192.168.5.30:54938 - "GET /api/status HTTP/1.1" 401 Unauthorized
|
||||
INFO: 192.168.5.30:54938 - "GET /login HTTP/1.1" 200 OK
|
||||
2026-02-20 22:29:29,168 - fan_controller - INFO - Fan 0xff speed set to 14%
|
||||
2026-02-20 22:29:29,168 - fan_controller - INFO - All fans set to 14% (Temp 38.0°C)
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:156: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
self._sessions[token] = (username, datetime.utcnow() + timedelta(days=7))
|
||||
INFO: 192.168.5.30:55757 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET / HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /favicon.ico HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 127.0.0.1:41586 - "GET /?theme=light HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:163: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
if datetime.utcnow() > expiry:
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:55757 - "GET /api/status HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:50571 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:50571 - "GET / HTTP/1.1" 500 Internal Server Error
|
||||
ERROR: Exception in ASGI application
|
||||
Traceback (most recent call last):
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
|
||||
result = await app( # type: ignore[func-returns-value]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
|
||||
return await self.app(scope, receive, send)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
|
||||
await super().__call__(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
|
||||
await self.middleware_stack(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
|
||||
raise exc
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
await self.app(scope, receive, _send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 83, in __call__
|
||||
await self.app(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
|
||||
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||
raise exc
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||
await app(scope, receive, sender)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 762, in __call__
|
||||
await self.middleware_stack(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 782, in app
|
||||
await route.handle(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 297, in handle
|
||||
await self.app(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 77, in app
|
||||
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||
raise exc
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||
await app(scope, receive, sender)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 72, in app
|
||||
response = await func(request)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 299, in app
|
||||
raise e
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 294, in app
|
||||
raw_response = await run_endpoint_function(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
|
||||
return await dependant.call(**values)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1592, in root
|
||||
return HTMLResponse(content=get_html(theme))
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1424, in get_html
|
||||
<style>body{padding-bottom:80px !important;}</style>
|
||||
^^^^^^^
|
||||
NameError: name 'padding' is not defined
|
||||
INFO: 192.168.5.30:64908 - "GET /http%3A//192.168.5.210%3A3000/favicon.ico HTTP/1.1" 404 Not Found
|
||||
INFO: 192.168.5.30:64908 - "GET /login HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:64908 - "GET /favicon.ico HTTP/1.1" 200 OK
|
||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:156: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||
self._sessions[token] = (username, datetime.utcnow() + timedelta(days=7))
|
||||
INFO: 192.168.5.30:52032 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||
INFO: 192.168.5.30:52032 - "GET / HTTP/1.1" 500 Internal Server Error
|
||||
ERROR: Exception in ASGI application
|
||||
Traceback (most recent call last):
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
|
||||
result = await app( # type: ignore[func-returns-value]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
|
||||
return await self.app(scope, receive, send)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
|
||||
await super().__call__(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
|
||||
await self.middleware_stack(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
|
||||
raise exc
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||
await self.app(scope, receive, _send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 83, in __call__
|
||||
await self.app(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
|
||||
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||
raise exc
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||
await app(scope, receive, sender)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 762, in __call__
|
||||
await self.middleware_stack(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 782, in app
|
||||
await route.handle(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 297, in handle
|
||||
await self.app(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 77, in app
|
||||
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||
raise exc
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||
await app(scope, receive, sender)
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 72, in app
|
||||
response = await func(request)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 299, in app
|
||||
raise e
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 294, in app
|
||||
raw_response = await run_endpoint_function(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
|
||||
return await dependant.call(**values)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1592, in root
|
||||
return HTMLResponse(content=get_html(theme))
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1424, in get_html
|
||||
<style>body{padding-bottom:80px !important;}</style>
|
||||
^^^^^^^
|
||||
NameError: name 'padding' is not defined
|
||||
INFO: 192.168.5.30:51945 - "GET /http%3A//192.168.5.210%3A3000/favicon.ico HTTP/1.1" 404 Not Found
|
||||
|
|
|
|||
Loading…
Reference in New Issue