Add comprehensive documentation, README, sensor server script, and systemd service

This commit is contained in:
devmatrix 2026-02-20 22:33:58 +00:00
parent 96abc9bff9
commit 8cf9957518
5 changed files with 553 additions and 196 deletions

472
README.md
View File

@ -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**

14
ipmi-controller.service Normal file
View File

@ -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

View File

@ -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

45
sensor-server.py Normal file
View File

@ -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()

View File

@ -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