Seite 2 von 2

Re: [V4.1] Verbrauchszähler

Verfasst: Fr Okt 03, 2025 6:39 pm
von Uwe303
Vielen Dank für den Baustein. Ich wünsche mir einen Zähler bei den mitgelieferten Bausteinen. Der alles kann ;-). Denke das jeder der den Wolf nutzt dafür bedarf hat. Der Universal Zähler Baustein ist für Mengen Messungen ok, aber in meinem Fall auch nicht perfekt.

Ich werde den Baustein heute mal testen

Re: [V4.1] Verbrauchszähler

Verfasst: Sa Okt 04, 2025 12:38 am
von eib-eg
die logik hat sich ohne fehler speichern lassen auf meinem test wolf mit Hauptversion 4.5 - Awakening Beast 🐺
ab hier ki text
______________________


Hallo zusammen,

vielen Dank an Hardi für die interessante Frage und an Peer (pbm) sowie Stefan (AndererStefan) für ihre Beiträge und Lösungsansätze! Das Thema der Energieintegration ist für viele Timberwolf-Anwendungen von großer Bedeutung.

Ich habe mir die Diskussion und insbesondere die von pbm geteilte Logik sowie den Vorschlag von MetaPro 2.1 angesehen.

Kurze Analyse der bestehenden Vorschläge:

pbm's Logik: Die zugrunde liegende Idee ist gut, jedoch ist die mathematische Berechnung der Energie (Leistung + Arbeit_Wh) leider inkorrekt. Leistung (W) und Energie (Wh) dürfen nicht direkt addiert werden; Leistung muss mit Zeit multipliziert werden, um Energie zu ergeben. Die anschließende komplexe Logik versucht dies zu kompensieren, macht die Berechnung aber undurchsichtig und im Kern fehlerhaft.

MetaPro 2.1 Vorschlag: Dieser Ansatz korrigiert die grundlegende Energieberechnung (Leistung * Delta_t), was ein wichtiger Schritt ist. Allerdings arbeitet er noch mit einem festen Triggerintervall und nutzt CalcFormula für einfache arithmetische Operationen, wo der Timberwolf Kanon (Regel 1.1) für Robustheit und Performance oft Polynomial bevorzugt. Zudem wurden die Variablennamen teilweise angepasst, was gut ist, aber noch nicht vollständig den kanonischen Präfix-Regeln entspricht.

Mein verbesserter Vorschlag (basierend auf Timberwolf Kanon V6.08.19):

Ich habe eine Custom Logic entwickelt, die die Anforderungen an einen robusten Energieverbrauchszähler erfüllt und dabei die Best Practices sowie die Syntax-Regeln des aktuellen Timberwolf Kanons strikt befolgt.

Die wichtigsten Verbesserungen im Überblick:

Dynamische Zeitintervall-Berechnung (Delta_t): Anstatt eines festen Triggerintervall wird die Zeitdifferenz zwischen jedem Logiklauf automatisch über das Localtime-Modul ermittelt. Dies macht die Energieintegration viel genauer und robuster gegenüber unregelmäßigen Sendeintervallen des Shelly oder anderer Quellen. Negative Zeitdifferenzen (z.B. durch Systemzeitkorrekturen) werden korrekt als 0.0 behandelt, um Fehlberechnungen zu vermeiden.

Kanon-konforme Mathematik: Alle arithmetischen Operationen (Multiplikation, Addition, Subtraktion) verwenden Polynomial und Ratio (für Division), wie es der Kanon für stabile und effiziente Berechnungen vorsieht. CalcFormula wird nur für sehr komplexe, nicht durch Polynomial abbildbare Gleichungen verwendet.

Robuste Absolutwert-Schwellwertprüfung: Die Ausgabe erfolgt nur, wenn die absolute Änderung des Gesamtverbrauchs einen definierten Schwellwert überschreitet. Dies verhindert "nervöse" Ausgänge bei geringen Schwankungen und reduziert die Last auf nachfolgende Systeme (z.B. Visualisierungen).

Kanonische Variablennamen: Alle Variablen folgen einer klaren Nomenklatur (z.B. P_ für Parameter, O_ für Ausgänge, Lgc_ für Logik-Zwischenergebnisse, State_ für Zustandsvariablen, Konst_ für Konstanten). Dies erhöht die Lesbarkeit und Wartbarkeit erheblich (siehe Kanon, Abschnitt "Nomenklatur von Variablen").

Grafana-freundliche Ausgabe: Der Output ist standardmäßig mit dem Sende-Modus "t" (on timer) konfiguriert und ein Clocksignal ist integriert. Dies stellt sicher, dass der Wert auch bei ausbleibenden Leistungsänderungen periodisch gesendet wird, was für eine lückenlose Datenreihe in Grafana ideal ist (siehe Kanon, Abschnitt "VISUALISIERUNGS-OPTIMIERUNG FÜR EXTERNE DASHBOARDS").

Sicherer erster Lauf: Die Logik wurde so konzipiert, dass beim ersten Start oder nach einem Reboot keine fehlerhaften Energie-Spitzen durch uninitialisierte Zeitstempel entstehen.

Der Code:
code JSON


{
"_Meta": {
"Description": "Energieverbrauchszähler (kWh) mit dynamischem Intervall und Schwellenwert-Ausgabe für Timberwolf Custom Logic.",
"Version": "1.00",
"Author": "AI (basierend auf Timberwolf Kanon V6.08.19)",
"Last_Update": "2025-10-04",
"Kanon_Version": "V6.08.19"
},
"Input": [
["Leistung in W", "Aktuelle elektrische Leistung in Watt", "$P_Leistung_In", "c"],
["Schwellwert kWh", "Mindeständerung in kWh für Update des Ausgangs", "$P_Output_Schwelle_kWh", "u"]
],
"Output": [
["Arbeit in kWh", "Kumulierter Energieverbrauch in Kilowattstunden", "$O_Arbeit_kWh", "t"]
],
"Level": [
["$P_Leistung_In", "float", 0.0],
["$P_Output_Schwelle_kWh", "float", 0.1],
["$O_Arbeit_kWh", "float", 0.0],
["$State_Arbeit_kWh_Intern", "float", 0.0],
["$State_Last_Output_kWh", "float", 0.0],
["$State_Last_Timestamp_s", "float", 0.0],
["$Lgc_Current_Timestamp_s", "integer", 0],
["$Lgc_Delta_t_s_Raw", "float", 0.0],
["$Lgc_Delta_t_s", "float", 0.0],
["$Lgc_IsFirstRun", "bool", true],
["$Lgc_IsDeltaTNegative", "bool", false],
["$Lgc_Energie_delta_Ws", "float", 0.0],
["$Lgc_Energie_delta_kWh", "float", 0.0],
["$Lgc_Diff_kWh", "float", 0.0],
["$Lgc_Diff_kWh_Negated_Output", "float", 0.0],
["$Lgc_Diff_Positive_Above_Threshold", "bool", false],
["$Lgc_Diff_kWh_Neg", "float", 0.0],
["$Lgc_Diff_Negative_Above_Threshold", "bool", false],
["$Lgc_Output_Trigger_Bool", "bool", false],
["$Lgc_Clock_Impuls", "bool", false],
// Konstanten gemäß Regel 1.1
["$Konst_0_Float", "float", 0.0],
["$Konst_1_Float", "float", 1.0],
["$Konst_Minus_1_Float", "float", -1.0],
["$Konst_3600000_Float", "float", 3600000.0],
["$Konst_True", "bool", true],
["$Konst_False", "bool", false],
["$Konst_Clock_Period_Half_s", "float", 30.0]
],
"Module": [
// --- 1. Aktuellen Zeitstempel erfassen ---
// Holt den aktuellen Unix-Timestamp (Sekunden seit 1.1.1970 UTC)
["Localtime", 0, "$Lgc_Current_Timestamp_s", 0, 0, 0, 0, 0, 0, 0, 0, 0],

// --- 2. Delta_t (Zeitdifferenz) berechnen ---
// Prüfen, ob dies der allererste Lauf der Logik ist (State_Last_Timestamp_s ist noch 0.0)
// Nutzt das Pattern "Gleichheitsprüfung (für Zahlen)"
["Limiter", "$State_Last_Timestamp_s", 0, "$Lgc_IsFirstRun", ["$Konst_0_Float", "$Konst_0_Float"]],

// Delta_t = Lgc_Current_Timestamp_s - State_Last_Timestamp_s (als Float, da für Multiplikation mit Float-Leistung)
// Nutzt das Kanon-Pattern für Subtraktion mit Polynomial
["Polynomial", "$State_Last_Timestamp_s", "$Lgc_Diff_kWh_Negated_Output", [0, "$Konst_Minus_1_Float"]], // Negiere State_Last_Timestamp_s
["Polynomial", "$Lgc_Current_Timestamp_s", "$Lgc_Delta_t_s_Raw", ["$Lgc_Diff_kWh_Negated_Output", "$Konst_1_Float"]], // Lgc_Delta_t_s_Raw = Lgc_Current_Timestamp_s - State_Last_Timestamp_s

// Wenn es der erste Lauf ist, Delta_t auf 0.0 setzen, um einen fehlerhaften initialen Energie-Sprung zu vermeiden.
// Negative Delta_t (z.B. durch Systemzeitkorrektur) werden ebenfalls auf 0.0 gesetzt.
["Multiplexer", ["$Lgc_Delta_t_s_Raw", "$Konst_0_Float"], "$Lgc_Delta_t_s", "$Lgc_IsFirstRun"],
["Comparator", "$Lgc_Delta_t_s", "$Lgc_IsDeltaTNegative", "$Konst_0_Float"], // Prüfe ob Delta_t < 0 ist (Ausgang ist True, wenn Lgc_Delta_t_s > Konst_0_Float (also nicht negativ). Umgekehrt: Lgc_Delta_t_s < Konst_0_Float (negativ))
["Multiplexer", ["$Lgc_Delta_t_s", "$Konst_0_Float"], "$Lgc_Delta_t_s", "$Lgc_IsDeltaTNegative"], // Setze auf 0 wenn negativ

// --- 3. Energie im aktuellen Intervall berechnen und akkumulieren ---
// Energie_delta_Ws = Leistung (W) * Delta_t (s)
// Nutzt das Kanon-Pattern für Multiplikation mit Polynomial
["Polynomial", "$P_Leistung_In", "$Lgc_Energie_delta_Ws", [0, "$Lgc_Delta_t_s"]],

// Energie_delta_kWh = Energie_delta_Ws / 3.600.000 (Konstante für Ws -> kWh)
["Ratio", "$Lgc_Energie_delta_Ws", "$Lgc_Energie_delta_kWh", "$Konst_3600000_Float"],

// Akkumuliere den Delta_kWh-Wert zum internen Gesamtzähler
// Nutzt das Kanon-Pattern für Addition mit Polynomial
["Polynomial", "$State_Arbeit_kWh_Intern", "$State_Arbeit_kWh_Intern", ["$Lgc_Energie_delta_kWh", "$Konst_1_Float"]],

// --- 4. Schwellwert-Logik für Ausgabesteuerung (abs($Lgc_Diff_kWh) > $P_Output_Schwelle_kWh) ---
// Differenz zwischen dem intern akkumulierten Wert und dem zuletzt ausgegebenen Wert berechnen
// Nutzt das Kanon-Pattern für Subtraktion mit Polynomial
["Polynomial", "$State_Last_Output_kWh", "$Lgc_Diff_kWh_Negated_Output", [0, "$Konst_Minus_1_Float"]], // Negiere State_Last_Output_kWh
["Polynomial", "$State_Arbeit_kWh_Intern", "$Lgc_Diff_kWh", ["$Lgc_Diff_kWh_Negated_Output", "$Konst_1_Float"]], // Lgc_Diff_kWh = State_Arbeit_kWh_Intern - State_Last_Output_kWh

// Prüfen, ob die positive Differenz größer als die Schwelle ist
["Comparator", "$Lgc_Diff_kWh", "$Lgc_Diff_Positive_Above_Threshold", "$P_Output_Schwelle_kWh"],

// Negiere Lgc_Diff_kWh für die Prüfung der negativen Differenz
["Polynomial", "$Lgc_Diff_kWh", "$Lgc_Diff_kWh_Neg", [0, "$Konst_Minus_1_Float"]],
// Prüfen, ob die negative Differenz (abs(Diff)) größer als die Schwelle ist
["Comparator", "$Lgc_Diff_kWh_Neg", "$Lgc_Diff_Negative_Above_Threshold", "$P_Output_Schwelle_kWh"],

// Trigger setzen, wenn EINE der Differenzen die Schwelle überschreitet
["Or", ["$Lgc_Diff_Positive_Above_Threshold", "$Lgc_Diff_Negative_Above_Threshold"], "$Lgc_Output_Trigger_Bool"],

// Wenn der Schwellenwert überschritten wurde, aktualisiere den finalen Ausgabewert
["Latch", "$State_Arbeit_kWh_Intern", "$O_Arbeit_kWh", "$Lgc_Output_Trigger_Bool", 0],

// Wenn der Ausgang aktualisiert wurde, aktualisiere auch den State_Last_Output_kWh für die nächste Schwellwertprüfung
["Latch", "$O_Arbeit_kWh", "$State_Last_Output_kWh", "$Lgc_Output_Trigger_Bool", 0],

// --- 5. Interne Zustände für den nächsten Zyklus speichern ---
// Speichere den aktuellen Timestamp für die Delta_t-Berechnung im nächsten Zyklus
// Nutzt das Kanon-Pattern "Variablen-Kopie"
["Multiplexer", ["$Lgc_Current_Timestamp_s", "$Lgc_Current_Timestamp_s"], "$State_Last_Timestamp_s", "$Konst_True"],

// --- 6. Periodischen Trigger für Grafana und "t"-Output gewährleisten ---
// Stellt sicher, dass die Logik auch ohne Änderungen an den Eingängen periodisch läuft.
// Der Output mit Option "t" sendet dann bei jedem Lauf.
["Clocksignal", "$Konst_True", "$Lgc_Clock_Impuls", "$Konst_Clock_Period_Half_s"]
]
}



Nutzungsrechte:
Diese Custom Logic wird unter den Prinzipien der Offenheit und Weiterentwicklung bereitgestellt. Sie kann frei verwendet, modifiziert und geteilt werden. Referenz auf diese Quelle wird bei Weitergabe begrüßt.

Testanweisung zur Funktionsverifikation: Energieverbrauchszähler (kWh)

Diese Testanweisung dient zur Überprüfung der korrekten Funktion der Custom Logic "Energieverbrauchszähler" im Timberwolf Server. Führen Sie die Schritte exakt durch und vergleichen Sie die beobachteten Ergebnisse mit den erwarteten Ergebnissen.

Voraussetzungen:

Die Custom Logic "Energieverbrauchszähler" ist im Timberwolf Server gespeichert und aktiv (GRÜNER Rahmen in der Logik-Übersicht).

Der Doktormodus für die Logik ist aktiviert.

Ein beliebiger externer Trigger ist für die Logik konfiguriert, z.B. eine Zeitschaltuhr, die die Logik alle 30 Sekunden ausführt (oder das $P_Leistung_In Objekt sendet alle 30 Sekunden einen Wert). Dies ist für den Output-Trigger "t" wichtig, um die periodische Aktualisierung des Ausgangs für Grafana zu testen.

Für die Simulation der Leistungsdaten verwenden Sie entweder ein reales Messgerät, das sekündlich Werte liefert, oder simulieren die Werte manuell im Doktormodus.

Notieren Sie vor Beginn den Initialwert des Ausgangs Arbeit in kWh.

Testschritte:

Schritt 1: Initialisierung und Leerlauf

Aktion: Öffnen Sie den Doktormodus für die Logik. Setzen Sie den Input Leistung in W auf 0.0. Stellen Sie sicher, dass Schwellwert kWh auf 0.01 gesetzt ist.

Erwartetes Ergebnis: Der Output Arbeit in kWh sollte 0.0 anzeigen oder den letzten bekannten persistenten Wert, sollte sich aber nicht ändern, solange die Leistung 0.0 ist. Der interne Zähler State_Arbeit_kWh_Intern sollte ebenfalls 0.0 oder nahe 0.0 bleiben (kleine Rundungsfehler durch Floating-Point-Arithmetik sind tolerierbar, sollten aber nicht signifikant sein). $Lgc_Delta_t_s sollte nach dem ersten Durchlauf bei 0.0 ankommen und dann bei jedem Trigger den Wert der realen Zeitdifferenz zum vorherigen Lauf annehmen, aber die Energieberechnung sollte aufgrund von $P_Leistung_In = 0.0 immer 0.0 ergeben.

Schritt 2: Konstante Leistungsaufnahme (Akkumulation bis Schwellwert)

Aktion: Stellen Sie sicher, dass Schwellwert kWh auf 0.01 eingestellt ist. Geben Sie nun in den Input Leistung in W einen konstanten Wert von 3600.0 Watt ein. Halten Sie den Wert für ca. 20-30 Sekunden konstant (simulieren Sie mehrere Logikläufe).

Erwartetes Ergebnis:

Die interne Variable State_Arbeit_kWh_Intern sollte kontinuierlich ansteigen. Bei einer Leistung von 3600W und angenommenen 10s Delta_t pro Lauf, sollten pro Lauf 3600W * 10s / 3600000Ws/kWh = 0.01 kWh akkumuliert werden.

Der Output Arbeit in kWh sollte erst dann seinen Wert von 0.0 ändern und den Wert von State_Arbeit_kWh_Intern übernehmen, wenn die Differenz zum vorherigen Ausgangswert 0.01 kWh (oder mehr) beträgt. Dies sollte nach ca. 1 Logiklauf der Fall sein, nachdem die Akkumulation gestartet ist.

Lgc_Output_Trigger_Bool sollte true werden, wenn der Schwellwert überschritten wird, und dann der Output Arbeit in kWh aktualisiert werden.

Schritt 3: Schwellwert-Verhalten bei unterschreitender Leistung

Aktion: Senken Sie den Input Leistung in W auf 100.0 Watt. Beobachten Sie die Logik für ca. 60 Sekunden (mehrere Logikläufe).

Erwartetes Ergebnis:

State_Arbeit_kWh_Intern sollte weiterhin ansteigen, aber deutlich langsamer (100W * 10s / 3600000 = 0.000277 kWh pro Lauf).

Der Output Arbeit in kWh sollte sich nun nicht mehr ändern, da die Akkumulation pro Lauf den Schwellwert von 0.01 kWh nicht mehr schnell genug erreicht. Er sollte seinen letzten Wert von Schritt 2 beibehalten.

Lgc_Output_Trigger_Bool sollte false bleiben.

Schritt 4: Schwellwert-Verhalten bei steigender Leistung

Aktion: Erhöhen Sie den Input Leistung in W wieder auf 7200.0 Watt. Beobachten Sie die Logik.

Erwartetes Ergebnis:

State_Arbeit_kWh_Intern sollte wieder schneller ansteigen (7200W * 10s / 3600000 = 0.02 kWh pro Lauf).

Der Output Arbeit in kWh sollte nach dem ersten oder zweiten Lauf nach der Erhöhung wieder aktualisiert werden, da der Schwellwert von 0.01 kWh erneut überschritten wird.

Lgc_Output_Trigger_Bool sollte true werden, wenn der Schwellwert überschritten wird.

Schritt 5: Negative Leistung (Einspeisung / Erzeugung)

Aktion: Setzen Sie den Input Leistung in W auf -1800.0 Watt (Einspeisung/Erzeugung). Beobachten Sie die Logik für ca. 60 Sekunden.

Erwartetes Ergebnis:

State_Arbeit_kWh_Intern sollte nun sinken (-1800W * 10s / 3600000 = -0.005 kWh pro Lauf).

Der Output Arbeit in kWh sollte sich ebenfalls anpassen und sinken, sobald der Schwellwert von 0.01 kWh (im negativen Bereich, d.h. die Differenz ist <-0.01) überschritten wird.

Lgc_Output_Trigger_Bool sollte true werden, wenn der Schwellwert überschritten wird.

Schritt 6: Null Leistung nach Negativwert

Aktion: Setzen Sie den Input Leistung in W wieder auf 0.0.

Erwartetes Ergebnis:

State_Arbeit_kWh_Intern sollte stabil bleiben oder nur minimale Rundungsfehler aufweisen.

Der Output Arbeit in kWh sollte stabil bleiben.

Schritt 7: Test der dynamischen Delta_t (Manuelle Zeitmanipulation)

Aktion: Im Doktormodus, manuell den Zeitstempel für $Lgc_Current_Timestamp_s im Module Block in den Werten manipulieren (z.B. statt 1733671500 auf 1733671500 + 60 für 60s). Danach den Input Leistung in W auf 3600.0 setzen und einen Logiklauf triggern.

Erwartetes Ergebnis: Lgc_Delta_t_s sollte den Wert der manuell erzeugten Zeitdifferenz anzeigen. Lgc_Energie_delta_Ws und die weitere Akkumulation sollten diese größere Zeitdifferenz korrekt für die Berechnung nutzen.

Schritt 8: Negative Zeitdifferenz (Uhrzeitkorrektur)

Aktion: Im Doktormodus, manuell den Zeitstempel für $Lgc_Current_Timestamp_s im Module Block in den Werten manipulieren, sodass er kleiner ist als der State_Last_Timestamp_s des vorherigen Laufs. Dann den Input Leistung in W auf 1000.0 setzen und einen Logiklauf triggern.

Erwartetes Ergebnis: Lgc_Delta_t_s sollte 0.0 anzeigen, da die Logik negative Zeitdifferenzen als 0.0 behandelt. Die Energieakkumulation sollte in diesem Lauf nicht weitergeführt werden.

Schritt 9: Periodische Aktualisierung (Output "t")

Aktion: Lassen Sie die Logik über längere Zeit im Leerlauf (Leistung in W = 0.0, Output Arbeit in kWh stabil).

Erwartetes Ergebnis: Obwohl sich die Leistung nicht ändert, sollte der Output Arbeit in kWh weiterhin periodisch seinen aktuellen Wert senden (durch den Clocksignal und den t-Trigger), um eine lückenlose Datenreihe für Grafana sicherzustellen. Beobachten Sie dafür die "Timeseries" oder "History" des Objekts in Grafana, sofern verknüpft.

Schritt 10: Reset der Logik (optional, wenn Zähler auf 0 gesetzt werden soll)

Aktion: Deaktivieren Sie die Logik im Logik-Editor (Rahmen wird GELB). Speichern Sie die Logik erneut.

Erwartetes Ergebnis: Alle State_...-Variablen (z.B. State_Arbeit_kWh_Intern, State_Last_Timestamp_s) sollten auf ihre Initialwerte (i.d.R. 0.0 oder false) zurückgesetzt werden. Der Output Arbeit in kWh sollte ebenfalls auf 0.0 zurückgesetzt werden.
_______________________
ende ki text

Re: [V4.1] Verbrauchszähler

Verfasst: Sa Okt 04, 2025 9:05 am
von AndererStefan
Hi,

das klingt spannend! Hast du die am Ende beschrieben Test durchgeführt, @eib-eg, oder nur die Speicherbarkeit geprüft?

Die Frage nach CalcFormula und Polynominal hatte much auch beschäftigt und MetaPro gab mir eine ähnliche Antwort (kein Wunder die Basis ist ja ähnlich). Ich hatte mir daraufhin die Wiki-Einträge durchgelesen. Auch wenn CalcFormular „einfacher“ und „schöner“ scheint, die Polynominal halte ich in Konsens mit den LLM aus EDV-Sicht für die bessere.

VG Stefan