ShineBridge v1.9.0 — Fokus auf Energiemanagement: Flash/OTA + Setup-Wizard ausgelagert

Repositionierung: ShineBridge macht ab jetzt eine Sache gut (Energiemanagement),
statt alles zu versuchen. Flash/OTA-Firmware-Update fürs ShineLAN-X und der
Ersteinrichtungs-Wizard sind kein Energiemanagement-Feature und wandern ins neue,
eigenständige Projekt 'growatt-diagnose-tool'.

- 4 Flash/OTA-Endpunkte aus main.py entfernt (probe/firmware/update/reboot)
- Flash-Tab, Flash-Panel und zugehöriges CSS/JS aus index.html entfernt
- Setup-Wizard-Overlay entfernt — Geräte werden über '+ Gerät hinzufügen' im
  Geräte-Tab angelegt (Button existierte bereits unabhängig vom Wizard)
- firmware/-Ordner (ShineLAN-X .bin-Dateien) entfernt, liegt jetzt im neuen Repo
- Dockerfile: COPY firmware/ entfernt
- Bugfix dabei gefunden: switchTab() referenzierte noch den entfernten
  'flash'-Tab per Index — hätte die Tab-Reihenfolge durcheinandergebracht

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
retr0
2026-08-25 13:14:12 +02:00
co-authored by Claude Sonnet 5
parent f5ed7c5df6
commit eb797ed8b7
8 changed files with 10 additions and 468 deletions
+2 -62
View File
@@ -1,7 +1,6 @@
import json
import logging
import os
import re
import threading
import time
import uuid
@@ -900,67 +899,8 @@ def api_spot_price():
log.warning("Spot-Price API Fehler: %s", e)
return jsonify({"ok": False, "data": _spot_cache.get("data", [])})
_IP_RE = re.compile(r'^(\d{1,3}\.){3}\d{1,3}$')
@app.get("/api/flash/probe")
def api_flash_probe():
import urllib.request as _ur
ip = request.args.get("ip", "").strip()
if not ip or not _IP_RE.match(ip):
return jsonify({"ota": False, "error": "invalid ip"}), 400
try:
with _ur.urlopen(f"http://{ip}/", timeout=5) as r:
data = json.loads(r.read().decode())
return jsonify({"ota": bool(data.get("ota")), "info": data})
except Exception:
return jsonify({"ota": False})
@app.get("/api/flash/firmware")
def api_flash_firmware():
fw_dir = "/firmware"
try:
files = sorted(f for f in os.listdir(fw_dir) if f.endswith(".bin"))
return jsonify({"files": files})
except Exception:
return jsonify({"files": []})
@app.post("/api/flash/update")
def api_flash_update():
import urllib.request as _ur
ip = request.args.get("ip", "").strip()
fw_name = request.args.get("fw", "").strip()
if not ip or not _IP_RE.match(ip):
return jsonify({"ok": False, "error": "invalid ip"}), 400
try:
if fw_name:
fw_path = os.path.join("/firmware", os.path.basename(fw_name))
with open(fw_path, "rb") as f:
data = f.read()
else:
data = request.get_data()
if len(data) < 256:
return jsonify({"ok": False, "error": "firmware too small"}), 400
req = _ur.Request(
f"http://{ip}/update", data=data, method="POST",
headers={"Content-Type": "application/octet-stream",
"Content-Length": str(len(data))})
with _ur.urlopen(req, timeout=90) as r:
return jsonify({"ok": True, "response": r.read().decode(errors="replace")})
except Exception as e:
return jsonify({"ok": False, "error": str(e)}), 502
@app.post("/api/flash/reboot")
def api_flash_reboot():
import urllib.request as _ur
ip = request.args.get("ip", "").strip()
if not ip or not _IP_RE.match(ip):
return jsonify({"ok": False, "error": "invalid ip"}), 400
try:
req = _ur.Request(f"http://{ip}/reboot", data=b"", method="POST")
with _ur.urlopen(req, timeout=10) as r:
return jsonify({"ok": True})
except Exception as e:
return jsonify({"ok": False, "error": str(e)}), 502
# Flash/OTA-Endpunkte ausgelagert nach growatt-diagnose-tool (v1.9.0) —
# ShineBridge fokussiert sich auf Energiemanagement, kein Firmware-Flashing mehr.
@app.get("/")
def index():