at immer am ersten (z.B. Dienstag) oder dritten (z.B. Donnerstag) ausführen lass

Begonnen von F_Klee, 15 Februar 2024, 16:04:13

Vorheriges Thema - Nächstes Thema

F_Klee

Hallo Leute,
um ein at immer am Letzten eines Monats ausführen zu lassen gibt es ja die Funktion at_ultimo(). Schön wäre vielleicht eine weitere Funktion, damit ein at immer am (z.B) ersten Dienstag im Monat ausgeführt wird. Häufig werden ja Treffen immer am ersten und dritten Dienstag eines Monats zu einer festen Uhrzeit durchgeführt (kann natürlich auch jeder andere Wochentag sein  ;) ). Man müsste also den Wochentag (1-7), 1-4 für ersten bis vierten Tag und die Uhrzeit übergeben. Eine Formel für Excel sieht so aus:

=A2-1-WOCHENTAG(A2-WT;3)+7*X

wobei A2 für das Datum des ersten Tags des aktuellen Monats, WT für den Wochentag (Montag=1) und X für den ersten bis vierten Tag steht.

Ich hatte mir Funktion at_ultimo() angesehen, habe aber mehr Fragen als Antworten  :) . Sicher kann jemand mit mehr Ahnung das recht schnell umsetzen. Könnte ja für mehrere Anwender interessant sein.

Gruß
Frank

JoWiemann

Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

rudolfkoenig

Eine aehnliche Funktionalitaet ist im holiday Modul implementiert: https://fhem.de/commandref_modular.html#holiday

Kurz: man definiert eine .holiday Datei in /opt/fhem/FHEM (Beispiele siehe /opt/fhem/FHEM/holiday/*), man legt eine holiday Inastanz an mit passenden Namen (nicht ganz intuitiv, ist aber so), und wertet die yesterday/today/tomorrow Readings dieser Instanz in einem at aus.

F_Klee

Hallo Jörg,
ich wäre nie auf die Idee gekommen, nach "Alarmclock" zu suchen  :) . Werde ich mal probieren. Danke!

betateilchen

Man könnte das wahrscheinlich auch über die cron-library machen, die es inzwischen in FHEM gibt.

Müsste man vermutlich in die 99_myUtils.pm bauen.

Oder irgendjemand erbarmt sich irgendwann, das at-Modul so zu erweitern, dass es auch Zeitangaben im Format eines crontab-Eintrags verarbeitet.
(oder baut - noch besser - gleich ein neues Modul 91_cron.pm als Alternative zu at)

Für den "ersten" bestimmten Wochentag eines Monats würde man das so bauen:

10 3 1-7 * *
und müsste dann im Ausführungsteil einfach nur noch prüfen, ob der gewünschte $wday für z.B. Mittwoch erreicht ist.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

herrmannj

Zitat von: betateilchen am 16 Februar 2024, 15:08:21Man könnte das wahrscheinlich auch über die cron-library machen, die es inzwischen in FHEM gibt.

Die cronlib kann das sogar eleganter weil sie einen erweiterten syntax implementiert. Die lib wird aktuell von JsonMod und (soweit ich weiß) einem Modul von JoWiemann verwendet. Ein 90_cron-pm habe ich geplant, falls jemand dort einspringen will: sehr gerne und ich unterstütze.

Anlaufstelle zum Einlesen des erweiterten syntax sind die Tests: https://svn.fhem.de/fhem/trunk/fhem/t/FHEM/90_Cron/10_Cronlib.t

Der erste Dienstag eines Monats kann so:
0 0 * * 2#F
0 0 * * 2#1
0 0 * * Tue#F
0 0 * * Tue#1
Letzter:
0 0 * * 2#L
0 0 * * 2#-1
oder
0 0 * * Tue#L
0 0 * * Tue#-1
Vorletzter:
0 0 * * 2#-2
btw: Freitag der 13:
0 0 13 * &5
usw...

F_Klee

Hallo Leute,
Alarmclock hat leider nicht (immer) funktioniert. Deshalb habe ich mich doch daran gesetzt, diese Funktion umzusetzen. Hier mein Ergebnis:
########################################
#
# Berechnung des Datums des
# x.ten Wochentags im Monat
#
# x=1-4 x.ter Wochentag im Monat
# x=9   letzter Wochentag im Monat
# w=1-7 Wochentag, 1=Montag
# h,m,s Uhrzeit Stunde, Minute, Sekunde
#
sub at_xwday($$$$$) {
    my ($x,$w,$h,$m,$s) = @_;

    my $ziel_tag = 0;

    # akt. Datum und Uhrzeit in Variablen aufteilen
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
        localtime(time);

    # bei Neuberechnung einen Monat addieren
    my $add = $data{AT_RECOMPUTE} ? 1 : 0;
    $mon = $mon + $add;

    my ($nm, $ny) = ($mon == 12) ? (0,$year+1) : ($mon,$year);

    # Letztes Vorkommen des Wochentags berechnen?
    if ($x == 9) {
        # Berechnen des letzten Tags des Monats
        # ersten Tag des Folgemonats als timestamp
        my ($xm, $xy) = ($mon == 11) ? (0,$year+1) : ($mon+1,$year);
        my $nt = mktime(0, 0, 0, 1, $xm, $xy);

        # letzter Tag des aktuellen Monats = erster Tag des Folgemonats minus ein Tag
        my ($letzter_tag_des_monats, $letzter_wochentag) = (localtime($nt - DAYSECONDS))[3,6];

        # Prüfen, ob der letzte Wochentag im Monat mit dem gewünschten Wochentag übereinstimmt
        $ziel_tag = $letzter_tag_des_monats;
        if ($letzter_wochentag > $w) {
           # Falls der letzte Wochentag später im Monat ist, subtrahiere die Differenz
           my $differenz = $letzter_wochentag - $w;
           $ziel_tag -= $differenz;
        } elsif ($letzter_wochentag < $w) {
           # Falls der letzte Wochentag früher im Monat ist, subtrahiere 7 plus die Differenz
           my $differenz = $w - $letzter_wochentag;
           $ziel_tag -= (7 - $differenz);
        }
    } else {
        # Berechne den Tag des ersten Vorkommens des Wochentags im Monat
        my $erster_tag = 1;
        my $erster_tag_des_monats = (localtime(mktime(0,0,0,$erster_tag,$nm,$ny)))[6];
        my $erstes_vorkommen = ($w - $erster_tag_des_monats + 7) % 7 + 1;

        # Berechne den Tag des x.ten Vorkommens des Wochentags im Monat
        $ziel_tag = $erstes_vorkommen + 7 * ($x - 1);

        # Überprüfe, ob das Datum im gültigen Bereich liegt
        if ($ziel_tag > 31 || $ziel_tag < 1) {
          return "Ungültiges Datum";
        }
    }
    return mktime($s,$m,$h,$ziel_tag,$nm,$ny);
}

Die ersten Tests liefen erfolgreich. "Letzter Wochentag" stimmt in diesem Monat. Am 28. kann ich berichten, ob die Neuberechnung ebenfalls funktioniert. ;D  Ihr könnt ja mal drüber schauen, ob es noch etwas zu optimieren gibt und die Funktion bei Bedarf gerne in das at-Modul einfügen.

F_Klee

"Letzter Wochentag im Monat" wurde für April auch korrekt berechnet. Sollte also funktionieren  :)

Bullilutz

Hallo F_Klee, entschuldige die (einfache) Frage, ich bin recht neu in dem Thema unterwegs. Wie nutze ich jetzt diese Funktion, wie definiere ich konkret ein AT? Deinen Code habe ich erfolgreich in meiner 99_myUtils.pm gespeichert. Ich möchte ein wiederholendes AT an jedem ersten Wochentag im Monat um 7:00 Uhr haben. Kannst du mir bitte dafür eine Beispieldefinition nennen? Danke und viele Grüße, Lutz

F_Klee

Hallo Lutz,
kein Grund sich zu entschuldigen. Ich bin alles andere als ein Experte und schreibe auch gerne Anleitungen, bei denen Experten gar nicht glauben können, dass man so etwas erklären muss. Ich habe das Ganze im Wiki veröffentlicht. Aufgrund deines Posts habe ich den Artikel um die Definitionen erweitert. Ich hoffe, du kommst klar.

Gruß
Frank

Bullilutz

Hallo Frank, jetzt habe ich es verstanden mit den zu übergebenden Parametern. Mein Beispiel: ich lasse uns monatlich am ersten Montag im Monat um 6:40 Uhr eine Nachricht via Telegram- BOT schicken, dass das Kind einen neuen Fahrschein braucht. -->
*{at_xwday(1,1,6,40,0)} {\ fhem("set [Name des Telegram- BOT] message \@-[Token des Telegram- BOT] Achtung, neuer Monat, Fahrschein wechseln!")}
Aber kommenden Monat gilt der neue Fahrschein ab Donnerstag, 02.05., ist das mit dem ersten  Werktag im Monat überhaupt machbar?

F_Klee

Der erste Wochentag und der erste Werktag im Monat sind ja etwas ganz anderes. Hier müsste der Algorithmus prüfen, ob der erste Tag im Monat ein Sonntag ist und muss dann einen Tag hinzu addieren. Schwierig sind dabei die Feiertage, speziell die beweglichen. Feiertage, die immer auf das selbe Datum fallen, könnte man ja in ein Array packen. Sind aber nicht sehr viele, die relevant sind.

Bullilutz

Hallo Frank, stimmt, dann werde ich das erstmal mit at_ultimo zum letzten Tag des Monats gegen Abend machen, das ist ja auch offiziell der Ablauf des Tickets. Ich denke aber, dass ich deinen Code nochmal nutzen werde, der bleibt jedenfalls in meiner my_Utils drin, danke nochmals! Gruß, Lutz