Anwendungsbeispiele für die countdownwatch

Begonnen von rischbiter123, 06 Juni 2020, 14:12:34

Vorheriges Thema - Nächstes Thema

rischbiter123

Hier könnte man Anwendungsbeispiele für die Funktion countdownwatch des Moduls 60_Watches.pm sammeln.

Ich fange im nächsten Post auch gleich mal an mit einer Möglichkeit, den Wait-Timer von DOIF anzuzeigen.

LG

Andreas
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

rischbiter123

#1
DOIF Wait-Timer:

1. DOIF anlegen (Sollte vorhanden sein, hier nur drei Beispiele)
       Der Trigger und die sets sind in diesem Fall Dummys.

defmod Probe DOIF ([TestProbe:"on"]) (set Test2 300) DOELSEIF ([TestProbe:"off"]) (set Test2 100)
attr Probe do always
attr Probe wait 10:18


defmod Proben DOIF ([TestProben:"on"]) (set Test3 300)(set Test3 200)(set Test3 100) DOELSEIF ([TestProben:"off"]) (set Test3 250)(set Test3 150)(set Test3 50) DOELSEIF ([TestProben:"Duo"]) (set Test3 275)(set Test3 175)(set Test3 75)
attr Proben do always
attr Proben wait 20,25,28:27,24,26:24,29,22


defmod Testen DOIF ([TestTesten:"on"]) (set Test4 300)(set Test4 200) DOELSEIF ([TestTesten:"off"]) (set Test4 100)(set Test4 000)
attr Testen do always
attr Testen wait 32,36:30,39


2. Code für die 99_myUtils oder angehängte Datei aus Antwort #4 nutzen

sub TimerDo($$) {
my ($Az,$Ay) = @_; # $Az gleich Devicename
my $Bz = (AttrVal($Az,"wait",60)); # Auslesen des Wait-Attribut
$Az.="count"; # Namen der Uhr erstellen
# Erstellen der Commandsequenz
my @Dz = split(/ /,$Ay);
my $Dz = split(/ /,$Ay); # Anzahl der Sequenzen
my $Dy = @Dz[$Dz-2]; # Filtern des cmd
# Aufteilen der Commandschritte
my @Ez = split(/_/,$Dy);
my $Ez = split(/_/,$Dy);

if ($Ez == 2) # Nur ein Timer
{
my @Cz = split(/:/,$Bz); # Aufteilen in die Befehlssequenzen
my $Ey2 = $Ez[$Ez-1]; # cmd-Sequenz
my $Fy2 = $Cz[$Ey2-1]; # Inhalt des Waittimers
&Countdown($Az,$Fy2) # Stellen und starten der Uhr
}
else {
my $Ey1 = @Ez[$Ez-2]; # cmd-Nr
my $Ey2 = @Ez[$Ez-1]; # cmd-Sequenz

my @Cz = split(/:/,$Bz); # Aufteilen in die Befehlssequenzen
my $Cz = split(/:/,$Bz); # Anzahl Befehlssequenzen
my $ST1 = "";
my $Fy2 = "";
if ($Ey1 eq "no")
{
fhem ("set $Az reset")
}
else
{
my $ST1 = $Cz[($Ey1-1)];
my @Fy1 = split(/,/,$ST1); # Aufteilen in die einzelnen Wait-Argumente
$Fy2 = $Fy1[$Ey2-1];
&Countdown($Az,$Fy2) # Stellen und starten der Uhr
};
};
}

sub Countdown($$) { # Starten der Uhr
my ($Az1,$Zz) = @_; # $Az1 gleich Devicename, $Zz gleich Einstellzeit
fhem ("set $Az1 countDownInit $Zz; set $Az1 start");
}


3. Notify erstellen

defmod Watch_notify notify .*:wait_timer:.* {TimerDo($NAME,$EVENT);;}
Das erste .* kann man auch durch den Namen des DOIF ersetzen, wenn man - der Übersicht halber- mehrere notify nehmen möchte.

4. Anzeigeuhr erstellen, der Name muss den Namen des DOIF + count sein.

defmod Testencount Watches Digital
attr Testencount digitalColorBackground C7C5BD
attr Testencount digitalColorDigits 0A0FA8
attr Testencount digitalDisplayPattern countdownwatch


Wie immer sind Verbesserungsvorschläge herzlich willkommen.

LG

Andreas
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

tarum

hallo,

gibt fehler in 99_myUtils

Bareword "count" not allowed while "strict subs" in use at ./FHEM/99_myUtils.pm line 485
Linux Server 22.04 CUL 868+433, CCU3, Homematic, Intertechno, FritzDect 200, JeeLink+LaCrosse, Duofern Stick+Rollotron Gurtwickler,smartVISU-3.0.0

rischbiter123

#3
Moin,
bei mir läuft es problemlos. Kann daher den Fehler leider nicht nachvollziehen.
LG
Andreas

Edit: Habe es jetzt bei mir noch mal gelöscht und aus dem Code neu eingefügt. Es wurde problemlos angenommen und auch im Logfile ist keine Fehlermeldung.
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

rischbiter123

Habe jetzt das count im ersten Beitrag in Anführungszeichen gesetzt. Damit dürfte der Fehler bei denen, bei denen er aufgetaucht ist, hoffentlich bereinigt sein. Die geänderte myWatchUtils ist im Anhang.

LG

Andreas
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

rischbiter123

On-for-Timer

Da on-for-timer (mWn) nicht direkt abgefragt werden kann, muß man es indirekt machen.

Statt z.B. das Device direkt per at zu setzen
defmod TestTimer2 at *15:05 set Decke on-for-timer 25

muss ein indirekter Aufruf mit z.B.
defmod TestTimer2 at *15:21 {Onfortimer("Decke",30);;}
erfolgen, wobei Decke in diesem Fall das zu schaltende Device ist.

Hier noch der Inhalt für die 99_myUtils:
sub Onfortimer($$) {
my ($Jz,$Lz) = @_; # $Jz gleich zu schaltendes Device, $Lz gleich Schaltzeit
fhem ("set $Jz on-for-timer $Lz");
$Jz.="count"; # Namen der Uhr erstellen
&Countdown($Jz,$Lz);
}

sub Countdown($$) { # Starten der Uhr
my ($Az1,$Zz) = @_; # $Az1 gleich Devicename, $Zz gleich Einstellzeit
fhem ("set $Az1 countDownInit $Zz; set $Az1 start");
}


Die sub Countdown kann gelöscht werden, wenn man das DOIF-Wait-Beispiel schon benutzt.

Eine Watch mit diesmal dem Namen des zu schaltenden Devices muss natürlich auch angelegt werden.

LG

Andreas
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

rischbiter123

Moin,
ich habe das Ganze jetzt mal so erweitert, daß man mehrere -sich nicht überschneidende- mit nur einer, bzw. zwei, Watches anzeigen kann.
Dazu werden als erstes die beiden Watches definiert.
Für die Anzeige des Devicenamen:
defmod Devicecounter Watches Digital
attr Devicecounter digitalColorBackground C7C5BD
attr Devicecounter digitalColorDigits 0A0FA8
attr Devicecounter digitalDisplayPattern text
attr Devicecounter digitalSegmentType 16
attr Devicecounter digitalTextDigitNumber 8

Wichtig ist, daß das Attribut 'digitalTextDigitNumber' gesetzt ist, da die Länge des anzuzeigenden Namens damit verglichen wird, um die Laufschrift ein- bzw. auszuschalten.
Für die Laufzeit:
defmod Zeitcounter Watches Digital
attr Zeitcounter digitalColorBackground C7C5BD
attr Zeitcounter digitalColorDigits 0A0FA8
attr Zeitcounter digitalDisplayPattern countdownwatch

Dann bitte den Teil in den myUtils komplett austauschen, da ich auch im 'Hauptprogramm' Änderungen vorgenommen habe. (oder wieder die angehängte Datei nutzen)
sub TimerDo($$) {
my ($Az,$Ay) = @_; # $Az gleich Devicename
my $Bz = (AttrVal($Az,"wait",60)); # Auslesen des Wait-Attribut
my $Na1 = (AttrVal($Az,"MeinName","Device")); # Eigenname
# Bei langem Eigennamen die Laufschrift einschalten
&Laufschrift(Na1);
$Az.="count"; # Namen der Uhr erstellen
# Erstellen der Commandsequenz
my @Dz = split(/ /,$Ay);
my $Dz = split(/ /,$Ay); # Anzahl der Sequenzen
my $Dy = @Dz[$Dz-2]; # Filtern des cmd
# Aufteilen der Commandschritte
my @Ez = split(/_/,$Dy);
my $Ez = split(/_/,$Dy);

if ($Ez == 2) # Nur ein Timer
{
my @Cz = split(/:/,$Bz); # Aufteilen in die Befehlssequenzen
my $Ey2 = $Ez[$Ez-1]; # cmd-Sequenz
my $Fy2 = $Cz[$Ey2-1]; # Inhalt des Waittimers
&Countdown($Az,$Fy2,$Na1) # Stellen und starten der Uhr
}
else {
my $Ey1 = @Ez[$Ez-2]; # cmd-Nr
my $Ey2 = @Ez[$Ez-1]; # cmd-Sequenz

my @Cz = split(/:/,$Bz); # Aufteilen in die Befehlssequenzen
my $Cz = split(/:/,$Bz); # Anzahl Befehlssequenzen
my $ST1 = "";
my $Fy2 = "";
if ($Ey1 eq "no")
{
fhem ("set $Az reset");
fhem ("set Devicecounter displayTextDel")
}
else
{
my $ST1 = $Cz[($Ey1-1)];
my @Fy1 = split(/,/,$ST1); # Aufteilen in die einzelnen Wait-Argumente
$Fy2 = $Fy1[$Ey2-1];
&Countdown($Az,$Fy2,$Na1) # Stellen und starten der Uhr
};
};
}

sub Onfortimer($$) {
my ($Jz,$Lz) = @_; # $Jz gleich zu schaltendes Device, $Lz gleich Schaltzeit
my $Na1 = (AttrVal($Jz,"MeinName","Device")); # Eigenname
&Laufschrift(Na1);
fhem ("set $Jz on-for-timer $Lz");
$Jz.="count"; # Namen der Uhr erstellen
&Countdown($Jz,$Lz,$Na1);
}

sub Laufschrift($) {
my (Na4) = @_;
my $La1 = (AttrNum("Devicecounter","digitalTextDigitNumber",8));
my $NaD = length($Na4);
if ($NaD > $La1)
{
fhem ("set Devicecounter textTicker on")
}
else
{
fhem ("set Devicecounter textTicker off")
};
return;
}

sub Countdown($$$) { # Starten der Uhr
my ($Az1,$Zz,$Na2) = @_; # $Az1 gleich Devicename, $Zz gleich Einstellzeit, $Na2 gleich Eigenname
if ($Na2 eq "Device")
{
fhem ("set $Az1 countDownInit $Zz; set $Az1 start")
}
else
{
fhem ("set Devicecounter displayTextSet $Na2");
fhem ("set Zeitcounter countDownInit $Zz; set Zeitcounter start")
};
}

Danach werden zwei Notifys angelegt.
Eines, das auf die Wait-Timer reagiert:
defmod Watch_notify notify .*:wait_timer:.* {TimerDo($NAME,$EVENT);;}
Dann eines, das den Anzeigenamen löscht, wenn der Timer abgelaufen ist:
defmod Zeitcounter_notify_1 notify Zeitcounter:stopped set Devicecounter displayTextDel
On-for-Timer:
Am Aufruf ändert sich nichts. Allerdings muss beim zu schaltenden Device ein userattr mit dem Namen MeinName angelegt werden und mit dem Namen gefüllt werden, der im Devicecounter angezeigt werden soll.
DOIF-Wait
Auch hier muss, allerdings im Doif, das userattr angelegt werden.
Wird das Attribut nicht angelegt, funktioniert das Ganze wie vorher.

4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

WilhelmK

coole Funktion-genau was ich gesucht habe.
Leider funktioniert es bei mir nicht mit readings, z.b.  wait 0,   ReadingsVal("gewaechshaustimer","Gewaechshausmin","")

rischbiter123

Moin,
wait ist auch kein Reading sondern ein Attribut. Daher auch AttrVal.
LG
Andreas
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2


WilhelmK

#10
Hi - habe bisher in FHEM schön mit der Countdownwatch gearbeitet und bin dabei das Ganze auf Tablet UI umzustellen.
Gibt es eine Möglichkeit die Anzeige in einem Reading auszulesen?

Habs schon selber gelöst - das Reading currtime wird ja erzeugt ;-)  - cooles Feature

Wzut

Zitat von: rischbiter123 am 06 Juni 2020, 14:33:21
Wie immer sind Verbesserungsvorschläge herzlich willkommen.
da fällt mir spontan einiges ein :) Bsp :
my @Ez = split(/_/,$Dy);
my $Ez = split(/_/,$Dy);

Ist IMHO schwer lesbar und nachvollziehbar, u.a. auch weil die Variablen Namen nichts zum Inhalt sagen. Leichter wird u.a. zb so
my ($cmd, $nr) =  split(/_/, $line) ;
Wenn du magst schreibe ich dir den Abschnitt mal so um wie ich ihn einsetzen würde.
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

rischbiter123

Moin Wzut,

gerne. Ich bin nicht sehr firm in Perl und war froh, etwas funktionierendes hinzubekommen, was ich mit anderen teilen wollte. Ich hatte auch gedacht, daß noch andere Anwendungsmöglichkeiten posten würden.

LG

Andreas
4*Raspi, Max Thermostate und Fensterkontakte, FB7590, Mysensors und NanoCUL, IT und Sonoff, zigbee2mqtt2

Wzut

OK, und noch ein Tipp : Gib deine 99_myUtils Änderungen über den FHEMWEB Editor ein und schaue ins Log nach dem save.
Ausser den üblichen redefineds finden sich u.U. auch solche Dinge :
2021.02.20 16:36:21 1: PERL WARNING: Scalar value @Ez[...] better written as $Ez[...] at ./FHEM/99_myUtils.pm line 106.
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

Wzut

#14
sodele, wie versprochen :
1. Wenn du die sub bzw die subs in eine eigene 99_xxxx.pm packst musst du auch Init  auf diesen Namen anpassen -> sub myWatchUtils_Initialize
Ich finde das mit der eigenen Datei übertrieben, aber jeder nach seinem Geschmack.

2. Du hast in deiner zweiten Version noch mehr eigene Devicenamen "hardcoded" in der Datei. IMHO haben absolute Namen dort nichts verloren,
der User sollte immer die Freiheit haben seine Geräte nach seinem Geschmack zu benennen und umzubenennen ohne dafür jedesmal irgendwelche subs anpassen zu müssen. Daher ist der Weg mit dem userattr im DOIF ein Schritt in die richtige Richtung (ich habe jetzt Watches drin, aber auch noch das Fallback auf DOIF Name + count)

3. Da wir hier subs haben muss der Ausstieg aus der sub mit return erfolgen, egal ob mitendrin oder am Ende.
Stichwort return : Meine Devise ist sobald es in einer sub nichts mehr zu tun gibt, raus da. Egal ob Fehler oder irgendwelche Bedinungen erreicht sind.
Ein frühes return erspart u.U. umständliche if else Blöcke. Ein return mit Rückgabewert hat zudem noch den Vorteil einen Eintrag im Log File zu hinterlassen.

4. Prüfungen : Verlass dich nicht auf deine Werte, bei den Usern kann das ganz anders ausschauen. Daher niemals blind auf irgendwelche Werte zugreifen sondern immer prüfen ob der Wert auch Sinn macht. Ein Abbruch ggf. mit return hat zudem den Vorteil das der User keine kryptischen Perl Warnings im Log hat sondern von dir definierte Fehlermeldungen.

5. Kleinkram : keine Prototypen, wo immer es Sinn macht einfache Anführungszeichen statt doppelte, keine unützen Variablen für Zwischenwerte die genau nur einmal gebraucht werden, shift statt @_ usw. Frag einfach warum etwas so und nicht anders ist oder du deinen Code gar nicht mehr wiedererkennst :) 


sub TimerDo {

    my $doif_name  = shift;
    my $event      = shift;
    my @counts     = split(/:/, AttrVal($doif_name, 'wait', '')); # Aufteilen in die Befehlssequenzen
    my $w_device   = AttrVal($doif_name, 'Watches', $doif_name.'count'); # Name des Ziel (Watches) Device

    return "no wait found in DOIF $doif_name" if (!@counts); # keine vorhanden
    return "no valid Watches device found, check attribute Watches in $doif_name"
        if (!exists($defs{$w_device}) || ($defs{$w_device}->{TYPE} ne 'Watches') );

    if ($event !~ m{ cmd_ }xms ) { # z.B. bei no timer ist kein cmd_x vorhanden
        CommandSet(undef, $w_device.' reset');
        return;
    }

    # Erstellen der Commandsequenz
    my @vals = split(/ /,$event); # Bsp -> wait_timer: Datum Uhrzeit cmd_x <Auslöser des notify>

    # my $cmd = $vals[int(@vals)-2];  wir brauchen das vorletzte Element -> cmd_x
    # alternativ : ohne rechnen direkt $vals[3] verwenden ? kürzere Events sollte es jetzt nicht mehr geben
    # my $cmd = $vals[3]

    my (undef, $nr, $sub_nr) = split(/_/, $vals[3]); # Bsp : cmd_2 , cmd_1_2

    $nr--; # Element Nr der waits ist immer um eins kleiner als cmd_x;
   
    return Countdown($w_device, (defined($counts[$nr])) ? $counts[$nr] : -1)  if (!$sub_nr); # Nur ein Timer   
 
    $sub_nr--; # ist auch immer um eins kleiner;

    my @count_arr = split(/,/, $counts[$nr]);
    return Countdown($w_device, (defined($count_arr[$sub_nr])) ? $count_arr[$sub_nr] : -1);
}

sub Countdown { # Starten der Uhr
    my $device = shift;
    my $count  = shift // return "invalid count"; # falls $count undef

    return "invalid count" if ($count < 0);
    my $ret = CommandSet(undef, "$device countDownInit $count");  # stellen der Uhr
    return $ret if ($ret); # da ging leider etwas schief
    return CommandSet(undef, $device.' start');  # starten der Uhr
}


Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher