Dies hat sehr dabei geholfen die Anlage zu verstehen, etliche Fehler zu finden und die Einstellung zu optimieren.
Was mir zu meinen Heizöleinkäufen fehlte, war eine genauere Ahnung zum Verbrauch. Inzwischen könnte man über Umwege wohl noch einen Füllstandsensor realisieren, über den Umweg der vorhandenen Daten und damit mit einem Betriebsstunden sollte aber auch eine ausreichend genaue Näherung möglich sein.
Alte SQLer mögen es nicht so sehr, aber mit dem Upgrade von influx 1 auf 2 kommt die neue, prozedurale Sprache Flux mit die, wenn etwas verstanden, auch sehr mächtig sein kann.
Ansatz:
Die Aufzeichnung der Leistungswerte der Heizung soll so aufbereitet werden, dass am Ende eine Betriebszeit raus kommt.
Ein Grenzwert sorgt dafür, dass die Pumpenzeiten (Heizkreis, Speicherladung) sowie die Vorwärmung außer Betracht bleiben und nur die reine Brenndauer übrig bleibt.
Code:
Code: Alles auswählen
from(bucket: "Homematic")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "Heizungsraum")
|> filter(fn: (r) => r["_field"] == "POWER")
|> map(fn: (r) => ({r with _value: if r._value > 135 then r._value else 0.0}))
|> map(fn: (r) => ({r with Power: r._value}))
|> map(fn: (r) => ({r with _value: if r._value > 0 then 1 else 0}))
|> difference()
|> map(fn: (r) => ({r with Power: if r._value > 0 then 0.0 else r["Power"]}))
|> map(fn: (r) => ({r with _value: if r["Power"] > 0 then 1 else 0}))
|> elapsed(unit: 1s)
|> map(fn: (r) => ({r with _value: r._value * r["elapsed"]}))
|> cumulativeSum()
|> last()
Im Detail:
1. Datenbank, hier Bucket genannt, wählen
Code: Alles auswählen
from(bucket: "Homematic")
Code: Alles auswählen
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
Code: Alles auswählen
|> range(start: 2023-01-01T00:00:00Z, stop: 2023-02-01T00:00:00Z)
Code: Alles auswählen
|> filter(fn: (r) => r["_measurement"] == "Heizungsraum")
|> filter(fn: (r) => r["_field"] == "POWER")
Code: Alles auswählen
|> map(fn: (r) => ({r with _value: if r._value > 135 then r._value else 0.0}))
weil wir den Orginalwert gleich überschreiben (nur im Prozess der Berechnung, nicht wirklich in der DB)
Code: Alles auswählen
|> map(fn: (r) => ({r with Power: r._value}))
Code: Alles auswählen
|> map(fn: (r) => ({r with _value: if r._value > 0 then 1 else 0}))
übrig bleiben nur die Einschaltvorgänge (1), Aussschaltvorgänge aka letzter Wert (-1), der Rest ist 0
Code: Alles auswählen
|> difference()
der Wert enthält bei mir noch den Zündzeitraum und den wollte ich nicht mitrechnen
das mag falsch sein (dann aber immerhin linear), soll jeder für sich entscheiden
Code: Alles auswählen
|> map(fn: (r) => ({r with Power: if r._value > 0 then 0.0 else r["Power"]}))
Code: Alles auswählen
|> map(fn: (r) => ({r with _value: if r["Power"] > 0 then 1 else 0}))
Code: Alles auswählen
|> elapsed(unit: 1s)
alle Nichtbetriebszeiten sind 0
Code: Alles auswählen
|> map(fn: (r) => ({r with _value: r._value * r["elapsed"]}))
Code: Alles auswählen
|> cumulativeSum()
|> last()
Es mag einfacher gehen, der Grenzwert ist sicher individuell festzusetzen und ob man die Zündzeit rausnehmen möchte bleibt auch jedem selbst überlassen. Sicher geht auch was mit dem Taschenmesser cuXd.
Zusammen mit Meterstab und Füllstandsprofil meinens Tankes komme ich so auf einen ziemlich realistischen Verbauch pro Betriebsstunde, Tag sogar ein Heizleistungswert lässt sich erschließen.
Aber vielleicht will jemand ja auch ganz andere Betriebszeiten messen.