Betriebsstundenzähler mit InfluxDB 2 auf Basis analoger Werte

Problemlösungen und Hinweise von allgemeinem Interesse zur Haussteuerung mit HomeMatic

Moderator: Co-Administratoren

Antworten
Benutzeravatar
fairtv
Beiträge: 220
Registriert: 08.08.2014, 16:57
Wohnort: Bei Erding
Hat sich bedankt: 1 Mal
Danksagung erhalten: 1 Mal

Betriebsstundenzähler mit InfluxDB 2 auf Basis analoger Werte

Beitrag von fairtv » 04.04.2023, 11:43

ich habe damals als frischer Eigenheimbesitzer der mir bislang unbekannten Ölheizungsanlage einen Leistungsmesser (HM-ES-PMSw1-SM) vorgeschaltet und den Heizungsvor-/ und rücklauf mit einem Temperaturdifferenzsensor (HM-WDS30-OT2-SM-2) versehen.
Dies hat sehr dabei geholfen die Anlage zu verstehen, etliche Fehler zu finden und die Einstellung zu optimieren.
BS-Graph.png
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()
Sieht im Influx-Explorer so aus:
BS-Script.png

Im Detail:
1. Datenbank, hier Bucket genannt, wählen

Code: Alles auswählen

from(bucket: "Homematic")
2. Zeitraum, diese Zeile stammt aus Query-Editor und bezieht sich auf die Einstellung dort

Code: Alles auswählen

  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
geht aber alternativ auch manuell spezifisch:

Code: Alles auswählen

  |> range(start: 2023-01-01T00:00:00Z, stop: 2023-02-01T00:00:00Z)
3. Messung auswählen (abhängig davon, wie man sie abgelegt und benannt hat)

Code: Alles auswählen

  |> filter(fn: (r) => r["_measurement"] == "Heizungsraum")
  |> filter(fn: (r) => r["_field"] == "POWER")
4. Grenzwert filtern, hier bei mir 135 Watt, dazu wird alles Kleinere auf 0 gesetzt

Code: Alles auswählen

  |> map(fn: (r) => ({r with _value: if r._value > 135 then r._value else 0.0}))
5. den Messwert in einen neuen, temporären Hilfszwischenspeicher retten (hier "Power"),
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}))
6. jetzt machen wir aus jedem Messwert größer 0 eine Art Bool (quasi für EIN)

Code: Alles auswählen

  |> map(fn: (r) => ({r with _value: if r._value > 0 then 1 else 0}))
7. damit wird die Differenz der 0/1 Aus/Ein Werte gesetzt
übrig bleiben nur die Einschaltvorgänge (1), Aussschaltvorgänge aka letzter Wert (-1), der Rest ist 0

Code: Alles auswählen

  |> difference()
8. jetzt wird werden in den gepufferten Leistungswerten die jeweils ersten (Einschalt-)Werte entfernt
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"]}))
9. nun überschreiben wir gemäß der jetzt noch gepufferten Leistungswerten den Wert wieder mit 0/1 Aus/Ein

Code: Alles auswählen

  |> map(fn: (r) => ({r with _value: if r["Power"] > 0 then 1 else 0}))
10. das erzeugt einen neuen Wert "elapsed" (sqlisch: Spalte) mit der Zeitdifferenz zum vorherigen Wert (sqlisch: Zeile)

Code: Alles auswählen

  |> elapsed(unit: 1s)
11. Trick: diese Zeitdifferenz multipliziert mit unseren 0/1 Aus/Ein ergibt die Betriebszeit dieses Wertes
alle Nichtbetriebszeiten sind 0

Code: Alles auswählen

  |> map(fn: (r) => ({r with _value: r._value * r["elapsed"]}))
12. Aufsummieren und (letzten, ist ja nur einer) Wert ausgeben

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.
Viele Grüße,
fairtv

Antworten

Zurück zu „HomeMatic Tipps & Tricks - keine Fragen!“