neue Features: ereignisgesteuertes Perl - DOIF-Perl

Begonnen von Damian, 25 Februar 2018, 21:29:16

Vorheriges Thema - Nächstes Thema

Ellert

Getestet habe ich, alles funktioniert wie es soll. Aus meiner Sicht kann es eingecheckt werden. Ein Beispiel werde ich noch basteln.

Damian

Zitat von: Ellert am 03 September 2018, 16:21:56
Getestet habe ich, alles funktioniert wie es soll. Aus meiner Sicht kann es eingecheckt werden. Ein Beispiel werde ich noch basteln.

Hast du auch ohne $hash getestet?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Zitat von: Damian am 03 September 2018, 16:42:56
Hast du auch ohne $hash getestet?
Ja, das funktioniert, erzeugt aber ein ungutes Gefühl.

Ellert

#48
Anliegend ein Beispiel mit BlockingCall im DOIF zur Abfrage von Kraftstoffpreisen.

Als Voraussetzung müssen folgende Pakete installiert werden
  libwww-mechanize-perl
  libwww-mechanize-treebuilder-perl
bzw. über CPAN
  WWW::Mechanize
  WWW::Mechanize::TreeBuilder

Die URL für die Tankstellenliste und die Tankstellennummern können per Attribut angegeben werden.

defmod Kraftstoffpreise DOIF subs {\
  use WWW::Mechanize;;\
  use WWW::Mechanize::TreeBuilder;;\
  \
  sub KraftstoffPreise_Do {\
    my ($pn) = @_;;\
    my $tstart = gettimeofday();;\
    my ($url) = AttrVal($pn,"URL","https://www.clever-tanken.de/tankstelle_liste?spritsorte=5&r=2&lat=&lon=&ort=22049");;\
    my @tali = split(/ /,AttrVal($pn,"Tankstellen","50686"));;\
    my $mech = WWW::Mechanize->new(autocheck => 1, onerror => undef);;\
    WWW::Mechanize::TreeBuilder->meta->apply($mech);;\
    my @price;;\
    my @preis;;\
    my @station;;\
    my @name;;\
    my @street;;\
    my @strasse;;\
    my @city;;\
    my @stadt;;\
    my @erg;;\
    my $ret = $pn;;\
    $mech->get($url);;\
    if ($mech->success()) {\
      for (my $i=0;;$i<@tali;;$i++){\
        @erg = $mech->look_down("id","tankstelle-".$tali[$i]);;\
        return undef unless ($erg[0]);;\
        @price = $erg[0]->look_down("class","price");;\
        if ($price[0]) {\
          $preis[$i] = $price[0]->as_text();;\
        } else {\
          $preis[$i] = ReadingsNum($pn,"price_".$i,"2.00");;\
        }\
        @station = $erg[0]->look_down("class","row fuel-station-location-name");;\
        $name[$i] = $station[0]->as_text();;\
        @street = $erg[0]->look_down("id","fuel-station-location-street");;\
        $strasse[$i] = $street[0]->as_text();;\
        @city = $erg[0]->look_down("id","fuel-station-location-city");;\
        $stadt[$i] = $city[0]->as_text();;\
        $ret .= "|".$preis[$i]."|".$name[$i]."|".$strasse[$i]."|".$stadt[$i];;\
      }\
    }\
    my $ret_dur = gettimeofday() - $tstart;;\
    $ret .= "|$ret_dur";;\
    return $ret;; \
  }\
\
  sub KraftstoffPreise_Done {\
    my ($string) = @_;;\
    return unless(defined($string));;\
    my @ret = split("\\|", $string);;\
    my $pn = $ret[0];;\
    my $dur = int($ret[@ret-1] * 100) / 100;;\
    delete($_blockingcalls{Kraftstoff});;\
    my $cnt = 0;;\
    set_Reading_Begin();;\
    for (my $i = 1;; $i < @ret-1;; $i += 4) {\
      $ret[$i] = $ret[$i] + 0.001;;\
      if ($i == 1) {\
        set_Reading_Update("state",sprintf("%.2f",$ret[$i])." $ret[$i+1]");;\
      } else {\
         set_Reading_Update("state", sprintf("%.2f",$ret[$i])." $ret[$i+1]") if (ReadingsNum($pn,"state","99") > $ret[$i]);;\
        set_Reading_Update("state",sprintf("%.2f",$ret[$i])." $ret[$i+1]") if (ReadingsNum($pn,"state","99") > $ret[$i]);;\
      }\
      set_Reading_Update("price_$cnt", sprintf("%.2f",$ret[$i])." $ret[$i+1]");;\
      set_Reading_Update("station_$cnt", "$ret[$i+1] $ret[$i+2]  $ret[$i+3]") if ("$ret[$i+1] $ret[$i+2] $ret[$i+3]" ne ReadingsVal($pn,"station_$cnt","none"));;\
      $cnt++;;\
    }\
    set_Reading_Update("dauer", $dur);;\
    set_Reading_End(1);;\
  }\
\
  sub KraftstoffPreise_Abort {\
    delete($_blockingcalls{Kraftstoff}) if(defined($_blockingcalls{Kraftstoff}));;\
    return undef;;\
  }\
}\
\
blockingcall {\
  ##BlockingCall($blockingFn, $arg, $finishFn, $timeout, $abortFn, $abortArg);;\
  my $pn = "$SELF";;\
  if ([+3600] or ["^$SELF$:^exec$"]) { ## calls the blocking function every hour with timeout of 30 s\
    $_blockingcalls{Kraftstoff} = ::BlockingCall("DOIF::KraftstoffPreise_Do", $pn, "DOIF::KraftstoffPreise_Done", 30, "DOIF::KraftstoffPreise_Abort", $pn) if (!defined($_blockingcalls{Kraftstoff}));;\
  }\
}\

attr Kraftstoffpreise userattr Tankstellen URL
attr Kraftstoffpreise Tankstellen 50686 20385 11706
attr Kraftstoffpreise URL https://www.clever-tanken.de/tankstelle_liste?spritsorte=5&r=2&lat=&lon=&ort=22049
attr Kraftstoffpreise room DOIF_Labor
attr Kraftstoffpreise setList exec
attr Kraftstoffpreise webCmd exec


Edit: Update wg. Package DOIF

Damian

Zitat von: Ellert am 03 September 2018, 18:01:50
Ja, das funktioniert, erzeugt aber ein ungutes Gefühl.

Warum? Besser so als mit $defs{$pn} zu hantieren.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Zitat von: Damian am 03 September 2018, 19:30:14
Warum? Besser so als mit $defs{$pn} zu hantieren.
Ja, besser ist es und in der geforkten FHEM-Instanz gibt es den Hash auch.

Damian

#51
Zitat von: Ellert am 03 September 2018, 18:40:47
Anliegend ein Beispiel mit BlockingCall im DOIF zur Abfrage von Kraftstoffpreisen.

Als Voraussetzung müssen folgende Pakete installiert werden
  libwww-mechanize-perl
  libwww-mechanize-treebuilder-perl
bzw. über CPAN
  WWW::Mechanize
  WWW::Mechanize::TreeBuilder
...

Wird wahrscheinlich die meisten überfordern. :)

Ich denke man könnte als Vorteil dieser Lösung folgende Punkte nennen:

-alles in einem Device gekapselt
-keine ausgelagerten Perlfunktionen in myUtils
-keine weiteren FHEM-Module erforderlich
-schneller programmiert als ein eigenständiges FHEM-Modul zu programmieren
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Und in diesem Fall übernimmt DOIF das Beenden der Blocking-Instanzen.

Damian

Zitat von: Ellert am 03 September 2018, 19:59:26
Und in diesem Fall übernimmt DOIF das Beenden der Blocking-Instanzen.

ja, der Anfang für etwas Neues ist immer etwas zäh.

Allerdings so wie ich vor über  4 Jahren https://forum.fhem.de/index.php/topic,23833.msg170608.html#msg170608 an die Zukunft von DOIF glaubte, glaube jetzt an die Zukunft des Perl-Modus, weil man intuitiver und flexibler Ereignissteuerung programmieren kann, als im FHEM-Modus.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Zitat von: Ellert am 03 September 2018, 05:15:11
Blokierende Funktionsaufrufe (blocking calls)

DOIF verwaltet blockierende Funktionsaufrufe, d.h. die in diesem Zusammenhang gestarteten FHEM-Instanzen werden gelöscht,

  beim Herunterfahren (shutdown),
  Wiedereinlesen der Konfiguration (rereadcfg),
  Änderung der Konfiguration (modify) und
  Deaktivieren des Gerätes (disabled).
...

Wo hast du im Code shutdown abgedeckt?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

#55
Ich habe mich auf die Info im Wiki verlassen, dass UndefFn reicht, um beim Herunterfahren die blockierende Instanz zu beenden.

Ich habe jetzt die ShutdownFn ergänzt und getestet.

Edit: Anhänge entfernt

Damian

Zitat von: Ellert am 03 September 2018, 23:20:44
Ich habe mich auf die Info im Wiki verlassen, dass UndefFn reicht, um beim Herunterfahren die blockierende Instanz zu beenden.

Ich habe jetzt die ShutdownFn ergänzt und getestet.

OK. Ich lasse es bei mir noch etwas laufen und dann werde ich die Version einchecken.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Zitat von: Damian am 04 September 2018, 08:16:29
OK. Ich lasse es bei mir noch etwas laufen und dann werde ich die Version einchecken.

Version mit blocking calls eingecheckt.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#59
In der kommenden DOIF-Version wird man mit set_Exec unmittelbar Perl/FHEM-Code verzögert ausführen können

z. B. Lampe nach 10 Sekunden ausschalten:

set_Exec("lampe_aus",10,'fhem"set lamp off"')


Das Beispiel Treppenhauslicht mit Bewegungsmelder aus der Commandref könnte dann so aussehen:

define di_light DOIF {
  if (["FS:motion"]) {                        # bei Bewegung
    fhem"set lamp on" if ([?lamp] ne "on");   # Lampe einschalten, wenn sie nicht an ist
    set_Exec("off",30,'fhem"set lamp off"');  # Timer namens "off" für das Ausschalten der Lampe auf 30 Sekunden setzen bzw. verlängern
  }
}


Edit: auch das Beispiel für Einknopf-Rollladensteuerung lässt sich in einem Block definieren

define di_shutter DOIF {
  if (["FS:^on$"] and !get_Exec("shutter")){          # wenn Taste betätigt wird und kein Timer läuft
    set_Exec("shutter",2,'fhem"set shutter down"');   # Timer zum shutter down auf zwei Sekunden setzen
  } else {                                            # wenn Timer läuft, d.h. ein weitere Tastendruck innerhalb von zwei Sekunden
    del_Timer("shutter");                             # Timer löschen
    fhem"set shutter up";                             # Rollladen hoch
  }
}


oder das Beispiel zum Zählen von Ereignissen innerhalb einer Zeitspanne:

define di_count DOIF {                         
  if (["FS:on"] and !get_Exec("counter")) {                                        # wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft
    $_count=1;                                                                     # setze count-Variable auf 1
    set_Exec("counter",3600,'Log (3,"count: $_count action") if ($_count > 10)');  # setze Timer auf eine Stunde zum Protokollieren der Anzahl der Ereignisse, wenn sie über 10 ist
  } else {
    $_count++;                                                                     # wenn Timer bereits läuft zähle Ereignis
  }
}
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF