feat: background device status check every 240s instead of on page load
- Add online, leds, version, last_check columns to wled_devices table - Background job checks all WLED devices every 4 minutes - Initial check on startup - Devices page reads status from DB (instant page load) - 'Alle jetzt pruefen' button triggers manual check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a7efb12d5c
commit
e88ab3ab08
24
app.py
24
app.py
@ -38,9 +38,13 @@ async def lifespan(application: FastAPI):
|
||||
# Start background polling
|
||||
interval = int(database.get_setting("poll_interval", "15"))
|
||||
bg_scheduler.add_job(scheduler.poll_status, "interval", seconds=interval, id="poll")
|
||||
bg_scheduler.add_job(scheduler.check_devices, "interval", seconds=240, id="device_check")
|
||||
bg_scheduler.start()
|
||||
logger.info("Polling gestartet (Intervall: %ds)", interval)
|
||||
|
||||
# Initial device check
|
||||
scheduler.check_devices()
|
||||
|
||||
yield
|
||||
|
||||
bg_scheduler.shutdown()
|
||||
@ -195,8 +199,13 @@ async def devices_page(request: Request):
|
||||
devices = conn.execute(
|
||||
"SELECT * FROM wled_devices ORDER BY name, hostname"
|
||||
).fetchall()
|
||||
last_check_row = conn.execute(
|
||||
"SELECT MAX(last_check) as lc FROM wled_devices WHERE last_check IS NOT NULL"
|
||||
).fetchone()
|
||||
conn.close()
|
||||
return templates.TemplateResponse(*_ctx(request, "devices", devices=devices))
|
||||
last_check = last_check_row["lc"] if last_check_row and last_check_row["lc"] else None
|
||||
return templates.TemplateResponse(*_ctx(request, "devices",
|
||||
devices=devices, last_check=last_check))
|
||||
|
||||
|
||||
@app.post("/api/devices")
|
||||
@ -241,15 +250,10 @@ async def delete_device(device_id: int):
|
||||
return RedirectResponse("/devices", status_code=303)
|
||||
|
||||
|
||||
@app.get("/api/devices/{device_id}/status")
|
||||
async def device_status(device_id: int):
|
||||
from wled_client import get_wled_info
|
||||
conn = database.get_db()
|
||||
dev = conn.execute("SELECT hostname FROM wled_devices WHERE id=?", (device_id,)).fetchone()
|
||||
conn.close()
|
||||
if not dev:
|
||||
return {"online": False}
|
||||
return get_wled_info(dev["hostname"])
|
||||
@app.post("/api/devices/check-all")
|
||||
async def check_all_devices():
|
||||
scheduler.check_devices()
|
||||
return RedirectResponse("/devices", status_code=303)
|
||||
|
||||
|
||||
# ── Assignments ────────────────────────────────────────────────────────────
|
||||
|
||||
24
database.py
24
database.py
@ -32,7 +32,11 @@ def init_db():
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
hostname TEXT NOT NULL UNIQUE,
|
||||
name TEXT DEFAULT '',
|
||||
segment_count INTEGER NOT NULL DEFAULT 2
|
||||
segment_count INTEGER NOT NULL DEFAULT 2,
|
||||
online INTEGER DEFAULT 0,
|
||||
leds INTEGER DEFAULT 0,
|
||||
version TEXT DEFAULT '',
|
||||
last_check TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS assignments (
|
||||
@ -71,6 +75,24 @@ def init_db():
|
||||
);
|
||||
""")
|
||||
|
||||
# Migrate: add columns if missing (for existing databases)
|
||||
try:
|
||||
conn.execute("ALTER TABLE wled_devices ADD COLUMN online INTEGER DEFAULT 0")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
conn.execute("ALTER TABLE wled_devices ADD COLUMN leds INTEGER DEFAULT 0")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
conn.execute("ALTER TABLE wled_devices ADD COLUMN version TEXT DEFAULT ''")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
conn.execute("ALTER TABLE wled_devices ADD COLUMN last_check TIMESTAMP")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
defaults = {
|
||||
"azure_client_id": "",
|
||||
"azure_client_secret": "",
|
||||
|
||||
28
scheduler.py
28
scheduler.py
@ -3,7 +3,7 @@ import os
|
||||
from datetime import datetime
|
||||
|
||||
from database import get_db, get_setting, log_activity
|
||||
from wled_client import build_wled_url, call_wled
|
||||
from wled_client import build_wled_url, call_wled, get_wled_info
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -197,3 +197,29 @@ def _process_user(user, aida_lines, settings, conn):
|
||||
display_name, aida_status or "-", teams_status,
|
||||
"aktualisiert" if wled_changed else "nur Dashboard",
|
||||
)
|
||||
|
||||
|
||||
def check_devices():
|
||||
"""Check all WLED devices and store status in DB. Called periodically."""
|
||||
conn = get_db()
|
||||
try:
|
||||
devices = conn.execute("SELECT id, hostname FROM wled_devices").fetchall()
|
||||
for dev in devices:
|
||||
info = get_wled_info(dev["hostname"])
|
||||
conn.execute("""
|
||||
UPDATE wled_devices
|
||||
SET online=?, leds=?, version=?, last_check=?
|
||||
WHERE id=?
|
||||
""", (
|
||||
1 if info.get("online") else 0,
|
||||
info.get("leds", 0) if info.get("online") else 0,
|
||||
info.get("version", "") if info.get("online") else "",
|
||||
datetime.now(),
|
||||
dev["id"],
|
||||
))
|
||||
conn.commit()
|
||||
logger.info("Device-Check: %d Geraete geprueft", len(devices))
|
||||
except Exception as e:
|
||||
logger.error("Device-Check Fehler: %s", e)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@ -4,9 +4,16 @@
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="mb-0">WLED-Geraete</h4>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="checkAllDevices()">
|
||||
<i class="bi bi-arrow-clockwise"></i> Alle pruefen
|
||||
</button>
|
||||
<div>
|
||||
<form method="post" action="/api/devices/check-all" class="d-inline">
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-arrow-clockwise"></i> Alle jetzt pruefen
|
||||
</button>
|
||||
</form>
|
||||
{% if last_check %}
|
||||
<small class="text-muted ms-2">Letzte Pruefung: {{ last_check }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
@ -30,20 +37,24 @@
|
||||
{% for device in devices %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="status-dot" id="status-{{ device.id }}"
|
||||
style="background: #999;" title="Nicht geprueft"></span>
|
||||
{% if device.last_check %}
|
||||
{% if device.online %}
|
||||
<span class="status-dot" style="background: #28a745;" title="Online"></span>
|
||||
{% else %}
|
||||
<span class="status-dot" style="background: #dc3545;" title="Offline"></span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="status-dot" style="background: #999;" title="Noch nicht geprueft"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ device.hostname }}</code>
|
||||
</td>
|
||||
<td>{{ device.name }}</td>
|
||||
<td><span id="leds-{{ device.id }}" class="text-muted">-</span></td>
|
||||
<td><span id="version-{{ device.id }}" class="text-muted">-</span></td>
|
||||
<td>{{ device.leds if device.leds else '-' }}</td>
|
||||
<td>{{ device.version if device.version else '-' }}</td>
|
||||
<td>{{ device.segment_count }}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="checkDevice({{ device.id }})" title="Status pruefen">
|
||||
<i class="bi bi-wifi"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
|
||||
data-bs-target="#edit-{{ device.id }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
@ -106,6 +117,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-muted">
|
||||
<small>{{ devices|length }} Geraete | Status wird alle 4 Minuten automatisch geprueft</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
@ -139,44 +153,3 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
const deviceIds = [{% for d in devices %}{{ d.id }}{% if not loop.last %},{% endif %}{% endfor %}];
|
||||
|
||||
function checkDevice(id) {
|
||||
const dot = document.getElementById('status-' + id);
|
||||
const leds = document.getElementById('leds-' + id);
|
||||
const ver = document.getElementById('version-' + id);
|
||||
dot.style.background = '#ffc107';
|
||||
dot.title = 'Pruefe...';
|
||||
|
||||
fetch('/api/devices/' + id + '/status')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.online) {
|
||||
dot.style.background = '#28a745';
|
||||
dot.title = 'Online';
|
||||
leds.textContent = data.leds || '-';
|
||||
ver.textContent = data.version || '-';
|
||||
} else {
|
||||
dot.style.background = '#dc3545';
|
||||
dot.title = 'Offline';
|
||||
leds.textContent = '-';
|
||||
ver.textContent = '-';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dot.style.background = '#dc3545';
|
||||
dot.title = 'Fehler';
|
||||
});
|
||||
}
|
||||
|
||||
function checkAllDevices() {
|
||||
deviceIds.forEach(id => checkDevice(id));
|
||||
}
|
||||
|
||||
// Auto-check on page load
|
||||
checkAllDevices();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user