Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3318d1344d | ||
|
|
c924955be9 | ||
|
|
89250e16bf | ||
|
|
789e25967a | ||
|
|
1c787349e3 | ||
|
|
1b78857b3d |
@@ -42,7 +42,7 @@ Wechselrichter / Energiezähler
|
||||
- **Sparklines** — Live-Graphen der letzten 5 Minuten pro Sensor
|
||||
- **EMS-Controller** — PV-Überschussladen der Kathrein Wallbox, Zwangsladen-Fallback mit konfigurierbarem Max-Ladestrom
|
||||
- **Überschuss-Geräte** — Zigbee2MQTT-Geräte bei PV-Überschuss automatisch einschalten
|
||||
- **Konfig-Export/Import** — JSON im Einstellungen-Tab
|
||||
- **Konfig-Export/Import** — JSON im Einstellungen-Tab, zusätzlich automatisches tägliches Backup nach `/share/shinebridge-backups/` (übersteht auch Add-on-Uninstall+Reinstall, 14 Tage Aufbewahrung)
|
||||
- **Port-Sicherheit** — `/api/*` nur über HAOS-Ingress erreichbar, kein Direktzugriff von außen
|
||||
- **Kein Cloud-Zwang** — vollständig lokal
|
||||
|
||||
|
||||
+28
-54
@@ -1,73 +1,39 @@
|
||||
# ShineBridge — Roadmap
|
||||
|
||||
> Seit v1.9.0 reines Energiemanagement-Tool. Hardware-/Firmware-Roadmap (ShineLAN-X, ShineWifi-X, shinediag) liegt jetzt im separaten Repo `growatt-diagnose-tool`.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- [ ] Firma: Wallbox lädt in einem ~6,5-Minuten-Zyklus (Laden→Abgeschlossen→Pausiert→Laden) statt durchzuladen — Root Cause noch offen, live-Diagnose folgt beim nächsten Ladevorgang
|
||||
- [ ] Growatt-only: Netzbezug nicht direkt messbar — nur Einspeisung via `power_to_grid`
|
||||
- [ ] Kathrein EVSE-Register 0x0060 (Charging-State) nur lesbar wenn Auto angeschlossen
|
||||
- [ ] NuttX OTA noch nicht auf echter Hardware getestet
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — ShineBridge Add-on
|
||||
## Nächste Schritte (Stand 2026-08-26)
|
||||
|
||||
- [x] Persistente History — SQLite `/data/history.db`, 7 Tage Retention (v1.3.0)
|
||||
- [x] Konfig-Export/Import — JSON im Einstellungen-Tab (v1.3.0)
|
||||
- [x] Goodwe GW10KN-ET — via goodwe-Bibliothek UDP/8899, 39 Sensoren (v1.4.0)
|
||||
- [x] Kathrein Wallbox — Modbus TCP, 18 Sensoren + EMS-Controller (v1.5.0)
|
||||
- [x] EMS-Controller — PV-Überschussladen + Zwangsladen-Fallback (v1.5.0)
|
||||
- [x] EMS Web UI — Konfiguration direkt im Gerätedialog (v1.5.1)
|
||||
- [x] grid_power Sensor — positiv = Netzbezug, negativ = Einspeisung (v1.5.2)
|
||||
- [x] Energie-Dashboard — HA-Style SVG-Flussdiagramm, animateMotion (v1.6.0)
|
||||
- [x] Abrechnungsperiode + Strompreise — kWh + Kosten je Periode (v1.7.0)
|
||||
- [x] Flexibler Tarif — EPEX Spot (aWATTar DE/AT) + Aufschlag (v1.8.16)
|
||||
- [x] Finanzen-Tab — Eigenversorgung, Spot-Vergleich, Abschlags-Tracker (v1.8.14–16)
|
||||
- [x] Überschuss-Geräte — Zigbee2MQTT-Geräte bei PV-Überschuss steuern (v1.8.8–13)
|
||||
- [x] Setup-Wizard — geführte Ersteinrichtung (MQTT + erster Wechselrichter) (v1.8.18)
|
||||
- [x] Flash-Wizard — ShineLAN-X direkt aus Web UI flashen, OTA + ST-Link-Anleitung (v1.8.18)
|
||||
- [x] Port-Sicherheit — `/api/*` nur über HAOS-Ingress erreichbar (v1.8.18)
|
||||
- [x] MQTT rc=5 — Reconnect-Loop verhindert, Fehlermeldung im UI (v1.8.18)
|
||||
- [x] Mobile-Layout — horizontaler Overflow behoben, Tabs scrollen (v1.8.19)
|
||||
- [ ] Hausverbrauch als berechneter Sensor: `PV + Netzbezug − Einspeisung − Bat_Laden + Bat_Entladen`
|
||||
- [ ] Weitere Growatt-Modelle — MOD 10000, SPH 10000 etc.
|
||||
- [ ] Ladekosten-Berechnung: kWh je Session × Arbeitspreis
|
||||
Reihenfolge nach Aufwand/Abhängigkeit, nicht nach Wichtigkeit.
|
||||
|
||||
---
|
||||
1. ~~**Automatischer täglicher Config-Export**~~ — erledigt (v1.11.0): täglicher Export nach `/share/shinebridge-backups/`, 14 Tage Aufbewahrung, sofort beim Start plus alle 24h.
|
||||
2. **Discord-Alerts** — bei Zwangsladen-Start, Fehlerzuständen (unbekannter Ladezustand), besonders günstigen Spot-Preisfenstern. Nutzt den bestehenden Homelab-Discord-Bot.
|
||||
3. **Sessions pro Fahrzeug trennen** — falls mehrere Fahrer/Autos an einer Wallbox laden, aktuell nur eine Gesamt-Statistik.
|
||||
4. **Lastspitzenkappung (Peak Shaving)** — Ladestrom drosseln, wenn Netzbezug (mehrere Autos + Haushalt gleichzeitig) einen Grenzwert überschreitet. Eigenes Sicherheits-Design nötig (was passiert bei Messfehler/Ausfall — nie ungebremst weiterladen).
|
||||
5. **Prognosebasiertes Laden** — PV-Wetterprognose (z.B. Open-Meteo/Solcast) + Spot-Preis kombinieren, damit Zwangsladen nicht stur nach Timeout entscheidet, sondern z.B. "morgen wird's sonnig, noch etwas warten" berücksichtigt. Größter Umfang, externe API-Anbindung nötig.
|
||||
6. **Chart-Feintuning** — läuft nebenbei mit, keine feste Priorität (z.B. Live-Kosten-Ticker statt nur Rückblick).
|
||||
7. **Auto-Cleanup verwaister MQTT-Geräte** — wenn ein Gerät aus ShineBridge entfernt wird, dessen MQTT-Discovery-Topics aktiv abmelden statt sie als tote HA-Geräte-Leichen zurückzulassen (siehe Fund unten).
|
||||
|
||||
## Phase 3 — Hardware-Erweiterungen
|
||||
**Zurückgestellt:** Mehrere Wallboxen fair verteilen — bei der Duplikat-Prüfung (siehe unten) stellte sich heraus, dass es aktuell nur eine echte Wallbox gibt. Erst relevant, wenn wirklich eine zweite dazukommt.
|
||||
|
||||
### ShineLAN-X (STM32F103RC)
|
||||
- [x] dapboot + NuttX produktiv (v1.0.0)
|
||||
- [x] OTA HTTP-Server implementiert — Zwei-Phasen Flash (Stage 1: Staging, Stage 2: SRAM) (v1.8.18)
|
||||
- [ ] OTA auf echter Hardware validieren
|
||||
- [ ] Weitere Firmware-Varianten (modellspezifische Builds)
|
||||
### Fund: Wallbox-Duplikat war kein echtes Duplikat
|
||||
|
||||
### Goodwe WiFi-Stick (WIFILAN_2.0 / HF-LPT230)
|
||||
- [x] UDP/8899 Protokoll via goodwe-Bibliothek integriert (v1.4.0)
|
||||
- [ ] JTAG-Analyse (TP15/TP16/TP17) — eigene Firmware wenn JTAG-eFuse nicht gesetzt
|
||||
- [ ] RS485-Direktzugriff via 18-Pin-Stecker
|
||||
Firma-HA zeigte drei Wallbox-Geräte (`wallbox_ladezustand`/`_2`/`_3`). Per `device_id()`/`device_attr()`-Template-Check verifiziert: drei unterschiedliche ShineBridge-`inv_id`s, aber nur **eine** davon (`1c042e72`) hat noch aktive Poll-Loop-Logeinträge — die anderen zwei sind verwaiste Reste aus früheren Config-Änderungen (0 Log-Treffer). Kein Konflikt-Risiko (keine drei gleichzeitigen Poller auf demselben physischen Gerät), nur unaufgeräumte Geräte-Registry. Manuell entfernbar unter Einstellungen → Geräte & Dienste → Geräte.
|
||||
|
||||
### ShineWifi-X als ShineBridge-Gateway
|
||||
- [ ] Schlankes ESP8266-Projekt: RS485 → Modbus TCP Bridge
|
||||
- [ ] Im ShineBridge Web UI verwaltbar (statt separater ESPHome-YAMLs)
|
||||
## Erledigt vor dieser Roadmap (Kontext)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Energiebilanz & Dashboard
|
||||
|
||||
- [x] Aggregat-Gerät „ShineBridge Gesamt" (v1.2.0)
|
||||
- [x] EPEX Spot-Chart — 24h-Balkendiagramm im Energie-Dashboard (v1.8.16)
|
||||
- [x] Eigenversorgungskarte — kWh + € gespart (v1.8.8)
|
||||
- [ ] Virtuelle MQTT-Sensoren für HA Energie-Dashboard automatisch anlegen
|
||||
- [ ] Prognose-gestütztes Laden — Spot-Preise × PV-Forecast für optimale Ladezeiten
|
||||
|
||||
---
|
||||
|
||||
## ShineDiag — Vor-Ort-Diagnose
|
||||
|
||||
- [x] WiFi-Hotspot „ShineDiag" (Pi 3B), http://10.0.1.1 (v2.0)
|
||||
- [x] Alle Sensoren, Rohdaten-Register-Dump, JSON-Export (v2.0)
|
||||
- [ ] Goodwe-Unterstützung (UDP/8899)
|
||||
- [ ] Kathrein Wallbox (EMS-Status, Ladezustand)
|
||||
- [x] `growatt-diagnose-tool`-Split — Flash/OTA, Setup-Wizard, ShineLAN-X, ShineWifi-X, shinediag ausgelagert (v1.9.0)
|
||||
- [x] Finanzen-Chart komplett überarbeitet — Wochen-Aggregation, responsiv, Beispiel-Vorschau (v1.9.1–1.9.6)
|
||||
- [x] ESPHome-Geräte-Anbindung (Long-Lived Token statt Supervisor-Proxy, `host_network` ist damit inkompatibel) (v1.10.0–1.10.3)
|
||||
- [x] Firma-EMS-Fixes: konfigurierbarer Max-Ladestrom, Zwangsladen-Retry, Fehlerzustand-Handling (v1.8.31)
|
||||
|
||||
---
|
||||
|
||||
@@ -101,3 +67,11 @@
|
||||
| v1.8.24 | 2026-05-07 | Fix: WR/Wallbox-Offline-Flapping — erst nach 3 aufeinanderfolgenden Lesefehlern offline schalten |
|
||||
| v1.8.25 | 2026-05-07 | Fix: TCP-Timeout auf max. 40% des Poll-Intervalls begrenzt (Wallbox + Modbus) — verhindert Poll-Überlappung |
|
||||
| v1.8.26 | 2026-05-07 | Fix: EMS-Countdown läuft ohne Auto — Kathrein 0x0060 IllegalAddress = kein Fahrzeug (default war fälschlich 1=Connected) |
|
||||
| v1.8.29 | 2026-05-26 | Fix: Port 8099 bindet auf 127.0.0.1 statt 0.0.0.0 |
|
||||
| v1.8.30 | 2026-05-28 | Fix: Ingress-Fix — Flask wieder auf 0.0.0.0 |
|
||||
| v1.8.31 | 2026-08-25 | Fix: Firma-EMS — konfigurierbarer Max-Ladestrom statt hardcoded 32A, Zwangsladen-Retry, Fehlerzustand pausiert statt zurückzusetzen |
|
||||
| v1.9.0 | 2026-08-25 | Repositionierung: Flash/OTA, Setup-Wizard, ShineLAN-X, ShineWifi-X, shinediag ausgelagert nach `growatt-diagnose-tool` |
|
||||
| v1.9.1–1.9.6 | 2026-08-25 | Finanzen-Chart: unlesbar bei vielen Tagen behoben, Wochen-Aggregation, abgerundete Balken, responsiv über gemessene Breite, Beispiel-Diagramm im Leerzustand |
|
||||
| v1.10.0 | 2026-08-25 | Feature: Geräte-Typ "Growatt ShineWifi-X (ESPHome)" — liest Werte aus bestehenden HA-Entities |
|
||||
| v1.10.1–1.10.2 | 2026-08-25 | Fix: Config-Import mit ha_entity-Geräten; Diagnose-Log für SUPERVISOR_TOKEN |
|
||||
| v1.10.3 | 2026-08-25 | Fix: HA-Entity-Anbindung auf Long-Lived Access Token umgestellt (host_network + Supervisor-Token inkompatibel) |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ShineBridge
|
||||
version: "1.10.0"
|
||||
version: "1.11.0"
|
||||
slug: shinebridge
|
||||
description: Energiemanagement für Growatt/Goodwe-Wechselrichter — PV-Überschussladen, Wallbox-EMS, Tarifvergleich, Zigbee2MQTT-Überschussgeräte
|
||||
url: https://gitea.bitfire.work/retr0/shinebridge
|
||||
@@ -13,7 +13,8 @@ host_network: true
|
||||
ingress: true
|
||||
ingress_port: 8099
|
||||
panel_icon: mdi:solar-power
|
||||
homeassistant_api: true
|
||||
map:
|
||||
- share:rw
|
||||
options:
|
||||
mqtt_broker: "core-mosquitto"
|
||||
mqtt_port: 1883
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from typing import Dict, Optional
|
||||
@@ -29,22 +28,24 @@ class HAEntityReader:
|
||||
(z.B. ein per ESPHome direkt in HA eingebundener ShineWifi-X-Stick) statt sie
|
||||
selbst per Modbus/UDP zu pollen.
|
||||
|
||||
Braucht 'homeassistant_api: true' in config.yaml — HAOS injiziert dann
|
||||
SUPERVISOR_TOKEN und die interne API ist unter http://supervisor/core/api
|
||||
erreichbar (kein manuell erstelltes Long-Lived Token nötig)."""
|
||||
Nutzt bewusst ein normales Long-Lived Access Token gegen die HA-REST-API statt
|
||||
den Supervisor-Proxy (http://supervisor/core/api): ShineBridge läuft mit
|
||||
host_network: true, dadurch ist der Container NICHT im internen Supervisor-
|
||||
Netzwerk und bekommt keinen SUPERVISOR_TOKEN injiziert. Der direkte API-Weg
|
||||
funktioniert trotzdem, weil HA Core dank host_network ganz normal unter
|
||||
localhost erreichbar ist."""
|
||||
|
||||
BASE_URL = "http://supervisor/core/api"
|
||||
|
||||
def __init__(self, entity_prefix: str):
|
||||
def __init__(self, entity_prefix: str, base_url: str, token: str):
|
||||
self.entity_prefix = entity_prefix
|
||||
self.token = os.environ.get("SUPERVISOR_TOKEN", "")
|
||||
self.base_url = (base_url or "http://localhost:8123").rstrip("/")
|
||||
self.token = token
|
||||
|
||||
def read(self, inverter=None) -> Optional[Dict[str, float]]:
|
||||
if not self.entity_prefix:
|
||||
log.error("[HA-Entity] Kein Entity-Prefix konfiguriert")
|
||||
return None
|
||||
if not self.token:
|
||||
log.error("[HA-Entity %s] Kein SUPERVISOR_TOKEN — 'homeassistant_api: true' in config.yaml gesetzt und Add-on neu installiert?", self.entity_prefix)
|
||||
log.error("[HA-Entity %s] Kein Long-Lived Access Token konfiguriert", self.entity_prefix)
|
||||
return None
|
||||
|
||||
result: Dict[str, float] = {}
|
||||
@@ -58,7 +59,7 @@ class HAEntityReader:
|
||||
|
||||
def _get_state(self, entity_id: str) -> Optional[float]:
|
||||
req = urllib.request.Request(
|
||||
f"{self.BASE_URL}/states/{entity_id}",
|
||||
f"{self.base_url}/api/states/{entity_id}",
|
||||
headers={"Authorization": f"Bearer {self.token}"},
|
||||
)
|
||||
try:
|
||||
|
||||
+58
-7
@@ -280,7 +280,11 @@ def _poll_loop(inv_cfg: Dict[str, Any], stop: threading.Event):
|
||||
log.info("[%s] Poll-Loop: %s @ %s (Goodwe UDP/8899) alle %ds",
|
||||
inv_id, inverter.name, host, interval)
|
||||
elif inverter.protocol == "ha_entity":
|
||||
reader = HAEntityReader(entity_prefix=inv_cfg.get("ha_entity_prefix", ""))
|
||||
reader = HAEntityReader(
|
||||
entity_prefix=inv_cfg.get("ha_entity_prefix", ""),
|
||||
base_url=inv_cfg.get("ha_base_url", "http://localhost:8123"),
|
||||
token=inv_cfg.get("ha_token", ""),
|
||||
)
|
||||
log.info("[%s] Poll-Loop: %s (HA-Entity Prefix: %s) alle %ds",
|
||||
inv_id, inverter.name, inv_cfg.get("ha_entity_prefix", ""), interval)
|
||||
elif inverter.protocol == "kathrein":
|
||||
@@ -804,10 +808,9 @@ def api_new_id():
|
||||
return jsonify({"id": uuid.uuid4().hex[:8]})
|
||||
|
||||
|
||||
@app.get("/api/export-config")
|
||||
def api_export_config():
|
||||
def _build_export_data() -> Dict[str, Any]:
|
||||
with State.lock:
|
||||
data = {
|
||||
return {
|
||||
"shinebridge_export": True,
|
||||
"version": 1,
|
||||
"exported_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
@@ -818,7 +821,47 @@ def api_export_config():
|
||||
},
|
||||
"inverters": State.inverters_cfg,
|
||||
}
|
||||
|
||||
|
||||
CONFIG_BACKUP_DIR = "/share/shinebridge-backups"
|
||||
CONFIG_BACKUP_RETENTION_DAYS = 14
|
||||
|
||||
|
||||
def _export_config_backup():
|
||||
"""Schreibt einen taeglichen Config-Export nach /share — bewusst NICHT nach
|
||||
/data, weil genau das der Ordner ist, der bei Add-on-Uninstall+Reinstall
|
||||
geloescht wird (siehe Vorfall 2026-08-25). /share uebersteht das."""
|
||||
try:
|
||||
os.makedirs(CONFIG_BACKUP_DIR, exist_ok=True)
|
||||
filename = f"config-{time.strftime('%Y-%m-%d')}.json"
|
||||
path = os.path.join(CONFIG_BACKUP_DIR, filename)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(_build_export_data(), f, indent=2, ensure_ascii=False)
|
||||
log.info("Config-Backup geschrieben: %s", path)
|
||||
|
||||
cutoff = time.time() - CONFIG_BACKUP_RETENTION_DAYS * 86400
|
||||
for name in os.listdir(CONFIG_BACKUP_DIR):
|
||||
fpath = os.path.join(CONFIG_BACKUP_DIR, name)
|
||||
if name.startswith("config-") and name.endswith(".json") and os.path.getmtime(fpath) < cutoff:
|
||||
os.remove(fpath)
|
||||
log.info("Altes Config-Backup gelöscht: %s", fpath)
|
||||
except OSError as e:
|
||||
log.error("Config-Backup fehlgeschlagen: %s", e)
|
||||
|
||||
|
||||
def _start_config_backup_scheduler():
|
||||
def _loop():
|
||||
_export_config_backup() # sofort beim Start, nicht erst nach 24h
|
||||
while True:
|
||||
time.sleep(86400)
|
||||
_export_config_backup()
|
||||
threading.Thread(target=_loop, daemon=True, name="config-backup").start()
|
||||
|
||||
|
||||
@app.get("/api/export-config")
|
||||
def api_export_config():
|
||||
from flask import Response
|
||||
data = _build_export_data()
|
||||
filename = f"shinebridge-config-{time.strftime('%Y%m%d-%H%M%S')}.json"
|
||||
return Response(
|
||||
json.dumps(data, indent=2, ensure_ascii=False),
|
||||
@@ -837,10 +880,17 @@ def api_import_config():
|
||||
if not isinstance(inverters, list):
|
||||
return jsonify({"error": "inverters ungültig"}), 400
|
||||
for inv in inverters:
|
||||
if not inv.get("modbus_ip"):
|
||||
model_id = inv.get("inverter_model")
|
||||
if model_id not in INVERTERS:
|
||||
return jsonify({"error": f"Unbekanntes Modell: {model_id}"}), 400
|
||||
protocol = INVERTERS[model_id].protocol
|
||||
# modbus_ip wird als generisches Host-Feld fuer alle IP-basierten Protokolle
|
||||
# wiederverwendet (modbus, goodwe_udp, kathrein) — nur ha_entity braucht keins.
|
||||
if protocol == "ha_entity":
|
||||
if not inv.get("ha_entity_prefix"):
|
||||
return jsonify({"error": f"ha_entity_prefix fehlt in Gerät {inv.get('name', '?')}"}), 400
|
||||
elif not inv.get("modbus_ip"):
|
||||
return jsonify({"error": f"modbus_ip fehlt in Gerät {inv.get('name', '?')}"}), 400
|
||||
if inv.get("inverter_model") not in INVERTERS:
|
||||
return jsonify({"error": f"Unbekanntes Modell: {inv.get('inverter_model')}"}), 400
|
||||
|
||||
with State.lock:
|
||||
if mqtt := data.get("mqtt"):
|
||||
@@ -944,6 +994,7 @@ if __name__ == "__main__":
|
||||
save_config()
|
||||
|
||||
_restart_all()
|
||||
_start_config_backup_scheduler()
|
||||
port = int(os.environ.get("INGRESS_PORT", "8099"))
|
||||
log.info("Web UI startet auf Port %d", port)
|
||||
app.run(host="0.0.0.0", port=port, threaded=True)
|
||||
|
||||
@@ -346,7 +346,13 @@
|
||||
<div id="ha-entity-fields" style="display:none">
|
||||
<div class="field"><label>HA Entity-Prefix</label>
|
||||
<input type="text" id="modal-ha-prefix" placeholder="growatt_wifi_x_1">
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:3px">Ohne "sensor."-Präfix — z.B. bei <code>sensor.growatt_wifi_x_1_dcpower</code> ist der Wert <code>growatt_wifi_x_1</code>. Braucht ein bereits per ESPHome in HA eingebundenes Gerät, keinen direkten Netzwerkzugriff.</div></div>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:3px">Ohne "sensor."-Präfix — z.B. bei <code>sensor.growatt_wifi_x_1_dcpower</code> ist der Wert <code>growatt_wifi_x_1</code>. Braucht ein bereits per ESPHome in HA eingebundenes Gerät.</div></div>
|
||||
<div class="field"><label>Home Assistant URL</label>
|
||||
<input type="text" id="modal-ha-url" placeholder="http://localhost:8123">
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:3px">Meist unverändert lassen — dank host_network erreicht ShineBridge HA Core direkt über localhost.</div></div>
|
||||
<div class="field"><label>Long-Lived Access Token</label>
|
||||
<input type="password" id="modal-ha-token" placeholder="Einstellungen → Profil → Sicherheit → Langlebige Zugriffs-Token">
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:3px">Kein Supervisor-Token nutzbar (host_network) — braucht ein manuell in HA erstelltes Token.</div></div>
|
||||
</div>
|
||||
<div class="field"><label>MQTT Topic-Präfix</label>
|
||||
<input type="text" id="modal-prefix" placeholder="growatt/wechselrichter1"></div>
|
||||
@@ -1081,6 +1087,8 @@ async function openModal(invId) {
|
||||
document.getElementById("modal-port").value = inv.modbus_port || 502;
|
||||
document.getElementById("modal-addr").value = inv.modbus_address || 1;
|
||||
document.getElementById("modal-ha-prefix").value = inv.ha_entity_prefix || "";
|
||||
document.getElementById("modal-ha-url").value = inv.ha_base_url || "http://localhost:8123";
|
||||
document.getElementById("modal-ha-token").value = inv.ha_token || "";
|
||||
document.getElementById("modal-prefix").value = inv.mqtt_topic_prefix || "";
|
||||
document.getElementById("modal-interval").value = inv.update_interval || 30;
|
||||
document.getElementById("ems-min-pv").value = inv.ems_min_pv ?? 1400;
|
||||
@@ -1097,6 +1105,8 @@ async function openModal(invId) {
|
||||
document.getElementById("modal-port").value = "502";
|
||||
document.getElementById("modal-addr").value = "1";
|
||||
document.getElementById("modal-ha-prefix").value = "";
|
||||
document.getElementById("modal-ha-url").value = "http://localhost:8123";
|
||||
document.getElementById("modal-ha-token").value = "";
|
||||
document.getElementById("modal-prefix").value = "growatt/wechselrichter" + (invertersList.length + 1);
|
||||
document.getElementById("modal-interval").value = "30";
|
||||
document.getElementById("ems-min-pv").value = "1400";
|
||||
@@ -1135,6 +1145,8 @@ async function saveInverter() {
|
||||
};
|
||||
if (model === "GROWATT_WIFI_X") {
|
||||
inv.ha_entity_prefix = document.getElementById("modal-ha-prefix").value.trim();
|
||||
inv.ha_base_url = document.getElementById("modal-ha-url").value.trim() || "http://localhost:8123";
|
||||
inv.ha_token = document.getElementById("modal-ha-token").value.trim();
|
||||
}
|
||||
if (model === "KATHREIN_WALLBOX") {
|
||||
inv.ems_min_pv = parseInt(document.getElementById("ems-min-pv").value);
|
||||
|
||||
Reference in New Issue
Block a user