Ermittlung von devices für NOTIFYDEV mittels notifyRegexpChanged

Begonnen von fruemmel, 12 Januar 2022, 13:07:41

Vorheriges Thema - Nächstes Thema

fruemmel

Hallo allerseits,

mir ist bei der Nutzung von apptime aufgefallen, dass sich viele meiner Devices (z. B. vom Typ notify, FileLog) mit Events beschäftigen, die für das device irrelevant sind. Ursache ist, dass die Variable NOTIFYDEV nicht gefüllt wird.
Das liegt meistens daran, wie die relevanten events definiert werden und tritt bei mir insbesondere bei notify und FileLog auf.

So wird z. B. bei der Definition eines FileLogs die regexp presentDevice:(absent|present) nicht erkannt (NOTIFYDEV nicht vorhanden). Wenn ich die regexp jedoch so schreibe:  (presentDevice:absent|presentDevice:present) dann wird NOTIFDEV mit dem Gerät presentDevice gefüllt. Ich finde aber, dass gerade eine Definition der Art <devicename>:(reading1|reading2...|readingN) besser lesbar und einfacher zu schreiben ist.

Ich würde mich freuen, wenn sich ein kompetenter Mensch die Funktion notifyRegexpChanged daraufhin einmal ansieht. Der dort verwendete reguläre Ausdruck ist schon jetzt nicht ganz ohne, ich traue mich da nicht so richtig ran. Zumal schon früh in der Funktion die regexp über | gesplittet wird.

Schade finde ich in dem Zusammenhang auch, dass wohl einige Module (mir aufgefallen bei 31_LightScene) die Variable NOTIFYDEV komplett ignorieren und sich um jedes Event kümmern. Ich kann zwar nicht bemessen, ob das in Summe wirklich relevante Rechenzeit kostet, aber bei vielen Devices ist das evtl. nicht zu vernachlässigen. Bei mir treten bei gut 1000 devices pro Stunde ca. 2000-3000 events auf. Wenn diese von vielen unbeteiligten Geräten betrachtet werden, kommt da einiges zusammen.

Wenn ich irgendwie mit Tests unterstützen kann, bin ich natürlich gerne mit dabei.

Gruß Wolfgang

Otto123

Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

fruemmel

Hallo Otto,

oh danke, den Thread hatte ich nicht gefunden. Ich vermute aber, dass sich viele User über die Zusammenhänge mit dem NOTIFYDEV keine Gedanken machen. Und eben vielleicht auch der ein oder andere Modulentwickler. Ich bin auch erst erschrocken, als ich über apptime gesehen habe, wieviele devices sich bei mir mit den Events beschäftigen. Ich bastel mir gerade eine Hilfskonstruktion, um wenigstens die "schwarzen Schafe" (unglückliche RegExp in den Definitionen) zu finden und manuell anzupassen. Oder gibt es in der Richtung evtl. auch schon was?

Beta-User

Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

fruemmel

Danke für die Links. Thema sieht nach Wespennest aus :-)

Wird denn notifyRegexpChanged nur aufgerufen, wenn ein device definiert oder umdefiniert wurde? Dann wäre es ja bzgl Performance unkritisch, wenn man hier noch etwas mehr Prüfung einbaut. Insb. für meine oben erwähnte regexp 
<devicename>:(reading1|reading2...|readingN)
fände ich super, wenn die erkannt würde und zu einem Eintrag in NOTIFYDEV führen würde. Ich nutze diesen Aufbau insb. bei FileLog-Definitionen relativ häufig.

Beta-User

Thema ist komplex... (und geht mir auch auf die Nerven).

Es ist aber nicht so einfach, die Bestimmung des NOTIFYDEV neu zu erfinden, Rudi hat (m.E. zurecht) Angst davor, das anzufassen - obwohl allen klar ist, dass es wünschenswert wäre, wenn das rückwärtskompatibel verbessert werden könnte.

Du hast mich aber auf den Gedanken gebracht, dass evtl. die Option bestehen könnte, das ganze nicht an der Pipe zu splitten, sondern iterativ vorzugehen und zu versuchen, das am Doppelpunkt zu versuchen und Klammern zu zählen.

Mal sehen, wird aber dauern...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Man muss die Sache nicht überbewerten. Früher hatte mein Modul ohne NOTIFYDEV gearbeitet. Inzwischen habe ich den Filter eingebaut und noch einen zusätzlichen , siehe https://forum.fhem.de/index.php/topic,103401.msg977507.html#msg977507

Meine Messung haben ca. 30 % weniger Last im Wartezustand ergeben. Allerding 30 % von "ursprünglich bereits wenig" ist nicht viel ;)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

rudolfkoenig

ZitatMan muss die Sache nicht überbewerten.
Ganz meine Rede :)

fruemmel

ZitatMan muss die Sache nicht überbewerten.

Ja das will ich auch gar nicht in Frage stellen. Ich stelle nur fest, dass mein fhem trotz eines ordentlichen Raspberry Pi 4 immer träger wird, und versuche die Ursachen einzugrenzen. Da fielen eben durch apptime die vielen unnötigen Event-Handler auf. Das ist sicher nicht die alleinige oder gar Hauptursache für die Trägheit, aber vielleicht ein Mosaiksteinchen was ich schonmal ausschließen möchte.
Ich überarbeite derzeit die suboptimalen regexp meiner devices und programmiere zum Test für mich eine Erweiterung der Funktion notifyRegexpChanged. Wenn das etwas bringen sollte, werde ich das hier natürlich kundtun.
Bis hierhin aber vielen Dank für Eure Antworten.

Damian

Zitat von: fruemmel am 13 Januar 2022, 08:37:29
Ja das will ich auch gar nicht in Frage stellen. Ich stelle nur fest, dass mein fhem trotz eines ordentlichen Raspberry Pi 4 immer träger wird, und versuche die Ursachen einzugrenzen. Da fielen eben durch apptime die vielen unnötigen Event-Handler auf. Das ist sicher nicht die alleinige oder gar Hauptursache für die Trägheit, aber vielleicht ein Mosaiksteinchen was ich schonmal ausschließen möchte.
Ich überarbeite derzeit die suboptimalen regexp meiner devices und programmiere zum Test für mich eine Erweiterung der Funktion notifyRegexpChanged. Wenn das etwas bringen sollte, werde ich das hier natürlich kundtun.
Bis hierhin aber vielen Dank für Eure Antworten.

Die einfachste und effizienteste Maßnahme um Systemlast drastisch zu reduzieren, ist das Setzen des event-on-change-Attributes. Ich habe es einfach für alle Devices gesetzt und musste es nur bei einem rückgängig machen. Sonst kannst du schauen, welche Events häufig kommen und ob sie ohne Änderung nötig sind.

Jedes Event im System produziert je nach Umfang ca. die 50-fache (5000 %) Last im System im Vergleich zum Setzen eines Readings ohne Event.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Beta-User

Das Reduzieren der Events ist sicher auch ein extrem wichtiger Baustein, die "Holzhammer-Methode" mit "alles auf .*" sagt mir allerdings aus verschiedenen Gründen nicht zu (auch wenn das _sehr oft_ paßt oder zumindest Teil der Lösung ist!).

Falls Homematic (CUL_HM) im Spiel ist: "commStInCh" (in den Hauptdevices) auf "off" zu stellen wäre auch eine gute Idee.

Auch wenn es "nur" um 30% Performance-Verlust geht, hier mal eine erste Variante mit geänderter Split-Logik. Das ganze ist sicher sehr viel unperformanter bei der Ermittlung der Devices, und ob das insgesamt ein gangbarer Weg ist oder doch unüberbrückbare Lücken auftreten, habe ich bisher noch nicht intensiver untersucht:
sub
notifyRegexpChanged2
{
  my ($hash, $re, $disableNotifyFn) = @_;

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  my $first = 1;
  my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
  my @list;
  my $numdef = keys %defs;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
    if ( $first && $outer ) {
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);
            $dev =~ s{\A.}{}x;
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
    #Log3('global',3 , "re splitted to $dev and $re") if $re;
    return delete $hash->{NOTIFYDEV} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = splitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      @list = (@list, @darr);
    }
    (undef, $re) = splitByPipe($re);
  }
  return delete($hash->{NOTIFYDEV}) if !@list;
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV} = join q{,}, @list;
  return;
}

sub splitByPipe {
    my $string = shift // return (undef,undef);
    # String in pipe-getrennte Tokens teilen, wenn die Klammerebene 0 ist
    my $lastChar = q{x};
    my $bracketLevel = 0;
    my $token = q{};
    my @chars = split q{}, $string;
    my $i = 0;
    my $repl;
    for my $char ( @chars ) {
        #Log3('global',3,"char is $char, last was $lastChar, level is $bracketLevel");
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $repl = "$token" . q{|};
            $repl =~ s{[{()}|*]}{.}g;
            $string =~ s{\A$repl}{}x;
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        if ( $i == scalar @chars ) {
          $repl = $token;
          $repl =~ s{[{}()\|\*]}{.}g;
          $string =~ s{\A$repl}{}x;
        }
        $lastChar = $char;
    }
    return ($token, $string);
}
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

rudolfkoenig

Zitathier mal eine erste Variante mit geänderter Split-Logik.
Ich fuerchte, das is zu hoch fuer mich, geschweige denn, dass ich die Nebeneffekte begreifen wuerde.

Ich habe jetzt 'ne Stunde ueber die alte Logik gebruetet, und mir ist dabei (wieder) bewusst beworden, dass auch diese Variante mit leicht aufwendigeren Regexps falsch "positive" Ergebnisse produziert. Eine Alternative ist ein explizites notifydev Attribut, ich kriege das aber nicht ohne Nebeneffekte generisch, als Teil des Frameworks hin.

Diese geschaetzten(!) 30% Verluste sind nur in speziellen Faellen relevant: es gibt viele Devices, die Events generieren, viele Events, viele "notify" Instanzen, und man setzt ueberall die "falschen" Regexps ein.
In diesem Fall empfehle ich die "falschen" Regexps mit { notifyRegexpCheck(<regexp>) } zu optimieren.

Beta-User

Vielleicht nochmal einen Schritt zurück:
Zitat von: rudolfkoenig am 16 Januar 2022, 13:39:06
In diesem Fall empfehle ich die "falschen" Regexps mit { notifyRegexpCheck(<regexp>) } zu optimieren.
Wer ist denn eigentlich unsere Zielgruppe?

Es kommen in Frage: Developer und User.
"An sich" handelt es sich bei notifyRegexpChanged() um eine interne Funktion, die dafür gemacht ist, dass Developer sie in Ihre Module einbauen. Wird aber tatsächlich nur von einem Teil so gehandhabt, s.u..

Bei den Usern gibt es zwei Guppen: Die, die das "Problem" kennen. Für die gibt es Hilfsmittel und Rückmeldungen wie diese hier: Muss es so unkomfortabel sein?
Und die User, die sich dazu keinen Kopf machen (meine Einschätzung: die ganz weit überwiegende Mehrheit!). Schlicht, weil "schlechte Beispiele" c&p übernommen werden (btw.: auch von mir gibt es solche im Wiki...).

MAn. sollte man die letzteren im Auge haben, die anderen beiden Gruppen können ihre Probleme selbst lösen, wenn sie wollen...

Zitat von: rudolfkoenig am 16 Januar 2022, 13:39:06
Ich fuerchte, das is zu hoch fuer mich, geschweige denn, dass ich die Nebeneffekte begreifen wuerde.
Dass das mit den Nebeneffekten en Riesen-Thema ist, ist unbestritten, Teil 1 glaube ich zwar nicht, werd's aber trotzdem nochmal erläutern.

Wir stellen fest: die bisherige Methode ist nicht in der Lage, NOTIFYDEV zu ermitteln, wenn irgendwo eine "oder"-regex auftaucht wie "Display:(testa|testb)", weil der split an der Pipe dazu führt, dass (meistens) keine passenden Geräte für die entstehenden Teile gefunden werden können.
Ergo: der "ungeprüfte Split" an einer Pipe als erste Stufe ist die eigentliche Ursache, dass in sehr vielen Fällen kein NOTIFYDEV ermittelt wird.

Meine gedanklichen Puzzleteile zur eventuellen Lösung:
- Grundsätzlich geht es darum, Devices zu ermitteln, an denen für das Zielgerät "relevante" Events stattfinden können.
- Diese Devices werden entweder
-- direkt (auch als regexp) reingeschrieben, es gibt sonst keine weiteren "ablenkenden" Infos (wie Reading-Namen);
-- durch einen Doppelpunkt vom "zugehörigen Rest" getrennt.
- User setzen gerne mal lieber eine Klammer zu viel wie zu wenig, man muss aufpassen, wenn "drumrum" Klammern sind. Die könnten zusammengehören (Sonderthema, macht es leider unleserlicher).

Ergo wird der übergebene String von vorne nach hinten zerstückelt, und zwar erst mal am ersten Doppelpunkt in zwei Teile. Alles, was nach dem Doppelpunkt bis zur nächsten Pipe (auf der "0"-Klammer-Ebene) kommt, ist für die Ermittlung von passenden Devices unnötiges Beiwerk und wird verworfen. Da bestimmte Sonderzeichen schwierig sind, werden die ersetzt durch einzelne Punkte.
Nach der "0-er"-Pipe beginnt das Spielchen von vorne.

Das war es im Prinzip auch schon.

Als "Bonus" gibt es noch die Option, devices per devspec zu markern. Das war bisher nicht vorgesehen, wird aber von einigen Modulautoren so gemacht - per direktem Hash-Zugriff.

Für eventuelle Tester:
Meine ersten Versuche sahen in etwa so aus:
{notifyRegexpChanged2($defs{weather}, '.*a|Display:(testa|testb)|TYPE=AMAD.*', 0)}
Nachdem die weitere Aufgabe aber darin bestand, erst mal festzustellen, ob und ggf. wo/bei welcher Art Ausdrücken wir ein (neues) Problem haben, habe ich das ganze jetzt mal im Testsystem wie folgt in fhem.pl eingebaut:

sub
notifyRegexpChanged($$;$)
{
  my ($hash, $re, $disableNotifyFn) = @_;
  notifyRegexpChanged2($hash, $re, $disableNotifyFn);

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  my @list2 = split(/\|/, $re);
  my @list = grep { m/./ }                                     # Forum #62369
             map  { (m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/ &&
                     ($defs{$1} || devspec2array($1) ne $1)) ? $1 : ""} @list2;
  if(@list && int(@list) == int(@list2)) {
    my %h = map { $_ => 1 } @list;
    @list = keys %h; # remove duplicates
    $hash->{NOTIFYDEV} = join(",", @list);
    $hash->{NOTIFYDEV_1} = join(",", @list);
  } else {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
  }
}

sub
notifyRegexpChanged2
{
  my ($hash, $re, $disableNotifyFn) = @_;

  #Log3('global',3 , "nRC2 called for $hash->{NAME}");
  #%ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV_2});
  #  $hash->{disableNotifyFn}=1;
    return;
  }
  #delete($hash->{disableNotifyFn});
  my $first = 1;
  my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
  my @list;
  my $numdef = keys %defs;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
    if ( $first && $outer ) {
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);
            $dev =~ s{\A.}{}x;
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
    #Log3('global',3 , "re splitted to $dev and $re") if $re;
    return delete $hash->{NOTIFYDEV_2} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = splitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      @list = (@list, @darr);
    }
    (undef, $re) = splitByPipe($re);
  }
  return delete($hash->{NOTIFYDEV2}) if !@list;
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV_2} = join q{,}, @list;
  return;
}

sub splitByPipe {
    my $string = shift // return (undef,undef);
    # String in pipe-getrennte Tokens teilen, wenn die Klammerebene 0 ist
    my $lastChar = q{x};
    my $bracketLevel = 0;
    my $token = q{};
    my @chars = split q{}, $string;
    my $i = 0;
    my $repl;
    for my $char ( @chars ) {
        #Log3('global',3,"char is $char, last was $lastChar, level is $bracketLevel");
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $repl = "$token" . q{|};
            $repl =~ s{[{()}|*]}{.}g;
            $string =~ s{\A$repl}{}x;
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        if ( $i == scalar @chars ) {
          $repl = $token;
          $repl =~ s{[{}()\|\*]}{.}g;
          $string =~ s{\A$repl}{}x;
        }
        $lastChar = $char;
    }
    return ($token, $string);
}


Startet man FHEM damit, kann man sich die Unterschiede schön auflisten lassen:
list .* NOTIFYDEV NOTIFYDEV_1 NOTIFYDEV_2
Zumindest im Testsystem (mit optimierten regexps) hatte ich keine Unterschiede zwischen NOTIFYDEV_1 (fhem.pl-Version aus notifyRegexpChanged()) und NOTIFYDEV_2 (per notifyRegexpChanged2() ermittelt), aber man sieht sehr schön, dass es einige Module gibt, die die Funktion nicht nutzen, die Liste mit NOTIFYDEV ist deutlich länger als die ersten beiden...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Es ist ein hausgemachtes Problem. Könnte der Entwickler passend zur der Schnittstelle einen reinen Device-Filter setzen, wäre die Filterung recht banal:

if ($dev->{NAME} !~ /$hash->{helper}{DEVFILTER}/) {return ""};

So mache ich es in meinem Modul, weil ich vom Anfang an die Trennung zwischen Device-Regex und Event-Regex an den User weiter gegeben habe, Syntax:

["<Regex für Devices>:<Regex für Events>"]
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Benni

Zitat von: Beta-User am 17 Januar 2022, 14:01:25
-- durch einen Doppelpunkt vom "zugehörigen Rest" getrennt.

Kleine Anmerkung dazu:

Die Regex muss keinen Doppelpunkt enthalten. Da sie nur matchen muss, kann es auch notify geben (gibt es auch!) in der Art:


device.event


Das wird aber auch aktuell von notifyRegexpCheck nicht erkannt.

gb#

Beta-User

Zitat von: Benni am 17 Januar 2022, 19:22:08
Die Regex muss keinen Doppelpunkt enthalten. Da sie nur matchen muss, kann es auch notify geben (gibt es auch!) in der Art:
Nun ja, gemeint hatte ich das (wieder etwas vereinfachend ausgedrückt) so: gibt es einen Doppelpunkt, ist davon auszugehen, dass er Device und Reading trennen soll. Fehlt er, ist davon auszugehen, dass der ganze Ausdruck als Device/devspec für devspec2array() aufgefasst werden soll.

Wer es unbedingt so notieren will, dass NOTIFYDEV nicht erkannt wird, darf das meinetwegen weiterhin so machen...

Ergänzend aber noch zu dem, was ich gestern zu den Unterschieden zwischen NOTIFYDEV, NOTIFYDEV_1 und NOTIFYDEV_2 geschrieben hatte: Oft wird bei den direkten "ich schreib das selbst in den Device-Hash"-Fällen schlicht auf "global" reagiert - es handelt sich also mutmaßlich eigentlich um ein (post-) Initialisierungs-Thema, warum da überhaupt mit NotifyFn() gearbeitet wird.
Da in https://forum.fhem.de/index.php/topic,64231.msg1201433.html#msg1201433 mal wieder jemand über at-Perl gestoplert wird, "nerve" ich mal wieder mit dem Hinweis, dass man das Thema m.E. auch insgesamt überdenken sollte...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Beta-User

...nach etwas geteste hat sich rausgestellt, dass das regexen zur Ermittlung des "verbleibenden Rests" wohl keine tolle Idee ist...

substr() scheint das besser zu machen, daher nochmal ein update, das beim ersten Testen auch mit sowas
defmod n_test_sm notify FB_.*:.(Short|Long).1_.*\(to.ccu\).*  set fakeRokudummy_dummy 3 test3 [a:$NAME:alias] $EVENTund sowas klarkommt:defmod n_test3 notify devstrich0:o[nf]+|rC2TestDummy[12]:.* {if ( $EVENT eq 'on' ) {}}

sub
notifyRegexpChanged($$;$)
{
  my ($hash, $re, $disableNotifyFn) = @_;
  notifyRegexpChanged2($hash, $re, $disableNotifyFn);

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  my @list2 = split(/\|/, $re);
  my @list = grep { m/./ }                                     # Forum #62369
             map  { (m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/ &&
                     ($defs{$1} || devspec2array($1) ne $1)) ? $1 : ""} @list2;
  if(@list && int(@list) == int(@list2)) {
    my %h = map { $_ => 1 } @list;
    @list = keys %h; # remove duplicates
    $hash->{NOTIFYDEV} = join(",", @list);
    $hash->{NOTIFYDEV_1} = join(",", @list);
  } else {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
  }
}

sub
notifyRegexpChanged2
{
  my ($hash, $re, $disableNotifyFn) = @_;

  #Log3('global',3 , "nRC2 called for $hash->{NAME}");
  #%ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV_2});
  #  $hash->{disableNotifyFn}=1;
    return;
  }
  #delete($hash->{disableNotifyFn});
  my $first = 1;
  my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
  my @list;
  my $numdef = keys %defs;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
    if ( $first && $outer ) {
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);
            $dev =~ s{\A.}{}x;
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
    #Log3('global',3 , "re splitted to $dev and $re") if $re;
    return delete $hash->{NOTIFYDEV_2} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = splitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      @list = (@list, @darr);
    }
    (undef, $re) = splitByPipe($re);
    #Log3('global',3 , "re now cleaned to $re");
  }
  return delete($hash->{NOTIFYDEV2}) if !@list;
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV_2} = join q{,}, @list;
  return;
}

sub splitByPipe {
    my $string = shift // return (undef,undef);
    # String in pipe-getrennte Tokens teilen, wenn die Klammerebene 0 ist
    my $lastChar = q{x};
    my $bracketLevel = 0;
    my $token = q{};
    my @chars = split q{}, $string;
    my $i = 0;
    for my $char ( @chars ) {
        #Log3('global',3,"char is $char, last was $lastChar, level is $bracketLevel");
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $string = substr $string, length($token) + 1;
            #Log3('global',3 , "pipe detected, exiting with token $token, string $string");
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        if ( $i == scalar @chars ) {
          $string = substr $string, length $token;
        }
        $lastChar = $char;
    }
    #Log3('global',3 , "no more pipes detected, exiting with token $token, string $string");
    return ($token, $string);
}
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

fruemmel

@Beta-User: Danke für die Forschungsarbeit!

Ich habe deine letzte Version bei mir jetzt eingebaut und ein paar Tests gemacht. Es werden deutlich mehr regexp erkannt als vorher. Eine bessere Erkennung gibt es bei mir allerdings auch nur bei den Device-Types notify, watchdog und FileLog. Das bestätigt auch deine Aussage, dass viele Modulentwickler das ohne notifyRegexpChanged oder eben gar nicht lösen. Schade finde ich das insb. bei Modulen wie LightScene, die in der notifyFunc auch anscheinend etwas mehr Zeit verbringen und sich dann um viele uninteressante events kümmern.

Was bisher keine Version erkennt, ist der folgende Ausdruck. Der soll für ein FileLog die Messwerte verschiedener Sensoren (LC_.*) sowie den Temperaturwert eines Heizungsmoduls (mykm200...) einsammeln.
(LC_.*(temperature|humidity)|mykm200:/system/sensors/temperatures/outdoor_t1).*
Aber solche Exoten kann man sicher immer konstruieren, und es geht ja eher darum die breite Masse zu erwischen. Außerdem kann man die regexp sicher leicht anpassen, damit sie erkannt wird.

Da man die perfekte Variante nicht oder nur mit großem Aufwand erreichen kann, finde ich es extrem hilfreich, wenn man seine "schwarzen Schafe" mit einem kleinen Diagnosetool selbst identifizieren könnte.
Einerseits wäre es also hilfreich, wenn man die Geräte identifiziert, bei denen die regexp nicht erkannt wird, und andererseits herausfinden kann, welche Geräte in einem bestimmten Zeitraum die meisten events verursachen. In der Richtung existiert aber noch nichts, oder?

Beta-User

Danke für die Rückmeldung. Hier nochmal eine zur Vorgehensweise passende Alternativ-Fassung zu der bereits von Rudi genannten Hilfsfunktion notifyRegexpCheck() - da ist intern nur die Hilfsfunktion "splitByPipe" in "notifyRegexpChangedsplitByPipe" umbenannt.

Ist gleich noch erweitert um die Erkennung der "Umschließenden Klammer samt Ende-.*":
sub
notifyRegexpCheck2
{
    my $re = shift // return 'No Expression to check provided!';
    my @list;
    my $consume = $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
    my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
    my $numdef = keys %defs;
    my $first = 1;
    while ($re) {
        (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
        if ( $first && $outer ) {
            $first = 0;
            my $ops = $dev =~ tr/(//;
            my $clos = $dev =~ tr/)//;
            if ( $ops > $clos ) {
                chop($re);
                $dev =~ s{\A.}{}x;
            }
        }
        $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
        push @list, q{.*: matches all (ignored)} if $dev eq '.*';

        while ($dev) {
          (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
          #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

          push @list, q{.*: matches all (ignored)} if $part eq '.*';
          if ($defs{$part}) {
              push @list, qq{$part: device $part (OK)};
          } else {
            my @ds = devspec2array($part);
            if($ds[0] ne $part) {
                push @list, "$part: devspec ".join q{,}, @ds." (OK)";
            } elsif ($numdef == @ds) {
                push @list, "$part: matches all devices (ignored)";
            } else {
                push @list, "$part: unknown (ignored)";
            }
          }
        }
        (my $other, $re) = notifyRegexpChangedsplitByPipe($re);
        push @list, "$other: irrelevant part (OK)";
    }
    push @list, ".*: irrelevant part (OK)" if $consume;
    return join "\n", @list;
}


Damit kann man feststellen, woran es hapert, und Rudi kann ggf. besser überlegen, ob so eine Umstellung wirklich die Nebenwirkungen hätte, die er fürchtet :) .

Geräte mit "guten NOTIFYDEV" zählen kann man mit "count".

DOIFtools haben afaik auch Möglichkeiten zu erfassen, wie oft Events bei einzelnen Geräten geworfen werden.

Was in jedem Fall "unzeitgemäß" ist, ist der Reading-Name, das scheint was "uraltes" zu sein, das so ein Reading generiert...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

#19
Ich würde da nicht zu viel Energie investieren. Es ist und bleibt ein heißes Eisen. Sobald irgendeine Definition danach in FHEM nicht funktioniert, kann man nachbessern und verärgert nur die Anwender.

Da viele Programme den Filter ohnehin nicht nutzen, deckt es nur einen Teil ab.

Ein nicht unerheblicher Teil der Events aller FHEM-Systeme läuft über diesen Code:

sub
DOIF_Notify($$)
{
  my ($hash, $dev) = @_;
  my $pn = $hash->{NAME};
  return "" if($attr{$pn} && $attr{$pn}{disable});
  return "" if (!$dev->{NAME});
  my $device;
  my $reading;
  my $internal;
  my $ret;
  my $err;
  my $eventa;
  my $eventas;
 

  if (!defined($hash->{helper}{DEVFILTER})) {
    return "";
  } elsif ($dev->{NAME} !~ /$hash->{helper}{DEVFILTER}/) {
    return "";
  }
...


Man kann jetzt messen wieviel Millisekunden ein Funktionsaufruf der notify-Funktion dauert bei einem nicht gesetzten NOTIFYDEV-Filter bis zum Ausstieg durch den eigenen Filter, bei Falschalarm - es werden nicht viele sein.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Ich habe mir mal den Spaß gemacht und eine Stoppuhr laufen lassen:

my %hash;
$hash{NAME}="Test";
my %dev;
$dev{NAME}="bla";
my %attr;
$hash{helper}{DEVFILTER}="keiner";

sub
DOIF_Notify($$)
{
  my ($hash, $dev) = @_;
  my $pn = $hash->{NAME};
  return "1" if($attr{$pn} && $attr{$pn}{disable});
  return "2" if (!$dev->{NAME});
  my $device;
  my $reading;
  my $internal;
  my $ret;
  my $err;
  my $eventa;
  my $eventas;


  if (!defined($hash->{helper}{DEVFILTER})) {
    return "3";
  } elsif ($dev->{NAME} !~ /$hash->{helper}{DEVFILTER}/) {
    return "kein Treffer";
  }
  return("Ende");
}
for(my $i=0;$i<10000000;$i++){
DOIF_Notify(\%hash,\%dev);
}


Ergebnis pro Aufruf bis zum return "kein Treffer" auf einem raspi 4: 0.002 Millisekunden

Wie viele Events gibt es wohl in einem durchschnittlichen FHEM-System pro Sekunde - es lohnt nicht darüber nachzudenken.

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

fruemmel

ZitatErgebnis pro Aufruf bis zum return "kein Treffer" auf einem raspi 4: 0.002 Millisekunden

Wie viele Events gibt es wohl in einem durchschnittlichen FHEM-System pro Sekunde - es lohnt nicht darüber nachzudenken.

Ich habe bei mir mal ein paar Werte ermittelt. Ich habe ca. 8.000 Events pro Stunde. Dabei ist schon einiges über event-on-change-reading etc. wegoptimiert. Mit der aktuellen (freigegebenen) regexp-Erkennung habe ich ca. 150 devices, die kein NOTIFYDEV setzen, aber eine NotifyFunc hinterlegt haben. Wenn ich diese Anzahl der Geräte mit der Anzahl der Events multipliziere, komme ich auf insgesamt ca. 1.200.000 Aufrufe der Notify-Functions pro Stunde (also gut 300/Sekunde), um die sich ein Event-Handler theoretisch kümmert. 
Wenn man deine 0.002 Millisekunden als Maßstab nimmt, ist das immer noch wenig Rechenzeit, zumal ja mehrere gleichzeitige Events eines Gerätes performanter behandelt werden können. Andererseits liegt es ja in den Händen des Moduls, wie schnell Events verworfen werden, die das Modul nicht interessieren (falls kein NOTIFYDEV).

Leider entstehen die Events aber ja nicht gleichverteilt, sondern teilweise gehäuft. Ich nutze FTUI recht intensiv, und es gibt immer wieder Momente, wo das System ziemlich verschlafen reagiert. Daher kam der Verdacht auf, dass das Zusammenspiel von Events und NOTIFYDEV auch einen Einfluss haben könnte.

Mir ist aber durch die Diskussion und  Beschäftigung mit dem Thema  auch klar geworden, dass ich noch viel Potential habe, um meine Events zu reduzieren und auch die regexp zu optimieren. Mal sehen, ob das am Ende spürbar hilft.

rudolfkoenig

ZitatWie viele Events gibt es wohl in einem durchschnittlichen FHEM-System pro Sekunde - es lohnt nicht darüber nachzudenken.
Ein durchschnittliches FHEM-System ist in diesem Fall irrelevant.

Die Optimierung wurde 2014 eingebaut, ausgeloest durch ein System mit 300+ Notify/FileLog/etc "NotifyFn" Instanzen und eine Eventrate von 100+/sec auf einem relativ schwachen Rechner (RPi 1?).
Auf einem RPi4 benoetigt diese Installation ca 300*100*0.002ms=60ms => 6% CPU fuer die "sinnlose" Pruefung. Ein RPi4 ist pro Core laut Internet 4 bis 6-mal schneller als ein RPi, da wuerde es also 24%-36% ausmachen. Wenn die restlichen Aktivitaeten das System schon zu 80% ausreizen, kann eine Optimierung einen wesentlichen Unterschied machen.
Und ich habe seither auch "schlimmere" Installationen gesehen.

Diese Optimierung ist sicher nur einer unter Vielen und ich erweitere es gerne, der Code muss aber wartbar (d.h.leicht verstaendlich) bleiben.


Beta-User

Hmm, ob das "wartbarer Code" ist, sei mal dahingestellt, hier jedenfalls mal ein "aktualisierter Satz" für weitere (Ergebnisvergleichs-) Tests für die, die es interessiert.

Auf dem Testsystem sah das auch mit diversen "typischen User-Definitionen" auf den ersten Blick ganz ok aus.

sub
notifyRegexpCheck2
{
    my $re = shift // return 'No Expression to check provided!';
    my $numdef = keys %defs;
    my @fm = devspec2array($re);
    if (@fm  && $fm[0] ne $re ) {
        return "$re: matches all devices (ignored)" if $numdef == @fm;
        my $lst = join q{,}, @fm;
        return "$re: devspec for devices only - $lst (OK)";
    }
    my $consume = $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
    my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
    my $first = 1;
    my @list;
    while ($re) {
        (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
        if ( $first && $outer ) {
            $first = 0;
            my $ops = $dev =~ tr/(//;
            my $clos = $dev =~ tr/)//;
            if ( $ops > $clos ) {
                chop($re);
                $dev =~ s{\A.}{}x;
            }
        }
        $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
        push @list, q{.*: matches all (ignored)} if $dev eq '.*';

        while ($dev) {
          (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
          #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

          push @list, q{.*: matches all (ignored)} if $part eq '.*';
          if ($defs{$part}) {
              push @list, qq{$part: device $part (OK)};
          } else {
            my @ds = devspec2array($part);
            if($ds[0] ne $part) {
                my $lst = join q{,}, @ds;
                push @list, "$part: devspec $lst (OK)";
            } elsif ($numdef == @ds) {
                push @list, "$part: matches all devices (ignored)";
            } else {
                push @list, "$part: unknown (ignored)";
            }
          }
        }
        (my $other, $re) = notifyRegexpChangedsplitByPipe($re);
        push @list, "$other: irrelevant part (OK)";
    }
    push @list, ".*: irrelevant part (OK)" if $consume;
    return join "\n", @list;
}

sub
notifyRegexpChanged($$;$)
{
  my ($hash, $re, $disableNotifyFn) = @_;
  notifyRegexpChanged2($hash, $re, $disableNotifyFn);

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  my @list2 = split(/\|/, $re);
  my @list = grep { m/./ }                                     # Forum #62369
             map  { (m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/ &&
                     ($defs{$1} || devspec2array($1) ne $1)) ? $1 : ""} @list2;
  if(@list && int(@list) == int(@list2)) {
    my %h = map { $_ => 1 } @list;
    @list = keys %h; # remove duplicates
    $hash->{NOTIFYDEV} = join(",", @list);
    $hash->{NOTIFYDEV_1} = join(",", @list);
  } else {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
  }
}

sub
notifyRegexpChanged2
{
  my ($hash, $re, $disableNotifyFn) = @_;
  #Log3('global',3 , "nRC2 called for $hash->{NAME}");
  #%ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV_2});
  #  $hash->{disableNotifyFn}=1;
    return;
  }
  #delete($hash->{disableNotifyFn});
  my $numdef = keys %defs;
  my @fm = devspec2array($re);
  if (@fm && $fm[0] ne $re) {
    return delete $hash->{NOTIFYDEV_2} if $numdef == @fm ;
    my $lst = join q{,}, @fm;
    $hash->{NOTIFYDEV_2} = $lst;
    return;
  }

  $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
  my $first = 1;
  my $outer = $re =~ m{\A\s*\(.+\)\s*\z}x; #check if outer brackets are given
  my @list;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
    if ( $first && $outer ) {
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);
            $dev =~ s{\A.}{}x;
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x if $outer; #remove outer brackets if given
    #Log3('global',3 , "re splitted to $dev and $re") if $re;
    return delete $hash->{NOTIFYDEV_2} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      @list = (@list, @darr);
    }
    (undef, $re) = notifyRegexpChangedsplitByPipe($re);
    #Log3('global',3 , "re now cleaned to $re");
  }
  return delete($hash->{NOTIFYDEV2}) if !@list;
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV_2} = join q{,}, @list;
  return;
}

sub notifyRegexpChangedsplitByPipe {
    my $string = shift // return (undef,undef);
    # String in pipe-getrennte Tokens teilen, wenn die Klammerebene 0 ist
    my $lastChar = q{x};
    my $bracketLevel = 0;
    my $token = q{};
    my @chars = split q{}, $string;
    my $i = 0;
    for my $char ( @chars ) {
        #Log3('global',3,"char is $char, last was $lastChar, level is $bracketLevel");
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $string = substr $string, length($token) + 1;
            #Log3('global',3 , "pipe detected, exiting with token $token, string $string");
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        if ( $i == scalar @chars ) {
          $string = substr $string, length $token;
        }
        $lastChar = $char;
    }
    #Log3('global',3 , "no more pipes detected, exiting with token $token, string $string");
    return ($token, $string);
}
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Zitat von: rudolfkoenig am 19 Januar 2022, 09:54:59
Diese Optimierung ist sicher nur einer unter Vielen und ich erweitere es gerne, der Code muss aber wartbar (d.h.leicht verstaendlich) bleiben.

Meine Rechnung bezog sich nur auf mein Modul, was ja diesbzgl. auch schon optimiert wurde. Die meisten Modulentwickler werden sich mit der Materie nicht so intensiv auseinandergesetzt haben, da wird mit Sicherheit mehr Zeit verbraten, bis man feststellt, dass man für ein bestimmtes Event nicht zuständig ist, also ist eine vernünftige Filtermöglichkeit von außen durch sinnvoll.

Wie auch immer, am Ende musst du Anpassungen in der fhem.pl verantworten.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Wir reden hier die ganze Zeit von einem Device-Filter und nicht von einem Event-Filter.

Der Modulentwickler könnte, unabhängig von der jetzigen Vorgehensweise, die Option bekommen einen reinen Device-Filter als Regex der "Zentrale" mitzuteilen?

Dann könnte man in fhem.pl bevor man unnötig die jeweilige notify-Funktion aufruft das Device $dev->{NAME} einfach gegen den Filter prüfen.

Damit würde ich immerhin in meinem Modul 0.002 Millisekunden pro unnötiges Event sparen, andere wahrscheinlich viel mehr, wenn sie den Filter setzen könnten.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Beta-User

Zitat von: Damian am 19 Januar 2022, 23:43:50
Wir reden hier die ganze Zeit von einem Device-Filter und nicht von einem Event-Filter.
Korrekt, der Name der Funktion verkürzt das m.E. etwas...

Zitat
Der Modulentwickler könnte, unabhängig von der jetzigen Vorgehensweise, die Option bekommen einen reinen Device-Filter als Regex der "Zentrale" mitzuteilen?
Das ist schon jetzt möglich. Einige Module setzen direkt eine devspec (wie für list zulässig) in NOTIFYDEV, z.B. msgDialog oder msgConfig (über ersteres bin ich neulich erst wg. RHASSPY gestolpert), der Weg klappt nur nicht über die bisherige Funktion.

ZitatDann könnte man in fhem.pl bevor man unnötig die jeweilige notify-Funktion aufruft das Device $dev->{NAME} einfach gegen den Filter prüfen. [...]
Genau so ist es mAn. bereits heute. In dem Moment, in dem NOTIFYDEV gesetzt wird, ist es vermutlich nicht mehr erforderlich (oder sinnvoll), nochmal eigene Optimierungen vorzunehmen.



In dem Zusammenhang ist mir aufgefallen, dass es in meinem Vorschlag auch sinnvoller ist, die regex und nicht die Device-Liste in NOTIFYDEV zu schreiben, also die innere Schleife so zu notieren:
    while ($dev) {
      (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      #@list = (@list, @darr); #alte Variante
      push @list, $part; #neue Variante
    }

Das macht es etwas unempfindlicher gegen zwischenzeitliche Umbenennungen der überwachten Devices.

@Rudi:
Würde es Sinn machen, hier einen featurelevel-abhängigen Vorschlag zu machen? Dann könnten ggf. willige Tester einfach den anpassen...?
Oder findest du den ganzen Code so nicht gut?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

rudolfkoenig

Wie geschrieben, der Code uebersteigt z.Zt. mein Verstaendnis, und bis ich alle Nebeneffekte begreife, wird es auch dauern.
Ich wuerde lieber einen anderen Weg gehen.

Zum Klarstellen:
- mit direktes Setzen von NOTIFYDEV kann jedes Modul die Optimierung selbst aktivieren, ein Aufruf von notifyRegexpChanged ist nicht erforderlich.
- notifyRegexpChanged ist fuer Module wie notify und FileLog, deren Regexp Geraet und Event zusammen bearbeitet. Es ist angepasst fuer von autocreate angelegten FileLogs, von den "EventMonitor create" Knopf angelegten Geraete bzw. von den "Wizards" erweiterten Regexps.
- das notifyRegexpChanged Problem ist nur bei grossen und schwer belasteten Installationen relevant, wo der Benutzer selbst "falsch" optimiert hat.

Ich habe vor in der notify und FileLog FHEMWEB Detailansicht eine Hilfe einzubauen, damit das Optimieren fuer den Benutzer einfacher faellt. Vmtl. ist meine urspruengliche Idee eines notifydev Attributs auch hilfreich.

Beta-User

Zitat von: rudolfkoenig am 20 Januar 2022, 09:20:42
Ich wuerde lieber einen anderen Weg gehen.
OK, dann stelle ich meine Bemühungen an der Stelle ein.

ZitatIch habe vor in der notify und FileLog FHEMWEB Detailansicht eine Hilfe einzubauen, damit das Optimieren fuer den Benutzer einfacher faellt. Vmtl. ist meine urspruengliche Idee eines notifydev Attributs auch hilfreich.
Vermutlich ist der Aufwand, den wizzard zu überarbeiten (den man m.e. sinnvollerweise auch für meine Variante anpassen müßte, was aber kein allzugroßes Hexenwerk sein dürfte), deutlich höher wie die Analyse dessen, was mein Code-Vorschlag so in etwa macht. Im Ergebnis muss man doch nur checken, ob NOTIFYDEV "gut" gefüllt ist, ab da geht es 1:1 weiter "as is". Das wäre also nur ein Übergangsthema, oder?

Was ich schwierig finde, ist die "Doppelpflege" von DEF und Attribut. Da sehe ich die größere Gefahr für _dauerhaften_ Supportaufwand wie wenn man das Problem "an der Wurzel" löst. (vermutlich ist das vergleichbar mit dem Dauerbrenner "addStateEvent").

Just my2ct.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

#29
Zitat von: rudolfkoenig am 20 Januar 2022, 09:20:42
Zum Klarstellen:
- mit direktes Setzen von NOTIFYDEV kann jedes Modul die Optimierung selbst aktivieren, ein Aufruf von notifyRegexpChanged ist nicht erforderlich.
- notifyRegexpChanged ist fuer Module wie notify und FileLog, deren Regexp Geraet und Event zusammen bearbeitet. Es ist angepasst fuer von autocreate angelegten FileLogs, von den

Also, irgendwie widersprechen sich die Aussagen und ich finde keine verbindliche Doku zum Setzen von NOTIFYDEV:

unter anderem finde ich z. B.

aus: https://forum.fhem.de/index.php/topic,64382.msg556060.html#msg556060

ZitatDie Funktion notifyRegexpChanged prueft sowas, und berechnet aus einem Regexp NOTIFYDEV bzw. loescht sie, falls NOTIFYDEV kontraproduktiv ist. Modulautoren sollten NOTIFYDEV nur ueber diese Funktion setzen, und nie direkt. Damit ist auch eine Aenderung/Optimierung eher moeglich.

Diese Frage : https://forum.fhem.de/index.php/topic,64449.msg557606.html#msg557606

wurde ebenfalls nicht eindeutig beantwortet.

Wo finde ich denn jetzt eine verbindliche Aussage, was ich als Modulautor (hier z. B. beim DOIF) tun kann? notifyRegexpChanged benutze ich laut Empfehlung, obwohl es nicht besonders sinnvoll für mein Modul ist, da bei mir Device-Regex und Event-Regex ohnehin getrennt behandelt wird, daher habe ich gar nicht das "notify"-Problem.



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

Beta-User

Zitat von: Damian am 20 Januar 2022, 10:05:56
Also, irgendwie widersprechen sich die Aussagen und ich finde keine verbindliche Doku zum Setzen von NOTIFYDEV:
Na ja, es gibt dann noch https://wiki.fhem.de/wiki/DevelopmentModuleAPI#notifyRegexpChanged.

Das "Grundproblem" ist aber weiter:
- Die Doku gibt den "Stand der Dinge" (halbwegs) wieder, und zwar nach dem Verständnis des jeweiligen Autors (und weder den wirklichen Ist, noch den Soll-Zustand);
- "gedacht" scheint es mal anders gewesen zu sein, als es jetzt (im wiki und von Rudi) beschrieben worden ist, teils scheinen mir auch in den Links genannte Einschränkungen (10 Devices?) nicht (mehr) zu bestehen;
- wichtiger als die Funktion zur Ermittlung des NOTIFYDEV ist imo, dass NOTIFYDEV gesetzt ist, wenn das möglich/sinnvoll ist (?). (In meinem Testsystem finde ich irritierend, was alexa da macht);
- Es ist relativ unklar (im Sinne von nicht der Doku zu entnehmen), wann es eigentlich sinnvoll ist, sich darüber Gedanken zu machen. Wer mit einem zentralen "Überwachungs-Device" viele Devices überwacht, aber intern eine gute Optimierung hat, braucht es uU. nicht, für alle anderen scheint es mir sinnvoll zu sein;
- dür Modulautoren ist die Ermittlungs-Funktion heute nur zu nutzen, wenn tatsächlich eine (nicht optimierte!) "Textkette" übergeben wird, die nichts mit devspec zu tun hat.

Von daher meine ich nach wie vor, es sei sinnvoll
- die zentrale Funktion so zu bauen, dass sie auch den typischen Anforderungen eines Moduls entspricht (insbes.: Übergabe einer devspec ermöglicht), und
- den "Kauderwelsch", den die User in der Realität halt so anliefern, im Kernbereich besser zu "übersetzen", als das derzeit der Fall ist.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Zitat von: Beta-User am 20 Januar 2022, 10:54:50
Na ja, es gibt dann noch https://wiki.fhem.de/wiki/DevelopmentModuleAPI#notifyRegexpChanged.

OK. Zum Selbersetzen von NOTIFYDEV steht da aber nichts.

Da bereits schon eine einfache Regex nur für Devicenamen scheitert

Beispiel:

defmod di_test DOIF (["bla"])

bedeutet im DOIF: bla kommt im Devicenamen vor, habe ich für solche Fälle schon meinen Filter setzen müssen: ^global$|bla, weil notifyRegexpChanged NOTIFDEV nicht setzt.

Wahrscheinlich müsste ich NOTIFYDEV auf .*bla.* direkt setzen, weil die Regex intern offenbar in ^ und $ eingepackt wird.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Beta-User

#32
Zitat von: Damian am 20 Januar 2022, 11:45:56
OK. Zum Selbersetzen von NOTIFYDEV steht da aber nichts.
Na ja, das eher allgemeine Thema ist in https://wiki.fhem.de/wiki/DevelopmentModuleIntro#X_Notify zu finden.

Zitat
Da bereits schon eine einfache Regex nur für Devicenamen scheitert
[...]
Wahrscheinlich müsste ich NOTIFYDEV auf .*bla.* direkt setzen, weil die Regex intern offenbar in ^ und $ eingepackt wird.
"Scheitern" würde ich es nicht nennen, mAn. ist die explizite Angabe dessen, was gewünscht ist vorzugswürdig (also ja: "einpacken" ala "global|.*bla.*" hilft), aber vermutlich ist das einfach eine Geschmacksfrage...

Doku wäre aber in jedem Fall gut... (EDIT: Es _ist_ durch den Verweis auf "devspec" eigentlich eindeutig, was man aber mAn. nur erkennt, wenn man weiß, wie es gemeint ist und genau den Punkt sucht...).
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Beta-User

#33
Jetzt doch noch der Vorschlag für featurelevel-abhängig Code mit ein paar mehr Kommentaren und einer Umstellung in notifyRegexpCheck(), mit der dann jeweils in einer Zeile zu finden sein sollte, was zusammengehört.

# Used for debugging
sub
notifyRegexpCheck($)
{
  if ($featurelevel<6.2) {
      return
      join("\n", map {
        if($_ !~ m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/) {
          "$_: no match (ignored)"
        } elsif($defs{$1})               {
          "$_: device $1 (OK)";
        } else {
          my @ds = devspec2array($1);
          if($ds[0] ne $1) {
            "$_: devspec ".join(",",@ds)." (OK)";
          } else {
            "$_: unknown (ignored)";
          }
        }
      } split(/\|/, $_[0]));
    }

    my $re = shift // return 'No Expression to check provided!';
    my $numdef = keys %defs;
    my @fm = devspec2array($re);
    if (@fm  && $fm[0] ne $re ) {
        return "$re: matches all devices (ignored)" if $numdef == @fm;
        my $lst = join q{,}, @fm;
        return "$re: devspec for devices only - $lst (OK)";
    }
    my $consume = $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
    my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
    my $first = 1;
    my @list;
    while ($re) {
        (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
        if ( $first && $outer ) {
            $first = 0;
            my $ops = $dev =~ tr/(//;
            my $clos = $dev =~ tr/)//;
            if ( $ops > $clos ) {
                chop($re);
                $dev =~ s{\A.}{}x;
            }
        }
        $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
        push @list, q{.*: matches all (ignored)} if $dev eq '.*';

        while ($dev) {
          (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
          push @list, q{.*: matches all (ignored)} if $part eq '.*';
          if ($defs{$part}) {
              push @list, qq{$part: device $part (OK)};
          } else {
            my @ds = devspec2array($part);
            if($ds[0] ne $part) {
                my $lst = join q{,}, @ds;
                push @list, "$part: devspec $lst (OK)";
            } elsif ($numdef == @ds) {
                push @list, "$part: matches all devices (ignored)";
            } else {
                push @list, "$part: unknown (ignored)";
            }
          }
        }
        (my $other, $re) = notifyRegexpChangedsplitByPipe($re);
        $list[-1] .= "; $other: irrelevant part (OK)" if $other;
    }
    $list[-1] .= "; .*: irrelevant part (OK)" if $consume;
    return join "\n", @list;
}

sub
notifyRegexpChanged($$;$)
{
  my ($hash, $re, $disableNotifyFn) = @_;

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  if ($featurelevel<6.2) {

      my @list2 = split(/\|/, $re);
      my @list = grep { m/./ }                                     # Forum #62369
                 map  { (m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/ &&
                         ($defs{$1} || devspec2array($1) ne $1)) ? $1 : ""} @list2;
      if(@list && int(@list) == int(@list2)) {
        my %h = map { $_ => 1 } @list;
        @list = keys %h; # remove duplicates
        $hash->{NOTIFYDEV} = join(",", @list);
      } else {
        delete($hash->{NOTIFYDEV});
      }
      return; #end for featurelevels up to 6.1
  }

  my $numdef = keys %defs;
  my @fm = devspec2array($re);
  if (@fm && $fm[0] ne $re) {
    return delete $hash->{NOTIFYDEV} if $numdef == @fm; #regex was provided, matching all devices
    $hash->{NOTIFYDEV} = $re;
    return;
  }

  $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;       # remove outer backets and closing .*
  my $first = 1;                            # we may have to treat enclosing brackets separately
  my $outer = $re =~ m{\A\s*\(.+\)\s*\z}x;  # check if outer brackets are given
  my @list;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2;   # get the first seperator for device/reading+rest?
    if ( $first && $outer ) {               # special treatment for outer brackets, checked by uneven number of brackes in expression that may be device/devspec
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);                      # in case if: remove last bracket from rest
            $dev =~ s{\A.}{}x;              # in case if: remove first bracket from devspec
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x if $outer; #remove outer brackets if given
    return delete $hash->{NOTIFYDEV} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
      return delete $hash->{NOTIFYDEV} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      push @list, $part;
    }
    (undef, $re) = notifyRegexpChangedsplitByPipe($re); # remove irrelevant stuff like reading names and values left to next pipe at highest level, then repeat checks with remaining chars right to pipe
  }
  return delete($hash->{NOTIFYDEV}) if !@list; #we got results at all?
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV} = join q{,}, @list;
  return;
}

sub notifyRegexpChangedsplitByPipe {
    my $string = shift // return (undef,undef);
    # split string in two pipe-separated tokens, but only in case if there is the same number of (unescaped) opening and closing brackets left to the pipe; returns tokes without pipe used for splitting
    my $lastChar = q{x}; # just something different to escape character \
    my $bracketLevel = 0;
    my $token = q{};                # start empty
    my @chars = split q{}, $string; # transform string to char array
    my $i = 0;                      # counter, needed to transform string to "" at last char of the string
    for my $char ( @chars ) {
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $string = substr $string, length($token) + 1;
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        return ($token,'') if $i == @chars; # done, end of string, no more pipe chars
        $lastChar = $char;
    }
    return ($token, $string);
}

Edit: Code in notifyRegexpChangedsplitByPipe bei end of string leicht vereinfacht
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

rudolfkoenig

ZitatEdit: Code in notifyRegexpChangedsplitByPipe bei end of string leicht vereinfacht
Tut mir Leid fuer die Arbeit, aber diese Funktion ist mir zu Zentral, und die Aenderung zu gewaltig.


ZitatAlso, irgendwie widersprechen sich die Aussagen und ich finde keine verbindliche Doku zum Setzen von NOTIFYDEV:
Da hast Du Recht.
Nach direktes setzen von NOTIFYDEV muss man %ntfyHash leeren.
Da das in der Zukunft sich aendern kann, ist es sinnvoller notifyRegexpChanged() aufzurufen, und als Regexp die Liste aller Geraete (oder devspecs), getrennt durch |, zu uebergeben.


Ich habe fuer FileLog und notify in FHEMWEB einen Hinweis eingebaut, falls das Setzen von NOTIFYDEV nicht funktioniert hat.
Bin noch nicht ganz sicher, ob das nicht die Buechse der Pandora wird.

Beta-User

Zitat von: rudolfkoenig am 20 Januar 2022, 20:54:20
Tut mir Leid fuer die Arbeit, aber diese Funktion ist mir zu Zentral, und die Aenderung zu gewaltig.
Kein Problem, Danke für's drüber nachdenken!

Zitat
Da hast Du Recht.
Nach direktes setzen von NOTIFYDEV muss man %ntfyHash leeren.
Da das in der Zukunft sich aendern kann, ist es sinnvoller notifyRegexpChanged() aufzurufen, und als Regexp die Liste aller Geraete (oder devspecs), getrennt durch |, zu uebergeben.
Das verstehe ich soweit, habe aber weiter ein praktisches Problem. Wenn ich _oder devspecs_ so interpretiere wie in der offiziellen Doku aufgeführt (https://fhem.de/commandref_modular.html#devspec), sind auch Ausdrücke wie rC2TestDummy[12] oder TYPE=AMAD.* zulässig.

Jetzt habe ich im Testsystem mal folgene Voraussetzungen:
list devstrich0,rC2TestDummy[12],TYPE=AMAD.*
liefert:
Zitatdevstrich0
rC2TestDummy2
AMADBridge
AMADDev_A

Für das Test-notify
defmod n_test4 notify devstrich0|rC2TestDummy[12]|TYPE=AMAD.* {}
ergibt aber "das Ausrufezeichen"
ZitatCould not optimize the regexp:

    devstrich0|rC2TestDummy[12]|TYPE=AMAD.*

How I tried (notifyRegexpCheck):

    devstrich0: device devstrich0 (OK)
    rC2TestDummy[12]: no match (ignored)
    TYPE=AMAD.*: no match (ignored)
Ergo muss man "devspec" in diesem Zusammenhang anders interpretieren als woanders. Nicht glücklich, und mAn. auch nicht geeignet, Modulautoren zur Verwendung der zentralen Funktion einzuladen.

Zitat
Ich habe fuer FileLog und notify in FHEMWEB einen Hinweis eingebaut, falls das Setzen von NOTIFYDEV nicht funktioniert hat.
Bin noch nicht ganz sicher, ob das nicht die Buechse der Pandora wird.
Schauen wir mal :) .
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Etwas anderer Ansatz. Ich habe mir mal den Spaß erlaubt und habe eine Zeile vor dem notify-Aufruf in fhem.pl eingefügt:

      next if(defined($defs{$n}->{DEVREGEX}) and $dev !~ /$defs{$n}->{DEVREGEX}/);
      my $r = CallFn($n, "NotifyFn", $defs{$n}, $hash);

Wenn nun der Modul-Entwickler in seinem Modul eine reine Regex auf die zu triggernden Devices im Internal DEVREGEX setzt, dann wird er nur noch beim Treffer benachrichtigt. Der Rest merkt von diesem Filter nichts. Ggf. könnte man noch einen Syntax-Check für die Regex durchführen, falls sie gesetzt wird.

Das könnte man auch über ein Attribut realisieren.


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

rudolfkoenig

$hash->{NOTIFYDEV} zu setzen und danach  %ntfyHash zu loeschen duerfte kaum aufwendiger sein, ist dafuer effizienter, da keine Regexp-Pruefung stattfindet.

Ich habe jetzt die Funktion setNotifyDev($hash, $notifydev) hinzugefuegt, um dem Ganzen einen API Anstrich zu geben.

Damian

Zitat von: rudolfkoenig am 22 Januar 2022, 10:22:48
$hash->{NOTIFYDEV} zu setzen und danach  %ntfyHash zu loeschen duerfte kaum aufwendiger sein, ist dafuer effizienter, da keine Regexp-Pruefung stattfindet.

Ich habe jetzt die Funktion setNotifyDev($hash, $notifydev) hinzugefuegt, um dem Ganzen einen API Anstrich zu geben.

Aber ist NOTIFYDEV nicht eine kommagetrennte Liste von Device-Regex? Das ist ja was anderes als eine Regex auf alle Devices.

Im DOIF gibt der User eine reine Regex für alle Devices an. Und die möchte ich nicht auseinandernehmen. Dass sowas nicht gut funktioniert,  haben ja bereist hier gesehen.

Daher denke ich, ist NOTIFYDEV selber setzen für mich leider keine Option.

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

Damian

Ich habe mal setNotifyDev($hash, $notifydev) bei mir ausprobiert.

Bsp.

Userdefintion für einen Trigger mit der Syntax ["<Device-Regex>"]

z. B. in der Bedingung von DOIF

(["ttt1|ttt2"] or ["bla1|bla2"])

Bedeutet wenn im Device ttt1 oder ttt2  oder im Device bla1 oder bla2 vorkommt

daraus habe ich bisher folgendes aufgerufen (was oft NOTIFYDEV nicht belegte):

notifyRegexpCheck($hash, "global|.*bla1|bla2.*|.*ttt1|ttt2.*")

jetzt mit

setNotifyDev($hash, "global|.*bla1|bla2.*|.*ttt1|ttt2.*")

funktioniert schon mal.

Ich habe jetzt statt Pipe Komma als Trenner eingebaut, was wohl eher im Sinne des Erfinders ist:

setNotifyDev($hash, "global,.*bla1|bla2.*,.*ttt1|ttt2.*")

beides scheint mit gesetztem NOTIFYDEV zu funktionierten.

Damit würden einige zig-tausend DOIF-Definitionen öfters 0,002 Millisekunden weniger das System belasten :)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

naja, ich musste noch nachbessern:

mit

setNotifyDev($hash, "global,.*(bla1|bla2).*,.*(ttt1|ttt2).*")

scheint jetzt zu funktionieren.

Ich glaube, ich muss noch gut testen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

bei so etwas:

Internals:
   DEF        ([la1] or [la2])
   DOIFDEV    ^global$|^la1$|^la2$
   FUUID      61e938c0-f33f-c0d4-9330-9bb4cb117c33e731
   MODEL      FHEM
   NAME       di_test
   NOTIFYDEV  global,.*(^la1$).*,.*(^la2$).*
   NR         117
   NTFY_ORDER 50-di_test
   STATE      initialized
   TYPE       DOIF
   VERSION    25295 2021-12-04 18:13:39


sieht NOTIFYDEV irgendwie gefährlich aus, da wollen wir hoffen, dass alle Perl-Versionen damit gleichermaßen klarkommen, in DOIFDEV sieht man den internen DOIF-Filter


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

Damian

So, mein System läuft noch  :)

Alle DOIF-Devices besitzen jetzt den NOTIFYDEV-Filter, das dürfte ein paar Millisekunden einsparen.

Ich frage mich, ob es nicht besser wäre statt mit Komma die einzelnen Regex-Angaben doch mit Pipe zu trennen, es hätte die gleiche Funktionalität, allerdings solle die Abarbeitung des Arrays in fhem.pl, da nur ein Element, etwas schneller gehen, also z. B. statt:

setNotifyDev($hash,"Wasserverbrauch,Wetter,Wasserzisterne,Aussensensor,wetter_com_broich,di_Regen,Stromzaehler,global,di_zaehler,ESPEasy_Eingang_CO2,di_vaillant,Tankstelle,RKI7,CUL_WZ,outsensor")

setNotifyDev($hash,"Wasserverbrauch|Wetter|Wasserzisterne|Aussensensor|wetter_com_broich|di_Regen|Stromzaehler|global|di_zaehler|ESPEasy_Eingang_CO2|di_vaillant|Tankstelle|RKI7|CUL_WZ,outsensor")


Das sollte mit beliebigen Regex-Angaben funktionieren.

Also auch z. B.

statt
setNotifyDev($hash,"global,test,.*(bla1|bla2).*,.*(bla1).*,.*(^bla1).*")

setNotifyDev($hash,"global|test|.*(bla1|bla2).*|.*(bla1).*|.*(^bla1).*")

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

Damian

ZitatIch frage mich, ob es nicht besser wäre statt mit Komma die einzelnen Regex-Angaben doch mit Pipe zu trennen, es hätte die gleiche Funktionalität, allerdings solle die Abarbeitung des Arrays in fhem.pl, da nur ein Element, etwas schneller gehen

Ich habe mir den Code in fhem.pl nochmal angeschaut, createNtfyHash() wird ja nur einmal aufgerufen, um die Listen aufzubereiten, also hält sich der Performancegewinn in Grenzen. Daher belasse ich es beim Komma und habe dafür die Option auf andere devspecs wie z. B. FILTER.

Ich war überrascht wie viele Devices in $ntfyHash in meinem Mini-Testsystem stecken, es sind vor allem Filelogs, notify benutze ich nicht, daher kann ich keine statistischen Aussagen dazu machen, immerhin kommen DOIF-Devices jetzt selten vor :)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Beta-User

Zitat von: rudolfkoenig am 22 Januar 2022, 10:22:48
Ich habe jetzt die Funktion setNotifyDev($hash, $notifydev) hinzugefuegt, um dem Ganzen einen API Anstrich zu geben.
Interessehalber - die Handhabung von "$hash->{disableNotifyFn}" soll beim Maintainer verbleiben...?

Und wenn schon "undef" als Argument möglich sein soll, warum dann zwingend?

Und: Macht es Sinn, überhaupt einen Filter zu setzen, wenn "alle" gemeint sein sollen?

Alternativer Vorschlag (bzgl. des "ne" auch immer noch vereinfachend...):
sub
setNotifyDev($;$$)
{
  my ($hash, $ntfydev, $disableNotifyFn) = @_;
  %ntfyHash = ();
  if($ntfydev) {
    delete $hash->{disableNotifyFn};
    return $hash->{NOTIFYDEV} = $ntfydev if $ntfydev ne '.*';
    delete($hash->{NOTIFYDEV});
} else {
    delete($hash->{NOTIFYDEV});
    if (!defined $disableNotifyFn || $disableNotifyFn) {
      $hash->{disableNotifyFn}=1;
    } else {
      delete $hash->{disableNotifyFn};
    }
  }
}
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

rudolfkoenig

ZitatUnd wenn schon "undef" als Argument möglich sein soll, warum dann zwingend?
Weil meiner Ansicht nach setNotifyDev($hash, undef) verstaendlicher ist, als setNotifyDev($hash).
Ich habe aus diesem Grund einen separaten setDisableNotifyFn($hash, 1) hinzugefuegt.

Beta-User

Danke, auch für die Erläuterung.
Bin zwar nicht sicher, ob es eine separate Funktion gebraucht hätte, aber so kann man es ggf. einfacher erklären.

Habe ein paar entsprechende Ergänzungen in https://wiki.fhem.de/wiki/DevelopmentModuleAPI bzw. https://wiki.fhem.de/wiki/DevelopmentModuleIntro reingenommen, dass das da nicht verloren geht.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Ich habe jetzt bei mir setDisableNotifyFn eingebaut, für den Fall, dass im DOIF-Device das Attribut disable gesetzt bzw. gelöscht wird.

Es scheint zu funktionieren. Irritierend finde ich allerdings die Kleinschreibung im Internal.
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

rudolfkoenig

ZitatKönnte es sein, dass setNotifyDev mit FHEM2FHEM nicht funktioniert?
Bei bestimmten Kombinationen ist das richtig.

NOTIFYDEV spezifiziert die Liste der lokalen FHEM-Geraete, deren Events zu einem NotifyFn Aufruf fuehren sollten.
Bei FHEM2FHEM:Log kann es vorkommen, dass ein Geraet lokal nicht vorhanden ist, und von FHEM2FHEM fuer die Event-Behandlung temporaer angelegt wird, und die relativ teure Neuerstellung der der Warteschlangen via createNtfyHash nicht durchgefuehrt wird.

Solche Faelle koennen auch notify verwirren: wenn lokal DEV1 existiert, DEV2 nur remote, das notify-Regexp auf DEV.* angelegt wird, dann wird notify fuer die DEV2-Events nicht aufgerufen. Fuer solche Faelle muss man entweder einen lokalen DEV2 (z.Bsp. als dummy) anlegen, oder den Regexp als (DEV1|DEV2) spezifizieren.

Die korrekte Loesung waere in FHEM2FHEM:Log vor und nach der Eventbehandlung solcher Geraete createNtfyHash aufzurufen.
Bin noch leicht unsicher, was wichtiger ist: ein in Sonderfaellen korrektes Verhalten, oder Geschwindigkeit.

Damian

ja, das habe ich mir schon gedacht, daher habe ich dem TE bereits geraten, ein entsprechendes Device im Hostsystem manuell anzulegen: siehe https://forum.fhem.de/index.php/topic,103401.msg1209300.html#msg1209300

Da bin ich mal gespannt, wie viele noch mit diesem Problemchen kommen, da es zuvor mit dem eigenen Filter (ohne NOTIFYDEV) im DOIF ohne Device-Kopie funktionierte.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

nächstes Problem kommt dann gleich um die Ecke: https://forum.fhem.de/index.php/topic,126321.msg1209412.html#msg1209412

Diese Regex hat wohl ohne NOTIFYDEV im DOIF funktioniert: Licht[.].*(?<!till)

mit NOTIFYDEV wohl nicht mehr
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF