feat: OTA firmware update per device with auto config backup
- Upload .bin firmware files via web UI - Update individual devices (button per device in table) - Auto config backup before each update - Shows current version vs available firmware - Firmware file management (upload/delete) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if request.query_params.get('fw_uploaded') %}
|
||||
<div class="alert alert-success alert-dismissible fade show">
|
||||
<i class="bi bi-check-circle"></i> Firmware hochgeladen.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if request.query_params.get('fw_updated') %}
|
||||
<div class="alert alert-success alert-dismissible fade show">
|
||||
<i class="bi bi-check-circle"></i> Update auf <strong>{{ request.query_params.get('fw_updated') }}</strong> gestartet. Geraet startet neu.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if request.query_params.get('fw_error') %}
|
||||
<div class="alert alert-danger alert-dismissible fade show">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
{% if request.query_params.get('fw_error') == 'format' %}Nur .bin Dateien erlaubt.
|
||||
{% elif request.query_params.get('fw_error') == 'nofile' %}Firmware-Datei nicht gefunden.
|
||||
{% elif request.query_params.get('fw_error') == 'failed' %}Update fehlgeschlagen. Config-Backup wurde erstellt.
|
||||
{% else %}Firmware-Fehler.{% endif %}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
@@ -64,6 +87,12 @@
|
||||
<i class="bi bi-wifi"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% if firmwares %}
|
||||
<button class="btn btn-sm btn-outline-warning" title="Firmware Update"
|
||||
data-bs-toggle="modal" data-bs-target="#ota-{{ device.id }}">
|
||||
<i class="bi bi-cloud-arrow-up"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
|
||||
data-bs-target="#edit-{{ device.id }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
@@ -112,6 +141,40 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- OTA Update Modal -->
|
||||
{% if firmwares %}
|
||||
<div class="modal fade" id="ota-{{ device.id }}" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="post" action="/api/firmware/{{ device.id }}/update">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Firmware Update</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Update fuer <strong>{{ device.name or device.hostname }}</strong></p>
|
||||
<p><small class="text-muted">Aktuelle Version: {{ device.version or 'unbekannt' }}</small></p>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Firmware waehlen</label>
|
||||
<select name="firmware_file" class="form-select">
|
||||
{% for fw in firmwares %}
|
||||
<option value="{{ fw.name }}">{{ fw.name }} ({{ fw.size }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="alert alert-info mb-0">
|
||||
<small><i class="bi bi-info-circle"></i> Die Config wird vor dem Update automatisch gesichert. Das Tuerschild startet nach dem Update neu.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-warning"><i class="bi bi-cloud-arrow-up"></i> Update starten</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if not devices %}
|
||||
@@ -159,6 +222,39 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Firmware Upload -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-cloud-arrow-up"></i> Firmware</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/api/firmware/upload" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">WLED Firmware (.bin)</label>
|
||||
<input type="file" name="firmware" class="form-control form-control-sm" accept=".bin" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-primary btn-sm w-100">
|
||||
<i class="bi bi-upload"></i> Hochladen
|
||||
</button>
|
||||
</form>
|
||||
{% if firmwares %}
|
||||
<hr>
|
||||
<small class="text-muted d-block mb-2">Verfuegbare Firmwares:</small>
|
||||
{% for fw in firmwares %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<small class="font-monospace">{{ fw.name }} <span class="text-muted">({{ fw.size }})</span></small>
|
||||
<form method="post" action="/api/firmware/{{ fw.name }}/delete" class="d-inline"
|
||||
onsubmit="return confirm('Firmware loeschen?')">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger py-0">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user