Flow für PV-Überschuss-Wallbox-Steuerung (Easee in Verbindung mit Tibber Pulse)

Moin,

PV-Anlage ist endlich in Betrieb und da ich weder einen Huawei Wallbox habe, noch den Easee Equalizer, musste eine Alternative her und was liegt näher, als das Ganze in Homey abzubilden?

Gesteuert wird das bei mir mit 3 Flows (2x Standard, 1x Advanced):

1. Laden Starten:

Den ersten “Set Dynamic Charger Current” kann man sich sparen, der bewirkt nichts.
Mit der Variable arbeite ich aus zwei Gründen:

1. Jedes Mal, wenn kurzzeitig weniger Überschuss vorhanden ist und das Laden noch nicht abgebrochen wurde, gibts eine Benachrichtigung, sobald er wieder über seine 1,2kw Überschuss kommt.

2. Habe ich so die Möglichkeit auch einen Ladevorgang manuell zu starten, der nicht automatisch “sofort” wieder beendet wird durch den zweiten Flow.

2. Laden Stoppen

Relativ simpel sobald der Netzbezug für 3 Minuten über 250w liegt, soll der Ladevorgang abgebrochen werden. Mein Gedanke: Damit kann man evtl kurzfristige Wolken abdecken, ohne dass ständig das Laden unterbrochen und wieder gestartet wird.

3. Modulation der Stromstärke

// ============================================================================
// PV-Überschuss-Lademodulation für Easee (defensiv/adaptiv)
// Strategie: bei Netzbezug SOFORT runterregeln, bei Überschuss ADAPTIV hoch.
// Zielsaldo Netz ~0 W, dabei bis +150 W Bezug toleriert (Totband).
// Steuert target_circuit_current = die bindende Strombegrenzung der Easee.
// (NICHT target_charger_current — der steht auf 32 A und ist wirkungslos,
//  weil der reale Ladestrom = Minimum aus beiden Werten ist.)
// ============================================================================
try {
  // --- Geräte-IDs & Grenzwerte ------------------------------------------------
  const PULSE_ID='<ID>';   // Tibber Pulse = schnelle Netzmessung
  const EASEE_ID='<ID>';   // Easee Wallbox
  const V=230, A_MIN=6, A_MAX=16;   // Netzspannung; min./max. Ladestrom (6 A = Easee-Untergrenze)
  const GRID_TARGET_W=0;      // gewünschter Netzsaldo (0 = weder Bezug noch Einspeisung)
  const DEADBAND_W=150;       // Totband: zwischen -150 W (Einspeisung) und +150 W (Bezug) wird nichts geregelt
  const STEP_UP_MAX=4;        // beim Hochregeln max. Sprung pro Zyklus in A (verhindert Überschwingen)
  const MIN_INTERVAL_MS=15000;// Mindestabstand zwischen zwei echten Regelzyklen (In-Script-Throttle)

  // --- Throttle: nicht öfter als alle 15 s regeln -----------------------------
  const now=Date.now();
  const S=(await global.get('pvmod'))||{lastRun:0};   // persistenter Zustand (letzter Lauf)
  if(S.lastRun && now-S.lastRun<MIN_INTERVAL_MS) return 'throttled';   // zu früh -> abbrechen

  // --- Geräte holen -----------------------------------------------------------
  const pulse=await Homey.devices.getDevice({id:PULSE_ID});
  const easee=await Homey.devices.getDevice({id:EASEE_ID});

  // Hilfsfunktion: liest eine Capability der Easee sicher aus (undefined falls nicht vorhanden)
  const C=c=>easee.capabilitiesObj[c]?easee.capabilitiesObj[c].value:undefined;

  // --- Lädt die Easee gerade überhaupt? (mehrere mögliche Status-Felder prüfen) ---
  const charging = C('evcharger_charging')===true
                || C('evcharger_charging_state')==='plugged_in_charging'
                || C('charger_status')==='Charging';
  // Wenn nicht geladen wird: Lauf vermerken und raus (nichts zu regeln)
  if(!charging){ S.lastRun=now; await global.set('pvmod',S); return 'laedt nicht'; }

  // --- Messwerte einlesen -----------------------------------------------------
  const grid=pulse.capabilitiesObj['measure_power'].value;   // Netzleistung: + = Bezug / - = Einspeisung
  const evPower=C('measure_power')||0;                        // aktuelle Ladeleistung der Easee (W)
  const curA=C('target_circuit_current')||A_MIN;             // aktuell gesetzter Strom-Limit-Wert (A)

  // Aktive Phasen zählen: je Phase p1/p2/p3, deren Strom >1 A ist -> mind. 1 (einphasig auf L3)
  const pc=(['p1','p2','p3'].map(x=>Math.abs(C('measure_current.'+x)||0)).filter(a=>a>1).length)||1;

  // --- Regelabweichung --------------------------------------------------------
  const err=grid-GRID_TARGET_W;   // >0: zu viel Bezug -> runter;  <0: Luft nach oben -> hoch
  // Innerhalb des Totbands: alles gut, nichts ändern
  if(Math.abs(err)<=DEADBAND_W){ S.lastRun=now; await global.set('pvmod',S); return 'stabil @ '+curA+' A (Netz '+Math.round(grid)+' W)'; }

  // --- Zielstrom berechnen ----------------------------------------------------
  // Verfügbare Leistung fürs Auto = aktuelle Ladeleistung minus Netzbezug (+ Zielsaldo),
  // geteilt durch (Phasenzahl × Spannung) = Amperewert, der den Netzsaldo aufs Ziel bringt.
  const aTarget=Math.round((evPower-grid+GRID_TARGET_W)/(pc*V));

  let a;
  if(err>0){
    a=aTarget;                                              // RUNTER: sofort auf Zielstrom (defensiv)
  } else {
    // HOCH: nur die halbe Reststrecke zum Ziel gehen, mind. 1 A, max. STEP_UP_MAX (=4 A) pro Zyklus
    const gap=aTarget-curA;
    a = gap>0 ? curA+Math.max(1,Math.min(STEP_UP_MAX,Math.round(gap*0.5))) : curA;
  }
  a=Math.max(A_MIN,Math.min(A_MAX,a));   // auf erlaubten Bereich 6–16 A begrenzen

  // --- Lauf vermerken (bevor gesetzt wird, damit Throttle sauber greift) -------
  S.lastRun=now; await global.set('pvmod',S);

  // --- Nur schreiben, wenn sich der Wert wirklich ändert -----------------------
  if(a!==curA){
    await easee.setCapabilityValue('target_circuit_current',a);   // neuer Strom-Limit gesetzt
    const msg='PV-Laden: '+(err>0?'runter':'hoch')+' '+curA+' -> '+a+' A ('+pc+'p, Netz '+Math.round(grid)+' W)';
    try{ await Homey.notifications.createNotification({excerpt: msg}); }catch(e){}  // Status in die Zeitleiste
    return msg;
  }
  return 'stabil @ '+curA+' A (Netz '+Math.round(grid)+' W)';

// --- Fehlerfall: Meldung in die Zeitleiste, Fehlertext zurückgeben ------------
} catch(e){ try{ await Homey.notifications.createNotification({excerpt:'PV-Laden Fehler: '+e.message}); }catch(_){}; return 'Fehler: '+e.message; }

Im Prinzip wird alle 15s gecheckt, ob sich der Überschuss verändert hat und dann angepasst. In der ersten Version war ich sehr vorsichtig und bin nur 1A hoch oder runtergegangen, das hat dann schon ein wenig gedauert, bis dann wirklich die Leistung da war, das ist bei wolkigen Wetter etwas blöd, so dass auch bis zu 4A auf einmal hochgegangen werden kann.

Die Benachrichtigungen funktionieren nicht, vmtl ist das aber auch ganz gut so. Im Zweifel alle 15s eine Meldung zu bekommen wird auch nervig sein.

Aktuell ist die Wallbox fest auf einphasig eingestellt, das scheint mit Homey auch nicht einstellbar zu sein.

Aktuel läuft das ganz gut, ich bin mir nur nicht sicher, ob ich evtl noch was an den Zeiten drehe oder sogar noch mehr Netzbezug zulasse, damit nicht zu häufig der Ladevorgang gestoppt und gestartet wird.