Hi!
Ich bastle gerade etwas mir Array's herum, um auszuprobieren, wie man sie besser einsetzen kann.
Dabei bin ich aufolgendes Problem gestoßen:
Wie kann man in Perl oder Fhem abfragen, ob ein Array angelegt wurde???
Und wenn nicht, möchte ich es anlegen.
mit:
if ( not defined @@Array) {\
my @@Array=("");;\
}
erhalte ich bei meinen Versuchen bisher immer:
defined(@array) is deprecated at (eval 1785) line 1.
(Maybe you should just omit the defined()?)
2013.02.14 23:37:22 3: Speicher return value: Global symbol "@Array" requires explicit package name at (eval 1785) line 1.
Missing right curly or square bracket at (eval 1785) line 2, at end of line
syntax error at (eval 1785) line 2, at EOF
MfG, MisterEltako.
Hi MisterEltako,
also die Meldung mit dem Deprecated sorgt zumindest nicht für den Abbruch. Das ist nur ein Hinweis.
Aber das folgende dahinter ist wichtig.
Das Array muss erst mittels "my" angelegt werden:
my @@Array;\
# Array füllen oder belegen...\
if (!defined(@@Array)) {\
@@Array=("");;\
}
Wenn das Array einmal angelegt wurde, kannst du es nicht nochmal mittels my anlegen. Das hat aber nichts mit einem etwaigen Verwenden zu tun...
Unter http://www.tizag.com/perlT/perlarrays.php (//www.tizag.com/perlT/perlarrays.php) z.B. findest du ein paar Hinweise zur Verwendung von Arrays.
Allgemein würde ich dir empfehlen, der besseren Lesbarkeit wegen, die Verwendung von Perl-Code in ein 99er-Modul auszulagern.
Grüße Reinerlein
Danke für die Hinweise!
Testvorhaben:
ich will 3 letzte Temperaturwerte eines Sensors in ein Array speichern.
Das habe ich auch hinbekommen, aber wenn das Array in der Abfragefunktion immer mit "my @@Array" angelegt wird kann ich ja keine Elemente mit "push" zu bestehenden hinzufügen, sondern fange wieder mit leerem Array an.
Also will ich abfragen ob:
Array da ist - dann "push Element.."
Array noch nicht da - dann "my @Array ..."
MfG, MisterEltako.
Hi MrEltako,
und genau das geht nicht ohne den Code auszulagern...
ein Notify in FHEM wird, grob gesagt, in einem eval-Kontext ausgeführt, und du brauchst etwas "übergeordnetes", um deine Variable zum nächsten Aufruf rüberzuretten.
Ein 99er Modul wird einmal zu Anfang geladen. Dabei kannst du dein Array anlegen lassen, sozusagen als "Modul-globale" Variable.
Bei jedem Notify rufst du eine Sub-Prozedur auf, die in dem Modul von dir definiert wurde. Dort hast du dann Zugriff auf diese Variable, und kannst sie verändern und verwenden...
z.B.:
.
.
my @oldTempValues;
sub notifyProcedure($) {
my ($value) = @_;
push @oldTempValues, $value;
foreach $tempElem (@oldTempValues) {
# Do Something interesting here...
}
}
.
.
Der Code soll dir natürlich nur einen Anhaltspunkt liefern...
Grüße Reinerlein
Hi Reinerlein!
Danke, das war das was ich suchte!!!
Ich habe es jetzt so gelöst:
in Fhem.cfg:
define 3_LetzteTemperatur dummy
attr 3_LetzteTemperatur room Array
define 2_VorletzteTemperatur dummy
attr 2_VorletzteTemperatur room Array
define 1_DrittletzteTemperatur dummy
attr 1_DrittletzteTemperatur room Array
define Temp dummy
attr Temp room Array
define Speicher notify Temp:.* {\
my $PP = Value("Temp");;\
Tempspeicher($PP);;\
}
in 99_myUtils:
my @Speicher=("na","na","na");
sub Tempspeicher($){
my ($P)=@_;
my $delTemp=shift(@Speicher);
push (@Speicher,$P);
Log 1, "@Speicher";
my $Temp3=@Speicher[-1];
my $Temp2=@Speicher[-2];
my $Temp1=@Speicher[-3];
Log 1, "Letzte:$Temp3,Vorletzte: $Temp2,Drittletzte: $Temp1";
fhem("set 3_LetzteTemperatur $Temp3");
fhem("set 2_VorletzteTemperatur $Temp2");
fhem("set 1_DrittletzteTemperatur $Temp1");
}
Vielen Dank an dich!
MfG, MisterEltako.
Hi MrEltakto,
schön, das es geholfen hat... allerdings:
Wenn du bereits drei Dummies dafür definiert hast, kannst du dir das Array wiederum sparen, da diese Dummies bereits sozusagen deine "globalen Variablen" darstellen.
Du kannst also auch folgendes tun:
define 3_LetzteTemperatur dummy
attr 3_LetzteTemperatur room Array
define 2_VorletzteTemperatur dummy
attr 2_VorletzteTemperatur room Array
define 1_DrittletzteTemperatur dummy
attr 1_DrittletzteTemperatur room Array
define Temp dummy
attr Temp room Array
define Speicher notify Temp:.* {\
fhem("set 1_DrittletzteTemperatur ".Value("2_VorletzteTemperatur"));\
fhem("set 2_VorletzteTemperatur ".Value("3_LetzteTemperatur"));\
fhem("set 3_LetzteTemperatur ".Value("Temp"));\
}
Damit hast du zunächst das gleiche erreicht wie mit der anderen Variante, nur dass du die Werte nicht doppelt ablegst (also im Array und und einem Dummy).
Aber was machst du danach mit den Werten? Die Beachtung dieser Werte erfolgt dann woanders? Bedenke, dass beim Setzen eines Wertes ein Event ausgelöst wird. In diesem (und deinem) Fall also drei Stück.
Wenn du diese Events nicht brauchst, kannst du anstatt "set" auch "setState" verwenden. Da wird dann nix getriggert...
Die Array-Variante (dann allerdings ohne irgendwelche Dummies) würde den Vorteil bringen, dass du eben keine Dummy-Devices anlegst, die ja auch von FHEM verwaltet werden müssen, und damit Ressourcen auffressen.
Das würde bedeuten, dass du die Verarbeitung der Werte bereits in der Sub-Prozedure erledigst, was ja u.U. bei deiner dir gestellten Aufgabe nicht geht...
Grüße Reinerlein
Hi!
Ja stimmt das ist tatsächlich doppelt. Ursprünglich hatte ich es nur mit dem Array angedacht, aber nach Änderungen von anderen Codezeilen in der 99_myUtils.pm und speichern, waren die Werte des Array's wieder weg. So dachte ich sie in den Dummy's "längerfristiger" speichern zu können.
Das ganze Projekt soll als Lösung für die Anfrage vom Benutzer My-FHEM in dem Thead: "Zugriff auf Sensordaten" dienen.
Über die Auslastung der Ressourcen hatte ich mir noch keine Gedanken gemacht, aber jetzt schon ;o).
MfG, MisterEltako.
Hi MisterEltako,
das ist natürlich ein Vorteil der Dummies. Der Zustand wird beim Speichern weggesichert, und beim Starten wieder hergestellt...
Das passiert bei den Variablen nicht...
Dazu könntest du dir aber auch alles beim Dummy "Temp" in diversen Readings ablegen. Dann wäre es nur ein Device, und irgendwie "zusammen, wo es hingehört" :-)
Grüße Reinerlein
Hi Reinerlein, MisterEltako,
ich sehe gerade diese Diskussion, vielen Dank für eure
Mühe meine Frage zu beantworten.
Hierzu einige Ergänzungen meinerseits. Ich benötige die historischen
Sensordaten um für meine Verwendung PID für Fussbodenheizung
den D-Anteil des Reglers über mehrere Abtastzeitpunkte auszurechnen.
Die Temperatur wird typischer Weise alle 5 Min abgetastet. Damit wird
das MODUL PID.pm ebenfalls alle 5 min aktualiesiert. der D-Anteil existiert
somit nur für 1 Abtastperiode, da danach meistens derselbe Temp Wert
gelesen wird und somit D=0.
Der Zugriff auf historische Werte der Zeitreihe ermöglicht also eine
langsame Berechnung von delta-Temp/ delta-Zeit.
Wenn der Zugriff sinnvoller Weise in ein Perl modul ausgelagert
wird wäre vielleicht für diese Anwendung ein modifizertes 99_PID_slow_cycle.pm
mit variabler Rückschau der Abtastzeitpunkte sinnvoll.
Leider sind meine Perl Fähigkeiten nahezu null. So das hier eure Hilfe
wunderbar wäre.
Grüße
@Reinerlein
Das mit dem Dummy und den Readings habe ich auch schon irgendwo gelesen.
Leider habe ich das nicht verstanden, wie ich dem Dummy Readings einfüge.
Doch nicht etwa mit "attr Dummy setList ...."????
MfG, MisterEltako.
Hi MisterEltako,
du kannst zum Setzen von Readings einfach folgenden Perl-Code verwenden:
readingsSingleUpdate($main::defs{DeviceName}, 'ReadingsName', 'ReadingsValue', 0);
Leider gibt es meines Wissens nach keine Funktion in fhem.pl, die den DeviceHash zurückgibt, wenn man den Devicenamen hat. Deswegen der etwas kryptische Code im ersten Parameter. Damit wird nur der DeviceHash eingesetzt.
Die '0' beim letzten Parameter bedeutet, dass hier keine Events getriggert werden sollen. Wenn du dort eine '1' hinschreibst, wird ein normales Event mit dem Wert des Readings ausgelöst.
Bedenke, dass beim DeviceName in den geschweiften Klammern wirklich keine Anführungszeichen stehen dürfen. Das ist eine Hashwert-Auflösung, kein Parameter...
Bei deinem Temp-Beispiel wäre das z.B. wie folgt:
define Speicher notify Temp:.* {\
readingsSingleUpdate($main::defs{Temp}, '1_DrittletzteTemperatur', ReadingsVal('Temp', '2_VorletzteTemperatur', ''), 0);\
readingsSingleUpdate($main::defs{Temp}, '2_VorletzteTemperatur', ReadingsVal('Temp', '3_LetzteTemperatur', ''), 0);\
readingsSingleUpdate($main::defs{Temp}, '3_LetzteTemperatur', Value("Temp"), 0);\
}
Die Umbrüche sind hier etwas unglücklich... Bitte korrigieren :-)
Grüße Reinerlein
Hi!
define Speicher notify Temp:.* {\
readingsSingleUpdate($main::defs{Temp},'1_DrittletzteTemperatur', ReadingsVal('Temp','2_VorletzteTemperatur', ''), 0);;\
readingsSingleUpdate($main::defs{Temp},'2_VorletzteTemperatur', ReadingsVal('Temp','3_LetzteTemperatur', ''), 0);;\
readingsSingleUpdate($main::defs{Temp},'3_LetzteTemperatur', Value("Temp"), 0);;\
}
führt zu Absturz von Fhem!!!!
Ist da was falsch? ;o)
MfG, MisterEltako
Hi MisterEltako,
manchmal sollte man Dinge testen, bevor man sie postet...
Die Prozedur scheint er nicht aufrufen zu wollen, obwohl ich sie in meinen Modulen massiv verwende...
Alternativ kannst du auch "setReadingsVal" verwenden (in einem kurzen Test bei mir hat es mit den grundsätzlichen Funktionen zumindest über das kleine Kommandofeld oben geklappt :-)
define Speicher notify Temp:.* {\
setReadingsVal($main::defs{Temp}, '1_DrittletzteTemperatur', ReadingsVal('Temp', '2_VorletzteTemperatur', ''), TimeNow());;\
setReadingsVal($main::defs{Temp}, '2_VorletzteTemperatur', ReadingsVal('Temp', '3_LetzteTemperatur', ''), TimeNow());;\
setReadingsVal($main::defs{Temp}, '3_LetzteTemperatur', Value("Temp"), TimeNow());;\
}
Versuch doch bitte das nochmal...
Grüße Reinerlein
Prima! Diese Variante klappt!!!!
Das ist echt toll das man sowas machen kann. Das eröffnet wieder neue Möglichkeiten....
Vielen Dank!
MfG, MisterEltako.
Noch eine Frage dazu:
Das im vorigen Post beschriebene Event setzen mit 1 oder 0 geht hierbei aber nicht oder?
MfG, MisterEltako.
Hi MisterEltako,
leider nicht, deswegen dachte ich ja erst daran die andere Variante zu verwenden. Aber was du noch probieren kannst (ich kann das bei mir gerade nicht nachstellen):
define Speicher notify Temp:.* {\
readingsBeginUpdate($main::defs{Temp});;\
readingsBulkUpdate($main::defs{Temp}, '1_DrittletzteTemperatur', ReadingsVal('Temp', '2_VorletzteTemperatur', ''));;\
readingsBulkUpdate($main::defs{Temp}, '2_VorletzteTemperatur', ReadingsVal('Temp', '3_LetzteTemperatur', ''));;\
readingsBulkUpdate($main::defs{Temp}, '3_LetzteTemperatur', Value("Temp"));;\
readingsEndUpdate($main::defs{Temp}, 1);;\
}
Damit wird das ganze als ein einziges Event erzeugt. Die "1" im "EndUpdate" ist nun der Marker für Event oder nicht...
Grüße Reinerlein
Hi!
So habe das jetzt in der neuen Form ausprobiert. Die Readings werden so auch angelegt!!! Funktioniert!!!
Jetzt die blöde Frage: Wie teste ich jetzt ob tatsächlich ein Event ausgelöst wird???
Und Frage 2: Kann man die Readings auch wieder löschen??? Oder nur durch löschen des Device und Neuanlegen?
MfG, MisterEltako.
Hi MisterEltako,
naja, du kannst einfach den EventMonitor aufmachen, und auf ein entsprechendes Event warten. Das ist auf der Hauptseite links unten im Menübereich...
Alternativ kannst du natürlich einfach ein Notify definieren, was darauf reagiert:
define Test notify Temp:1_DrittletzteTemperatur.* set gong on
Schaltet einen Gong an, wenn der Wert von "Temp:1_DrittletzteTemperatur" geändert wird...
Zu 2.: Eigentlich ist das nicht vorgesehen. Du kannst ein "undef" zuweisen. Dann hat das Teil, passend mit Zeitstempel versehen, halt keinen Wert mehr, aber es ist immer noch klar, zu welchem Zeitpunkt das mal passiert ist.
Natürlich kannst du direkt die interne Datenstruktur anfassen. Aber vorsicht, da passieren gerne mal so Fehler wie neulich mit der abgeschnittenen fhem.cfg-Datei :-)
Dafür könntest du folgendes probieren:
delete $main::defs{Temp}{READINGS}{1_DrittletzteTemperatur};
Damit würde das Reading "1_DrittletzteTemperatur" des Devices "Temp" komplett (also auch der Zeitstempel) aus der internen FHEM-Struktur entfernt werden.
Ich habe das jetzt nicht probiert, einfach mal auf einer Testmaschine mit gesicherten Konfigurationen ausprobieren...
Grüße Reinerlein
Eine kleine Frage zum Thema Fußbodenheizung:
ich wollte das in etwa so realisieren:
Der Wasserfluss wird per Pulsweitenmodulation mit einer Zykluszeit von 15 Minuten (900s) gesteuert. in diesem Zyklus läuft ein wiederkehrender at-job im FHEM. dieser at Job triggert den PID per Dummyvariable jedes Mal mit einem neuen Temperaturwert. der Output des PID (0-900) wird genutzt um den set-for timer für das Ventil anzusteuern.
Ist es dann noch notwendig den Aufwand um die Mittelwertbildung der Temperatur wie in diesem Thread beschrieben zu treiben?
Wäre es möglich Dummies generisch im PID bekannt zu machen?
Viele Grüße,
Uwe
Die Temp Werte Historie dient zur Reduktion des Quantisierungsrauschen der Funk Temp Sensoren
Auflösung der ws300 0,1°. Mittelwertbildung über breitere Abtastspannen z.B 3-4 Abtastungen
reduziert das Rauschen. --> Regler stabiler.
Gruß