[90_at.pm] ein at immer am Monatsletzten ausführen lassen

Begonnen von betateilchen, 01 Januar 2021, 12:09:53

Vorheriges Thema - Nächstes Thema

betateilchen

Es gibt ja immer wieder den Wunsch, am Monatsletzten um 23:59 Uhr irgendwelche Aufgaben von FHEM erledigen zu lassen (z.B. bestimmte Monatswerte loggen).
Bisher ist der gerne zitierte Lösungsweg "prüfe jeden Tag um 23:59, ob morgen ein neuer Monat ist. Wenn nicht, mache nix."
Dafür existiert dann ein at, das 30 von 31 Mal im Monat überhaupt nix zu tun hat. Effektiv ist das nicht.

Mit der hier beschriebenen Funktion ultimo() wird die Aufgabe eleganter gelöst.
Die Funktion kann man in die 99_myUtils.pm einbauen und dann verwenden.


sub ultimo {
  my ($h,$m,$s) = @_;
  $h //= 23;
  $m //= 59;
  $s //= 0;
  my $add = $data{AT_RECOMPUTE} == 1 ? DAYSECONDS : 0;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time+$add);
  my ($nm, $ny) = ($mon == 11) ? (0,$year+1) : ($mon+1,$year);
  return timelocal($s,$m,$h,1,$nm,$ny) - DAYSECONDS;
}


Man definiert einmalig ein wiederholendes at device, bei dem das Ergebnis aus der Funktion als Zeitangabe verwendet wird.


defmod at_ultimo at *{ultimo} {}


Damit wird ein at angelegt, das jeweils am letzten Tag des Monats um 23:59 Uhr irgendwas ausführt (im Beispiel wird nix gemacht, deshalb stehen da nur die zwei geschweiften Klammern).


Internals:
   COMMAND    {}
   DEF        *{ultimo} {}
   FUUID     
   NAME       at_ultimo
   NR         269
   PERIODIC   yes
   RELATIVE   no
   REP        -1
   STATE      Next: 2021-01-31 23:59:00
   TIMESPEC   {ultimo}
   TRIGGERTIME 1612133940
   TRIGGERTIME_FMT 2021-01-31 23:59:00
   TYPE       at
   READINGS:
     2020-12-31 23:59:00   state           Next: 2021-01-31 23:59:00
Attributes:


Wie man am reading "state" erkennt, wurde das at gestern Abend um 23:59 Uhr ausgeführt und dann der nächste Ausführungszeitpunkt für den 31.01.2021 ermittelt.

Für Fortgeschrittene: man kann den Funktionsaufruf auch um eine Uhrzeit ergänzen. ultimo(17,23,45) würde ein at anlegen, das am Monatsletzten um 17:23:45 Uhr ausgeführt wird. Die Angabe ist optional, standardmäßig wird 23:59 Uhr verwendet.




Mein Vorschlag/Wunsch wäre, dass die Funktion innerhalb von FHEM bereitgestellt wird, entweder im at-Modul selbst oder in der 99_Utils.pm.
Sollte dies befürwortet werden, wird der fehlende commandref-Teil zur Funktion natürlich noch von mir nachgeliefert.


--
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

Wenn die Aufnahme auch von Anderen gewuenscht wird, dann werde ich es machen.
Vorschlag: 90_at.pm, es sei denn jemandem faellt es ein, wie es mit anderen Modulen verwendbar ist.

Eine flexiblere aber auch aufwendigere Variante ist die Abfrage einer selbstdefinierten holiday-Instanz.

betateilchen

Zitat von: rudolfkoenig am 01 Januar 2021, 12:51:34
Eine flexiblere aber auch aufwendigere Variante ist die Abfrage einer selbstdefinierten holiday-Instanz.

Dazu bräuchte man aber mindestens zwei devices, zum Einen die holiday Instanz selbst und zum Anderen ein notify oder at oder irgendwas anderes, um die holiday Instanz auszuwerten.
Außerdem müsst das holiday-file regelmäßig (z.B. Schaltjahr) manuell gepflegt werden.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

Ich dachte eher an etwas wie
define myAt at *17:23:45 { fhem("set lamp on") if(Value("MyHoliday") eq "ultimo") }
Aber ich will es gar nicht als die bessere Loesung anpreisen, man muss definitiv ein holiday definieren und die Datei manuell fuellen.

betateilchen

Genau DIESES Vorgehen hatte ich doch eingangs beschrieben, und genau das wollte ich ändern: Dein at wird jeden Tag ausgeführt und hat 30 von 31 Mal pro Monat nix zu tun  8)

Zitat von: betateilchen am 01 Januar 2021, 12:09:53
EBisher ist der gerne zitierte Lösungsweg "prüfe jeden Tag um 23:59, ob morgen ein neuer Monat ist. Wenn nicht, mache nix."
Dafür existiert dann ein at, das 30 von 31 Mal im Monat überhaupt nix zu tun hat.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

Das ist richtig, die Konfiguration moechte ich aber sehen, wo man das merkt.

betateilchen

Zitat von: betateilchen am 01 Januar 2021, 12:09:53
wird der fehlende commandref-Teil zur Funktion natürlich noch von mir nachgeliefert.

für den Fall der Fälle :)


Index: 90_at.pm
===================================================================
--- 90_at.pm    (Revision 23474)
+++ 90_at.pm    (Arbeitskopie)
@@ -445,7 +445,7 @@
       any spaces or tabs.<br>

       &lt;datespec&gt; is either ISO8601 (YYYY-MM-DDTHH:MM:SS) or number of
-      seconds since 1970.
+      seconds since 1970 or {perlfunc()}.
     </ul>
     <br>

@@ -502,6 +502,16 @@
           cron or filter the date in a perl expression, see the last example and
           the section <a href="#perl">Perl special</a>.
       </li>
+      <li>To execute a FHEM command on every last day of the month,<br/>
+          the function <code>ultimo()</code> can be used as perlfunc for datespec.</br>
+          <code>define at_ultimo at *{ultimo()} set lamp1 off</code><br/>
+          This will create an at device which will be executed at 23:59:00
+          on the last day of month.</br>
+          ultimo() can take additional parameters to specify an other time on this day<br/>
+          <code>define at_ultimo at *{ultimo(12,23,45)} set lamp1 off</code><br/>
+          This will create an at device which will be executed ad 12:34:45
+          on the last day of month.<br/>
+      </li>
     </ul>
     <br>
   </ul>
@@ -632,7 +642,7 @@
       {perlfunc()} darf keine Leerzeichen enthalten.<br>

       &lt;datespec&gt; ist entweder ISO8601 (YYYY-MM-DDTHH:MM:SS) oder Anzahl
-      der Sekunden seit 1970.
+      der Sekunden seit 1970 oder {perlfunc()}.

     </ul>
     <br>
@@ -690,6 +700,18 @@
       filtern. Siehe hierzu das letzte Beispiel und das <a href="#perl">Perl
       special</a>.  </li>

+      <li>Um einen FHEM Befehl immer am letzten Tag des Monats auszuführen,
+          kann die Funktion <code>ultimo()</code> als perlfunc für eine datespec
+          verwendet werden.</br>
+          <code>define at_ultimo at *{ultimo()} set lamp1 off</code><br/>
+          Hiermit wird ein at device erzeugt, der immer am letzten Tag des Monats
+          um 23:59:00 Uhr ausgeführt wird.<br/>
+          ultimo() kann drei optionale Parameter verarbeiten, um eine andere Uhrzeit
+          anzugeben.<br/>
+          <code>define at_ultimo at *{ultimo(12,23,45)} set lamp1 off</code><br/>
+          Es wird ein at device erzeugt, das immer um 12:34:45 am Monatsletzten
+          ausgeführt wird.<br/>
+      </li>
     </ul>
     <br>
   </ul>
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

guhu

FHEM 5.9 auf Synology DS918+ (in Docker), HM-CFG-USB2 mit hmlan, HM-CC-RT-DN, HM-SEC-SC-2, nanoCUL,a-culfw,deCONZ,Brennenstuhl-Steckdosen,-FB
Module:ENIGMA2,SONOS,FRITZBOX,FB_CALLLIST,WDT_TIMER,VCONTROL300,WITHINGS

rudolfkoenig

Ich wollte es als at_ultimo() in 90_at.pm einbauen.
Beim Testen gabs aber eine Fehlermeldung:
fhem> define at_ultimo at {at_ultimo}  BLA
the function "at_ultimo" must return a timespec and not Undefined subroutine &main::timelocal called at ./FHEM/90_at.pm line 424.
.
fhem>

betateilchen

ups...

In meiner 99_myUtils.pm steht dazu (schon lange, nicht wegen dieser neuen Funktion)

use Time::Local;

Ich hoffe, dass das im perl Standard vorhanden ist, ansonsten sollten wir die Funktion vielleicht aus 90_at.pm rauslassen, um keine neuen Abhängigkeiten zu schaffen.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

Soweit ich sehe, entspricht timelocal in diesem Fall dem bereits eingesetzten mktime, ich habe es ausgetauscht. Bei mir fuehrt das trotzdem zu:
PERL WARNING: Use of uninitialized value $data{"AT_RECOMPUTE"} in numeric eq (==) at ./FHEM/90_at.pm line 420.

Das habe ich durch Weglassen der == 1 gefixt. Gabs bei Dir damit keine Probleme?

Habe die Funktion als at_ultimo samt Doku eingecheckt. Damit ist sie als
defmod at_ultimo at *{at_ultimo} {}
aufzurufen

betateilchen

Zitat von: rudolfkoenig am 12 Januar 2021, 20:47:41
Das habe ich durch Weglassen der == 1 gefixt. Gabs bei Dir damit keine Probleme?

Das war mir nicht aufgefallen.

Zitat von: rudolfkoenig am 12 Januar 2021, 20:47:41
Habe die Funktion als at_ultimo samt Doku eingecheckt.

super, Danke!
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

HeikoGr

Ich versetehe den Quellcode leider nicht 100%, aber funktioniert die vorgestellte ultimo Funktion auch mit Schaltsekunden (https://de.wikipedia.org/wiki/Schaltsekunde)?
*duckundwegrenn*


betateilchen

Hast Du vor dem Schreiben Deiner Frage wenigstens mal die Suchfunktion hier im Forum benutzt?
Die Frage bezüglich Schaltsekunden in FHEM hat Rudi schon vor zwei Jahren beantwortet.

Zitat von: rudolfkoenig am 05 Februar 2019, 11:37:38
Zitat von: Christoph Morrison am 05 Februar 2019, 11:01:10
Darf ich anmerken, dass Tage nicht immer exakt 86400 Sekunden lang sind?

Aus Sicht von FHEM ist das aber egal, die Bibliotheken ignorieren die Schaltsekunde, wenn es um das Konvertieren der Zeit seit 1970 geht.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

rudolfkoenig

In der Berechnungen zugrundeliegenden "Anzahl Sekunden seit 1970" sind Schaltsekunden nicht vorhanden. Wenn eine Schaltsekunde hinzugefuegt/weggenommen wird, dann streckt/verkuerzt NTP/etc den Taktgeber leicht, so dass nach eine Weile die Uhrzeit wirder stimmt. D.h. der Computer duerfte nie 23:59:60 anzeigen, und auch ein 23.59:59 gibts immer.