FHEM Forum

FHEM => Sonstiges => Thema gestartet von: CoolTux am 21 Juli 2015, 16:59:51

Titel: readingsBulkUpdate
Beitrag von: CoolTux am 21 Juli 2015, 16:59:51

sub AMAD_Parse($$) {
   my($name,$valuestring) = @_;

   my @array=split('@@',$valuestring);
   foreach (@array){fhem("setreading $name $_\n");}
}


Wie kann ich hier ein readingsupdate mit dem empfohlenen readingsBeginUpdate, readingsBulkUpdate, readingsEndUpdate machen.
Leider weiß ich nicht wie viele Readings in der Tat reinkommen, daher die foreach Schleife.


Grüße
Leon
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 21 Juli 2015, 19:11:10
Hallo Leon,

wenn du in einem Modul bist, das seine eigenen Readings ändert, solltest du generell statt

{fhem("setreading $name $_\n");}

besser

readingsSingleUpdate($hash, $readingName, $readingValue, 1);

verwenden.

Dann ist auch der Umbau nicht mehr schwer. Zunächst solltest du den split so machen, dass du readingName und readingValue getrennt bekommst, indem du sie in einer Hash-Variablen pufferst. Anschließend kannst du folgendes machen:


readingsBeginUpdate($hash);
while (($t, $v) = each %buffer) {
  readingsBulkUpdate($hash, $t, $v) if (defined($v));
}
readingsEndUpdate($hash, 1);


Hast du den Modul-Hash nicht, aber bist zumindest in FHEM, kannst du ihn dir im Normalfall so holen:

my $hash = $defs{$name};


wobei $name der Modul-Instanzname ist.

LG, Jens

Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 21 Juli 2015, 20:08:12
Hallo Jens,

Vielen Dank für Deine Antwort.
Ja ich bin in einem Modul welches seine eigenen Readings ändert. Es ist ein Modul was ich selber schreibe. Eigentlich habe ich keine Ahnung von FHEM Development und nur bedingt von Perl. Aber pah hat mal zu mir gesagt "keine Ahnung hatten wir alle mal, das ist keine Ausrede". Tja und nun sitze ich hier und pauke. Das besondere an dem Modul ist, so denke ich, das es seine Readings nicht holt sonder ungefragt zugesendet bekommt. Per http request mittels ?cmd=.

Das mit dem buffer hatte ich auch schon gelesen und auch probiert. Leider hatte ich Fehler bekommen. ein readingsSingleUpdate wäre nicht gut, da ja die Menge der zu aktualisierenden Readings unbekannt ist. Auf jeden Fall immer mehr wie eines, min 2.

Hier mal mein Modul, bitte nicht so genau auf die Funktionen achten, einige liegen nur brach da für später.

##############################################
# $Id: 98_AMAD.pm 8809 2015-07-21 11:09:00Z leongaultier $
package main;

use strict;
use warnings;

sub
AMAD_Initialize($)
{
  my ($hash) = @_;

  $hash->{DefFn}     = "AMAD_Define";
  $hash->{UndefFn}   = "AMAD_Undef";
  $hash->{AttrFn}    = "AMAD_Attr";
  $hash->{ParseFn}    = "AMAD_Parse";
  $hash->{AttrList}  = "readingList setList interval ". $readingFnAttributes;
}

###############################################################################

sub
AMAD_Define($$)
{
  my ($hash, $def) = @_;
  my $name = $hash->{NAME};
  my @a = split("[ \t][ \t]*", $def);

  return "Wrong syntax: use define <name> AMAD <IP>" if(int(@a) < 3);
  $hash->{".host"}  = $a[2];

  return undef;
}

################################################################################

sub AMAD_Undef($$)
{
    my ($hash, $arg) = @_;
    RemoveInternalTimer($hash);
    if(defined($hash->{helper}{RUNNING_PID}))
    {
     BlockingKill($hash->{helper}{RUNNING_PID});
    }
    return undef;
}

################################################################################

sub AMAD_Attr($$)
{
    my ($cmd,$name, $attrName,$attrVal) = @_;
my $hash = $defs{$name};

if ($cmd eq "set") {

   if ($attrName eq "interval")
   {
     if (int($attrVal) < 2) {$attrVal="5";}
     $hash->{".interval"}  = $attrVal;
     $attr{$name}{interval} = $attrVal;
   }
  }

   return undef;
}

###############################################################################

sub AMAD_Parse($$) {
   my($name,$valuestring) = @_;

   my @array=split('@@',$valuestring);
   foreach (@array){fhem("setreading $name $_\n");}
}

################################################################################

# Funktion welche von Automagic über http request aufgerufen wird und Infos überträgt

sub pushAutomagicInfo($$) {
   my($name,$valuestring) = @_;

   AMAD_Parse($name,$valuestring);
}

################################################################################

1;


Also das eigentliche Programm auf dem Handy oder Tablet heißt Automagic. Diverse Trigger stoßen unterschiedliche Flows an, welche Daten einsammeln und diese dann in Form von


http://fhem.local/fhem?cmd={pushAutomagicInfo('Device,readingName1 $reading1@@readingName2 $reading2....')}


sendet.

Die Funktion pushAutomagicInfo nimmt nur entgegen und gibt die Werte sofort weiter an die Funktion AMAD_Parse, welche dann entsprechend die Readings setzen soll.

So nun werde ich mal versuchen Deine Tips zu verinnerlichen und meine Funktion entsprechend an zu passen.
Kann ich mich wieder melden wenn ich nicht weiter komme? Also falls Du Lust und Zeit hast und ich tausend Fragen. Ich will das wirklich lernen.


Grüße
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 21 Juli 2015, 21:01:55
Hallo Leon,

du kannst gerne weiter Fragen stellen, ich werde Antworten, so gut ich kann. Habe mich selbst mit Trial and Error an die Sache heran getastet und inzwischen schon einen kleinen Überblick über die Möglichkeiten.

Die _Parse-Funktion ist normalerweise eine spezielle Funktion mit vordefinierter Parameterliste, da sie von anderen Modulen aufgerufen werden kann (z.B. vom Telnet-Server, wenn er Daten empfängt). Da du sie nur intern mit eigener Parameterliste verwendest, solltest du sie nicht in der _Initialize-Funktion als ParseFn registrieren. Funktionieren wird das aber auch ohne die Änderung.

LG, Jens
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 21 Juli 2015, 21:11:55
Hallo,

Ich hatte das ganze vorher ohne Pars. Wusste nicht was das genau soll. Klang nur gut für mich als ich das gelesen hatte. Bleiben wir beim kleinsten gemeinsamen.
Och habe ein Modul was ich definieren und un-definieren kann. Ausserdem kann ich Attribute vergeben. Das mache ich mit den entsprechenden dafür vorgesehenen FHEM eigenen Funktionen.

Ich habe eine eigene Funktion, welche so aus schaut


sub pushAutomagicInfo($$) {
   
   my ($name, $pushstring) = @_;
   my $hash = $defs($name);
   my ($t,$v) = split('@@','@@'$pushstring);

   readingsBeginUpdate($hash);
   while (($t, $v) = each %buffer) {
      readingsBulkUpdate($hash, $t, $v) if (defined($v));
   }
   readingsEndUpdate($hash, 1);
}


Diese wird von extern aufgerufen. Mehr will ich erstmal nicht. Die Feinheiten kommen später. Die Funktion wird aufgerufen und Werte werden übergeben. Die Funktion verarbeitet die Werte und soll dann entsprechend der Werte die Readings setzen. Das alles soll erstmal die eine Funktion machen.

Ich habe noch Probleme mit dem Code. Ein Reload bringt folgende Fehlermeldung

Global symbol "$defs" requires explicit package name at ./FHEM/98_AMAD.pm line 74.
syntax error at ./FHEM/98_AMAD.pm line 74, near "$defs("
syntax error at ./FHEM/98_AMAD.pm line 75, near "'@@'$pushstring"
Global symbol "$hash" requires explicit package name at ./FHEM/98_AMAD.pm line 77.
Global symbol "%buffer" requires explicit package name at ./FHEM/98_AMAD.pm line 78.


Wo und wie definiere ich $defs ?
Sind bestimmt voll die Basics   :-[
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 21 Juli 2015, 21:25:06
Hallo Leon,

da sieht für mich aus wie 2 Tippfehler:

Z74: my $hash = $defs{$name};

Z75: my ($t,$v) = split('@@',  $pushstring);

$defs wird von FHEM global definiert, deshalb kannst du einfach darauf zugreifen.

LG, Jens
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 21 Juli 2015, 21:40:16
Ah ok das habe ich verstanden. Ich danke Dir

Aber wieso eigentlich nur

split('@@',  $pushstring);


@@ soll mein Trenner sein und keine Variable. das sieht so aus

('$device',readingname1 reading1@@readingname2 reading2@@...')


Wie kann ich jetzt mein $t und mein $v da rausbekommen?

Und ich habe noch ein


Global symbol "%buffer" requires explicit package name at ./FHEM/98_AMAD.pm line 78.
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 21 Juli 2015, 21:47:26
Hi Leon,

mein letzter Vorschlag behebt nur den Syntax-Error, aber er löst noch nicht die Aufgabe. Wollte dir noch etwas Arbeit lassen.  :D

Das ganze wird so nicht gehen, aber vielleicht etwa so:


  my @pairs = split('@@',  $pushstring);
  my %buffer;
  foreach (@pairs ) {
    my @pair = split(' ',  $_);
    $buffer{$pair[0]} = $pair[1];
  }


Damit hast du die Readings im %buffer und kannst weitermachen.

LG, Jens
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 21 Juli 2015, 22:02:22
supi damit komme ich weiter. aber ich denke ich werde statt einem leerzeichen einen selbst definierten 2. Trenner nehmen. Denn es kann auch mal vor kommen das ein Readingwert ein Leerzeichen hat. Da würde er ja dann falsch splitten oder?

PS: Mega DANK für Deine Geduld     ;D
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 21 Juli 2015, 22:24:23
Kein Thema, freut mich dass es geholfen hat.

Wenn du ein Leerzeichen in den Nutzdaten haben kannst, dann nimm einen anderen Trenner (z.B. "=").

Willst du aber beliebige Nutzdaten transportieren, muss du schon beim Eintüten ein zusätzliches Escapezeichen wählen (z.B. "\") und es in den Nutzdaten vor jeden Trenner schreiben. Dann kannst du den Trenner "=" und einen Trenner in den Nutzdaten "\=" unterscheiden. Hast du ein Escapezeichen in den Nutzdaten, muss auch das gedoppelt werden. Das macht das Parsen hinterher natürlich nicht leichter, aber zumindest eindeutig.

LG, Jens
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 22 Juli 2015, 09:38:17
Guten Morgen,

Ich musste gestern abbrechen, Kopfaua   ;D
Ich habe jetzt mal versucht unsere Ausarbeitungen zusammen zu bringen. Und zwar eins zu eins so wie Du es geschrieben hast. Ich denk mal beim verstehen und zusammen setzten habe ich bestimmt murx gemacht.


Das ist jetzt meine Funktion


sub pushAutomagicInfo($$) {

   my ($name, $pushstring) = @_;
   my $hash = $defs{$name};

   my @pairs = split('@@',  $pushstring);
   my %buffer;
   foreach (@pairs) {
      my @pair = split(' ', $_);
      $buffer{$pair[0]} = $pair[1];
   }
   readingsBeginUpdate($hash);
   my $t;
   my $v;
   while (($t, $v) = each %buffer) {
      readingsBulkUpdate($hash, $t, $v) if (defined($v));
   }
   readingsEndUpdate($hash, 1);

}


Und das kommt als Fehler


Not enough arguments for main::pushAutomagicInfo at (eval 67330) line 1, near "'automagicNexus5Marko,powerLevel 65@@powerPlugged 1')"


Es fehlt der Zusammenhang von

$buffer{$pair[0]} = $pair[1];

und $t $v, sehe ich das Richtig.
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 22 Juli 2015, 10:02:13
Hi Leon,

ZitatNot enough arguments for main::pushAutomagicInfo
bedeutet, dass du diese Funktion mit keinem oder nur einem Argument aufgerufen hast, aber sie erwartet ja nun mal zwei, wegen des ($$).

LG, jensb
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 22 Juli 2015, 10:21:29
Juhu,

Ich werde irre das funktioniert. Hammer es geht. Frreuuuuuuu. Luftsprung

Melde mich wenn ich verstanden habe wieso das geht. lach

LG
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 22 Juli 2015, 10:39:17
Ok soweit blicke ich da durch. Muss unbedingt viel mehr Perl lesen.

Aber eines verstehe ich noch nicht


$buffer{$readings[0]} = $readings[1];


dem Skalar buffer wird das erste Element von readings zugewiesen aber was bedeutet  = $readings[1] und wieso die geschweiften klammern {$readings[0]}
Titel: Antw:readingsBulkUpdate
Beitrag von: jensb am 22 Juli 2015, 14:21:45
%buffer ist eine Hash-Variable

mit $buffer{key} kann man auf einen gehashten Wert zugreifen (mit key = String), die {} sind die Zufgriffsoperatoren für Hashes

@readings ist eine Array-Variable

mit $readings[n] kann man auf die Werte zugreifen (mit n ab 0), die [] sind die Zugriffsoperatoren für Arrayelemente

LG, Jens
Titel: Antw:readingsBulkUpdate
Beitrag von: CoolTux am 22 Juli 2015, 14:34:38
Hallo Jens,

Ich danke Dir für Deine Erklärungen. Das wird ne lange Busheimfahrt mit viel Buch lesen    ;D
Die nächsten Tage schaue ich dann mal wie ich mein Modul erweitern kann. Als nächstes kommen SET Funktionen. Ich will Automagic mittels http request (HttpUtils) einen String zur Verarbeitung senden.
Mal schauen wie weit ich da komme.


LG
Leon