HourCounter
HourCounter ist ein Perl-Modul, das die Anzahl von Ereignissen erfasst.
Bei bipolaren Ereignissen wird zusätzlich die Puls- sowie die Pausendauer ermittelt.
Im vorliegenden Beispiel wird ein Betriebsstundenzähler mit einem MAX-Fensterkontakt realisiert.
Features
- Ermittlung von Betriebsstunden, Auslastung, Verbräuchen, Schalthäufigkeiten
- Ermittlung der Häufigkeit, Puls- Pausendauer pro Tag
- Ermittlung der Puls-/Pausendauer der letzten Schaltperiode
- Bereitstellung von Ereignissen zur Fortführung der Kumulation für Wochen- Tages- u. Monatswerte
- unterstützende Funktionen zur Ablage von Tages-, Wochen- und Monatswerten
Zielsetzung des WIKI-Artikels
Erläuterung der Funktionalität zur weiterführenden Diskussion im Forum. Forum
Status
Das Modul befindet sich noch in der Evaluierungs-Phase.
Die aktuelle Version ist hier zu finden.
Voraussetzungen
keine besonderen Voraussetzungen. Das Modul ist anders als das Vorgängerskript 99_UtilsMaxCounter nicht mehr an Max-Komponenten gebunden.
Definition
Abstrakt
define <name> HourCounter <regexp_for_ON> [<regexp_for_Off>]
<regexp_for_ON> ist ein regulärer Ausdruck der das Ereignis beschreibt.
Wenn auch [<regexp_for_Off>] definiert ist, so sprechen wir von einem bipolarem Ereignis, das einen
EIN- sowie einen AUS-Zustand aufweist.
<regexp_for_ON> berschreibt in diesem Fall die positive Flanke,[<regexp_for_Off>] die negative Flanke.
Die Struktur des regexp-Ausdruckes ist analog zu jener beim Notify aufgebaut.
Konkret
define CN.Test HourCounter SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0
Hier wird der HourCounter CN.TEST definiert. Ein MAX-Fensterkontakt mit Namen SHUTTER.JOHN wird als Ereignis-Geber verwendet. Das Reading "onoff" wird als Trigger für unserem Zähler genutzt. Bei den Fensterkontakten sehen diese Ereignisse wie folgt aus:
2013-11-15 23:19:12 MAX SHUTTER.JOHN onoff: 1 .... 2013-11-15 23:19:24 MAX SHUTTER.JOHN onoff: 0
Wenn man einen Dummy als Ereignis-Geber verwenden will, lautet die Definition wie folgt:
define CN.Test HourCounter myDummy:1 myDummy:0
Die neue Instanz weist folgende Struktur auf
Internals: CFGFN DEF SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0 NAME CN.Test NR 738 NTFY_ORDER 50-CN.Test STATE 1 TYPE HourCounter Readings: 2014-02-04 20:59:22 clearDate 2014-02-04 20:59:22 2014-02-04 20:59:57 countsOverall 1 2014-02-04 20:59:57 countsPerDay 1 2014-02-04 20:59:57 pauseTimeIncrement 35 2014-02-04 20:59:57 pauseTimeOverall 35 2014-02-04 20:59:57 pauseTimePerDay 35 2014-02-04 21:00:01 pulseTimeIncrement 4 2014-02-04 21:00:01 pulseTimeOverall 4 2014-02-04 21:00:01 pulseTimePerDay 4 2014-02-04 20:59:57 state 1 2014-02-04 21:00:00 tickHour 1 2014-02-04 21:00:01 value 0 Helper: OFF_Regexp SHUTTER.JOHN:onoff:.0 ON_Regexp SHUTTER.JOHN:onoff:.1 calledByEvent changedTimestamp 2014-02-04 21:00:01 forceClear forceDayChange forceHourChange forceMonthChange forceWeekChange isFirstRun sdRoundHourLast 1391544000 value 0 cmdQueue:
Readings
Reading | Beschreibung |
---|---|
clearDate | Datum, zu dem alle kumulativen Readings über set .. clear gelöscht wurden |
countsOverall | Absolutzähler für das Auftreten des ON-Ereignisses |
countsPerDay | Tageszähler für das Auftreten des ON-Ereignisses |
pauseTimeIncrement | Zeitdauer in Sekunden der Pause-Phase der letzten Periode |
pauseTimeOverall | Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen |
pauseTimePerDay | Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen des akt. Tages |
pulseTimeIncrement | Zeitdauer in Sekunden der Puls-Phase der letzten Periode |
pulseTimeOverall | Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen |
pulseTimePerDay | Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen des akt. Tages |
value | Aktueller Schaltzustand gemäss ON/OFF Ereignis,mit 1=letztes Ereignis war ON-Ereignis |
tickDay | Event wird nach Tageswechsel gefeuert bevor die Tageszähler resettiert werden |
tickHour | Event wird nach Stundenwechsel gefeuert |
tickMonth | Event wird nach Monatswechsel gefeuert |
tickWeek | Event wird nach Wochenwechsel gefeuert |
forceHourChange | simuliert einen Stundenwechsel |
forceDayChange | simuliert einen Tageswechsel |
forceWeekChange | simuliert einen Wochenwechsel |
forceMonthChange | simuliert einen Monatswechsel |
Web-Oberfläche
Anwendung
Nachfolgende Darstellung zeigt das Einschaltverhalten eines Heizungskessels zusammen mit den abgeleiteten Werten.
- die Kurve "Brenner EIN" zeigt die Trigger-Signale unseres ON/OFF Filters, also das Ein-/Ausschalten des Brenners
- die Kurve "Brenner-Starts" zeigt die über den Tag aufgelaufenen Starts, also chronologisch das Anwachsen von Reading countsPerDay
- die Kurve "Betriebsstunden" zeigt die aufgelaufene Zeit aus dem Reading pulseTimePerDay umgerechnet zu Stunden
- die Kurve "Dauer" zeigt die Dauer des letzten Pulses in Sekunden
- die Kurve Auslastung zeigt das Verhältnis des Readings pulseTimePerDay zur seit Tagesbeginn vergangenen Zeit.
Chart der Aktualwerte
Wir benötigen hierzu ein File-Archiv für die aufgelaufenen Daten.
define CN.Test.File FileLog ./log/CN.Test-%Y.log (CN\.Test:.*)
Man erhält nach den ersten Ereignissen Einträge in folgender Form:
2013-11-16_12:45:40 CN.Test value: 1 2013-11-16_12:45:40 CN.Test 1 2013-11-16_12:46:21 CN.Test pulseTimeIncrement: 41 2013-11-16_12:46:21 CN.Test pulseTimePerDay: 41 2013-11-16_12:46:21 CN.Test pulseTimeOverall: 41 2013-11-16_12:46:21 CN.Test value: 0 2013-11-16_12:50:38 CN.Test countsPerDay: 2 2013-11-16_12:50:38 CN.Test countsOverall: 2 2013-11-16_12:50:38 CN.Test pauseTimeIncrement: 257 2013-11-16_12:50:38 CN.Test pauseTimePerDay: 756 2013-11-16_12:50:38 CN.Test pauseTimeOverall: 756 2013-11-16_12:50:38 CN.Test value: 1 2013-11-16_12:50:38 CN.Test 2
Nun kann man den Chart definieren:
Für die Kurve "Brenner EIN" verwenden wir CN.Test.value. Damit diese als unterste Kurve dargestellt wird transformieren wir den Wert 1 auf -2 und alle anderen (also die 0) auf -21 mit folgender Funktion:
$fld[3]=~"1"?-2:-19
Die "Brenner Starts" können wir direkt von countsPerDay ableiten.
Für die "Betriebsstunden" verwenden wir pulseTimePerDay. Da diese in Sekunden vorliegen teilen wir den Wert durch 3600, um Stunden zu erhalten.
$fld[3]/=3600
Als letzten versorgen wir noch die Kurve "Dauer" mit pulseTimeIncrement. Da wir diese in Minuten haben wollen ist ebenfalls eine Umformung nötig.
$fld[3]/=60
Somit sind die Basis-Kurven angelegt.
Erweiterungen
Es wurde im Forum viele Wünsche formuliert, weitere Funktionalitäten für den HourCounter einzuführen.
- Aggregation über bestimmte oder ganz freie Zeiträume
- komplexe Berechnungen, die zum Verbrauch führen
- Zuordnung von Verbräuchen zu unterschiedlichen Countern nach bestimmten Bedingungen
Vor allem die Aggregation erfasster Werte in Stunden-, Tages- ,Wochen- und Monatswerten ist eine sinnvolle Erweiterung bei der Verbrauchserfassung.
HourCounter bietet Schnittstellen an, die es ermöglichen das Modul selbst mit neuen Eigenschaften zu erweitern.
Die Referenz-Implementierung in 99_UtilsHourCounter.pm zeigt, wie dies skript-technisch zu realisieren ist.
Installation
99_UtilsHourCounter ist in das Verzeichnis der Module zu kopieren (bei raspi /opt/fhem/FHEM).
Readings
99_UtilsHourCounter erweitert den HourCounter um folgende Funktionen:
Reading | Beschreibung |
---|---|
appCountsPerHour | Stundenzähler, wird bei Stundenwechsel aktualisiert |
appCountsPerHourTemp | Arbeitszähler zu appCountsPerHour |
appCountsPerDay | Tageszähler, wird bei Tageswechsel aktualisiert (Arbeitszähler ist countsPerDay) |
appCountsPerWeek | Wochenzähler, wird bei Wochenwechsel aktualisiert |
appCountsPerWeekTemp | Arbeitszähler zu appCountsPerWeek |
appCountsPerMonth | Monatszähler, wird bei Monatswechsel aktualisiert |
appCountsPerMonthTemp | Arbeitszähler zu appCountsPerMonth |
appOpHoursPerDay | Betriebsstunden des Tages |
appOpHoursPerDayTemp | Arbeitszähler zu appOpHoursPerDay |
appOpHoursPerWeek | Betriebsstunden der Woche |
appOpHoursPerWeekTemp | Arbeitszähler zu appOpHoursPerWeek |
appOpHoursPerMonth | Betriebsstunden des Monats |
appOpHoursPerMonthTemp | Arbeitszähler appOpHoursPerMonth |
appUtilization | Auslastung = pulseTimePerDay /(vergangene Sekunden seit Tagesbeginn) * 100 |
appUtilizationTemp | Arbeitsvariable zu appUtilization |
Beginn der Woche ist jeweils der Sonntag.
Mit folgender Anweisung aktivieren wir die Erweiterungen:
define CN.Event notify CN.Test:(countsOverall:|value:|tickHour:|tickDay:|tickWeek:|tickMonth:).* {appHCNotify("%NAME","%EVTPART0","%EVTPART1");;}
Spätestens nach einer steigenden und einer fallenden Flanke sind die zuvor genannten app*-Readings zu sehen.
Die neuen Readings werden automatisch in den "Setter" der Web-Oberflächen aufgenommen.
Dies gilt für alle Readings, die mit "app" beginnen.
Somit können die neuen Readings beliebig manipuliert werden.
Archiv für Tages-/Wochen-/Monatswerte anlegen
Nun wollen wir die aggregierten Werte in eine eigene Datei speichern. Dies gelingt mit
define CN.Test.FileDay FileLog ./log/CN.Test-Day-%Y.log CN.Test:app\w+(Utilization|PerHour|PerDay|PerWeek|PerMonth)(?!Temp).*
Im Klartext:
- verwende alle Werte des Counters CN.Test, deren Reading mit "app" beginnt
- und die einen der Terme appUtilization|PerHour|PerDay|PerWeek|PerMonth beinhalten
- und die danach nicht dem Term "Temp" beinhalten
Fragen und Antworten
ToDo