From a0e7e3f7a8f91f0251deb0322f53be4edef932d0 Mon Sep 17 00:00:00 2001 From: netbie Date: Fri, 5 Jun 2026 23:32:06 +0200 Subject: [PATCH] Initial commit --- README.md | 396 +++++++++++++++++++++++++++++++++++++++++++ app_linux.py | 121 +++++++++++++ app_windows.py | 121 +++++++++++++ templates/index.html | 386 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1024 insertions(+) create mode 100644 app_linux.py create mode 100644 app_windows.py create mode 100644 templates/index.html diff --git a/README.md b/README.md index 399f372..90ac96b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,398 @@ +<<<<<<< HEAD # wol-dashboard +======= +# โšก Wake-on-LAN Dashboard + +A modern, self-hosted web dashboard to remotely power on PCs via Magic Packet. +Built with Python (Flask) and a responsive HTML/CSS/JS frontend โ€” works on desktop and mobile. + +![Python](https://img.shields.io/badge/Python-3.10+-blue) +![Flask](https://img.shields.io/badge/Flask-3.0+-lightgrey) +![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux-green) + +--- + +## ๐Ÿ“‹ Features + +- โšก Wake up PCs remotely via Magic Packet (Wake-on-LAN) +- ๐ŸŸข Real-time Online/Offline status via Ping +- ๐Ÿ” Automatic network scan to discover devices (nmap) +- โž• Manually add devices (Name, MAC, IP) +- ๐Ÿ—‘ Remove devices from the list +- ๐Ÿ“ฑ Fully responsive โ€” works on mobile and desktop +- ๐Ÿ”„ Auto-refresh status every 30 seconds + +--- + +## ๐Ÿ—‚ Project Structure +wol-dashboard/ +โ”œโ”€โ”€ app.py # Flask backend for Windows +โ”œโ”€โ”€ app_linux.py # Flask backend for Linux +โ”œโ”€โ”€ devices.json # Device database (auto-created on first save) +โ”œโ”€โ”€ README.md +โ””โ”€โ”€ templates/ +โ””โ”€โ”€ index.html # Web frontend (shared for both platforms) + +--- + +## ๐Ÿ–ฅ๏ธ Installation โ€” Windows + +### Step 1 โ€” Install Python + +Open **PowerShell or CMD as Administrator** and run: + +```cmd +winget install -e --id Python.Python.3.13 +``` + +Close and reopen CMD after installation. Verify: + +```cmd +python --version +pip --version +``` + +> **Tip:** If `python` opens the Microsoft Store instead, go to: +> **Settings โ†’ Apps โ†’ Advanced App Settings โ†’ App Execution Aliases** +> and disable `python.exe` and `python3.exe`. + +--- + +### Step 2 โ€” Install nmap + +```cmd +winget install -e --id Insecure.Nmap +``` + +Close and reopen CMD. Verify: + +```cmd +nmap --version +``` + +--- + +### Step 3 โ€” Set up the project + +```cmd +cd C:\Users\\Desktop\wol-dashboard + +python -m venv venv +venv\Scripts\activate + +pip install flask wakeonlan +``` + +--- + +### Step 4 โ€” Create project files + +Make sure your folder contains these files: +wol-dashboard/ +โ”œโ”€โ”€ app.py +โ”œโ”€โ”€ devices.json โ† optional, auto-created +โ””โ”€โ”€ templates/ +โ””โ”€โ”€ index.html + +Create the templates folder if it doesn't exist: + +```cmd +mkdir templates +``` + +--- + +### Step 5 โ€” Start the server + +```cmd +venv\Scripts\activate +python app.py +``` + +You should see: +Running on http://0.0.0.0:5000 + + +Open in your browser: **http://localhost:5000** + +--- + +### Optional โ€” Allow port 5000 through Windows Firewall + +```cmd +netsh advfirewall firewall add rule name="Flask WOL" dir=in action=allow protocol=TCP localport=5000 +``` + +--- + +## ๐Ÿง Installation โ€” Linux (Debian / Ubuntu) + +### Step 1 โ€” Install system dependencies + +```bash +sudo apt update +sudo apt install python3 python3-venv python3-full nmap -y +``` + +Verify: + +```bash +python3 --version +nmap --version +``` + +--- + +### Step 2 โ€” Set up the project + +```bash +cd ~/DEV/wol-dashboard + +python3 -m venv venv +source venv/bin/activate + +pip install flask wakeonlan +``` + +--- + +### Step 3 โ€” Grant nmap network permissions (once) + +nmap needs elevated privileges for ARP scans. Instead of running as root, grant capabilities once: + +```bash +sudo setcap cap_net_raw,cap_net_admin+eip $(which nmap) +``` + +--- + +### Step 4 โ€” Create project files + +```bash +mkdir -p templates +# Place app_linux.py and templates/index.html in the folder +``` + +--- + +### Step 5 โ€” Start the server + +```bash +source venv/bin/activate +python app_linux.py +``` + +You should see: +Running on http://0.0.0.0:5000 + + +Open in your browser: **http://localhost:5000** + +--- + +### Optional โ€” Auto-start on boot with systemd + +Create a service file: + +```bash +sudo nano /etc/systemd/system/wol-dashboard.service +``` + +Paste the following (adjust paths): + +```ini +[Unit] +Description=Wake-on-LAN Dashboard +After=network.target + +[Service] +User=YOUR_USERNAME +WorkingDirectory=/home/YOUR_USERNAME/DEV/wol-dashboard +ExecStart=/home/YOUR_USERNAME/DEV/wol-dashboard/venv/bin/python app_linux.py +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +Enable and start: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable wol-dashboard +sudo systemctl start wol-dashboard +sudo systemctl status wol-dashboard +``` + +--- + +## ๐Ÿ“ฑ Accessing from Phone or Other Devices + +1. Find your server IP: + - **Windows:** Open CMD โ†’ `ipconfig` โ†’ look for **IPv4 Address** + - **Linux:** Run `hostname -I` + +2. Open on any device in the same network: +http://192.168.x.x:5000 + + +--- + +## โš™๏ธ Wake-on-LAN Setup on Target PCs + +For a PC to be woken up, it must be configured correctly. + +### BIOS / UEFI +- Enter BIOS on boot (usually `DEL`, `F2`, or `F12`) +- Find and enable: `Wake-on-LAN`, `Power On By PCI-E`, or `Resume By LAN` + +### Windows Target PC +1. Open **Device Manager** +2. Expand **Network Adapters** โ†’ right-click your adapter โ†’ **Properties** +3. Tab **Power Management**: +- โœ… Allow this device to wake the computer +4. Tab **Advanced**: +- Set `Wake on Magic Packet` โ†’ **Enabled** + +### Linux Target PC +```bash +sudo apt install ethtool + +# Check current WoL status +sudo ethtool eth0 | grep Wake + +# Enable WoL (replace eth0 with your adapter name) +sudo ethtool -s eth0 wol g +``` + +To make it permanent, add to `/etc/rc.local` or create a systemd service. + +Find your adapter name with: +```bash +ip link show +``` + +--- + +## ๐Ÿ—„ devices.json + +Devices are stored in `devices.json` in the project root. It is created automatically when you save the first device via the UI. + +You can also edit it manually: + +```json +[ +{ + "name": "Gaming-PC", + "mac": "AA:BB:CC:DD:EE:FF", + "ip": "192.168.1.100" +}, +{ + "name": "Work Laptop", + "mac": "11:22:33:44:55:66", + "ip": "192.168.1.105" +} +] +``` + +| Field | Required | Description | +|--------|----------|--------------------------------------------------| +| `name` | โœ… | Display name shown on the dashboard card | +| `mac` | โœ… | MAC address โ€” colons or dashes both accepted | +| `ip` | โŒ | IP address โ€” required for online status ping | + +--- + +## ๐Ÿ”„ Daily Usage + +### Windows +```cmd +cd C:\Users\\Desktop\wol-dashboard +venv\Scripts\activate +python app.py +``` + +### Linux +```bash +cd ~/DEV/wol-dashboard +source venv/bin/activate +python app_linux.py +``` + +Or create a startup script `start.sh`: + +```bash +#!/bin/bash +cd ~/DEV/wol-dashboard +source venv/bin/activate +python app_linux.py +``` + +```bash +chmod +x start.sh +./start.sh +``` + +--- + +## ๐Ÿ” Platform Differences + +| Feature | `app.py` (Windows) | `app_linux.py` (Linux) | +|----------------------|------------------------|------------------------------| +| Ping command | `ping -n 1 -w 500` | `ping -c 1 -W 1` | +| ARP command | `arp -a` | `arp -n` | +| MAC format in ARP | `AA-BB-CC-DD-EE-FF` | `aa:bb:cc:dd:ee:ff` | +| nmap line endings | `\r\n` | `\n` | +| Elevated rights | Run CMD as Admin | `setcap` once | +| Auto-start | Task Scheduler | systemd service | +| `index.html` | โœ… Shared | โœ… Shared | + +--- + +## ๐Ÿ“ฆ Dependencies + +| Package | Install via | Purpose | +|-------------|--------------|--------------------------| +| `flask` | pip | Web framework | +| `wakeonlan` | pip | Send Magic Packets | +| `nmap` | System | Network device scanning | + +Install Python packages: + +```bash +pip install flask wakeonlan +``` + +--- + +## ๐Ÿ”’ Security Notice + +This dashboard is designed for **trusted local networks only**. +It has no authentication built in. Do not expose port 5000 to the internet without: +- A reverse proxy (e.g. nginx) with HTTPS +- HTTP Basic Auth or a login system +- A VPN (e.g. WireGuard, Tailscale) + +--- + +## ๐Ÿ›  Troubleshooting + +| Problem | Solution | +|----------------------------------|--------------------------------------------------------------------------| +| `python` opens Microsoft Store | Disable App Execution Aliases in Windows Settings | +| `nmap` not found | Install nmap and restart terminal | +| `venv\Scripts\activate` fails | Run `python -m venv venv` first | +| PC does not wake up | Enable WoL in BIOS and network adapter settings | +| Status always shows Offline | Make sure IP is correct and target allows ping (check firewall) | +| Port 5000 blocked on Windows | Add firewall rule: `netsh advfirewall firewall add rule name="Flask WOL" dir=in action=allow protocol=TCP localport=5000` | +| Scan finds no devices | Run `setcap` on Linux or start CMD as Admin on Windows | +| Page not loading | Check Flask is running and no error in terminal | +| `externally-managed-environment` | Use a virtual environment: `python3 -m venv venv` | + +--- + +## ๐Ÿ“„ License + +MIT License โ€” free to use, modify and distribute. +>>>>>>> edfb358 (Initial commit) diff --git a/app_linux.py b/app_linux.py new file mode 100644 index 0000000..a21046d --- /dev/null +++ b/app_linux.py @@ -0,0 +1,121 @@ +from flask import Flask, render_template, request, jsonify +from wakeonlan import send_magic_packet +import json, os, subprocess, re, socket, concurrent.futures + +app = Flask(__name__) +DEVICES_FILE = "devices.json" + +def load_devices(): + if os.path.exists(DEVICES_FILE): + with open(DEVICES_FILE, "r") as f: + return json.load(f) + return [] + +def save_devices(devices): + with open(DEVICES_FILE, "w") as f: + json.dump(devices, f, indent=2) + +def ping(ip): + """Ping a device to check if it is online (Linux).""" + try: + result = subprocess.run( + ["ping", "-c", "1", "-W", "1", ip], + capture_output=True, timeout=3 + ) + return result.returncode == 0 + except: + return False + +@app.route("/") +def index(): + return render_template("index.html") + +@app.route("/wake/", methods=["POST"]) +def wake(mac): + try: + send_magic_packet(mac) + return jsonify({"status": "success", "message": "Magic Packet sent!"}) + except Exception as e: + return jsonify({"status": "error", "message": str(e)}), 500 + +@app.route("/devices", methods=["GET"]) +def get_devices(): + return jsonify(load_devices()) + +@app.route("/devices", methods=["POST"]) +def add_device(): + data = request.json + devices = load_devices() + devices.append({ + "name": data["name"], + "mac": data["mac"], + "ip": data.get("ip", "") + }) + save_devices(devices) + return jsonify({"status": "success"}) + +@app.route("/devices/", methods=["DELETE"]) +def delete_device(idx): + devices = load_devices() + if 0 <= idx < len(devices): + devices.pop(idx) + save_devices(devices) + return jsonify({"status": "success"}) + return jsonify({"status": "error"}), 404 + +@app.route("/status", methods=["GET"]) +def get_status(): + """Check online status of all devices in parallel.""" + devices = load_devices() + def check(d): + ip = d.get("ip", "") + if not ip: + return None + return ping(ip) + with concurrent.futures.ThreadPoolExecutor() as executor: + results = list(executor.map(check, devices)) + return jsonify(results) + +@app.route("/scan", methods=["POST"]) +def scan_network(): + """Scan the local network for active devices using nmap.""" + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + local_ip = s.getsockname()[0] + s.close() + subnet = ".".join(local_ip.split(".")[:3]) + ".0/24" + + result = subprocess.check_output( + f"nmap -sn {subnet} --system-dns", + shell=True, stderr=subprocess.DEVNULL + ).decode() + + arp_output = subprocess.check_output("arp -n", shell=True).decode() + + found = [] + nmap_hosts = re.findall(r"Nmap scan report for (.+)\nHost is up", result) + + for host in nmap_hosts: + ip_match = re.search(r"\((\d+\.\d+\.\d+\.\d+)\)", host) + ip = ip_match.group(1) if ip_match else host.strip() + name = re.sub(r"\s*\(.*?\)", "", host).strip() + + arp_match = re.search( + rf"{re.escape(ip)}\s+\S+\s+([0-9a-f]{{2}}(?::[0-9a-f]{{2}}){{5}})\s", + arp_output, re.IGNORECASE + ) + if arp_match: + mac = arp_match.group(1).upper() + found.append({ + "name": name if name != ip else f"Device ({ip})", + "mac": mac, + "ip": ip + }) + + return jsonify({"status": "success", "devices": found}) + except Exception as e: + return jsonify({"status": "error", "message": str(e)}), 500 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/app_windows.py b/app_windows.py new file mode 100644 index 0000000..d048a2a --- /dev/null +++ b/app_windows.py @@ -0,0 +1,121 @@ +from flask import Flask, render_template, request, jsonify +from wakeonlan import send_magic_packet +import json, os, subprocess, re, socket, concurrent.futures + +app = Flask(__name__) +DEVICES_FILE = "devices.json" + +def load_devices(): + if os.path.exists(DEVICES_FILE): + with open(DEVICES_FILE, "r") as f: + return json.load(f) + return [] + +def save_devices(devices): + with open(DEVICES_FILE, "w") as f: + json.dump(devices, f, indent=2) + +def ping(ip): + """Ping a device to check if it is online (Windows).""" + try: + result = subprocess.run( + ["ping", "-n", "1", "-w", "500", ip], + capture_output=True, timeout=2 + ) + return result.returncode == 0 + except: + return False + +@app.route("/") +def index(): + return render_template("index.html") + +@app.route("/wake/", methods=["POST"]) +def wake(mac): + try: + send_magic_packet(mac) + return jsonify({"status": "success", "message": "Magic Packet sent!"}) + except Exception as e: + return jsonify({"status": "error", "message": str(e)}), 500 + +@app.route("/devices", methods=["GET"]) +def get_devices(): + return jsonify(load_devices()) + +@app.route("/devices", methods=["POST"]) +def add_device(): + data = request.json + devices = load_devices() + devices.append({ + "name": data["name"], + "mac": data["mac"], + "ip": data.get("ip", "") + }) + save_devices(devices) + return jsonify({"status": "success"}) + +@app.route("/devices/", methods=["DELETE"]) +def delete_device(idx): + devices = load_devices() + if 0 <= idx < len(devices): + devices.pop(idx) + save_devices(devices) + return jsonify({"status": "success"}) + return jsonify({"status": "error"}), 404 + +@app.route("/status", methods=["GET"]) +def get_status(): + """Check online status of all devices in parallel.""" + devices = load_devices() + def check(d): + ip = d.get("ip", "") + if not ip: + return None + return ping(ip) + with concurrent.futures.ThreadPoolExecutor() as executor: + results = list(executor.map(check, devices)) + return jsonify(results) + +@app.route("/scan", methods=["POST"]) +def scan_network(): + """Scan the local network for active devices using nmap.""" + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + local_ip = s.getsockname()[0] + s.close() + subnet = ".".join(local_ip.split(".")[:3]) + ".0/24" + + result = subprocess.check_output( + f"nmap -sn {subnet}", + shell=True, stderr=subprocess.DEVNULL + ).decode(errors="ignore") + + arp_output = subprocess.check_output("arp -a", shell=True).decode(errors="ignore") + + found = [] + nmap_hosts = re.findall(r"Nmap scan report for (.+)\r?\nHost is up", result) + + for host in nmap_hosts: + ip_match = re.search(r"\((\d+\.\d+\.\d+\.\d+)\)", host) + ip = ip_match.group(1) if ip_match else host.strip() + name = re.sub(r"\s*\(.*?\)", "", host).strip() + + arp_match = re.search( + rf"{re.escape(ip)}\s+([0-9a-f]{{2}}(?:[:-][0-9a-f]{{2}}){{5}})", + arp_output, re.IGNORECASE + ) + if arp_match: + mac = arp_match.group(1).replace("-", ":").upper() + found.append({ + "name": name if name != ip else f"Device ({ip})", + "mac": mac, + "ip": ip + }) + + return jsonify({"status": "success", "devices": found}) + except Exception as e: + return jsonify({"status": "error", "message": str(e)}), 500 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..0e5eb31 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,386 @@ + + + + + + WoL Dashboard + + + + +
+ +

Wake-on-LAN

+

Wake up your devices remotely via Magic Packet

+
+ +
+
Total: 0
+
Online: 0
+
Offline: 0
+ +
+ +
+ + +
+
+ ๐Ÿ”

Scan Network

โ–ผ +
+
+

Finds all active devices on your local network. Takes 10โ€“30 seconds.

+ +
+
+
+
+ + +
+
+ โž•

Add Device

โ–ผ +
+
+ + + + +
+
+ + + +