HourCounter: Unterschied zwischen den Versionen
John (Diskussion | Beiträge) |
Maista (Diskussion | Beiträge) K (URL nicht mehr erreichbar) |
||
(68 dazwischenliegende Versionen von 8 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
{{Infobox Modul | |||
Bei bipolaren Ereignissen wird zusätzlich die Puls- sowie die Pausendauer ermittelt. | |ModPurpose=HourCounter dient zum Zählen von Ereignissen und zur Erfassung von Betriebs-/Ruhe-Zeiten | ||
Im vorliegenden Beispiel wird ein Betriebsstundenzähler mit einem MAX-Fensterkontakt realisiert. | |ModType=h | ||
<!-- |ModCmdRef=LightScene -- nicht erforderlich, da Modultyp x --> | |||
|ModForumArea=MAX | |||
|ModTechName=98_HourCounter.pm | |||
|ModOwner=John ([http://forum.fhem.de/index.php?action=profile;u=806 Forum] / [[Benutzer Diskussion:John|Wiki]])}} | |||
[[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 == | == Features == | ||
Zeile 7: | Zeile 14: | ||
* Ermittlung der Häufigkeit, Puls- Pausendauer pro Tag | * Ermittlung der Häufigkeit, Puls- Pausendauer pro Tag | ||
* Ermittlung der Puls-/Pausendauer der letzten Schaltperiode | * Ermittlung der Puls-/Pausendauer der letzten Schaltperiode | ||
* Bereitstellung von Ereignissen zur Fortführung der Kumulation für Wochen- | * Bereitstellung von Ereignissen zur Fortführung der Kumulation für Tages-, Wochen- und Monatswerte | ||
* unterstützende Funktionen zur Ablage von Tages-, Wochen- und Monatswerten | * unterstützende Funktionen zur Ablage von Tages-, Wochen- und Monatswerten | ||
== Zielsetzung des WIKI-Artikels == | == Zielsetzung des WIKI-Artikels == | ||
Erläuterung der Funktionalität zur weiterführenden Diskussion im | Erläuterung der Funktionalität zur weiterführenden Diskussion im {{Link2Forum|Topic=12216|Message=72596|LinkText=Forum}}. | ||
== | |||
== | == Aktuelle Version == | ||
Das Modul ist seit dem 24.10.2014 Bestandteil von FHEM. | |||
== Definition == | == Definition == | ||
===Abstrakt=== | === Abstrakt === | ||
<pre>define <name> HourCounter <regexp_for_ON> [<regexp_for_Off>]</pre> | <pre>define <name> HourCounter <regexp_for_ON> [<regexp_for_Off>]</pre> | ||
<regexp_for_ON> ist ein regulärer Ausdruck der das Ereignis beschreibt. | <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 | Wenn auch [<regexp_for_Off>] definiert ist, so sprechen wir von einem bipolarem Ereignis, das einen | ||
EIN- sowie einen AUS-Zustand aufweist. | EIN- sowie einen AUS-Zustand aufweist. | ||
<regexp_for_ON> | |||
Die Struktur des regexp-Ausdruckes ist analog zu jener beim Notify aufgebaut. | <regexp_for_ON> beschreibt 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=== | ===Konkret=== | ||
<pre>define CN.Test HourCounter SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0</pre> | <pre>define CN.Test HourCounter SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0</pre> | ||
Hier wird der HourCounter CN.TEST definiert. | Hier wird der HourCounter CN.TEST definiert. | ||
Ein MAX-Fensterkontakt mit Namen SHUTTER. | Ein MAX-Fensterkontakt mit Namen SHUTTER.JOHN wird als Ereignis-Geber verwendet. | ||
Das Reading "onoff" wird als Trigger für unserem Zähler genutzt. | Das Reading "onoff" wird als Trigger für unserem Zähler genutzt. | ||
Bei den Fensterkontakten sehen diese Ereignisse wie folgt aus: | Bei den Fensterkontakten sehen diese Ereignisse wie folgt aus: | ||
Zeile 44: | Zeile 46: | ||
</pre> | </pre> | ||
Soll ein Dummy als Ereignis-Geber verwendet werden, lautet die Definition wie folgt: | |||
<pre> define CN.Test HourCounter myDummy:1 myDummy:0</pre> | <pre> define CN.Test HourCounter myDummy:1 myDummy:0</pre> | ||
Die neue Instanz weist folgende Struktur auf | Die neue Instanz weist folgende Struktur auf: | ||
<pre> | <pre> | ||
Internals: | Internals: | ||
CFGFN | CFGFN | ||
DEF SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0 | DEF SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0 | ||
NAME CN.Test | NAME CN.Test | ||
NR | NR 738 | ||
NTFY_ORDER 50-CN.Test | NTFY_ORDER 50-CN.Test | ||
STATE | STATE 1 | ||
TYPE HourCounter | TYPE HourCounter | ||
Readings: | 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: | Helper: | ||
OFF_Regexp SHUTTER.JOHN:onoff:.0 | OFF_Regexp SHUTTER.JOHN:onoff:.0 | ||
ON_Regexp SHUTTER.JOHN:onoff:.1 | ON_Regexp SHUTTER.JOHN:onoff:.1 | ||
calledByEvent | calledByEvent | ||
changedTimestamp | changedTimestamp 2014-02-04 21:00:01 | ||
forceClear | forceClear | ||
forceDayChange | forceDayChange | ||
forceHourChange | |||
forceMonthChange | |||
forceWeekChange | |||
isFirstRun | isFirstRun | ||
sdRoundHourLast | sdRoundHourLast 1391544000 | ||
value -1 | value 0 | ||
cmdQueue: | |||
</pre> | |||
Damit nicht zu viele Ereignisse in den Log-Dateien landen, kann man diese sinnvoll einschränken, so daß | |||
nur Änderungen das Feuern von Events auslösen | |||
<pre> | |||
attr CN.BRENNER event-on-change-reading .* | |||
</pre> | |||
Wenn sich nun jedoch über Stunden und Tage nichts ändert, sieht man in den Charts keine Daten mehr. | |||
Mit dieser Anweisung wird erreicht, daß alle Readings nach Aktualisierung spätesten nach 1 Stunden einen Event feuern, auch wenn sich der Wert nicht ändert. | |||
Eine Ausnahme hiervon sollen machen die tick*-Readings, deren Events sollen immer sofort gefeuert werden, | |||
wenn sie aktualisiert werden. | |||
<pre> | |||
attr CN.BRENNER event-min-interval tick.*:0,.*:3600 | |||
</pre> | </pre> | ||
Zeile 86: | Zeile 105: | ||
{| class="wikitable sortable" | {| class="wikitable sortable" | ||
|- | |- | ||
! Reading !! Beschreibung | ! Reading !! class="unsortable" | Beschreibung | ||
|- | |- | ||
| clearDate || Datum, zu dem alle kumulativen Readings über set .. clear gelöscht wurden | | clearDate || Datum, zu dem alle kumulativen Readings über set .. clear gelöscht wurden | ||
Zeile 94: | Zeile 113: | ||
| countsPerDay || Tageszähler für das Auftreten des ON-Ereignisses | | countsPerDay || Tageszähler für das Auftreten des ON-Ereignisses | ||
|- | |- | ||
| pauseTimeIncrement || Zeitdauer in Sekunden der | | pauseTimeIncrement || Zeitdauer der Pausen-Phase in Sekunden ; wird zyklisch aktualisiert | ||
|- | |||
| pauseTimeEdge || Zeitdauer der letzten komplettierten Pausen-Phase | |||
|- | |- | ||
| pauseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen | | pauseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen | ||
Zeile 100: | Zeile 121: | ||
| pauseTimePerDay || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen des akt. Tages | | pauseTimePerDay || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen des akt. Tages | ||
|- | |- | ||
| pulseTimeIncrement || Zeitdauer | | pulseTimeIncrement || Zeitdauer der Puls-Phase in Sekunden ; wird zyklisch aktualisiert | ||
|- | |||
| pulseTimeEdge || Zeitdauer der letzten komplettierten Pulse-Phase | |||
|- | |- | ||
| pulseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen | | pulseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen | ||
Zeile 109: | Zeile 132: | ||
mit 1=letztes Ereignis war ON-Ereignis <br /> | mit 1=letztes Ereignis war ON-Ereignis <br /> | ||
und 0=letztes Ereignis war OFF-Ereignis | und 0=letztes Ereignis war OFF-Ereignis | ||
|- | |||
| tickUpdated || Event wird gefeuert, wenn die operativen Readings beschrieben worden sind (nicht unbedingt gändert) | |||
|- | |||
| tickChanged || Event wird nach Änderung von value gefeuert | |||
|- | |||
| tickDay || Event wird nach Tageswechsel gefeuert bevor die Tageszähler resettiert werden | |||
|- | |||
| tickHour || Event wird nach Stundenwechsel gefeuert | |||
|- | |||
| tickWeek || Event wird nach Wochenwechsel gefeuert | |||
|- | |||
| tickMonth || Event wird nach Monatswechsel gefeuert | |||
|- | |||
| tickYear || Event wird nach Jahreswechsel gefeuert | |||
|- | |||
| forceHourChange || simuliert einen Stundenwechsel | |||
|- | |||
| forceDayChange || simuliert einen Tageswechsel | |||
|- | |||
| forceWeekChange || simuliert einen Wochenwechsel | |||
|- | |||
| forceMonthChange || simuliert einen Monatswechsel | |||
|- | |||
| forceYearChange || simuliert einen Jahreswechsel | |||
|} | |} | ||
Zeile 120: | Zeile 167: | ||
[[Datei:13_11_15_HourCounter_Chart.png]] | [[Datei:13_11_15_HourCounter_Chart.png]] | ||
* die Kurve "Brenner EIN" zeigt die Trigger-Signale | * die Kurve "Brenner EIN" zeigt die Trigger-Signale des 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 "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 "Betriebsstunden" zeigt die aufgelaufene Zeit aus dem Reading pulseTimePerDay umgerechnet zu Stunden | ||
Zeile 127: | Zeile 174: | ||
=== Chart der Aktualwerte === | === Chart der Aktualwerte === | ||
Wir benötigen hierzu ein File-Archiv für die aufgelaufenen Daten. | Wir benötigen hierzu ein File-Archiv für die aufgelaufenen Daten. | ||
<pre>define CN.Test.File FileLog ./log/CN.Test-%Y.log (CN\.Test:.*)</pre> | <pre>define CN.Test.File FileLog ./log/CN.Test-%Y.log (CN\.Test:.*)</pre> | ||
Zeile 154: | Zeile 200: | ||
Die "Brenner Starts" können wir direkt von countsPerDay ableiten.<br /><br /> | Die "Brenner Starts" können wir direkt von countsPerDay ableiten.<br /><br /> | ||
Für die "Betriebsstunden" verwenden wir pulseTimePerDay. Da diese in Sekunden vorliegen teilen wir den Wert durch 3600, um Stunden zu erhalten. | Für die "Betriebsstunden" verwenden wir pulseTimePerDay. Da diese in Sekunden vorliegen teilen wir den Wert durch 3600, um Stunden zu erhalten. | ||
<pre>fld[3]/=3600</pre> | <pre>$fld[3]/=3600</pre> | ||
Als letzten versorgen wir noch die Kurve "Dauer" mit pulseTimeIncrement. Da wir diese in Minuten haben wollen ist ebenfalls eine Umformung nötig. | Als letzten versorgen wir noch die Kurve "Dauer" mit pulseTimeIncrement. Da wir diese in Minuten haben wollen ist ebenfalls eine Umformung nötig. | ||
<pre>fld[3]/=60</pre> | <pre>$fld[3]/=60</pre> | ||
Somit sind die Basis-Kurven angelegt. | Somit sind die Basis-Kurven angelegt. | ||
=== | === Erweiterungen === | ||
Es wurden 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 ==== | |||
Die jeweils aktuelle Version von 99_UtilsHourCounter kann über diesen | |||
[https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/99_UtilsHourCounter.pm Link] bezogen werden. | |||
<br /><br /> | |||
Die Datei ist in da Unterverzeichnis FHEM vom FHEM-Homverzeichnis zu kopieren. | |||
<br /> | |||
z.B. bei Raspberry Pi: /opt/fhem/FHEM | |||
<br /><br /> | |||
Nach dem Kopieren und einen Neustart von FHEM kann man überprüfen, ob FHEM diese Datei findet. | |||
Wenn man das Menü "Edit Files " anwählt, wird auch 99_UtilsHourCounter angezeigt. | |||
==== Readings ==== | |||
99_UtilsHourCounter aus dem contrib-Verzeichnis der FHEM-Installation erweitert den HourCounter um folgende Funktionen: | |||
{| class="wikitable sortable" | |||
|- | |||
! Reading !! class="unsortable" | 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 | |||
|- | |||
| appCountsPerYear || Jahreszähler, wird bei Jahreswechsel aktualisiert | |||
|- | |||
| appCountsPerYearTemp || Arbeitszähler zu appCountsPerYear | |||
|- | |||
| 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 | |||
|- | |||
| appOpHoursPerYear || Betriebsstunden des Jahres | |||
|- | |||
| appOpHoursPerYearTemp || Arbeitszähler appOpHoursPerYear | |||
|- | |||
| appUtilization || Auslastung = pulseTimePerDay /(vergangene Sekunden seit Tagesbeginn) * 100 | |||
|- | |||
| appUtilizationTemp || Arbeitsvariable zu appUtilization | |||
|} | |||
Beginn der Woche ist jeweils der Sonntag.<br /> | |||
Mit folgender Anweisung aktivieren wir die Erweiterungen: | |||
:<code>define CN.EVENT notify CN\..*:tick.* { appHCNotify("$NAME","$EVTPART0","$EVTPART1");;}</code> | |||
Spätestens nach einer steigenden und einer fallenden Flanke sind die zuvor genannten app*-Readings zu sehen. | |||
{{Randnotiz|RNTyp=Info|RNText=Die gezeigten define-Anweisungen müssen jeweils in einer Zeile stehen (keine Zeilenumbrüche!).}} | |||
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. | |||
===== Für Anfänger die noch keine Erfahrungen mit Regular Expressions haben:===== | |||
Benennt eure Hourcounter nach dem Muster CN.<euer Wunschname>. Dann wird der notify immer funktionieren.<br /> | |||
Beispiel: statt "PelletsCounter" wählt den Namen "CN.PelletsCounter" | |||
==== Archiv für Tages-/Wochen-/Monats-/Jahreswerte anlegen ==== | |||
Nun wollen wir die aggregierten Werte in eine eigene Datei speichern. Dies gelingt mit | |||
:<code>define CN.Test.FileDay FileLog ./log/CN.Test-Day-%Y.log CN.Test:app\w*(Utilization|PerHour|PerDay|PerWeek|PerMonth|PerYear)(?!Temp).* </code> | |||
Im Klartext: | |||
* verwende alle Werte des Counters CN.Test, deren Reading mit "app" beginnt | |||
* und die einen der Terme appUtilization|PerHour|PerDay|PerWeek|PerMonth|PerYear beinhalten | |||
* und die danach nicht dem Term "Temp" beinhalten | |||
== Fragen und Antworten == | == Fragen und Antworten == | ||
==== Betriebsstundenzähler über Leistungsmessung ableiten ==== | |||
'''Frage:''' | |||
Ich würde gerne zählen, wenn ich mehr Strom als Standy verbrauche (also mehr als 2Watt) | |||
und keine Betriebsstunden zählen, wenn der Verbrauch unter 2 Watt ist. Ist das möglich? | |||
<u>Beispiel für die Events</u> | |||
<pre> | |||
013-11-18_19:40:32 XXX power: 1.9 | |||
2013-11-18_19:40:32 XXX consumption: 2 | |||
2013-11-18_19:40:32 XXX consumptionTotal: 2 | |||
2013-11-18_19:40:36 XXX power: 27 | |||
2013-11-18_19:40:36 XXX consumption: 2 | |||
2013-11-18_19:40:36 XXX consumptionTotal: 2 | |||
2013-11-18_19:40:42 XXX power: 34.6 | |||
2013-11-18_19:40:42 XXX consumption: 2 | |||
</pre> | |||
'''Antwort''' | |||
Die hier vorgestellte Lösung überprüft ob der Wert des Events power eine oder zwei Ziffern vor dem Komma hat. | |||
Deshalb wir hier erst gezählt, wenn die Schwelle von 10Watt überschritten wird. | |||
<pre> | |||
define CN.Test HourCounter XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$ XXX:power:\s[0-9]{1}(\.[0-9]{1,3})*$ | |||
</pre> | |||
Erläuterung zu <regexp_for_ON> = XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$ | |||
* "XXX" bezeichnet das Device, der Term danach ist der regexp-Filte für das On-Ereignis | |||
* "power:" das Ereignis muss mit diesem Term beginnen | |||
* "\s" es muss ein Leerzeichen folgen | |||
* "[0-9]{2,}" es müssen mindestens 2 Ziffern folgen | |||
* "(\.[0-9]{1,3})*" wenn ein Punkt folgt, dann müssen auf diesen mindestens 1..3 Ziffern folgen | |||
* "$" danach darf kein weiteres Zeichen mehr folgen | |||
'''Antwort Möglichkeit 2''' | |||
In dieser Lösung bekommt das entsprechende Device was mit HourCounter überwacht werden soll ein userReadings "onoff". Dieses Reading wird dann zum Schalten von Hour Counter verwendet: | |||
<pre> | |||
define GPIO4_DS18B20_Waermepumpe_Vorlauf GPIO4 28-000005956079 | |||
attr GPIO4_DS18B20_Waermepumpe_Vorlauf alias Wärmepumpe Vorlauf | |||
attr GPIO4_DS18B20_Waermepumpe_Vorlauf model DS18B20 | |||
attr GPIO4_DS18B20_Waermepumpe_Vorlauf room GPIO4 | |||
attr GPIO4_DS18B20_Waermepumpe_Vorlauf userReadings onoff {(ReadingsVal("GPIO4_DS18B20_Waermepumpe_Vorlauf","temperature",0) >28)?1:0;;} | |||
define Waermepumpe_HourCounter HourCounter GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1 GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0 | |||
attr Waermepumpe_HourCounter room 2_Fussbodenheizung | |||
</pre> | |||
Erläuterungen zu dem Code: "{(ReadingsVal("GPIO4_DS18B20_Waermepumpe_Vorlauf","temperature",0) >28)?1:0;;}" | |||
* "(ReadingsVal("GPIO4_DS18B20_Waermepumpe_Vorlauf","temperature",0) >28)" Diese Bedingung für das userReadings onoff prüft bei jedem Event, ob der Wert von temperature größer als 28 ist. | |||
* "?1:0" Ist dies der Fall wird das userReading onoff auf 1 gesetzt andernfalls auf 0. | |||
Auf Basis deses UserReadings wird dann der HourCounter definiert: | |||
* "GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1" Einschaltbedingung für HourCounter | |||
* "GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0" Abschaltbedingung für Hour Counter | |||
==== Welche Anwendungsfälle sind denkbar ? ==== | |||
{{Link2Forum|Topic=12216|Message=175163|LinkText=Aus dem Forum}} | |||
* Betriebsstundenzähler für meine "Fliegenkiller-Steckdose" | |||
* Nutzungsdauer beschränken für TV,Internet oder Spielkonsolen für entnervte Eltern | |||
* Nutzungsdauer ermitteln zur Energieeinsparung (Klimageräte, Ventilatoren, Dunstabzugshauben etc.) | |||
* Lüftungsverhalten ermitteln (wie lange Fenster pro Tag geöffnet) | |||
{{Link2Forum|Topic=12216|Message=195358|LinkText=Brenner Starts/Verbrauch + akkumulierte Werte}} | |||
{{Link2Forum|Topic=12216|Message=196358|LinkText=Ölverbrauch+Solar-Ladung}} | |||
[http://voizchat.de/gaszaehler-verbrauch-erfassen-mit-fhem-und-raspberry-gpio/ Gaszähler mit HourCounter realisieren] (URL tot) | |||
==== Seltene Schaltvorgänge ==== | |||
Die Schaltvorgänge sind über den Tag sehr wenige. Die Aktualisierung erfolgt immer erst | |||
bei der negativen Flanke. Wie kann man eine häufigere Aktualisierung erreichen ? | |||
'''Antwort''' | |||
Ab Version 1.0.0.6 ist wurde das Attribut "interval" eingeführt; es ist auf 60 Minuten voreingestellt und kann von 5..60 im 5 Minuten-Raster festgelegt werden. | |||
Es bestimmt, nach welcher Zeit Puls-/Pausendauer aktualisiert werden sollen, unabhängig vom Auftreten einer Schaltflanke. | |||
==== Korrekte Darstellung der akkumulierten Daten im Chart ==== | |||
'''Frage''' | |||
"appCountsPerDay: 4" bezieht sich auf die Counts des Tages 2014-06-16, trägt aber selbst den Zeitstempel 2014-06-17 und wird demnach in einem Chart auch über den Tag " 2014-06-17" dargestellt. | |||
Das Problem betrifft alle akkumulierten Daten des HourCounters. | |||
Wie erreicht man im Chart die korrekte Darstellung ? | |||
'''Antwort''' | |||
Das Thema wurde {{Link2Forum|Topic=12216|Message=239929|LinkText=hier}} disktuiert. | |||
Eine Lösung findet man mit [[LogProxy]]. | |||
Damit läßt sich ein negativer Offset für die X-Achse definieren, so daß die Daten wieder korrekt dargestellt werden. | |||
[[Kategorie:MAX | [[Kategorie:MAX]] | ||
[[Kategorie:HOWTOS]] | |||
[[Kategorie:Heizungssteuerung]] |
Aktuelle Version vom 28. Januar 2022, 20:31 Uhr
HourCounter | |
---|---|
Zweck / Funktion | |
HourCounter dient zum Zählen von Ereignissen und zur Erfassung von Betriebs-/Ruhe-Zeiten | |
Allgemein | |
Typ | Hilfsmodul |
Details | |
Dokumentation | EN / DE |
Support (Forum) | MAX |
Modulname | 98_HourCounter.pm |
Ersteller | John (Forum / Wiki) |
Wichtig: sofern vorhanden, gilt im Zweifel immer die (englische) Beschreibung in der commandref! |
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 Tages-, Wochen- und 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.
Aktuelle Version
Das Modul ist seit dem 24.10.2014 Bestandteil von FHEM.
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> beschreibt 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
Soll ein Dummy als Ereignis-Geber verwendet werden, 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:
Damit nicht zu viele Ereignisse in den Log-Dateien landen, kann man diese sinnvoll einschränken, so daß nur Änderungen das Feuern von Events auslösen
attr CN.BRENNER event-on-change-reading .*
Wenn sich nun jedoch über Stunden und Tage nichts ändert, sieht man in den Charts keine Daten mehr. Mit dieser Anweisung wird erreicht, daß alle Readings nach Aktualisierung spätesten nach 1 Stunden einen Event feuern, auch wenn sich der Wert nicht ändert. Eine Ausnahme hiervon sollen machen die tick*-Readings, deren Events sollen immer sofort gefeuert werden, wenn sie aktualisiert werden.
attr CN.BRENNER event-min-interval tick.*:0,.*:3600
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 der Pausen-Phase in Sekunden ; wird zyklisch aktualisiert |
pauseTimeEdge | Zeitdauer der letzten komplettierten Pausen-Phase |
pauseTimeOverall | Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen |
pauseTimePerDay | Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen des akt. Tages |
pulseTimeIncrement | Zeitdauer der Puls-Phase in Sekunden ; wird zyklisch aktualisiert |
pulseTimeEdge | Zeitdauer der letzten komplettierten Pulse-Phase |
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 |
tickUpdated | Event wird gefeuert, wenn die operativen Readings beschrieben worden sind (nicht unbedingt gändert) |
tickChanged | Event wird nach Änderung von value gefeuert |
tickDay | Event wird nach Tageswechsel gefeuert bevor die Tageszähler resettiert werden |
tickHour | Event wird nach Stundenwechsel gefeuert |
tickWeek | Event wird nach Wochenwechsel gefeuert |
tickMonth | Event wird nach Monatswechsel gefeuert |
tickYear | Event wird nach Jahreswechsel gefeuert |
forceHourChange | simuliert einen Stundenwechsel |
forceDayChange | simuliert einen Tageswechsel |
forceWeekChange | simuliert einen Wochenwechsel |
forceMonthChange | simuliert einen Monatswechsel |
forceYearChange | simuliert einen Jahreswechsel |
Web-Oberfläche
Anwendung
Nachfolgende Darstellung zeigt das Einschaltverhalten eines Heizungskessels zusammen mit den abgeleiteten Werten.
- die Kurve "Brenner EIN" zeigt die Trigger-Signale des 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 wurden 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
Die jeweils aktuelle Version von 99_UtilsHourCounter kann über diesen
Link bezogen werden.
Die Datei ist in da Unterverzeichnis FHEM vom FHEM-Homverzeichnis zu kopieren.
z.B. bei Raspberry Pi: /opt/fhem/FHEM
Nach dem Kopieren und einen Neustart von FHEM kann man überprüfen, ob FHEM diese Datei findet.
Wenn man das Menü "Edit Files " anwählt, wird auch 99_UtilsHourCounter angezeigt.
Readings
99_UtilsHourCounter aus dem contrib-Verzeichnis der FHEM-Installation 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 |
appCountsPerYear | Jahreszähler, wird bei Jahreswechsel aktualisiert |
appCountsPerYearTemp | Arbeitszähler zu appCountsPerYear |
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 |
appOpHoursPerYear | Betriebsstunden des Jahres |
appOpHoursPerYearTemp | Arbeitszähler appOpHoursPerYear |
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\..*:tick.* { 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.
Für Anfänger die noch keine Erfahrungen mit Regular Expressions haben:
Benennt eure Hourcounter nach dem Muster CN.<euer Wunschname>. Dann wird der notify immer funktionieren.
Beispiel: statt "PelletsCounter" wählt den Namen "CN.PelletsCounter"
Archiv für Tages-/Wochen-/Monats-/Jahreswerte 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|PerYear)(?!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|PerYear beinhalten
- und die danach nicht dem Term "Temp" beinhalten
Fragen und Antworten
Betriebsstundenzähler über Leistungsmessung ableiten
Frage: Ich würde gerne zählen, wenn ich mehr Strom als Standy verbrauche (also mehr als 2Watt) und keine Betriebsstunden zählen, wenn der Verbrauch unter 2 Watt ist. Ist das möglich?
Beispiel für die Events
013-11-18_19:40:32 XXX power: 1.9 2013-11-18_19:40:32 XXX consumption: 2 2013-11-18_19:40:32 XXX consumptionTotal: 2 2013-11-18_19:40:36 XXX power: 27 2013-11-18_19:40:36 XXX consumption: 2 2013-11-18_19:40:36 XXX consumptionTotal: 2 2013-11-18_19:40:42 XXX power: 34.6 2013-11-18_19:40:42 XXX consumption: 2
Antwort Die hier vorgestellte Lösung überprüft ob der Wert des Events power eine oder zwei Ziffern vor dem Komma hat. Deshalb wir hier erst gezählt, wenn die Schwelle von 10Watt überschritten wird.
define CN.Test HourCounter XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$ XXX:power:\s[0-9]{1}(\.[0-9]{1,3})*$
Erläuterung zu <regexp_for_ON> = XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$
- "XXX" bezeichnet das Device, der Term danach ist der regexp-Filte für das On-Ereignis
- "power:" das Ereignis muss mit diesem Term beginnen
- "\s" es muss ein Leerzeichen folgen
- "[0-9]{2,}" es müssen mindestens 2 Ziffern folgen
- "(\.[0-9]{1,3})*" wenn ein Punkt folgt, dann müssen auf diesen mindestens 1..3 Ziffern folgen
- "$" danach darf kein weiteres Zeichen mehr folgen
Antwort Möglichkeit 2 In dieser Lösung bekommt das entsprechende Device was mit HourCounter überwacht werden soll ein userReadings "onoff". Dieses Reading wird dann zum Schalten von Hour Counter verwendet:
define GPIO4_DS18B20_Waermepumpe_Vorlauf GPIO4 28-000005956079 attr GPIO4_DS18B20_Waermepumpe_Vorlauf alias Wärmepumpe Vorlauf attr GPIO4_DS18B20_Waermepumpe_Vorlauf model DS18B20 attr GPIO4_DS18B20_Waermepumpe_Vorlauf room GPIO4 attr GPIO4_DS18B20_Waermepumpe_Vorlauf userReadings onoff {(ReadingsVal("GPIO4_DS18B20_Waermepumpe_Vorlauf","temperature",0) >28)?1:0;;} define Waermepumpe_HourCounter HourCounter GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1 GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0 attr Waermepumpe_HourCounter room 2_Fussbodenheizung
Erläuterungen zu dem Code: "{(ReadingsVal("GPIO4_DS18B20_Waermepumpe_Vorlauf","temperature",0) >28)?1:0;;}"
- "(ReadingsVal("GPIO4_DS18B20_Waermepumpe_Vorlauf","temperature",0) >28)" Diese Bedingung für das userReadings onoff prüft bei jedem Event, ob der Wert von temperature größer als 28 ist.
- "?1:0" Ist dies der Fall wird das userReading onoff auf 1 gesetzt andernfalls auf 0.
Auf Basis deses UserReadings wird dann der HourCounter definiert:
- "GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1" Einschaltbedingung für HourCounter
- "GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0" Abschaltbedingung für Hour Counter
Welche Anwendungsfälle sind denkbar ?
- Betriebsstundenzähler für meine "Fliegenkiller-Steckdose"
- Nutzungsdauer beschränken für TV,Internet oder Spielkonsolen für entnervte Eltern
- Nutzungsdauer ermitteln zur Energieeinsparung (Klimageräte, Ventilatoren, Dunstabzugshauben etc.)
- Lüftungsverhalten ermitteln (wie lange Fenster pro Tag geöffnet)
Brenner Starts/Verbrauch + akkumulierte Werte
Gaszähler mit HourCounter realisieren (URL tot)
Seltene Schaltvorgänge
Die Schaltvorgänge sind über den Tag sehr wenige. Die Aktualisierung erfolgt immer erst bei der negativen Flanke. Wie kann man eine häufigere Aktualisierung erreichen ?
Antwort
Ab Version 1.0.0.6 ist wurde das Attribut "interval" eingeführt; es ist auf 60 Minuten voreingestellt und kann von 5..60 im 5 Minuten-Raster festgelegt werden. Es bestimmt, nach welcher Zeit Puls-/Pausendauer aktualisiert werden sollen, unabhängig vom Auftreten einer Schaltflanke.
Korrekte Darstellung der akkumulierten Daten im Chart
Frage
"appCountsPerDay: 4" bezieht sich auf die Counts des Tages 2014-06-16, trägt aber selbst den Zeitstempel 2014-06-17 und wird demnach in einem Chart auch über den Tag " 2014-06-17" dargestellt. Das Problem betrifft alle akkumulierten Daten des HourCounters. Wie erreicht man im Chart die korrekte Darstellung ?
Antwort
Das Thema wurde hier disktuiert. Eine Lösung findet man mit LogProxy. Damit läßt sich ein negativer Offset für die X-Achse definieren, so daß die Daten wieder korrekt dargestellt werden.