Datenbankabfrage mit Perl

Begonnen von Wolle02, 11 März 2021, 07:33:38

Vorheriges Thema - Nächstes Thema

Wolle02

Ich weiß nicht, ob das Thema hier wirklich hinpasst, aber bei den Profis in der Perl Ecke darf ich nicht schreiben. Aber vielleicht liest das der eine oder andere hier auch:

Ich habe bei mir einen Traccar Server laufen. Unsere Handys melden ihre Position an den Server, der die Daten in eine MariaDB loggt. Jetzt möchte ich gerne diese Positionsdaten in FHEM verfügbar machen und habe dazu nach einer Möglichkeit gesucht die Daten in Fhem aus der Datenbank auszulesen. Dazu habe ich im Internet verschiedene Beispiele gefunden, die ich für meine Zwecke angepasst habe. Aktuell habe ich in meiner 99_myUtils folgenden Code stehen:

sub traccar()
{
my $db_user = "xxx";
my $db_pass = "xxx";
my $db_name = "traccar";
my $db_host = "xxx";
my $db_port = "3307";
my $sql_Wolle = "SELECT latitude,longitude FROM tc_positions WHERE deviceid = '1' ORDER BY servertime DESC LIMIT 1";
my $sql_Tanja = "SELECT latitude,longitude FROM tc_positions WHERE deviceid = '2' ORDER BY servertime DESC LIMIT 1";

my $db = DBI->connect("DBI:mysql:database=$db_name;host=$db_host;port=$db_port", "$db_user", "$db_pass");

my $db_result_Wolle = $db->prepare($sql_Wolle);
$db_result_Wolle->execute() or die $db_result_Wolle->err_str;

while (my ($col1, $col2) = $db_result_Wolle->fetchrow_array() ) {
    fhem("setreading Geos Wolle_latitude $col1;
          setreading Geos Wolle_longitude $col2")
}

my $db_result_Tanja = $db->prepare($sql_Tanja);
$db_result_Tanja->execute() or die $db_result_Tanja->err_str;

while (my ($col3, $col4) = $db_result_Tanja->fetchrow_array() ) {
    fhem("setreading Geos Tanja_latitude $col3;
          setreading Geos Tanja_longitude $col4")
}

$db->disconnect();
}


Das ist in euren Augen bestimmt ganz grauselig, aber es funktioniert soweit.  ;D

Leider ist die Funktionalität dahingehend eingeschränkt, dass Fhem beim Aufruf der Funktion vollständig blockiert.   :-\

Kann mir jemand helfen, wie man das unblockierend hinbekommt oder hat vielleicht eine ganz andere Idee wie ich die Daten unblockierend aus der Datenbank auslesen kann?

Danke und Gruß
Wolle

rudolfkoenig

ZitatLeider ist die Funktionalität dahingehend eingeschränkt, dass Fhem beim Aufruf der Funktion vollständig blockiert.   :-\

Ich kenne keine einfache Methode es nicht blockierend zu machen.

Aber vielleicht hilft eine der folgenden Ideen:

- sicherstellen, dass die Abfrage nicht lange dauert (servertime sollte Primaerschluessel sein oder Teil einer Index)

- wenn machbar, sollte die DB auf dem gleichen Rechner, wie FHEM laufen

- connect und prepare kann man "cachen", d.h. nach dem ersten mal wiederverwenden. Logik ist notwendig fuer den Fall, dass die Verbindung unterbrochen wurde.

- die komplette Funktion kann man mit dem BlockingCall FHEM Framework in einem separaten Prozess auslagern, setreading muss aber im HauptProzess sein, d.h. der Rueckgabewert der BlockingCall Funktion muss ausgewertet werden.

- mein Favorit ist ein MariaDB UDF (User Defined Function), was per Trigger ausgeloest wird, und einen konfigurierbaren setreading in FHEM ausfuehrt, z.Bsp via  HTTP. Das waere aber zu entwickeln. Es existiert ein sys UDF, mit dem man OS-Befehle ausfuehren kann, damit auch fhem.pl im Client-Mode (erfordert eine telnet Definition). UDF ist aber die anspruchsvollste Variante, und erfordert einen Zugang zum FHEM-Server von der DB.

Wolle02

Hallo Rudolf,

vielen Dank für deine Tipps.
Ich habe schon befürchtet, dass das nicht so ohne weiteres geht.

Um deinem Tipp zu folgen, dass die DB auf dem gleichen Rechner laufen sollte wie fhem, habe ich mir zwischenzeitlich überlegt auf meiner Synology (da läuft die DB) eine Mini-Fhem Instanz zu installieren, die nur diesen Code ausführt und per fhem2fhem an das Hauptfhem angebunden ist und die Events überträgt. Dann wird zumindest das Hauptfhem nicht blockiert.

Wernieman

#3
Warum immer gleich "groß" denken? Anstatt eines komplette FEM kannst Du auch ein Script/Deamon ausführen, was es zu FHEM pushed.
Besser wäre natürlich die Idee mit MariaDB UDF. Die würde doch  bei Änderungen in der Tabelle "von sich aus" getrickert, oder? Spart das pollen
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Otto123

Hi,

habe mit gelesen und ein bisschen nachgedacht. Eine einfache Variante wäre das ganz einfach in einem extra Prozess laufen zu lassen? Per cron?
Ich hatte mal ne Perl Sub gebaut die FHEM von "außen" über HTTP anspricht. Du braucht ja nur den fhem() Aufruf dadurch zu ersetzen und dein Perl Script läuft doch ansonsten unabhängig von FHEM?

https://wiki.fhem.de/wiki/CsrfToken-HowTo

Gruß Otto
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

Wolle02

#5
Danke euch für die Tipps. Meine Berührungspunkte mit Perl waren bisher nur in Fhem. Ich muss erstmal schauen, wie man ein Perlscript regulär aufruft.  ::) Deshalb ja die Überlegung mir dem Mini-Fhem.  :D

Otto123

Wie jedes andere Script auch: interpreter scriptname oder "über shebang" am Anfang des Script und execute Flag.
Was kannst Du besser als Perl? Shell?

Wie rufts Du jetzt eigentlich die Sub aus 99_myUtils auf? Zeitlich getriggert?
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

Wolle02

Zitat von: Otto123 am 11 März 2021, 10:48:14
Wie jedes andere Script auch: interpreter scriptname oder "über shebang" am Anfang des Script und execute Flag.

Ja, war natürlich Blödsinn von mir. Klar, dass das so geht. Hab das Script jetzt auch in eine traccar.pl geschrieben und aufgerufen. Funktioniert und gibt die Koordinaten auf der Konsole aus.
Jetzt muss ich mir nur überlegen wie ich die nach Fhem pushe.

Zitat
Wie rufts Du jetzt eigentlich die Sub aus 99_myUtils auf? Zeitlich getriggert?

Ja, so hatte ich mir das gedacht. Ein at mit z.B. 15 Minutenintervall. Für das externe Perlscript würde ich dann tatsächlich einen Cronjob machen.

Otto123

#8
Zitatwie ich die nach Fhem pushe.

Bau einfach den Perlcode aus meinem Wiki link ein. Anstatt fhem(setreading ...) machst Du dort fhemcl(url, setreading..) :)
sub fhemcl($$)
{
   use URI::Escape;
   use LWP::UserAgent;
     my ($hosturl,$fhemcmd) = @_;
     my $token;
   # get token
     my $ua = new LWP::UserAgent;
     my $url = "$hosturl/fhem?XHR=1/";
     my $resp = $ua->get($url);
        $token = $resp->header('X-FHEM-CsrfToken');
   # url encode the cmd
     $fhemcmd = uri_escape($fhemcmd);
     $url = "$hosturl/fhem?cmd=$fhemcmd&fwcsrf=$token&XHR=1";
     $resp = $ua->get($url)->content;
     # cut the last character, the additional newline
     return substr $resp,0,-1
}
# Usage
#{fhemcl("http://raspib3:8083","{ReadingsVal('Melder','state','error')}")}
#{fhemcl("http://raspib3:8083","set Test2 toggle")}
#{fhemcl("http://raspib3:8083","list global")}
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

Wolle02

Danke Otto. Den Code hatte ich gesehen ...... aber nicht verstanden. Da brauche ich erstmal ein bisschen Zeit, um zu bereifen was das tut.  :D Aber ich probier das einfach mal aus. Try and Error  :)

Ansonsten wäre doch einfach auch was per Telnet möglich oder? Ist zwar unverschlüsselt, aber ist ja nur auf dem gleichen Server innerhalb meines Netzes.

CoolTux


  use IO::Socket::INET;

  my $host = "127.0.0.1";
  my $port = "7072";
  my $socket = IO::Socket::INET->new('PeerAddr' => $host,'PeerPort' => $port,'Proto' => 'tcp')
      or die qq(Can't bind to port $port!\n);

  Eventuell wenn Passwortabfrage
  $socket->waitfor('/Password:/');


  print $socket qq(setreading DEVICENAME READINGNAME $VALUE\n);

  $socket->close;


Dies natürlich in ein Perlscript abseits von FHEM
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

Otto123

#11
Zitat von: Wolle02 am 11 März 2021, 11:09:01
, um zu bereifen was das tut.  :D Aber ich probier das einfach mal aus. Try and Error  :)
naja er tut genau das fhemcl("http://raspib3:8083","set Test2 toggle")  ist das unlogisch?  ???
Also FHEM Befehle über HTTP - in der URL kannst Du auch basicauth einschließen.

Die lange Erklärung:
Er holt den aktuellen csrf_token, ohne den ist ein HTTP Zugriff auf FHEM (in der Standard Config) nicht möglich.
Kodiert die notwendige URL (alle Zeichen in der URL müssen "uri escaped" werden aus Leerzeichen wird %20 usw.)
sendet den kompletten String zu FHEM und erhält die response, das heisst Du kannst alles machen was die Kommandozeile von FHEM kann, inklusive Werte abfragen :)
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

Wolle02

Auch dir danke für den Code CoolTux. Muss ich mir auch erstmal näher anschauen, um das zu begreifen.

Könnt ihr mir eventuell kurz erklären, was bei euren (für mich sehr kompliziert ausehenden) Lösungen der wesentlich Vorteil ist bzw. einfach gefragt: Was spricht gegen ein simples

perl fhem.pl 7072 [i]password[/i] "setreading Geos Test Test"

Wolle02

Zitat von: Otto123 am 11 März 2021, 11:38:50
naja er tut genau das fhemcl("http://raspib3:8083","set Test2 toggle")  ist das unlogisch?  ???
Also FHEM Befehle über HTTP - in der URL kannst Du auch basicauth einschließen.

Nein, das ist überhaupt nicht unlogisch. Bitte nicht falsch verstehen. Das Ergebnis habe ich schon verstanden nur den Weg dahin verstehe ich nicht.

Otto123

#14
Nichts. Aber du brauchst Telnet (per Standard nicht definiert) und Du bauchst die fhem.pl dort wo Du den Befehl abschickst.
fhem.pl arbeitet intern mindestens genauso "kompliziert" :)

Du kannst auch per netcat (nc) Daten per telnet an ein entferntes FHEM senden.

Zitatden Weg dahin verstehe ich nicht.
Du kopierst den Code genauso in dein Perlscript und rufst die sub analog der fhem() sub auf wie gezeigt?
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