Hauptmenü

99 utils

Begonnen von misave, 11 Dezember 2020, 15:05:03

Vorheriges Thema - Nächstes Thema

misave

Hallo zusammen,

da ich gerade ein altes System mit fhem 5.6 auf 6.0 umziehe und alle Geräte händisch mit der Importfunktion übernehme, lernt man nun neue Dinge dazu. Ein Thema ist die Nutzung von 99_myutils.

Vorweg, ich habe das commandref gelesen. Mir als Anfänger im Thema utils bleiben aber ein paar Punkte unklar, die wohl auch anderen am Anfang nicht so klar sind.

Durch einen Video-Kurs habe ich eine eigene utils angelegt, die mir eine Routine liefert, dass die fritzbox prüft und rückmeldet, ob eine bestimmte MAC_Adresse im Netz ist. Das Training hat die Routine vorab geschrieben. Sie funktioniert und ich muss noch nicht wissen warum das geht. Eine solche Datei muss nach der commandref mit utils.pm enden. und prompt steht dort ein Beispiel drin welches so "99_myUtils_Homematic.pm" heißt. Für den Anfänger stellt sich dann die Frage, wieso dieses Beispiel korrekt sein soll wo die Datei doch auf ..atic.pm endet. ich hätte verstanden wenn sie 99_Homematic_myUtils.pm endet.

Aus dem commandref weiss ich, dass eine eigene 99_myutils.pm IMMER wie folgt anfangen muss:

package main;
use strict;
use warnings;

Danach dürfen beliebig viele Leerzeilen sein.

Dann steht in der commandref diese Initialize-Zeile mit

sub
myUtils_Initialize($$)
{
   my ($hash) = @_;
}

Der Name der Programmdatei muss mit dem Namen der Initialize-Routine übereinstimmen. Wenn Sie Ihr Programm also 99_Werkzeugkasten.pm nennen, muss die im code dargestellte initialize-Routine sub Werkzeugkasten_Initialize heißen.

Gehört diese Initialize genau einmal in die Datei oder gehört sie in JEDE subroutine, die in dieser Datei drin stehen. Im kursiven Text steht einmal Programmname wo ich das Wort Dateiname erwarten würde. Deshalb verstehe ich das nicht. Demnach kann ich nur einmal in einer sub auf den Namen der Datei ...pm verweisen. Oder ist damit wieder der AufrufnamederRoutine gemeint der dann nix mit dem Dateinamen zu tun hat?

Wichtig ist dann noch, dass die Datei mit

1;

enden muss.

Ich leite daraus ab, dass es diese Zeile genau einmal geben sollte, denn nach diese Zeile wird nix mehr beachtet.

In diese eine 99_myUtils.pm datei kann ich demnach beliebig viele subroutinen reinschreiben. Wie werden diese Routinen voneinander getrennt? Sie beginnen ja immer mit der
sub Aufrufnamederroutine....

Ist das erneute Auffinden einer Zeile sub dann das Ende der aktuellen Verarbeitung und bedeutet quasi "ich habe fertig" und das ermittelte Ergebnis wird zurück an die aufrufende Routine geschickt?

Und diese o. g. Initialize-Funktion, die laut commandref mit dem DATEINAMEN (aber als Programmname im Text steht) zusammenhängt erscheint dann in jedem sub erneut als Kopie oder bezieht sie sich auf den Namen der sub-Routine und die commandref nennt das dann Programm? Verwirrend finde ich. Ich vermute, dass jede subroutine eine eigene Initialize-Routine hat und diese nichts mit dem Namen der 99_myUtils.pm zu tun hat?

Eine eigene Subroutine beginnt immer mit der Zeile
sub AufrufnamederRoutine ($) {....... wobei $ je übergebenem Wert steht.

Diese wäre dann die jeweilige Initialize-Routine pro Programm==Subroutine, oder steht die einmal pro Dateiname ganz oben als quasi erste Sub...?

Zusammenfassung aus meiner Sicht:

1. ich kann beliebig viele 99_my_Utils.pm anlegen. Der Dateiname muss Utils beinhalten und ansonsten kann er pro Gerätetyp sein oder eine Datei für alle "at"Befehle oder so was. Es kann auch alles in eine Datei geschrieben werden.

package main;
use string;
use warnings;    # Wahrscheinlich gibt es noch viele weitere mögliche use-zeilen

2. jede dieser Dateien hat genau eine sub routine zum Initialize am Anfang, die auf den Namen der Datei referenziert (siehe Beispiel aus commandref mit 99-Werkzeugkasten.pm)

sub Dateiname_Initialize($$)  #Immer mit 2 $$

3. in dieser ersten sub muss auch einmal der my ($hash)= @_; als Zeile genannt werden. Das ; beendet die zeile und gehört nicht zu my (@hash)

4. diese erste sub endet einfach durch ihre letzte Klammer zu }

5. es können dann in jeder dieser Dateien n verschiedene subroutinen reingeschrieben sein. Beim Laden von FHEM werden alle AufrufnamenderRoutinen (alles nach jedem sub...) verwaltet und warten darauf aufgerufen zu werden. In welcher Datei.pm sie drin geschrieben wurden ist dann egal.

6. Jedes sub hat ihren Aufrufnamen und nennet die Anzahl der übergebenen werte Dazu hat jede sub auch das Einlesen mit dem hash

sub aufrufnamenderroutine($) {

my $variable = 0;
my ($reading) = @_;

ab hier beginnt dann das Abarbeiten von Befehlen / Berechnugsschritten

7. die sub routine endet einfach mit dem letzten Klammer zu

}

8. eine neue sub beginnt einfach wieder nach der letzten } und beliebig vielen leerzeilen mit dem "Kennwort" sub....

9. Die Zeile
1;
signalisiert das Ende der einzulesenden Dinge

Ist das alles so richtig?


und in festes Ende mit
1;

4. Jede sub beginnt mit der Zeile

sub Aufrufnamederroutine($)

und muss dann am Anfang irgendwo die Zeile mit dem Hash

my ($reading) = @_;

haben.

Könnte man nicht die commandref mit einem durchgängigen Beispiel zur 99utils füllen. Also Name der Datei, dann den Kopf der in jeder Datei drin sein muss und dann die erste Sub....., dann die zweite Sub...., und bis zum Ende mit der 1;

Danke und entschuldigt den langen Text.
Michael aus Jüchen

Raspi 2, 2XHMLan, SCC Busware, diverse HM und FS20 Komponenten,
Rpi 3 mit Buster lite und FHEM 6.0
IoBroker auf separatem Raspi 2, zig bee CONZ Stick, Nextcloud auf raspi2

Beta-User

...das kommt alles mit der Zeit...

Alles, was einen passenden Dateinamen enthält, lädt fhem.pl beim Starten automatisch, es können also beliebig viele sein, die müssen halt initialisiert werden, und zwar pro "Modul"-Datei einmal.

Wie weit eine sub geht, bestimmt die Klammerebene, man kann auch innerhalb einer sub weitere "subsub" definieren, die sind halt dann nur in diesem "lexikalischen Kontext" gültig.

Prototypen-Vewendung ("sub mySub($) {") ist umstritten, hier gängig, aber ich mache es z.B. nicht mehr (und packe neuerdings auch alles in packages ein, dann muss ich mir keinen Kopf machen, ob ich nicht versehentlich eine Funktion im "main::-Kontext" überschreibe), und verwende tendenziell auch eher "shift", das sieht dann z.B. so aus und funktioniert genauso.
myUtils_Initialize  {
   my $hash = shift;
}


Ich habe einige myUtils-Files, insbesondere auch eine, um erst mal neue Funktionen zu testen (Wiederfinden einer Funktion in einer längeren myUtils macht Freude, und wenn die beim Systemstart nicht geladen werden kann, ist meistens nicht viel kaputt...)

Ansonsten einfach am Beispiel lernen und konkrete Fragen stellen, das kommt dann irgendwann, und für den Rest gilt https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it ;) .
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

misave

Danke schon mal, aber...

Alles, was einen passenden Dateinamen enthält, lädt fhem.pl beim Starten automatisch, es können also beliebig viele sein, die müssen halt initialisiert werden, und zwar pro "Modul"-Datei einmal.


bedeutet also dass diese Zeilen mit main und der Initialize-Routine EINMAL in der Datei ganz oben steht und blöderweise auch als sub...geschrieben wird.

Wie weit eine sub geht, bestimmt die Klammerebene, man kann auch innerhalb einer sub weitere "subsub" definieren, die sind halt dann nur in diesem "lexikalischen Kontext" gültig.


also wie ich vermutet habe, dass ein sub einfach mit der letzten Klammer endet.

Und prompt kommen hier neue böhmische Dörfer:

Prototypen-Vewendung ("sub mySub($) {") ist umstritten, hier gängig, aber ich mache es z.B. nicht mehr (und packe neuerdings auch alles in packages ein, dann muss ich mir keinen Kopf machen, ob ich nicht versehentlich eine Funktion im "main::-Kontext" überschreibe), und verwende tendenziell auch eher "shift", das sieht dann z.B. so aus und funktioniert genauso.

ich verwende shift;??? ist das eine Variable? anstelle @_;?

und das hier werde ich beherzigen:

Ich habe einige myUtils-Files, insbesondere auch eine, um erst mal neue Funktionen zu testen (Wiederfinden einer Funktion in einer längeren myUtils macht Freude, und wenn die beim Systemstart nicht geladen werden kann, ist meistens nicht viel kaputt...)

Schauen mer mal...

Michael aus Jüchen

Raspi 2, 2XHMLan, SCC Busware, diverse HM und FS20 Komponenten,
Rpi 3 mit Buster lite und FHEM 6.0
IoBroker auf separatem Raspi 2, zig bee CONZ Stick, Nextcloud auf raspi2

Beta-User

Zitat von: misave am 11 Dezember 2020, 15:52:24
bedeutet also dass diese Zeilen mit main und der Initialize-Routine EINMAL in der Datei ganz oben steht und blöderweise auch als sub...geschrieben wird.
Steht doch in der "Musterfile": Initialize an den Dateinamen anpassen, eigene Funktionen hinter die Markierung... Den Kopf braucht jede, und wenn man weiß, was man braucht, wird man ggf. auch weitere "use" haben. (Sowas dann aber besser, wenn man mit packages arbeitet, sonst geht gerne mal was kaputt. In der Perl-Ecke gibt es z.B. eine nette Diskussion um "min" und "max"...)

Zitat
also wie ich vermutet habe, dass ein sub einfach mit der letzten Klammer endet.
Ja, mit der letzten Klammer auf der richtigen Ebene ;) .

Zitat
Und prompt kommen hier neue böhmische Dörfer:
Gibt sich irgendwann; stöbere ggf. einfach in der Perl-Ecke, die ist nicht zu umfangreich, aber es gibt viel zu entdecken.

"shift" ist eine Array-Funktion, die das erste Element aus einem Array (Liste finde ich zwischenzeitlich eigentlich "schöner", das ist nicht so abstrakt) holt. Ohne ausdrückliches Argument bezieht sich shift halt auf @_...
Ok, das ist jetzt auch wieder tiefes Böhmen, genauso wie "defined-or".

Beispiel: Ich habe eine Funktion, mit der eine Milight-Fernbedienung meine Musikanlage steuert. Die besteht aus einem MPD-Dienst und einem Verstärker. Damit kann z.B. der Helligkeitswert bzw. Saturation in Lautstärke umgesetzt werden (brightness geht an den MPD, Saturation an den Verstärker). Damit andere das auch nutzen können, die keinen von FHEM aus steuerbaren Verstärker haben, wird dann Saturation ebenfalls zur Lautstärkesteuerung des MPD verwendet. Übergeben wird:

- der Name des MPD-Geräts (in FHEM);
- $EVENT beinhaltet die Info über "was" und "wieviel"; ohne das geht es nicht, also direkter Abbruch, wenn nicht angegeben.
- der Name des Verstärkers (in FHEM), der ist optional. Wird er nicht angegeben, wird der MPD-Name verwendet...

Das abarbeiten dieser "Liste" sieht dann so aus:
sub MPDcontrol {
  my $name  = shift;
  my $event = shift // return;
  my $avrname  = shift // $name;

und das ganze wird so aufgerufen:
attr MiLight_RC_WZ readingList milight/updates/0x5D47/fut089/0:.* { FHEM::attrT_MiLight_Utils::MPDcontrol('myMPD',$EVENT, 'AVR_name') }
Klarer?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

misave

Na ja klarer...

Plötzlich tauchen in der Programmzeile ,,/,, auf, die mir bisher nicht untergekommen sind.

Ich rechne Dir hoch an, dass Du dich hier bemühst.

Ich sage mal, dass die allermeisten User aus Schnipseln lernen, aber genau das Schnipsel was ich jetzt haben will gibt es nicht. So meine Erfahrung hier mit dem Beispiel der Batterieüberwachung. Die Batterien als readinsGroup aufrufen ist einfach. Aber dann kommt der Schnipsel mit der Warnung, wenn der Status irgendeiner Batterie nicht mehr ok ist und die Batterie dieses Gerätes getauscht werden sollte. Das Beispiel nutzt dann FBMail, ich will aber pushover bzw. Telegram nutzen. Da ich mit roommates arbeite die auch eine individuell festgelegte Kommunikation haben reicht bei mir in der define Zeile oben ein, demnach in fhem ,,gesprochen"

msg push @rr_Michael |Benzin| Der aktuelle Preis für Super bei HAWA beträgt [OUT_TankenHAWA:e10] Euro und bei SB [OUT_TankenSB:e10] Euro

Fuhrt direkt zu einer pushover Meldung.

In der DEF-Definition später kommen dann automatisch () drum rum. Aber dies msg......Zeile nun in ein if einbauen...bisher klappt es nicht, zumal ich ja nun auf readings der Batterie zugreife ($NAME und $EVENT) und das dann mit diesen blöden Klammern und womöglich;; escaped muss, schrecklich.

Und wenn dann Perl in DEF-Fenster nicht meckert und ich mit einem Trigger eine Batterie auf low setze und nix passiert, stehe ich da. Und das logfile bzw. Eventmonitor sagt mir dann zwar dass mein Text nur als gesamter Text vorhanden ist oder Fhem das Modul msg sucht ....

Was es geben müsste ist so was wie ein Handbuch für Softwareanwendungen in Unternehmen. Da werden, zumindest bei uns, 90% aller Anwendungsfälle aufgeschrieben damit der blöde User (also ich), immer irgendwo nachgucken können, was sie falsch gemacht haben.

Das ist hier sicher nicht leistbar denn wir als Firma zahlen Unsummen für solche ,,perfekten" Handbücher.

Genug gejammert, irgendwann findet man seine Lösung oder man lässt es bleiben. Die blöde Pushnachricht wegen der Batterie habe ich nun seit mehr als 5 Jahren nicht, also geht es auch ohne.

Michael aus Jüchen

Raspi 2, 2XHMLan, SCC Busware, diverse HM und FS20 Komponenten,
Rpi 3 mit Buster lite und FHEM 6.0
IoBroker auf separatem Raspi 2, zig bee CONZ Stick, Nextcloud auf raspi2

MadMax-FHEM

Eventuell ist ja das was: https://forum.fhem.de/index.php/topic,82637.msg747514.html#msg747514

(ich habe mittlerweile einige "Erweiterungen" gebastelt [an meinem Original-Code / bin noch nicht "umgestiegen"]: Batterie-Haltbarkeit, also wie lange seit dem letzten Wechse. Batterietyp, also welche Batterie und wie viele davon pro Gerät und auch eine "Liste" mit Batterien die ich zuhause habe. Da wird dann bei Warnung geprüft, ob noch ausreichend Batterien da sind, wenn nicht steht das in der Warnung und nach dem Wechsel wird das "Lager" automatisch "reudziert"...)

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

Prof. Dr. Peter Henning

@misave: Ernst gemeinter Tipp: Perl lernen, dafür gibt es im Netz genügend Tutorials. Sonst wird das _gar nichts_.

LG

pah

Beta-User

Zitat von: Prof. Dr. Peter Henning am 11 Dezember 2020, 21:24:18
@misave: Ernst gemeinter Tipp: Perl lernen, dafür gibt es im Netz genügend Tutorials. Sonst wird das _gar nichts_.
Kann dem nur zustimmen...

Zitat von: misave am 11 Dezember 2020, 19:37:26
Was es geben müsste ist so was wie ein Handbuch für Softwareanwendungen in Unternehmen. Da werden, zumindest bei uns, 90% aller Anwendungsfälle aufgeschrieben damit der blöde User (also ich), immer irgendwo nachgucken können, was sie falsch gemacht haben.
Perl-Grundlagen sollten klar sein, aber ab da gibt es aber zumindest zwei Dokumente, die man kennen sollte:
- https://fhem.de/commandref_modular_DE.html#perl (die "modular"-Commandref ist sowieso eine vorzügliche Quelle, da sie erst mal nur den "Rahmen" lädt. Da stehen die wirklich wichtigen Dinge drin.
- wer tiefer einsteigt, findet in der https://wiki.fhem.de/wiki/DevelopmentModuleIntro näheres zur Funktionsweise der Module.

Grundsätzlich ist es so, dass es für ~90% aller Anwendungsfälle Module gibt, die die betreffenden Aufgaben automatisieren können. Man muss sie "nur" passend konfigurieren.

Was das konkrete Beispiel mit der Message angeht, kannst du den Befehl ja als erstes schlicht mal in den "fhem"-Wrapper einpacken:
fhem("msg push @rr_Michael |Benzin| Der aktuelle Preis für Super bei HAWA beträgt [OUT_TankenHAWA:e10] Euro und bei SB [OUT_TankenSB:e10] Euro");

und dann ins Log schauen, falls es nicht klappt...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

misave

Zitat von: Beta-User am 12 Dezember 2020, 07:04:42


Was das konkrete Beispiel mit der Message angeht, kannst du den Befehl ja als erstes schlicht mal in den "fhem"-Wrapper einpacken:
fhem("msg push @rr_Michael |Benzin| Der aktuelle Preis für Super bei HAWA beträgt [OUT_TankenHAWA:e10] Euro und bei SB [OUT_TankenSB:e10] Euro");

und dann ins Log schauen, falls es nicht klappt...

Guten Morgen,

der normale Befehl in der fhem-Kommandozeile mit meiner msg...funktioniert tadellos. Ebenso, wenn der Befehl in einer Def doif drin ist und entsprechenes sendet. Da es sich hierbei nur eine Abfolge von ()-Ausdrücken handelt mit dem Einsetzen eines Readings e5 aus einem Gerät OUT_TankenHAWA klappt es auch als stündliche bzw. einmal am Morgen Push Nachricht. Aber das ganze nun in ein if..mit der Prüfung von n Batterien aus deren readings battery und daraus eine msg machen, die dann irgendwie in {} kommt, stellt direkt eine Hürde auf....Die ;; mal ganz außen vor...
Michael aus Jüchen

Raspi 2, 2XHMLan, SCC Busware, diverse HM und FS20 Komponenten,
Rpi 3 mit Buster lite und FHEM 6.0
IoBroker auf separatem Raspi 2, zig bee CONZ Stick, Nextcloud auf raspi2

Prof. Dr. Peter Henning

@misave:

Ich sehe immer noch die gleichen Anfängerfehler. Offenbar ist hier vollkommen unklar:

- der Unterschied zwischen den Schichten der FHEM-Skriptsprache und Perl
- der Übergang von einer zur anderen Schicht
- die Bedeutung von geschweiften Klammern
- die Bedeutung von einzelnen oder doppelten ";"
- die Bedeutung der Parameter bei einem Unterprogrammaufruf in Perl
- das Modulkonzept von FHEM
- das Modulkonzept von Perl
- die Bedeutung von Groß- und Kleinschreibung in Perl und FHEM
- die Verarbeitung von Strings in Perl

Ich glaube, die Liste könnte man sogar noch fortführen. Also noch einmal die Bitte: Erst einmal diese Grundlagen klarmachen - sonst muss man bei jeder selbstgeschriebenen Codezeile erneut Hilfestellung leisten.

LG

pah