diff --git a/README.md b/README.md index fc25a92..799019e 100644 --- a/README.md +++ b/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 -U -P 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** \ No newline at end of file diff --git a/ipmi-controller.service b/ipmi-controller.service new file mode 100644 index 0000000..a001873 --- /dev/null +++ b/ipmi-controller.service @@ -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 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b4b1ffa..93ed10b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 \ No newline at end of file diff --git a/sensor-server.py b/sensor-server.py new file mode 100644 index 0000000..806731e --- /dev/null +++ b/sensor-server.py @@ -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() \ No newline at end of file diff --git a/server.log b/server.log index 2f0a78c..000bc11 100644 --- a/server.log +++ b/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 + + ^^^^^^^ +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 + + ^^^^^^^ +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