Kathrein Reg 0x0060 liefert IllegalAddress wenn kein Fahrzeug angeschlossen.
Default war 1 (STATE_CONNECTED) → EMS nahm Auto als verbunden an → Countdown.
IllegalAddress ist kein sporadischer Lesefehler, sondern das definierte Signal
der Wallbox für "kein Fahrzeug". Default auf 0 (STATE_IDLE) → EMS kehrt sofort
zu "kein Fahrzeug" zurück, _no_pv_since-Timer wird nicht gestartet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Logs zeigen: Bei 10s Poll-Rate + 10s TCP-Timeout läuft ein fehlgeschlagener
Connect exakt so lange wie das Interval. stop.wait() wird 0 → nächster Poll
startet sofort → zweite parallele TCP-Verbindung → Wallbox überfordert → Spirale.
Fix: timeout = min(5.0, interval * 0.4). Bei 15s → 5.0s Timeout; bei 10s → 4.0s.
Ein Fehler belegt max. 40% des Intervalls, der Rest ist Wartezeit vor dem nächsten Versuch.
Gilt für WallboxReader (Kathrein) und ModbusReader (ShineLAN-X / SDM-630).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ein einzelner UDP-Paketverlust (Goodwe) oder TCP-Timeout (Modbus) hat sofort
MQTT-Status "offline" getriggert. Bei 10s Poll-Rate reicht ein Ausreißer.
Fix: _fail_count pro Poll-Loop, OFFLINE_THRESHOLD=3. Erst wenn 3 Reads in Folge
scheitern (≥30s bei 10s Interval) wird offline publiziert. Erfolg resettet
den Zähler auf 0 und stellt online sofort wieder her.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
e_total_imp vom Goodwe-WR ist ein Lifetime-Zähler seit Inbetriebnahme.
Beim ersten Verbinden speichert save_period_start_if_new() den aktuellen
Wert als Periodenstart → Delta = 0.
Fix: _int_import/_int_export werden je Poll-Zyklus aus grid_power integriert
(W × dt / 3.600.000 → kWh), in der measurements-DB persistiert und
beim Neustart aus der DB wiederhergestellt. AGG_SENSOR_IDS bevorzugt nun
_int_import vor e_total_imp, SDM-630 (import_kwh) bleibt erste Wahl.
Private Keys (Prefix _) werden nicht an MQTT gepublished.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
State.mqtt_cfg wurde beim Start nur mit 4 MQTT-Keys initialisiert — alle
Tarif/Billing-Keys fehlten, wurden nach Neustart auf Defaults zurückgesetzt.
Fix: alle persistenten Keys aus load_config() in State.mqtt_cfg übernehmen.
Finanzen-Tab: mehr Abstände, größere Karten (22px Wert), Abschnittsüberschriften,
Trennlinie vor dem Chart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fEur/fKwh waren lokale Funktionen in renderEnergy() — loadFinance()
konnte sie nicht aufrufen (ReferenceError außerhalb des Scopes).
Beide Funktionen in den globalen Scope verschoben, lokale Kopien entfernt.
loadFinance() Rendering-Block in try/catch gewrappt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug 1 — Eigenversorgungskarte verschwindet wenn PV offline:
- savings_kwh wird jetzt gesetzt wenn pv_total vorhanden (grid_exp muss
nicht mehr explizit vorhanden sein, default 0.0)
- Karte bleibt sichtbar auch wenn Wechselrichter offline geht
Bug 2 — Stromtarif-Einstellungen gehen beim Speichern verloren:
- savePrices(): parseInt mit || 1 Fallback, verhindert NaN → JSON-null
- api_save_config(): None-Checks + try/except für alle numerischen Keys,
save_config() wird garantiert immer aufgerufen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Alle erledigten Features als [x] markiert, neue Einträge für
Flash-Wizard, NuttX OTA, Setup-Wizard, MQTT rc=5, Port-Sicherheit,
Mobile-Layout. Changelog bis v1.8.19 ergänzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MQTT:
- rc=5 (Not Authorized) stoppt Reconnect-Loop via _auth_failed Flag
- Fehlermeldung im MQTT-Einstellungen-Banner sichtbar
Sicherheit:
- /api/* nur über HAOS-Ingress (X-Ingress-Path) oder Loopback erreichbar
Flash-Wizard (Baustelle B):
- Neuer Tab "Flash" mit IP-Eingabe und OTA-Modus-Erkennung
- OTA: integrierte oder eigene Firmware via POST /api/flash/update auf Stick
- Fortschrittsbalken + Polling bis Stick nach Reset wieder online
- ST-Link-Erstflash-Anleitung (Pinout, st-flash Kommando)
- Firmware-Binaries im Docker-Image unter /firmware/
NuttX OTA (Baustelle A, shinelanx-modbus):
- ota_http.c: Zwei-Phasen OTA für STM32F103 Single-Bank Flash
Stage 1: Firmware in Staging-Bereich (obere Flashhälfte) schreiben
Stage 2: .ramfuncs aus SRAM heraus — Staging → App-Bereich kopieren, Reset
- ota_http.h, Makefile und main.c entsprechend erweitert
- ld.script.dfu: .ramfuncs in .data Section → Ausführung aus SRAM
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Verhindert Datenverlust wenn HAOS den Container während eines Saves
stoppt. Schreibt erst config.json.tmp, dann atomares os.replace().
Hält config.json.bak als Fallback für den nächsten Start.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Täglicher Tarif-Tracking: kWh + EPEX-Ø-Preis werden ab jetzt täglich
gespeichert. Neuer Finanzen-Tab zeigt Balkendiagramm (Festpreis vs.
Spot hypothetisch), Summen-Karten und Empfehlung ob flexibler Tarif
sich lohnen würde.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neues optionales Feature: Abschlags-Übersicht im Energie-Dashboard.
Zeigt Bereits bezahlt, Grundpreis anteilig, Energiekosten sowie
voraussichtliche Nachzahlung oder Guthaben für das laufende Abrechnungsjahr.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Beschriftung über jedem Eingabefeld (Name, Z2M Name, Schwellwert, etc.)
- Checkbox "Invertiert": EIN bei Netzbezug, AUS bei Überschuss
- Live-Tab: ↑/↓ Pfeil zeigt normale/invertierte Logik
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- min_on_minutes pro Gerät: Gerät bleibt mindestens X Minuten an
auch wenn Überschuss unter Ausschaltschwelle fällt (Boiler-Fix)
- Live-Tab zeigt verbleibende Mindestlaufzeit während Gerät läuft
- get_states() liefert on_minutes für Laufzeit-Tracking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SDM-630 liefert grid_power (negativ=Einspeisung), wurde von active_power-
Logik nicht erfasst. Jetzt einheitlich über grid_power-Aggregat.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- MQTT Subscribe auf zigbee2mqtt/bridge/devices beim Connect
- Neuer Endpoint GET /api/z2m-devices liefert Friendly Names + Beschreibung
- Eingabefeld für Z2M Name als Datalist-Combo (tippen oder aus Liste wählen)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vollständige Formel: total_energy_total - grid_export_kwh deckt
Direktverbrauch (PV→Haus) + Batterie-Umweg (PV→Bat→Haus) ab.
Fallback auf bat_discharge_total wenn kein PV-Zähler verfügbar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bat_discharge_total + bat_charge_total werden jetzt als Perioden-Starts getrackt
- savings_kwh = Batterie-Entladung der Periode (→ Haus + Auto)
- savings_eur = savings_kwh × effektiver Importpreis (Festpreis oder Börsendurchschnitt)
- Neue Karte "Eigenversorgung" (lila) neben Netzbezug/Einspeisung
- Preis-Refaktor: eff_price für Import, Export, Ersparnis aus einer Berechnung
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Die Growatt SPH Register 1009/1011 liefern korrekte Werte.
Die Ableitung via ac_power_total war unnötig und fehleranfällig.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bat_charge und bat_discharge waren vertauscht, Hausverbrauch daher falsch.
Goodwe ET Library Konvention: pbattery1 positiv = Entladen, negativ = Laden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Register 1009 (bat_discharge) liefert beim SPH im Lademodus falsche Werte.
Neue Ableitung: bat_net = pv1+pv2 + power_to_user - power_to_grid - ac_power_total
Alle vier Quellen sind direkt gemessene, verlässliche Register.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vorher: rev:dchOn — bei gleichzeitig aktiven bat_charge/discharge (Messrauschen)
zeigte die Richtung Entladen obwohl Laden aktiv war.
Jetzt: rev:!(batChW >= batDchW) — Richtung folgt dem Nettostrom.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- hasEV-Flag aus invertersList: nur KATHREIN_WALLBOX zeigt EV-Node
- Ohne Wallbox: flacheres Layout (viewBox 520x278 statt 410)
- SEG-Array und SVG-Node konditionell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Grid-Icon: Sinuswelle (AC-Symbol) statt A-förmiger Übertragungsturm
- Batterie-Node: wird angezeigt sobald bat_* Aggregate vorhanden, nicht erst wenn bat_soc != null
- batSub: SOC-Prozent nur wenn bat_soc nicht null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Kreisförmige Nodes wie im HA Energie-Dashboard (r=44, farbiger Rand)
- Bezier-Kurven mit wandernden Punkten via animateMotion (3 Dots/Pfad)
- Flussrichtung korrekt: reverse für Export, Entladen
- Inaktive Pfade gedimmt, keine Dots
- CSS flowFwd/flowBwd entfernt (nicht mehr benötigt)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vorher: has_pv = surplus >= min → fiel auf False sobald Auto zu laden begann
Jetzt: has_pv = (surplus + wallbox_power) >= min → stabil während Laden
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AGG_FIRST: grid_power wird nicht summiert sondern erste Messung genommen
(Goodwe CT-Klemme misst bereits Gesamt-Netzleistung inkl. Growatt)
- Growatt-Proxy: grid_power = -power_to_grid wenn kein Grid-Meter vorhanden
(Growatt-only: Einspeisung korrekt, Netzbezug nicht messbar)
- Goodwe + Growatt: Goodwe-Wert hat Vorrang durch Gerätereihenfolge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Goodwe ET liefert active_power > 0 bei Einspeisung (nicht negativ).
Falsche Negierung führte zu PV-Überschuss = 0 → EMS lud nie.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- charging_state Default 0→1 (EV Connected) wenn Register nicht lesbar
- EMS-Status auf INFO hochgestuft inkl. PV-Überschuss
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- wallbox_client: Bei Reg-Fehler Range überspringen statt komplett abbrechen
→ Meter-Daten kommen weiter wenn EVSE-Block (0x0060) nicht verfügbar
- wallbox_client: set_current() schreibt jetzt auch EMS_RELAIS_REG (0x00A1)
mit korrekter Phasen-Bitmaske (1→0x0001, 2→0x0003, 3→0x0007)
- ems_controller: phases wird an set_current() und Zwangsladen übergeben
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Modal zeigt EMS-Felder nur bei KATHREIN_WALLBOX (toggleEmsSection)
- openModal lädt ems_min_pv/timeout/target_hour/phases aus Gerätekonfig
- saveInverter speichert EMS-Parameter in inverters.json
- main.py übergibt EMS-Konfig aus inv_cfg an EmsController()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Binary nuttx-mbusd-shinelanx-dfu.bin unterliegt Apache 2.0 / BSD 3-Clause /
BSD 2-Clause — Urheber Martin Walle (github.com/mwalle/shinelanx-modbus)
wird nun korrekt im README genannt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Flask-App + mobile Web UI für Diagnose vor Ort ohne HAOS/MQTT.
Pi 3B: eth0 → ShineLAN-X (DHCP), wlan0 → Hotspot "ShineDiag".
Browser auf http://10.0.1.1: Modell wählen, alle Sensoren auslesen,
Rohdaten-Register-Dump, Export als JSON.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sensorwerte werden in /data/history.db (SQLite, WAL-Modus) persistiert
und überleben damit Add-on-Neustarts. Beim Start werden die letzten 300
Messpunkte pro Sensor in die In-Memory-Deque geladen, sodass Sparklines
sofort Daten zeigen. Retention: 7 Tage. Neue API: GET /api/history.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Register 0x0030 liefert je nach SDM-630-Firmware-Variante den Phasenwinkel
statt der Gesamtwirkleistung. total_power wird jetzt zuverlässig aus
power_l1 + power_l2 + power_l3 berechnet und überschreibt den Hardware-Wert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
127.0.0.1-Binding verursacht 502 Bad Gateway weil HA Supervisor Ingress
nicht von localhost aus verbindet. host_network: true + 0.0.0.0 ist die
korrekte Konfiguration für HAOS Add-ons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add-on heißt jetzt ShineBridge (slug: shinebridge). Hardware-Name
"Growatt ShineLAN-X" im README und in der Dokumentation unverändert.
WICHTIG: Slug-Änderung erfordert Neuinstallation des Add-ons in HA
(altes Add-on deinstallieren, ShineBridge neu installieren).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Flask bindet auf 127.0.0.1 statt 0.0.0.0 — Port 8099 nicht mehr
direkt im LAN erreichbar (host_network: true umgeht sonst HA-Auth)
- XSS: esc() Funktion + HTML-Escaping für alle user-controlled Werte
in innerHTML (inv.name, modbus_ip, mqtt_topic_prefix, s.name, s.unit)
- API: POST /api/inverters-config validiert inverter_model, Port (1-65535),
Modbus-Adresse (1-247) vor dem Speichern
- _poll_loop: int()-Aufrufe in try/except — kein Thread-Crash bei
ungültiger Config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Statt Modell-Name ("Growatt MIC 1500 TL-X") wird der vom User vergebene
Name ("Dach Süd") als HA-Gerätename in MQTT Discovery verwendet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SVG-Sparkline pro Sensor-Karte, farbkodiert nach device_class
- Backend: (timestamp, value) deque pro Sensor, API filtert auf 300s
- Kein Datenverlust bei Neustart (In-Memory, reicht für Trendanzeige)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Unbegrenzt viele Wechselrichter über Web UI verwaltbar (Add/Edit/Delete)
- Pro Wechselrichter: eigener Poll-Thread, MQTT-Topic-Präfix, HA Device
- Shared MQTT-Publisher: eine Verbindung für alle Wechselrichter
- Migration: bestehende Single-Inverter-Config wird automatisch übernommen
- Live-Daten: pro Wechselrichter mit Online/Offline-Badge
- config.yaml: nur noch MQTT global, Wechselrichter über /data/config.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fehlerzähler zeigte kumulierte Timeouts vor dem NuttX-Flash (4282)
was wie ein Growatt-Fehlercode aussah. Status-Pills zeigen Verbindung.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fetches verwendeten absolute Pfade (/api/...) → gingen an HA REST API statt Add-on.
Hinter HA Ingress-Proxy muss der Pfad relativ zur Seiten-URL sein.
Fix: BASE = window.location.href + "./" → apiUrl() für alle fetch-Aufrufe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Container-Netz 172.30.33.x kann ShineLAN-X (10.10.20.190) nicht erreichen.
host_network: true gibt Zugriff auf das lokale Netzwerk.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
USB PID auf 0x5740 (STM32 Virtual COM Port) gesetzt — Wechselrichter
akzeptiert nur diesen PID und pollt den IN-Endpoint sonst nicht.
Product-String ebenfalls auf Originalwert gesetzt.
MODBUS_BAUD auf 9600 (Growatt-Standard, baud-rate ist bei USB CDC
virtuell aber beeinflusst CDC Line Coding).
usbReady-Check von == 127 auf >= 8 gelockert — verhindert dass
ein voller TX-Buffer alle Sensoren sofort mit 0xFE abwürgt.
Debug-String zeigt jetzt avS (Zyklusstart) und avE (Zyklusende)
zur Diagnose des TX-Buffer-Verlaufs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>