FRITZBOX: Best Practice automatisiert Anrufe in Spam-Telefonbuch&sip_filter-Attribut

Begonnen von TomLee, 30 November 2024, 16:38:13

Vorheriges Thema - Nächstes Thema

frober

D.h. prinzipiell funktioniert es, nur dass anstelle von 30s es 6s Wartezeit sind?

Hmm, was passiert mit dem Timer während FHEM blockiert? Evtl. stimmt der timestamp im Log durch die Blockade nicht. Kannst du das Ergebnis verifizieren, stimmt es oder wird die Sub wirklich zu früh ausgeführt.

Raspi 3b mit Raspbian Bullseye und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

TomLee

Die Sub wird wirklich zu früh aufgerufen, die Liste enthält nicht die zu ergänzende neue Nummer.

JoWiemann

Hallo Thomas,

ich glaube Dir bleibt nichts anderes übrig als Dich mit: https://wiki.fhem.de/wiki/Blocking_Call zu beschäftigen.

Grüße Jörg
Jörg Wiemann

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

Master: CubieTruck; Debian; Aktuelles FHEM

TomLee

Also wsl. dann doch wie vermutet:

Zitat von: TomLee am 01 Dezember 2024, 21:27:19Wsl. wird man irgendwie im Perl-Script-Ausführungsfluss auch weiter machen können, ist aber dann bestimmt mehr Aufwand, für das gleiche Ergebnis wie mit der cmdalias-Variante.

Weil die Stimmung derzeit passt, besteht die Möglichkeit, das ich mich in das Blocking_Call eindenken/einarbeiten werde...

TomLee

Hi,

bin jetzt um die Erfahrung reicher wie so ein Blocking_Call-Aufruf umzusetzen wäre. Wie er mir aber bei meinem Anliegen nützlich und möglichst nicht umständlich weiterhelfen soll ist mir nicht klar oder ich bin mir mal wieder selbst im Weg. Verstehe es so das man aus dem geforkten Prozess die Rückgabe des letzten fhem("get $cmdevice showPhonebookEntries 1",1) an die finishFN übergeben müsste. In dem geforkten Prozess hab ich aber doch genau das gleiche Problem nach dem rereadPhonebook wie jetzt auch. Ist mir to much.



Hab mich weiter damit beschäftigt warum das mit InternalTimer nicht klappt. Da hat sich rausgestellt das wenn ich eine normale Sub (keine anonyme) angebe, alles funzt wie gedacht. Dabei stört mich aber das ich eine extra Sub brauch und der auch die Devices via Parameter übergeben muss.

Also hab ich mir das mit der anonymen Sub nochmal genauer angeschaut. Da hat sich rausgestellt das man die Referenz auf die anonyme Sub in InternalTimer angeben muss das es funzt  ::) :

##############################################
# $Id: myUtilsTemplate.pm 21509 2020-03-25 11:20:51Z rudolfkoenig $
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub Alexa_Routines_Utils_Initialize {
  my $hash = shift//return;
  return;
}

# Enter you functions below _this_ line.


sub newspamentrie {
my $cmdevice = shift // return; # FB_CALLMONITOR Name
my $fbdevice = shift; # FRITZBOX Name
my $sipdev = shift; # SIP Name
my $callforwardingnumber = shift; # call forwarding number
my $entries= fhem("get $cmdevice showPhonebookEntries 1",1); # read current Spambook
my @numbers = $entries =~ m/^.{3}(0\d+)/gm; # determine numbers
my $entriename = 'Spam'.(scalar(@numbers)+1); # new entriename Spam+number of numbers
my $entrienumber = (ReadingsVal($cmdevice,'external_number',-1) eq "$callforwardingnumber"?ReadingsVal($cmdevice,'internal_number','-1'):ReadingsVal($cmdevice,'external_number','-1')); # Don't use call forwarding number

my $sub = sub {
my $newentries = fhem("get $cmdevice showPhonebookEntries 1",1);#read updated Spambook
$newentries = join(',',$newentries =~ m/^.{3}(0\d+)/gm); # create comma separated list of numbers
fhem("sleep 2;attr -silent $sipdev sip_filter $newentries"); # push list in SIP-Device
};

fhem("set $fbdevice phoneBookEntry new 1 0 $entriename work: $entrienumber;sleep 2;set $cmdevice rereadPhonebook"); # add last call to spam book and reread Phonebook
InternalTimer(gettimeofday() + 30, \&$sub, undef); # call Sub in 30s
}

1;


Den regulären Ausdruck zum ermitteln der Nummern hab ich angepasst.
Als ich mich das erste mal damit beschäftigt hatte kam bei einem showPhonebookEntries 1 :
Phonebook: Spam / Id: 1

   Number           Name
   -------------------------------
   04042236852   - Spam1
   04085599911    - Spam10
   0721509531027  - Spam11
   039155721386   - Spam12
   ...

Jetzt kommt hinter dem Name noch eine Nummer:

Phonebook: Spam / Id: 1

   Number           Name
   -------------------------------
   04042236852   - Spam1 [<122>]
   04085599911    - Spam10 [<140>]
   0721509531027  - Spam11 [<141>]
   039155721386   - Spam12 [<142>]
   ...

Spätestens beim 100. Eintrag im Spambook oder einer Nummer hinter dem Name mit 0+numerischer Wert als Inhalt hätte der bisherige reguläre Ausdruck versagt.


Wär cool wenn das jemand bei sich nachvollziehen würde.

TomLee

Auch wenn die Funktion eigentlich tut was sie soll, hat mich bisher gestört das man keine Bestätigung/Rückmeldung erhält, das der Eintrag auch erfolgt ist oder was schief ging und keiner erfolgte.

Man kann es auf verschiedene Weise angehen, mir als Laie gefällt mein Ansatz es anzugehen, schliesse aber nicht aus das es ggf. doch umständlich gelöst ist.
Ein paar Variablen hab ich noch umbenannt und den regex zum ermitteln der Nummern angepasst.

Zitatsub newspamentry {
my $cmdev = shift // return; # FB_CALLMONITOR name
my $fbdev = shift; # FRITZBOX name
my $sipdev = shift; # SIP name
my $pushdev = shift; # tb_TelegramBot name
my $callforwardingnumber = ReadingsVal($fbdev,'diversity1_dest','-1'); # call forwarding number
my $entries= CommandGet(undef,"$cmdev showPhonebookEntries 1"); # read current Spambook
my @numbers = $entries =~ m/^\s*(0\d+)/gm; # determine numbers
my $entriename = 'Spam'.int(time); # new entriename Spam+Unixtimestamp
my $entrienumber = (ReadingsVal($cmdev,'external_number','-1') eq "$callforwardingnumber"?ReadingsVal($cmdev,'internal_number','-1'):ReadingsVal($cmdev,'external_number','-1')); # Don't use call forwarding number

my $sub = sub {
    my $newentries = fhem("get $cmdev showPhonebookEntries 1",1);#read updated Spambook
    my @newentries = ($newentries =~ m/^\s*(0\d+)/gm); #create an array and assign matching values   
    my %diff; # empty hash
    @diff{@newentries} = (); # all elements from @newentries as keys
    delete @diff{@numbers};  # deletes the elements that also appear in @newentries
    my $diffstr = join(", ", keys %diff);
    fhem("set $pushdev msg Fehler, kein neuer Eintrag erfolgt!") if !$diffstr; # send message if flag set
    fhem("set $pushdev msg $diffstr wurde im Spambook ergänzt.") if $diffstr;   

    $newentries = join(',',$newentries =~ m/^\s*(0\d+)/gm); # create comma separated list of numbers
    fhem("attr -silent $sipdev sip_filter $newentries"); # push list in SIP-Device
    };

fhem("set $fbdev phoneBookEntry new 1 0 $entriename work: $entrienumber;sleep 2;set $cmdev rereadPhonebook"); # add last call to spam book and reread Phonebook
InternalTimer(gettimeofday() + 40, \&$sub, undef); # call Sub in 40s
}

edit: regex zum ermitteln der Nummern angepasst

TomLee

Den Telefonbucheintragname über die Anzahl der Telefonbucheinträge zu bestimmen ist nicht praktikabel, stell ich fest.
Mein erster Gedanke es anders anzugehen, wäre eine vierstellige (oder mehr) Zufallszahl an 'Spam' anzuhängen.

Wie löst das ein erfahrener Programmierer, das nie doppelte Namen vergeben werden?


TomLee

Hab mich dazu entschieden einfach den Unixtimestamp anzuhängen, was in dem Fall völlig ausreichend einzigartig ist.

TomLee

Hab den Code weiter optimiert. Mit perlcritic bis brutal. Ob das sein muss? Nee, denk ich, habs halt einfach mal gemacht, weils mich "gepackt" hat.
Dabei hat sich herausgestellt das man doch nur $sub (statt der Referenz \&$sub) in InternalTimer übergeben kann (zumindest klappt es nach wenigen Tests), keine Ahnung was ich damals verkehrt gemacht habe.

Zum prüfen hab ich eine Spam.pl erstellt:
package Spam;

use strict;
use warnings;
use POSIX qw(gettimeofday);

our $VERSION = '1.0';

#--------------------------------------------------------------
# Subroutine: newspamentry
# Purpose: Adds a new entry to the <Spambook> phonebook and updates the SIP filter list.
# Call: newspamentry($cmdev, $fbdev, $sipdev, $pushdev);
#--------------------------------------------------------------
sub newspamentry {
my $cmdev = shift // return; # FB_CALLMONITOR name
my $fbdev = shift; # FRITZBOX name
my $sipdev = shift; # SIP name
my $pushdev = shift; # tb_TelegramBot name
my $callforwardingnumber = ReadingsVal($fbdev,'diversity1_dest','0123456789'); # Read the configured call forwarding number; use fallback if not available
my $entries= CommandGet(undef,"$cmdev showPhonebookEntries 1"); # Retrieve current entries from the spam phonebook (phonebook #1)
my @numbers = $entries =~ m/^\s*(0\d+)/gmsx; # Extract all phone numbers starting with 0
my $entriename = 'Spam'.int time; # Create a new entry name: "Spam" + current timestamp
my $entrienumber = (ReadingsVal($cmdev,'external_number','-1') eq "$callforwardingnumber" ? ReadingsVal($cmdev,'internal_number','-1') : ReadingsVal($cmdev,'external_number','-1')); # Determine which number to use for the new entry (avoid the call forwarding number)

# Define a subroutine that will be executed after 40 seconds (for verification and update)
my $sub = sub {
    my $newentries = CommandGet(undef,"$cmdev showPhonebookEntries 1"); # Read the updated spam phonebook entries
    my @newentries = ($newentries =~ m/^\s*(0\d+)/gmsx); # Extract all phone numbers again
    my %diff; # Create an empty hash used to compute the difference between old and new entries
    @diff{@newentries} = (); # Add all new entries as hash keys
    delete @diff{@numbers};  # Remove any that existed before (only keep new ones)
    my $diffstr = join q(,), keys %diff; # Join the new numbers (differences) into a string
    CommandSet(undef,$pushdev . ' msg ' . ($diffstr ? $diffstr . ' wurde im Spambook ergänzt!' : 'Fehler, kein neuer Eintrag erfolgt!')); # Send a message to confirm the update or report an error
    $newentries = join q(,),$newentries =~ m/^\s*(0\d+)/gmsx; # Convert all numbers to a comma-separated list
    CommandAttr(undef,"-silent $sipdev sip_filter $newentries"); # Push the updated list to the SIP device (refresh filter-Attribute)
    };

fhem("set $fbdev phoneBookEntry new 1 0 $entriename work: $entrienumber;sleep 2;set $cmdev rereadPhonebook"); # Add the last caller to the Fritzbox spam phonebook, wait 2s, then reread the phonebook
return InternalTimer(gettimeofday() + 40, $sub, undef); # Schedule the subroutine to run in 40 seconds (to allow for sync delay)
}
1;

Es bleiben zwei Meldungen:
bla:~$ LANG=en_US.UTF-8 perlcritic --brutal Spam.pl
Code is not tidy at line 1, column 1.  See page 33 of PBP.  (Severity: 1)
40 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 39, column 39.  Unnamed numeric literals make code less maintainable.  (Severity: 2)

Wenn ich den Code wegen Code is not tidy at line 1, column 1. mit perltidy formatiere, verschwindet die Meldung. Es liegt aber nicht an der ersten Zeile, an was genau hab ich noch nicht festgestellt. Mit der Formatierung kann ich mich auch nur teilweise anfreunden, darum hab ich sie nicht übernommen und ignoriere die Meldung.

Wegen 40 is not one of the allowed literal values (0, 1, 2). ...
Der Lesbarkeit wegen, eine Konstante zu verwenden

use constant SPAMBOOK_DELAY => 60;
...
return InternalTimer(gettimeofday() + SPAMBOOK_DELAY, $sub, undef)
ignoriere ich erstmal auch.

Bei dem Code bleibt für mich Laie weiterhin die Frage offen, ob es sinnvoll ist:
fhem("set $fbdev phoneBookEntry new 1 0 $entriename work: $entrienumber;sleep 2;set $cmdev rereadPhonebook");durch zwei CommandSet mit InternalTimer zu ersetzen?