Compare commits

..

68 Commits

Author SHA1 Message Date
dc6dfc38ad usr/local/bin/custom/poe.sh aktualisiert 2025-10-12 19:33:11 +02:00
7561791a0b usr/local/bin/custom/poe.sh aktualisiert 2025-10-12 19:29:39 +02:00
c7266f73f3 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:21:06 +02:00
1a742c56c9 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:20:40 +02:00
9cdcc59038 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:20:11 +02:00
6523fdf675 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:19:42 +02:00
d846856cb4 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:19:02 +02:00
94142f9ead srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:17:25 +02:00
8c93eab3a5 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:17:09 +02:00
ab6ecd773d srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:16:51 +02:00
970f7dbeb5 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:16:34 +02:00
19f6390e22 srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:16:08 +02:00
268800d31a srv/poe_manager/static/css/style.css aktualisiert 2025-10-12 19:14:41 +02:00
7b0c847e67 srv/poe_manager/app.py aktualisiert 2025-10-12 18:25:08 +02:00
007d9f919f revert 7c045a65b2
revert srv/poe_manager/templates/index.html aktualisiert
2025-10-12 18:24:13 +02:00
7c045a65b2 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:23:57 +02:00
98023092a2 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:23:16 +02:00
28fa9087a4 srv/poe_manager/app.py aktualisiert 2025-10-12 18:22:37 +02:00
804753dde8 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:20:53 +02:00
eaf2e2f78e srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:18:45 +02:00
50d5f58af4 srv/poe_manager/app.py aktualisiert 2025-10-12 18:18:08 +02:00
12d2511695 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:13:29 +02:00
3bd2aca4f5 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:11:26 +02:00
b6e9ff3f98 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:09:58 +02:00
398629eaaa srv/poe_manager/templates/index.html aktualisiert 2025-10-12 18:09:04 +02:00
3eb78b46a7 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:51:21 +02:00
681888a36a DB 2025-10-12 17:45:24 +02:00
e8df4f937b srv/poe_manager/templates/users.html aktualisiert 2025-10-12 17:43:58 +02:00
29823bb4ef srv/poe_manager/templates/switche.html aktualisiert 2025-10-12 17:43:46 +02:00
a2920a98bd srv/poe_manager/templates/devices.html aktualisiert 2025-10-12 17:43:12 +02:00
7f0871fa64 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:33:30 +02:00
5ae38a9e20 srv/poe_manager/app.py aktualisiert 2025-10-12 17:33:05 +02:00
de23b132ed srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:31:41 +02:00
746972e321 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:31:08 +02:00
dfba8789a7 srv/poe_manager/app.py aktualisiert 2025-10-12 17:28:05 +02:00
194e3cfbf9 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:22:31 +02:00
4e10fe8ac0 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:16:23 +02:00
7ae131b5f4 srv/poe_manager/app.py aktualisiert 2025-10-12 17:12:40 +02:00
09ee8c245e srv/poe_manager/app.py aktualisiert 2025-10-12 17:09:43 +02:00
bb07a598d7 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:06:38 +02:00
4ef10fc39d srv/poe_manager/app.py aktualisiert 2025-10-12 17:06:04 +02:00
d73fda1935 revert aa2895ccf5
revert srv/poe_manager/templates/index.html aktualisiert
2025-10-12 17:03:52 +02:00
ad9dfcee7a revert 4ae255b67f
revert srv/poe_manager/templates/index.html aktualisiert
2025-10-12 17:03:00 +02:00
4ae255b67f srv/poe_manager/templates/index.html aktualisiert 2025-10-12 17:00:36 +02:00
aa2895ccf5 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 16:53:23 +02:00
7c81f3a299 srv/poe_manager/app.py aktualisiert 2025-10-12 16:52:24 +02:00
cef96cd7cd Devices DB 2025-10-12 16:39:42 +02:00
b4cc41c272 v1.2 2025-10-12 13:35:39 +00:00
0d784a0530 srv/poe_manager/app.py aktualisiert 2025-10-12 15:22:08 +02:00
34ef2c4e2a srv/poe_manager/app.py aktualisiert 2025-10-12 15:20:21 +02:00
a26a91ec5a srv/poe_manager/templates/index.html aktualisiert 2025-10-12 15:17:21 +02:00
ef548b9532 srv/poe_manager/templates/index.html aktualisiert 2025-10-12 15:14:10 +02:00
466f3615ed usr/local/bin/custom/poe.sh aktualisiert 2025-10-12 15:09:22 +02:00
3dc554e165 srv/poe_manager/app.py aktualisiert 2025-10-12 14:58:56 +02:00
78ed058d69 srv/poe_manager/generate_ips.py aktualisiert 2025-10-12 14:53:59 +02:00
d3f209941b srv/poe_manager/generate_ips.py aktualisiert 2025-10-12 14:51:08 +02:00
9f98d240ca srv/poe_manager/app.py aktualisiert 2025-10-12 14:43:58 +02:00
d88135fc12 srv/poe_manager/app.py aktualisiert 2025-10-12 14:37:34 +02:00
34eae3ca9d srv/poe_manager/app.py aktualisiert 2025-10-12 14:35:18 +02:00
f05936226d srv/poe_manager/app.py aktualisiert 2025-10-12 14:33:49 +02:00
d6c15d3d49 srv/poe_manager/app.py aktualisiert 2025-10-12 14:32:37 +02:00
7a1e9d88e5 srv/poe_manager/app.py aktualisiert 2025-10-12 14:29:02 +02:00
2f1c7b5653 srv/poe_manager/app.py aktualisiert 2025-10-12 14:10:10 +02:00
d1e43a1220 srv/poe_manager/app.py aktualisiert 2025-10-12 14:03:06 +02:00
fe921254c8 srv/poe_manager/app.py aktualisiert 2025-10-12 13:58:25 +02:00
f27e73ae72 srv/poe_manager/app.py aktualisiert 2025-10-12 13:55:40 +02:00
63b8e33434 srv/poe_manager/app.py aktualisiert 2025-10-12 13:50:22 +02:00
ad09b4f6a2 srv/poe_manager/templates/devices.html aktualisiert 2025-10-12 13:48:59 +02:00
9 changed files with 209 additions and 93 deletions

View File

@@ -3,8 +3,9 @@ from flask import Flask, render_template, request, redirect, url_for, flash
from flask_login import LoginManager, login_user, login_required, logout_user, UserMixin, current_user from flask_login import LoginManager, login_user, login_required, logout_user, UserMixin, current_user
from flask_bcrypt import Bcrypt from flask_bcrypt import Bcrypt
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
import sqlite3 from datetime import datetime
import glob, os, re from collections import defaultdict
import sqlite3, glob, os, re
app = Flask(__name__) app = Flask(__name__)
app.secret_key = "309cc4d5ce1fe7486ae25cbd232bbdfe6a72539c03f0127d372186dbdc0fc928" app.secret_key = "309cc4d5ce1fe7486ae25cbd232bbdfe6a72539c03f0127d372186dbdc0fc928"
@@ -81,6 +82,34 @@ def logout():
logout_user() logout_user()
return redirect(url_for('login')) return redirect(url_for('login'))
def get_last_seen(dev_name: str):
"""Letztes Mal, dass ein Gerät erreichbar war."""
log_files = glob.glob("/var/log/rpi-*.log")
if not log_files:
return None
latest_time = None
# alle Logs durchgehen
for logfile in sorted(log_files):
with open(logfile, "r") as f:
for line in f:
line = line.strip()
if f"{dev_name} ist erreichbar!" in line:
try:
ts_str = line.split(" ")[0] + " " + line.split(" ")[1] # "YYYY-MM-DD HH:MM:SS"
ts = datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S")
if latest_time is None or ts > latest_time:
latest_time = ts
except Exception:
continue
if latest_time:
datetime_str = latest_time.strftime("Zuletzt Online am %d.%m.%Y um %H:%M Uhr")
return f"{datetime_str}"
return None
@app.route("/") @app.route("/")
@login_required @login_required
def index(): def index():
@@ -90,26 +119,47 @@ def index():
c.execute("SELECT mac, name, is_active FROM devices ORDER BY name ASC") c.execute("SELECT mac, name, is_active FROM devices ORDER BY name ASC")
devices = c.fetchall() devices = c.fetchall()
# Intervall aus DB (Minuten) laden devices = sorted(devices, key=lambda d: d[1][0].upper())
grouped_devices = defaultdict(list)
for d in devices:
first_letter = d[1][:2].upper()
grouped_devices[first_letter].append(d)
# Intervall aus DB laden
c.execute("SELECT value FROM settings WHERE key='interval'") c.execute("SELECT value FROM settings WHERE key='interval'")
row = c.fetchone() row = c.fetchone()
interval = int(row[0]) if row else 5 # Default 5 Minuten interval = int(row[0]) if row else 5
conn.close() conn.close()
# Status aus letztem Log ermitteln # Status aus Logdateien ermitteln
import glob, os
log_files = glob.glob("/var/log/rpi-*.log") log_files = glob.glob("/var/log/rpi-*.log")
status_dict = {} status_dict = {}
last_seen_dict = {}
if log_files: if log_files:
latest_log = max(log_files, key=os.path.getctime) latest_log = max(log_files, key=os.path.getctime)
with open(latest_log, "r") as f: with open(latest_log, "r") as f:
for line in f: lines = f.readlines()
for dev in devices:
if dev[1] in line:
status_dict[dev[0]] = "online" if "erreichbar" in line else "offline"
# Template rendern mit Devices, Status und Intervall for dev in devices:
return render_template("index.html", devices=devices, status=status_dict, interval=interval) last_status = None
for line in reversed(lines):
if f"{dev[1]} ist erreichbar!" in line:
last_status = "online"
break
elif f"{dev[1]} ist nicht erreichbar!" in line:
last_status = "offline"
break
if last_status:
status_dict[dev[0]] = last_status
if last_status == "offline":
last_seen_dict[dev[0]] = get_last_seen(dev[1])
else:
status_dict[dev[0]] = "unbekannt"
return render_template("index.html", grouped_devices=grouped_devices, devices=devices, status=status_dict, last_seen=last_seen_dict, interval=interval)
@app.route("/settings", methods=["GET", "POST"]) @app.route("/settings", methods=["GET", "POST"])
@login_required @login_required
@@ -179,7 +229,7 @@ def devices():
switch_hostname = request.form.get('switch_hostname') switch_hostname = request.form.get('switch_hostname')
is_active = 1 if 'is_active' in request.form else 0 is_active = 1 if 'is_active' in request.form else 0
if not all([mac, rpi_ip, port, name, switch_hostname]): if not all([mac, rpi_ip, name]):
flash("Alle Felder müssen ausgefüllt sein!") flash("Alle Felder müssen ausgefüllt sein!")
return redirect(url_for('devices')) return redirect(url_for('devices'))
@@ -216,22 +266,22 @@ def devices():
old_mac = request.form.get('old_mac') old_mac = request.form.get('old_mac')
mac = request.form.get('mac') mac = request.form.get('mac')
rpi_ip = request.form.get('rpi_ip') rpi_ip = request.form.get('rpi_ip')
port = request.form.get('port') port = request.form.get('port') or None
name = request.form.get('name') name = request.form.get('name')
switch_hostname = request.form.get('switch_hostname') or None switch_hostname = request.form.get('switch_hostname') or None
is_active = 1 if 'is_active' in request.form else 0 is_active = 1 if 'is_active' in request.form else 0
# --- Prüfen, ob es sich um eine Switchnderung handelt --- # --- Nur Switch ändern ---
if mac is None and rpi_ip is None and port is None and name is None and switch_hostname: # Prüfen, ob nur das Switch-Feld gesendet wurde und die anderen Felder leer sind
# Nur den Switch ändern if 'switch_hostname' in request.form and all(not f for f in [mac, rpi_ip, name]):
device = conn.execute("SELECT name, switch_hostname FROM devices WHERE mac=?", (old_mac,)).fetchone() device = conn.execute("SELECT name, switch_hostname FROM devices WHERE mac=?", (old_mac,)).fetchone()
if not device: if not device:
flash("Gerät nicht gefunden!") flash("Gerät nicht gefunden!")
return redirect(url_for('devices')) return redirect(url_for('devices'))
old_switch = device['switch_hostname'] or "unbekannt" old_switch = device['switch_hostname'] or "unbekannt"
device_name = device['name'] device_name = device['name']
switch_hostname = request.form.get('switch_hostname') or ""
try: try:
conn.execute(""" conn.execute("""
@@ -240,27 +290,36 @@ def devices():
WHERE mac=? WHERE mac=?
""", (switch_hostname, old_mac)) """, (switch_hostname, old_mac))
conn.commit() conn.commit()
flash(f"Switch von {device_name} geändert: {old_switch}{switch_hostname}") flash(f"Switch von {device_name} geändert: {old_switch}{switch_hostname or 'Kein Switch'}")
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
flash("Fehler beim Ändern des Switch!") flash("Fehler beim Ändern des Switch!")
return redirect(url_for('devices')) return redirect(url_for('devices'))
if not all([old_mac, mac, rpi_ip, port, name]): # --- Normales Gerät bearbeiten ---
flash("Alle Felder müssen ausgefüllt sein!") # Pflichtfelder prüfen
return redirect(url_for('devices')) if not all([old_mac, mac, rpi_ip, name]):
flash("Felder 'MAC', 'IP' und 'Name' müssen ausgefüllt sein!")
return redirect(url_for('devices'))
# Prüfen auf doppelte IP außer das aktuelle Gerät # Prüfen auf doppelte IP außer das aktuelle Gerät
ip_device = conn.execute("SELECT name FROM devices WHERE rpi_ip=? AND mac<>?", (rpi_ip, old_mac)).fetchone() ip_device = conn.execute(
"SELECT name FROM devices WHERE rpi_ip=? AND mac<>?",
(rpi_ip, old_mac)
).fetchone()
if ip_device: if ip_device:
flash(f"IP-Adresse existiert bereits für Gerät '{ip_device['name']}'!") flash(f"IP-Adresse existiert bereits für Gerät '{ip_device['name']}'!")
return redirect(url_for('devices')) return redirect(url_for('devices'))
# Prüfen auf doppelte MAC außer das aktuelle Gerät # Prüfen auf doppelte MAC außer das aktuelle Gerät
mac_device = conn.execute("SELECT name FROM devices WHERE mac=? AND mac<>?", (mac, old_mac)).fetchone() mac_device = conn.execute(
"SELECT name FROM devices WHERE mac=? AND mac<>?",
(mac, old_mac)
).fetchone()
if mac_device: if mac_device:
flash(f"MAC-Adresse existiert bereits für Gerät '{mac_device['name']}'!") flash(f"MAC-Adresse existiert bereits für Gerät '{mac_device['name']}'!")
return redirect(url_for('devices')) return redirect(url_for('devices'))
# Update durchführen
try: try:
conn.execute(""" conn.execute("""
UPDATE devices UPDATE devices
@@ -272,6 +331,7 @@ def devices():
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
flash("Fehler beim Aktualisieren des Geräts!") flash("Fehler beim Aktualisieren des Geräts!")
# ----------------------- # -----------------------
# Gerät löschen # Gerät löschen
# ----------------------- # -----------------------
@@ -300,7 +360,7 @@ def devices():
switches.hostname AS switch_hostname switches.hostname AS switch_hostname
FROM devices FROM devices
LEFT JOIN switches ON devices.switch_hostname = switches.hostname LEFT JOIN switches ON devices.switch_hostname = switches.hostname
ORDER BY switches.hostname ASC ORDER BY switches.hostname ASC, devices.name ASC
""").fetchall() """).fetchall()
conn.close() conn.close()
@@ -434,36 +494,43 @@ def logs():
return render_template('logs.html', log_content=log_content, log_name=os.path.basename(latest_log), interval=interval) return render_template('logs.html', log_content=log_content, log_name=os.path.basename(latest_log), interval=interval)
def load_device_status(): def load_device_status():
"""
Liest das aktuellste rpi-Logfile und extrahiert den letzten Status jedes Devices.
Gibt ein Dictionary zurück: {Device-Name: 'online'/'offline'}
"""
status = {} status = {}
# Devices aus DB laden (Name → MAC)
conn = sqlite3.connect("sqlite.db")
conn.row_factory = sqlite3.Row
devices = conn.execute("SELECT mac, name FROM devices").fetchall()
name_to_mac = {d['name']: d['mac'] for d in devices}
conn.close()
# Logfile
log_files = glob.glob("/var/log/rpi-*.log") log_files = glob.glob("/var/log/rpi-*.log")
if not log_files: if not log_files:
return status return status
latest_log = max(log_files, key=os.path.getctime) latest_log = max(log_files, key=os.path.getctime)
# Jede Zeile des Logs lesen
with open(latest_log, "r") as f:
lines = f.readlines()
# Regex für Ping-Ergebnisse
online_re = re.compile(r"(\S+) ist erreichbar!") online_re = re.compile(r"(\S+) ist erreichbar!")
offline_re = re.compile(r"(\S+) ist nicht erreichbar!") offline_re = re.compile(r"(\S+) ist nicht erreichbar!")
for line in lines: with open(latest_log, "r") as f:
line = line.strip() for line in f:
m_online = online_re.search(line) line = line.strip()
m_offline = offline_re.search(line) m_online = online_re.search(line)
if m_online: m_offline = offline_re.search(line)
status[m_online.group(1)] = 'online' if m_online:
elif m_offline: name = m_online.group(1)
status[m_offline.group(1)] = 'offline' mac = name_to_mac.get(name)
if mac:
status[mac] = 'online'
elif m_offline:
name = m_offline.group(1)
mac = name_to_mac.get(name)
if mac:
status[mac] = 'offline'
return status return status
@app.route("/users", methods=["GET", "POST"]) @app.route("/users", methods=["GET", "POST"])
@login_required @login_required
def users(): def users():

View File

@@ -6,7 +6,10 @@ def generate_ips_list():
conn = sqlite3.connect(DB_PATH) conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row conn.row_factory = sqlite3.Row
switches = {row['hostname']: row for row in conn.execute("SELECT hostname, ip, username, password FROM switches")} switches = {row['hostname']: row for row in conn.execute(
"SELECT hostname, ip, username, password FROM switches"
)}
devices = conn.execute(""" devices = conn.execute("""
SELECT mac, rpi_ip, port, name, switch_hostname SELECT mac, rpi_ip, port, name, switch_hostname
FROM devices FROM devices
@@ -16,11 +19,17 @@ def generate_ips_list():
for dev in devices: for dev in devices:
switch = switches.get(dev['switch_hostname']) switch = switches.get(dev['switch_hostname'])
if not switch: if switch:
continue switch_ip = switch['ip']
password = decrypt_password(switch['password']) switch_user = switch['username']
switch_pass = decrypt_password(switch['password'])
else:
switch_ip = ""
switch_user = ""
switch_pass = ""
port = dev['port'] or "" port = dev['port'] or ""
print(f"{dev['rpi_ip']}:{dev['name']}:{switch['ip']}:{switch['hostname']}:{port}:{switch['username']}:{password}") print(f"{dev['rpi_ip']}:{dev['name']}:{switch_ip}:{dev['switch_hostname'] or 'kein Switch'}:{port}:{switch_user}:{switch_pass}")
if __name__ == "__main__": if __name__ == "__main__":
generate_ips_list() generate_ips_list()

Binary file not shown.

View File

@@ -87,25 +87,33 @@ pre {
#log-container { #log-container {
position: relative; position: relative;
height: calc(100vh - 150px); /* Füllt die Seite minus Header */ height: calc(100vh - 252px); /* Füllt die Seite minus Header */
padding: 1rem; /* optional, Abstand innen */
box-sizing: border-box; /* damit Padding nicht die Höhe sprengt */
display: flex;
flex-direction: column;
} }
#log-box { #log-box {
height: 97%; flex: 1 1 auto; /* füllt den Container, berücksichtigt Header/Padding */
overflow: auto; overflow: auto;
white-space: pre-wrap; white-space: pre-wrap;
font-family: monospace; font-family: monospace;
border: 1px solid #dee2e6; /* Bootstrap-like border */ border: 1px solid #dee2e6;
padding: 1rem;
background-color: #f8f9fa; background-color: #f8f9fa;
padding: 0.5rem;
} }
#refresh-timer { #refresh-timer {
position: absolute; position: absolute;
bottom: 10px; bottom: 18px;
right: 10px; right: 30px;
font-size: 0.9em; font-size: 0.9em;
color: gray; color: gray;
background-color: rgba(255,255,255,0.7); /* optional, besser lesbar */
padding: 2px 4px;
border-radius: 3px;
pointer-events: none; /* damit Scrollbar nicht blockiert wird */
} }
/* Tabelle anpassen */ /* Tabelle anpassen */

View File

@@ -8,7 +8,7 @@
<button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#deviceModal" onclick="openDeviceModal()">Neues Gerät hinzufügen</button> <button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#deviceModal" onclick="openDeviceModal()">Neues Gerät hinzufügen</button>
{% endif %} {% endif %}
<table class="table table-bordered"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th>Hostname</th> <th>Hostname</th>
@@ -89,11 +89,12 @@
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label>Port</label> <label>Port</label>
<input type="text" name="port" class="form-control" required placeholder="z.B. 3"> <input type="text" name="port" class="form-control" placeholder="z.B. 3">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label>Switch</label> <label>Switch (optional)</label>
<select name="switch_hostname" class="form-select" required> <select name="switch_hostname" class="form-select">
<option value="">Kein Switch</option>
{% for sw in switches %} {% for sw in switches %}
<option value="{{ sw['hostname'] }}">{{ sw['hostname'] }}</option> <option value="{{ sw['hostname'] }}">{{ sw['hostname'] }}</option>
{% endfor %} {% endfor %}
@@ -137,7 +138,7 @@
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label>Port</label> <label>Port</label>
<input type="text" name="port" id="edit_port" class="form-control" required placeholder="z.B. 3"> <input type="text" name="port" id="edit_port" class="form-control" placeholder="z.B. 3">
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@@ -161,7 +162,8 @@
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<select name="switch_hostname" id="switch_select" class="form-select" required> <select name="switch_hostname" id="switch_select" class="form-select">
<option value="">Kein Switch</option>
{% for sw in switches %} {% for sw in switches %}
<option value="{{ sw['hostname'] }}">{{ sw['hostname'] }}</option> <option value="{{ sw['hostname'] }}">{{ sw['hostname'] }}</option>
{% endfor %} {% endfor %}

View File

@@ -7,29 +7,37 @@
Nächste Prüfung in -- Sekunden Nächste Prüfung in -- Sekunden
</span> </span>
</h2> </h2>
<div class="row row-cols-1 row-cols-md-6 g-3"> <div class="row g-3">
{% for d in devices %} {% for letter, group in grouped_devices.items() %}
<div class="col"> <div class="row g-3">
<div class="card text-center p-2"> {% for d in group %}
<div class="card-header">{{ d[1] }}</div> <div class="col-6 col-md-4 col-lg-3 col-xl-2">
<div class="card-body"> <div class="card text-center p-2"
<span class="fw-bold" style="color: {% if last_seen.get(d[0]) %}
{% if d[2] == 0 %}gray title="{{ last_seen[d[0]] }}"
{% elif status[d[0]]=='online' %}green {% elif status[d[0]] == 'offline' %}
{% else %}red title="Noch nie online"
{% endif %};"> {% endif %}>
{% if d[2] == 0 %} <div class="card-header">{{ d[1] }}</div>
Deaktiviert <div class="card-body">
{% else %} <span class="fw-bold" style="color:
{% if status[d[0]] %}{{ status[d[0]]|capitalize }}{% else %}Unbekannt{% endif %} {% if d[2] == 0 %}gray
{% endif %} {% elif status[d[0]] == 'online' %}green
</span> {% else %}red
{% endif %};">
{% if d[2] == 0 %}
Deaktiviert
{% else %}
{% if status[d[0]] %}{{ status[d[0]]|capitalize }}{% else %}Unbekannt{% endif %}
{% endif %}
</span>
</div>
</div> </div>
</div> </div>
{% endfor %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const intervalMinutes = {{ interval | int }}; // aus DB const intervalMinutes = {{ interval | int }}; // aus DB

View File

@@ -5,7 +5,7 @@
<!-- Button zum Hinzufügen --> <!-- Button zum Hinzufügen -->
<button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#addSwitchModal">Neuen Switch hinzufügen</button> <button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#addSwitchModal">Neuen Switch hinzufügen</button>
<table class="table table-bordered"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th>Hostname</th> <th>Hostname</th>

View File

@@ -8,7 +8,7 @@
<button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#userModal" onclick="openUserModal()">Neuen Benutzer</button> <button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#userModal" onclick="openUserModal()">Neuen Benutzer</button>
{% endif %} {% endif %}
<table class="table table-bordered"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th class="col-ip">Username</th> <th class="col-ip">Username</th>

View File

@@ -87,23 +87,45 @@ EOF
echo "" > "$LOGFILE" echo "" > "$LOGFILE"
MAX_PARALLEL=10 # maximal gleichzeitig laufende Geräte
while true; do while true; do
echo "--------------------------------------------------------------------" >> "$LOGFILE" echo "--------------------------------------------------------------------" >> "$LOGFILE"
python3 /srv/poe_manager/generate_ips.py | while IFS=: read -r rpi_ip dev_name switch_ip switch_hostname switch_port switch_user switch_pass; do python3 /srv/poe_manager/generate_ips.py | while IFS=: read -r rpi_ip dev_name switch_ip switch_hostname switch_port switch_user switch_pass; do
if ping -c 1 -W 2 "$rpi_ip" &> /dev/null; then # Funktion für ein Gerät
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist erreichbar!" >> "$LOGFILE" check_device() {
else local rpi_ip="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist nicht erreichbar!" >> "$LOGFILE" local dev_name="$2"
local switch_ip="$3"
local switch_hostname="$4"
local switch_port="$5"
local switch_user="$6"
local switch_pass="$7"
# Nur PoE neu starten, wenn Port vorhanden ist if ping -c 4 -W 1 "$rpi_ip" &> /dev/null; then
if [ -n "$switch_port" ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist erreichbar!" >> "$LOGFILE"
disable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" else
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE auf Port $switch_port am Switch $switch_hostname deaktiviert." >> "$LOGFILE" echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist nicht erreichbar!" >> "$LOGFILE"
sleep 2
enable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" if [ -n "$switch_port" ] && [ "$switch_port" != "None" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE auf Port $switch_port am Switch $switch_hostname aktiviert." >> "$LOGFILE" disable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass"
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE auf Port $switch_port am Switch $switch_hostname deaktiviert." >> "$LOGFILE"
sleep 2
enable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass"
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE auf Port $switch_port am Switch $switch_hostname aktiviert." >> "$LOGFILE"
fi
fi fi
fi }
# Job in Hintergrund starten
check_device "$rpi_ip" "$dev_name" "$switch_ip" "$switch_hostname" "$switch_port" "$switch_user" "$switch_pass" &
# Limit auf MAX_PARALLEL Jobs
while [ "$(jobs -rp | wc -l)" -ge "$MAX_PARALLEL" ]; do
sleep 0.2
done
done done
wait # alle Hintergrundjobs beenden, bevor sleep
sleep "$SLEEP" sleep "$SLEEP"
done done