FHEM Tutorial-Reihe - Part 28: Energiemessung + Notification wenn Spülmaschine fertig
Wieviel Strom verbrauchen eigentlich Eure Geräte so? Also nicht die kleinen, die selten Mal an sind, sondern wirkliche Energiefresser wie z.B. der Kühlschrank oder ein Warmwasserspeicher. Diese Frage habe ich mir auch gestellt und mir daher für mein Z-Wave-System ein paar von den Fibaro-Zwischensteckern bestellt, welche man sowohl drahtlos schalten kann, als auch die aktuellen Leistungsdaten übermittelt bekommt. Zusätzlich lasse ich mich jetzt per Push-Notification informieren, wenn die Spülmaschine fertig ist und ich diese ausräumen kann.
Was wird benötigt?
- Eine FHEM-Installation
- Ein Zwischenstecker, welcher die aktuelle Leistung misst
- Das entsprechende Interface für FHEM
Video
Komponenten
Befehle
Generell werden alle Geräte hier beim Pairing automatisch angelegt. Daher brauchen wir nur das notify entsprechend konfigurieren. Da mache ich es eigentlich immer so, dass ich erst das notify mit einem leeren “Aktionsblock” anlege, und später in FHEM dann über den viel besseren Editor dann programmiere.
define n_KU_Spuelmaschine_start notify KU_Spuelmaschine:power:.* {}
Dann bearbeite ich das define und fügen folgenden Inhalt ein:
KU_Spuelmaschine:power:.* {
if (ReadingsNum("KU_Spuelmaschine", "power", 0) > 10 && ReadingsVal("KU_Spuelmaschine", "running", "off") eq "off") {
fhem("setreading KU_Spuelmaschine running on");
}
if (ReadingsNum("KU_Spuelmaschine", "power", 0) < 1 && ReadingsVal("KU_Spuelmaschine", "running", "off") eq "on") {
fhem("setreading KU_Spuelmaschine running off");
fhem("set WEB_Pushover msg 'Spülmaschine' 'Die Spülmaschine ist fertig'");
}
}
Wie im Video erklärt erledige ich mit einem notify direkt mehrere Aufgaben. Das ist sinnvoll um den Code möglichst kurz zu halten und Dinge die zusammen gehören übersichtlich an einer Stelle zu finden. Möchte ich die Notification also irgendwann einmal nicht mehr haben, lösche ich einfach das komplette notify und muss nicht an zig andere Dummy-Devices denken.
Erklärung
Generell funktioniert das Ganze wie folgt:
- Die Spülmaschine wird eingeschaltet und das power-Reading ändert sich, wodurch das notify reagiert
- Sollte die Spülmaschine in diesem Moment mehr als 10 Watt verbrauchen, gehen wir davon aus dass sie eingeschaltet wurde und setzen auf der Spülmaschine das Reading “running” auf “on”. Falls der Wert kleiner als 10 Watt ist, passiert gar nichts, da das Reading “running” ja noch nicht auf “on” steht.
- Jetzt kommen noch zig hundert power-Änderungen, welche den aktuellen Verbrauch dokumentieren. Da aber alle Werte für “power” größer als 1 Watt sind und das Reading “running” ja bereits auf “on” steht, passiert nichts.
- Wenn die Spülmaschine fertig ist, schaltet sich diese ab. Dadurch ändert sich das Reading “power” natürlich wieder (z.B. auf 0) und nun greift der zweite Teil des notify, denn 0 ist kleiner als 1 und das Reading “running” steht ja noch auf “on”.
- Wir setzen also in diesem Fall das Reading “running” wieder auf “off” (um die erste if-Bedingung wieder scharf zu machen) und senden eine Push-Notification, dass die Spülmaschine jetzt fertig ist.
Eigentlich gar nicht so schwer, oder? Und das alles, ohne auch nur an Watchdog, Dummy oder was auch immer zu denken. Keep it simple!
Zusätzliches
Falls der Editor nicht so schön aussehen sollte wie bei mir, setzt das Attribut “JavaScripts” auf dem WEB-Device einmal wie folgt:
attr WEB JavaScripts codemirror/fhem_codemirror.js
Update
{ReadingsTimestamp("KU_Spuelmaschine", "running", 0)} // Gibt mir die aktuelle Zeit von einem Reading zurück
{time_str2num(ReadingsTimestamp("KU_Spuelmaschine", "running", 0))} // Formatiert die Reading-Zeit in einen Unix-Timestamp
{ReadingsAge("KU_Spuelmaschine", "running", 0)} // Alter des Readings in Sekunden
{gettimeofday()} // 1529514418.37716 (aus Time::HiRes, gibt die Sekunden und Mikrosekunden seit 01.01.1970 00:00 zurück)
{time()} // 1529514712 (gibt die Sekunden seit 01.01.1970 00:00 zurück => Unix Timestamp)
{localtime()} // Wed Jun 20 19:08:33 2018 (gibt ein Array zurück => $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
{gmtime()} // Wed Jun 20 17:10:32 2018 (genau wie localtime, nur GMT und nicht aktuelle Zeitzone)
{TimeNow()} // 2018-06-20 19:05:57 entspricht {FmtDateTime(gettimeofday())}
{time_str2num(TimeNow())} // Wandelt ein FHEM-Datum in einen Unix-Timestamp um (in diesen Fall den aktuellen Zeitpunkt) => Ist also genau das gleiche wie {time()}
{time() - time_str2num(ReadingsTimestamp("KU_Spuelmaschine", "running", 0))} // Ist das gleiche wie ReadingsAge()
Datums- und Zeit-Funktionen
Nochmal etwas übersichtlicher als Tabelle. Welchen Arten gibt es, das aktuelle Datum und die Uhrzeit zu holen?
Aufruf | Rückgabe | Erklärung | |
---|---|---|---|
Aktueller Unix-Timestamp | time() |
1529514712 | Sekunden seit 01.01.1970 00:00 |
Aktueller Unix-Timestamp | gettimeofday() |
1529514418.37716 | Mikrosekunden seit 01.01.1970 00:00 |
Aktuelles Datum und Uhrzeit | localtime() |
Wed Jun 20 19:08:33 2018 | Array mit allen Zeitinformationen |
Aktuelles Datum und Uhrzeit (GMT) | gmtime() |
Wed Jun 20 17:10:32 2018 | Array mit allen Zeitinformationen |
Aktuelles Datum und Uhrzeit (FHEM) | TimeNow() |
2018-06-20 19:05:57 | String im FHEM-Format |
Hilfs-Funktionen (Umrechnung)
Funktion | Rückgabe | Beispiel | |
---|---|---|---|
FHEM-Datum zu Unix-Timestamp | time_str2num($v) |
1529514712 | time_str2num(TimeNow()) == time() |
Unix-Timestamp zu FHEM-Datum | FmtDateTime($v) |
2018-06-20 19:05:57 | FmtDateTime(gettimeofday()) == TimeNow() |
Zeitstempel eines Readings | ReadingsTimestamp($g, $r, $d) |
2018-06-20 19:05:57 | ReadingsTimestamp('KU_Spuelmaschine', 'running', 0) |
Alter eines Readings (in Sekunden) | ReadingsAge($g, $r, $d) |
154 | ReadingsAge('KU_Spuelmaschine', 'running', 0) |
Beispiele
Aufgabe | Code |
---|---|
Unix-Timestamp eines Readings | time_str2num(ReadingsTimestamp('KU_Spuelmaschine', 'running', 0)) |
Alter eines Readings in Sekunden entspricht ReadingsAge | time() - time_str2num(ReadingsTimestamp('KU_Spuelmaschine', 'running', 0)) |
Aktuelle Uhrzeit im FHEM-Format | strftime('%H:%M:%S', localtime()) |
Perl-Beispiele aus dem Video
Für die myUtils - eine Funktion um mit Zeitdifferenzen leichter zu Arbeiten. Die Funktion gibt entweder die Zeit in Sekunden seit der Änderung des angegebenen Readings zurück, oder -1 falls das Reading nicht gefunden wurde. Theoretisch könnte auch 0 zurückgegeben werden, wenn diese Funktion in der gleichen Sekunde nach der Änderung eines Readings aufgerufen wird.
Anstatt dieser Funktion kann auch ReadingsAge() aus dem Core genutzt werden.
sub secondsSinceReadingChange($$) {
my ($device, $reading) = @_;
my $readingsTimestamp = ReadingsTimestamp($device, $reading, "failed");
if ($readingsTimestamp ne "failed") {
return time() - time_str2num($readingsTimestamp);
} else {
return -1;
}
}
KU_Spuelmaschine:power:.* {
# Wenn die Spülmaschine eingeschaltet wurde
if (ReadingsNum($NAME, "power", 0) > 10 && ReadingsVal($NAME, "running", "off") eq "off") {
fhem("setreading $NAME running on");
}
# Wenn die Spülmaschine länger als 5 Minuten lief und dann keinen Strom mehr verbraucht -> Notification
if (ReadingsNum($NAME, "power", 0) < 1 && ReadingsVal($NAME, "running", "off") eq "on") {
if (secondsSinceReadingChange($NAME, "running") > 300) {
fhem("set WEB_Pushover msg 'Spülmaschine' 'Die Spülmaschine ist fertig'");
fhem("set lametric msg 'i1167' 'Spülmaschine fertig' 'notifications:water1' '1'");
fhem("set Wunderlist addTask Spülmaschine ausräumen");
}
fhem("setreading $NAME running off");
}
# Wenn Fehler aufgetreten ist, und die Spülmaschine nach 4 Stunden nicht automatisch ausgegangen ist
if (secondsSinceReadingChange($NAME, "running") > 14400 && ReadingsVal($NAME, "running", "off") eq "on") {
fhem("setreading $NAME running off");
fhem("set WEB_Pushover msg 'Spülmaschine' 'Die Spülmaschine läuft seit 4 Stunden und ist nicht ausgegangen'");
}
}