Befehl für off-for-timer in 99_myUtils.pm

Begonnen von synaps-o-dan, 24 Juni 2017, 17:43:39

Vorheriges Thema - Nächstes Thema

synaps-o-dan

Hallo zusammen,
als Fingerübung habe ich einen Funktion für die 99_myUtils.pm gebaut, die analog des Befehls on-for-timer, der für viele schaltbare Devices existiert, eine Funktionalität für off_for_timer zu realisieren. Diese fehlt bei vielen  Devices, z.B. Homematic. Die Funktion schaltet das angegebene Device aus, falls es an ist, und legt ein at an, das das Device nach der ebenfalls anzugebenden Zeitdauer wieder einschalter. Dazu kommen noch einige Abfragen, z.B. ob das Device existiert usw. Vielleicht kann der eine oder andere den Befehl ja gebrauchen. Manöverkritik ist willkommen.
Folgendes kommt in die 99_myUtils.pm:
sub off_for_timer {
my $device = shift @_;
my $timespan = shift @_;

# $device und $timespan muessen definert sein, sonst Abbruch
if (not defined $device) {
Log3 (undef, 1, "off_for_timer: Das device wurde nicht angegeben, ich tue nichts.");
return -1;
}
if (not defined $timespan) {
Log3 (undef, 1, "off_for_timer: Die Zeitdauer wurde nicht angegeben, ich tue nichts.");
return -2;
}

# Festlegen, wie geschwätzig die Funktion im logfile sein wird.
# loglever 1: Alles wird mitgeloggt
# loglevel 5: nur Fehler werden mitgeloggt
my $loglevel = 5;

Log3 (undef, $loglevel, "off-for-timer wurde aufgerufen, device: \"$device\", Zeitdauer: $timespan Sekunden.");

# Pruefen, ob das device ueberhaupt existiert, ansonsten Abbruch
if (not $defs{$device}) {
Log3 (undef, 1, "off_for_timer: Das device: \"$device\", existiert nicht, ich tue nichts.");
return -3;
}

# Zum Wiedereinschalten des device wird spaeter ein at definiert. Falls es das bereits gibt, wird es erst geloescht.
my $name_at = 'at_off_'.$device;
if (Value($name_at) ne "") {
Log3 (undef, $loglevel, "off-for-timer hat bereits ein at mit dem Namen \"$name_at\" gefunden (Value: ".Value($name_at)."), ich lösche es.");
fhem("delete $name_at");
}
else {
Log3 (undef, $loglevel, "off-for-timer wird ein at mit dem Namen \"$name_at\" erzeugen.");
}

# Welchen Status hat das device?
my $Status = ReadingsVal($device, 'state', 'notfound');
Log3 (undef, $loglevel, "off_for_timer: Der state des Gerätes \"$device\" ist \"$Status\".");

# Das hier sollte nicht passieren: es wird kein Status gefunden. Also Abbruch
if ($Status eq 'notfound') {
Log3 (undef, 1, "off_for_timer: Das Gerät \"$device\" hat keinen state on/off, ich tue nichts.");
return -4;
}
elsif ($Status eq 'on') {
Log3 (undef, $loglevel, "off_for_timer: Das Gerät \"$device\" ist an, ich schalte es aus und in $timespan Sekunden wieder an.");
fhem("set $device off");
}
elsif ($Status eq 'off') {
Log3 (undef, $loglevel, "off_for_timer: Das Gerät \"$device\" ist aus, ich schalte es in $timespan Sekunden wieder an.");
}
else {
# Falls ein anderer Status als on oder off zurueckgegeben wird
Log3 (undef, 1, "off_for_timer: Das Gerät \"$device\" hat einen state \"$Status\". Mit diesem state kann ich nicht anfangen, ich tue nichts.");
return -5;
}

# Definiere ein at, um das device wieder einzuschalten nach der timespan
# to do: timespan > 60 Sekunden zulassen, indem eine korrekte Zeitangabe fuer das at erzeugt wird
my $body_at = "define $name_at at +00:00:".sprintf("%02d", $timespan)." set $device on";
Log3 (undef, $loglevel, "off_for_timer: Ich erzeuge nun das folgende at: $body_at");
fhem ($body_at);

# Goodie: das at wird in die Raeume des device gelegt
my $room_at = AttrVal($device, 'room', undef);
Log3 (undef, $loglevel, "off_for_timer: Das at $name_at kommt in den Raum $room_at.");
fhem("attr $name_at room $room_at");
fhem('save');

return 0;
}


To Dos:
1. timespan > 60 Sekunden zulassen, indem eine korrekte Zeitangabe für das at erzeugt wird

edit 2017-06-24: Zwei Anmerkungen vom User KölnSolar eingearbeitet:
1. pattern matching durch Vergleichsoperatoren ersetzt
2. Wenn device off, wird es in $timespan Sekunden eingeschaltet

Grüße,
Daniel
fhem auf Raspberry Pi 3
5 x Set aus jew. 1x FHT80B + 1xFHT8V + 1x FHT80TF-2
HM: 1 x HM-ES-PMSw1-Pl, 2 x HM-LC-Sw1-FM, 2 x HM-LC-Sw1PBU-FM, 3 x HM-Sec-SD, 2 x HM-PB-2-WM55, 2 x HM-Sec-MDIR-2
3 x EM-1000 EM
Onewire: insgesamt 11 Onewire-Sensoren an einem LinkUSB Adapter

KölnSolar

Hi Daniel,
dann denke ich mal laut  ;)
Find ich gut das zu posten. Hilft dem ein oder anderen bestimmt.

Sicher, dass das
Zitat$Status =~ 'notfound'
funktioniert ? Vergleichsoperator müsste doch ne bei Stringvergleich sein(meint der auch nicht Perlexperte).

Das lesen und ggfs. löschen des at's könnest Du Dir mit defmod ersparen.

Warum nicht auch ausführen, wenn das device off ist ? Analog on-for-timer.

Grüße Markus
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

synaps-o-dan

Hallo Markus,
danke für dass Feedback.

Zitat von: KölnSolar am 24 Juni 2017, 18:10:26
Vergleichsoperator müsste doch ne bei Stringvergleich sein(meint der auch nicht Perlexperte).
Mit =~ wird ein pattern matching (Stichwort regular expressions) angeworfen, im Prinzip schaut Perl bei $Status =~ 'notfound' nach, ob der Sting 'notfound' im Inhalt der Variable $Status vorkommt und gibt im skalaren Kontext die Anzahl der matches zurück. Mit den Vergleichsoperatoren ne, eq usw. prüft Perl ob zwei String identisch sind. In dem Beispiel hätte ich eigentlich besser Vergleichsoperatoren verwendet, wegen besserer Lesbarkeit, besserer Wartbarkeit des Codes und besserer Performance. Ich werde das gleich ändern. =~ funktioniert aber auf jeden Fall.

Zitat von: KölnSolar am 24 Juni 2017, 18:10:26
Das lesen und ggfs. löschen des at's könnest Du Dir mit defmod ersparen.
Grüße Markus
defomd kenne ich noch gar nicht, werde ich mir anschauen!

Zitat von: KölnSolar am 24 Juni 2017, 18:10:26
Warum nicht auch ausführen, wenn das device off ist ? Analog on-for-timer.
Hm, ich hatte mir überlegt, dass ein ausgeschaltetes device auch nach einem off-for-timer Befehl aus bleiben soll. Alternativ könnte man ein ausgeschaltetes device zeitverzögert nach $timespan Sekunden anschalten. Werde ich umsetzen.

Grüße,
Daniel
fhem auf Raspberry Pi 3
5 x Set aus jew. 1x FHT80B + 1xFHT8V + 1x FHT80TF-2
HM: 1 x HM-ES-PMSw1-Pl, 2 x HM-LC-Sw1-FM, 2 x HM-LC-Sw1PBU-FM, 3 x HM-Sec-SD, 2 x HM-PB-2-WM55, 2 x HM-Sec-MDIR-2
3 x EM-1000 EM
Onewire: insgesamt 11 Onewire-Sensoren an einem LinkUSB Adapter

KölnSolar

ZitatMit =~ wird ein pattern matching (Stichwort regular expressions) angeworfen
Jaja, wenn man in C unterwegs ist, verlieren sich die Perl-Feinheiten wieder :-[ Gut, dass ich meine Aussage mit Nicht-Perl-Experte relativiert hatte  ;D
Have fun
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

Benni

Statt des at könntest du auch ein benanntes sleep nehmen, das würde m.E. einiges vereinfachen.

Siehe sleep und cancel

justme1968

mal abgesehen von fingerübung: so etwas sollte man besser auf basis der setextensions lösen. die machen das (und noch mehr) für die meisten devices die das anbieten jetzt schon. der haupt vorteil das das verhalten, format und internals dann identisch sind und man den timer auch visualisieren kann.

ich tippe mal das  mit cmdalias und setextentions eine generelle lösung möglich ist.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968