FHEM Forum

FHEM => Anfängerfragen => Thema gestartet von: McShire am 05 Februar 2021, 00:59:34

Titel: Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 05 Februar 2021, 00:59:34
Hallo zusammen,
ich möchte gerne einen wiederkehrenden Algorithmus in einer subroutine in einer Datei 99_myUtils...pm ausführen lassen. Dabei muss ich innerhalb der sub auch devices bzw. dummies auslesen oder ändern.

Als Parameter können in Perl nur Scalare übergeben werden.
Nun möchte ich eine Reference auf ein device übergeben und darüber Zugriff auf das device im sub haben und im aufrufenden Block den geänderten Wert bekomme.

Ich habe schon viel versucht und gelesen und ausprobiert, aber leider bisher ohne Erfolg. Hat jemand eine Idee wie ich z.B.
aus einem notify über $NAME das auslösende Gerät als Parameter oder
ein dummy device, dessen Wert in der sub geändert und zurückgegeben wird,
an die sub übergeben und zurückgeben kann?
Viel Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: pwlr am 05 Februar 2021, 01:59:22
Moin,

ein als Beispiel passendes notify hab ich grad nicht, aber hier ne Version mit einem userReading:
pct:(trigger_cnt.*) {userReadings_02($name)}

und dann in einer sub in der 99_myUtils...pm:
sub userReadings_02($) {
# translate state into reading pct (open, closed, tilted) - used for sensors
# open   -> 100
# tilted ->  50
# closed ->   0

my($name) = @_;
my$sub_name = "userReadings_02";
Log3 $name, 4, "$name $sub_name: start";

my$pct=ReadingsVal($name,"state","300");
my$returnValue = $pct;

if ($pct eq "closed") {
$returnValue = 0;
};
if ($pct eq "tilted") {
$returnValue = 50;
};
if ($pct eq "open") {
$returnValue = 100;
};
Log3 $name, 4, "$name $sub_name: end with returnvalue $returnValue";
return $returnValue;
};


Im userReading ist $name der device-name des aufrufenden Devices, den Du in der sub mit
my($name) = @_;
bekommst. Und dann kannst Du Dich "weiterhangeln" und z.B. Redings des Devices lesen.
Der Returncode aus der sub landet dann im reading pct.

Es können auch mehrere Paramerter überben werden, die werden dann in einer durch Komma getrennten Liste analog zu dem Beispiel übergeben.
sub userReadings_02($$) {
my($name, $parameter) = @_;


Mit verbose im Device kannst Du die Debug-Ausgabe Log3 $name, 4, "$name ..."
steuern.

EDIT : Keine eigenen Readings des Devices auf diese Art überschreiben / ändern. Mein System verabschiedet sich dann in einen Loop and bye..

Alles klar ?
Moin
Bernd
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: CoolTux am 05 Februar 2021, 05:32:23
Du kannst in Perl nicht nur Scalare bei Funktionsaufrufen der Funktion über geben. Es gehen auch Hash oder Array. Sogar Referenzen kann man über geben und auch zurück geben.
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Christoph Morrison am 05 Februar 2021, 11:24:02
Zitat von: pwlr am 05 Februar 2021, 01:59:22

my$sub_name = "userReadings_02";


Den Namen der aktuellen Subroutine bekommst du übrigens über
(caller(0))[3], z.B.


use Data::Dumper;
use feature 'say';

sub eine_sub {
    say Dumper((caller(0))[3]);
}

eine_sub();
# $VAR1 = 'main::eine_sub';


Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 05 Februar 2021, 12:26:16
Danke für die Infos. Ich werde es damit probieren. Es wird sicherlich klappen.
Falls nicht, frage ich noch einmal nach.
Soweit ich das in den Tutorials gelesen und verstanden habe, muss man bei allen
Parametern, die nicht scalare sind, als Parameter die Reference auf das jeweilige Element
übergeben.
Viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: pwlr am 05 Februar 2021, 12:40:53
Moin,

ZitatDen Namen der aktuellen Subroutine bekommst du übrigens über
Code: [Auswählen]

(caller(0))[3]

danke für die Info, guter Tipp  !!!
Bernd
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 05 Februar 2021, 22:22:28
Vielen Dank für die Anregungen. Ich werde das jetzt über globale Variable lösen.
Ein Beispiel für den Aufruf und die Auswertung:


{  #test fuer subroutine
         
           
            if (ReadingsVal('SD_GT_E0E65_B', 'state', 'undef') eq 'on')  {
                 
                 FK_open_do('closed',15.0,$NAME);;

            }
 
            elsif (ReadingsVal('SD_GT_E0E65_B', 'state', 'undef') eq 'off') {
               
                FK_open_do('open',5.0,$NAME);;

            }

            fhem "set Hobby_EA $main::old_FK_Hobby";;
            fhem "set Hobby_Test $main::old_HZ_Hobby";;
            fhem "set FK_name $main::name";;
      }


Die Sub in myUtils sieht dann so aus:


##############################################
# $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
myUtils_WS_Initialize($$)
{
  my ($hash) = @_;

# globale Variable zur Steuerung der Alarmanlage und Heizung durch die Fensterkontakte
# Hierin werden jeweils die alten Werte vor einer Aenderung gespeichert

  our $old_FK_Hobby = 'closed';
  our $old_HZ_Hobby = 5.0;
  our $name = 'xxx'

}

# Enter you functions below _this_ line.

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

# FK_open_do:
# fuehrt Reaktionen auf das Oeffen eines Fensters aus:
# der Name des jeweiligen Fensterkontakts ist Bestandteil des auslösenden Ereignisses
# und steht in $NAME. Für den MAX!-Thermostat in dem Raum gilt:
# Die sub setzt bei Öffnen des Fensters bei Anwesenheit die Solltemperatur für die
# Heizung (sofern vorhanden) auf 5 Grad, speichert die vorherige Temperatur
# und setzt den Sollwert im Thermostat beim Schliessen des Fensters wieder
# auf den alten Wert. Dabei wird der mode auto während der Öffnungszeit auf man gesetzt.
#
# Bei Abwesenheit wird bei Öffnen des Fensters der Alarm ausgelöst und per
# Anruf auf dem Mobiltelefon und als Email gemeldet.
#
# Parameter: Name des Fensterkontaktes, i.a. in $NAME
#            Alarmzustand: "on", "off"
#            Anwesenheit:state "anwesend", "abwesend"

sub FK_open_do {

# parameter in local Variable
  my $Hobby_test = $_[0];
  my $Hobby_AE   = $_[1];
  my $name       = $_[2];

# parameter in globla Variable, auch im aufrufendem Programm verfügbar
  $main::old_FK_Hobby  = $_[0];
  $main::old_HZ_Hobby  = $_[1];
  $main::name          = $_[2];

}


1;


Für Verbesserungen bin ich immer zu haben
Viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: CoolTux am 06 Februar 2021, 10:32:51
Sorry aber verstehe da nicht wirklich was Du da machen willst. Für mich sieht es alles andere als der richtige Weg aus.
Wenn ich das richtig verstehe übergibst Du Deiner Funktion doch nur einen Skalar mit dem Wert aus $NAME. Und dann liest Du in der Funktion die Übergabe in mehrere Skalare weil Du denkst es ist ein Array? Ich verstehe das ganze Konstrukt nicht. Tut mir leid. Und das mit den globalen Variablen ist für mich auch mehr wie Fragwürdig. Das sollte man lieber lassen.
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 06 Februar 2021, 11:09:16
Dieses script ist nicht das Programm was dort entstehen soll sondern nur ein Beispiel,
dass so die Daten aus dem notify heraus an die sub übergeben werden können, $Name ist das auslösende device, brauche ich, weil ich in der sub für unterschiedliche Geräte immer den gleichen Algorithmus anwende. Die Auslösung im notify ist nachher .*open und in der sub werden dann das auslösende Gerät und die zugehörigen anderen devices behandelt.
Die Variablen, die ich an die sub übergebe, werden in der sub behandelt und als globale Variable werden dem Aufrufprogramm die veränderten Werte zur Verfügung gestellt.
Man hätte die Parameter auch als Reference übergeben können, dann werden die Änderungen auch im Aufrufprogramm verfügbar. Aber dass hat mit Dummys irgendwie nicht geklappt.
Viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: CoolTux am 06 Februar 2021, 11:41:24
Was spricht gegen


...
my ($old_FK_Hobby,$old_HZ_Hobby,$name) = FK_open_do('open',5.0,$NAME);
...
fhem "set Hobby_EA $old_FK_Hobby";
fhem "set Hobby_Test $old_HZ_Hobby";
fhem "set FK_name $name";
...



Sub

sub FK_open_do {

# parameter in local Variable
  my $Hobby_test = $_[0];
  my $Hobby_AE   = $_[1];
  my $name       = $_[2];

  return ($Hobby_test,$Hobby_AE,$name);
}


1;
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Beta-User am 06 Februar 2021, 14:30:51
Also für mich sieht die ganze Konstruktion auch suboptimal aus, und irgendwie ist auch das "Parameter in lokale Variable" "eigenwillig"....

Zitat von: McShire am 06 Februar 2021, 11:09:16
Man hätte die Parameter auch als Reference übergeben können, dann werden die Änderungen auch im Aufrufprogramm verfügbar. Aber dass hat mit Dummys irgendwie nicht geklappt.
Wieso Dummy?
Wieso irgendwo in globalen Variablen?

Falls du selbstgebastelte Abhängigkeiten irgendwo hinterlegen willst, sind mAn. entweder das Reading associatedWith (das ist aber eher als Generalverweis brauchbar) oder userAttr das "Mittel der Wahl".

Bei mir ist z.B. folgende "Kette" eingebaut:
Tür- oder Fensterkontakt wird open/closed/tilted => (ein globales) notify wird ausgelöst und ruft eine sub auf. Übergeben wird
{ myWinContactNotify ($NAME, $EVENT, 30)}Der Code schaut dann über Attribute, welche Thermostate dazugehören (eigentlich: welche mit denen gepeerten virtuellen Kontakte) und setzt die dann (bei Öffnen zeitverzögert => letztes Argument, wie lange gewartet werden soll) auf offen oder zu. Dabei wird zur "Sendezeit" nochmal geschaut, ob noch irgendeiner der zugehörigen Tür/FK's überhaupt offen ist (das ganze auch nur, wenn  grade Heizperiode ist...).

Geht alles über Attributabfrage und ist über FHEMWEB direkt einsehbar, kein "Rumgepfusche" mit Variablennamen, die ggf. irgendjemand anderes grade aus welchen Gründen auch immer auch in main  haben will...

(Ansonsten gäbe es auch noch %data, das ist für User-Infos vorgesehen...)
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 06 Februar 2021, 15:35:42
Danke für die Infos,
als Anfänger kennt man halt noch nicht alle Möglichkeiten und
findet auch nicht alles in den tutorials.
Ich werde jetzt mal mit Euren Hinweisen eine mehr professionelle Lösung versuchen.

Letztlich soll es bei mir genau wie oben beschrieben, eine Kette werden. ein Fenster oder eine Tür werden geöffnet -> .*open Ereignis
triggert ein notify, -> Name des auslösers, sowie die aktuelle Situation (abwesend (Alarm ja, nein -> SID, Debianmail), Designated Temp, usw.)
werden an eine sub übergeben, in der Sub die entsprechenden Maßnahmen für die zum Auslöser (Fensterkontakt) relevanten Geräte und routinen,
-> Status und Eistellungen zuück ans FHEM
mit den jetzt erhaltenen Infos wird es wohl klappen.

Der Umstieg vom Algol und Cobol der 60er und 70er auf moderne Sprachen ist halt nicht an einem Tag zu machen.
viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Beta-User am 06 Februar 2021, 15:52:56
Vielleicht dann mal hier der virtuelle Fensterkontakt mit den userAttr:
defmod Virtueller_Tuerkontakt_WZ CUL_HM 33221101
attr Virtueller_Tuerkontakt_WZ userattr myRealFK myTimeout myRealTempsensor
attr Virtueller_Tuerkontakt_WZ devStateIcon set_offen:fts_door_slide_open@red:geschlossen set_geschlossen:fts_door_slide@green:offen
attr Virtueller_Tuerkontakt_WZ eventMap /postEvent open:offen/postEvent closed:geschlossen/
attr Virtueller_Tuerkontakt_WZ group Türen und Fenster
attr Virtueller_Tuerkontakt_WZ icon fts_door_right_open
attr Virtueller_Tuerkontakt_WZ model VIRTUAL
attr Virtueller_Tuerkontakt_WZ myRealFK Balkontuer,Terrassentuer_WZ,Fenster_Wohnzimmer_SSO,Fenster_Wohnzimmer_SSW
attr Virtueller_Tuerkontakt_WZ peerIDs 2E7A9B03,2E832C03
attr Virtueller_Tuerkontakt_WZ room Steuerung->Heizung
attr Virtueller_Tuerkontakt_WZ webCmd :

Der myUtils-Code dazu (könnte man auch noch verschönern, aber geht...):
sub myWinContactNotify {  #three parameters, last (timeout) is optional
  my ($window, $event, $timeout) = @_;
  $timeout = 90 if !$timeout;
  my @virtuals = devspec2array("TYPE=CUL_HM:FILTER=model=VIRTUAL:FILTER=myRealFK=.*$window.*");
  for my $virtual (@virtuals) {
    my $myreals = AttrVal($virtual,"myRealFK","");
    if ($event =~ /open|tilted/) {
      my $checktime = gettimeofday()+$timeout;
      InternalTimer($checktime,"myTimeoutWinContact",$virtual);   
    } else {
      my @wcs = split(',',$myreals);
      my $openwc = 0;
      for my $wc (@wcs) {
        $openwc++ if (ReadingsVal($wc,"state","closed") ne "closed");
        last if $openwc;
      }
      CommandSet (undef,"$virtual geschlossen") if !$openwc;
    }
  }
  return;
}

sub myTimeoutWinContact {
  my $name = shift // return;
  #my $name = $hash{NAME};
  return if !ReadingsVal("Heizperiode","state","off") eq "on";
  my $myreals = AttrVal($name,"myRealFK","");
  my @wcs = split(',',$myreals);
  my $openwc = 0;
  for my $wc (@wcs) {
    $openwc++ if ReadingsVal($wc,"state","closed") ne "closed";
    last if $openwc;
  }
  CommandSet (undef,"$name offen") if $openwc;
  return;
}
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 10 Februar 2021, 22:45:12
Danke für den Tip und das Beispiel.
Ich habe zwar einige Zeit gebraucht, um den virtuellen Kontakt und das Handling der userattr zu verstehen sowie die Funktion der sub.
Offenbar geht es um mehrere FK, die alle die gleiche Aktion (Heizung Wohnzimmer ein / nach Zeit wieder aus ) ausführen, dabei gibt es offenbar mehrere virtuelle FK, jeweils für die realen FK in einem Zimmer oder Bereich:
Lange habe ich gebraucht das define einzugeben und es zum Laufen zu bringen. Die Fehlermeldung ist immer: Erst das Device anlegen:
Bis mir dann die Eingabe kam, das HM-devices 6-stellige Definition haben und darum nur 332211 aber nicht 33221101 geht.

Bei mir ist es etwas einfacher, ich habe für jedes Zimmer nur FK, der beim Öffnen die Heizung herunter regelt und beim Schließen den alten Zustand (gespeichert in userattr (Temp, Mode) wiederherstellt und FKs wo es keine Heizung gibt userattr HZ_Steu 0).
Je nach Status anwesend / abwesend und oder Alarm an/aus wird für einige Bereiche oder das ganze Haus die Alarmanlage gesteuert.
Langer rede kurzer Sinn:
Die Speicherung der Werte in Userattr haben den Durchbruch gebracht, dass hätte ich ohne das Beispiel nicht verstanden. Da ich nur jeweils einen realen FK habe können bei mir die virtuelleN FK für diesen Zweck wegfallen und die Routine wird ziemlich einfach. Auch die Funktionen AttrVal und OldreadingsVal kannt eich vorher nicht. devspec2arry sowieso nicht. Viel gelernt.
Vielen Dank und bleib gesund.
Werner

Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Beta-User am 11 Februar 2021, 16:22:56
Sorry, dass ich das an einem etwas "schwierigen" Device erläutern musste, hatte halt grade nichts besseres...
Diese HM-Kanal-Devices haben schon 8 Stellen, es gibt aber noch (jeweils) ein "Mutterdevice" mit 6 Stellen dazu, aber letztlich ist das ja für die Funktionalität nicht sooo wichtig.

Wie du richtig erkannt hast, gibt es mehrere "Echt-" Kontakte, die zu einem virtuellen zusammengefasst werden, der dann seinerseits wieder für einen oder mehrere Thermostate "zuständig" ist (gelöst über direktes peering, das regelt dann automatisch runter bzw. wieder zurück auf den zuvor eingestellten Wert (bzw. das, was das Wochenprogramm ggf. sagt)).

Falls ich aktuelle Werte zwischenspeichern müßte, würde ich das aber nicht über Attribute machen, sondern über Readings! Siehe "setreading" und "ReadingsVal()" bzw. "ReadingsNum()" in den Perl-Specials der commandref (bzw. deletereading). Auch da kann man sich passende Namen einfallen lassen, ich beginne mittlerweile alles, was "von mir" ist (und nicht über Module automatisiert kommt) in der Regel mit "my.*". Ansonsten ist es praktsich dasselbe...

Meine Handhabung ist die:
Alles, was statisch ist, soll in Attribute, die Konfiguration will ich möglichst wenig anfassen.
Alles, was sich im laufenden Betrieb ändert, kommt möglichst in Readings.

Das klappt im Großen und Ganzen, es gibt allerdings manche Module, die da teilweise eine etwas andere Handhabung "aus alter Zeit" haben und auch z.B. Werte, die nach diesem Verständnis als Reading geführt werden sollten in ein Attribut schreiben; ist aber die Ausnahme und meistens auch unveränderlich.

Was das mit Readings als "Marker" angeht: Z.B. hatte ich früher (vor der Umstellung auf AutoShuttersControl) zur Steuerung meiner Tag/Nacht-Fahrten meiner Rollläden einen WeekdayTimer, der einfach über einen FILTER immer die Rollläden auf- und zugemacht hat, die  zum Ausführungszeitpunkt eben einen bestimmten Readingwert gesetzt hatten. Auch zu FILTER findest du einige wenige markante Beispiele in der commandref.

Viel Spaß noch beim Weitertüfteln!
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 13 Februar 2021, 11:14:52
Danke für die weiteren Erläuterungen.
Es ist gut, dass es von Dir ein komplexes Beispiel ist. Daran habe ich einiges gelernt.
Ich hatte mich vorher schon ein bisschen mit arrays und references beschäftigt, konnte ich gut zum
Verständnis gebrauchen. Aber ich habe doch noch nicht genug gelernt oder verstanden.
zunächst ein bisschen Code:


.*open {  fhem "set FK_name $NAME";;                                                      #FK_name ist Dummy in fhem
                                                                                                                #FK_name1 .. FK_name3 Dummies
#FK_name, da die direkte Verwendung von $NAME nicht immer funktioniert, FK_name schon, warum ????
   
       my $HZname = 'undef';;
       my $rHZname = 'undef';;
       my $HZtemp = 'undef';;
       my $HZtempneu ='undef';;
       
       fhem "set FK_name1 $NAME open: anwesend,Alarm off: save Temp, set Temp 5.0 Grad";;        #nur debugtext in Dummy
                                                                                                                                                #hier wird $NAME wird HM_6Bxxxx

       Log 1, ReadingsVal('FK_name', 'state', 'undef');;                                              #Ergebnis HM_6Bxxxx , korrekt
       Log 1, AttrVal("HM_6B9B29", 'myHzDev', 'undef');;                                          #Ergebnis HZ_Raum,  korrekt
       Log 1, AttrVal(ReadingsVal('FK_name', 'state', 'undef'), 'myHzDev', 'undef');;     #Ergebnis HZ_Raum.  korrekt

       $HZname = AttrVal(ReadingsVal('FK_name', 'state', 'undef'), 'myHzDev', 'undef');;  #Ergebnis HZ_Raum, korrekt
       fhem "set FK_name1 $HZname";;
       Log 2, $HZname;; #Ergebnis korrekt
       $rHZname = \$HZname;;
       $HZtemp = ReadingsVal($HZname, 'desiredTemperature', 'undef');;                   
       Log 2, $HZtemp;;                                                                                              #Ergebnis 13.0, korrekt
       Log 3, $$rHZname;;                                                                                            #Ergebnis 13.0, korrekt
       fhem "set room=Heizung:FILTER=NAME=HZ_Raum desiredTemperature 9.0";;         #Ergebnis 13.0, korrekt
       #fhem "set HZname desiredTemperature 7.0";;                                                   #Ergebnis falsch
       $HZtempneu = ReadingsVal($HZname, 'desiredTemperature', 'undef');;
       Log 2, $HZtempneu;;


       Log 1, AttrVal(ReadingsVal('$Var1', 'state', 'undef'), 'myHzDev', 'undef');;       #Ergebnis undef
       Log 1, AttrVal(ReadingsVal('$NAME', 'state', 'undef'), 'myHzDev', 'undef');;       #Ergebnis undef

       #fhem "set FK_name3 AttrVal(ReadingsVal('FK_name', 'state', 'undef'), 'myHzDev', 'undef')";;
             #sollte eigentlich HZ_Hobby sein, aber schreibt den ganzen String in FK_name3

       #und wie setze ich dann einen neuen Attributwert für Temperatur in HZ_Raum? etwa so
       #fhem "set AttrVal(ReadingsVal('FK_name', 'state', 'undef'), 'myHzDev', 'undef') desiredTemperature 7.0";;
       # geht leider nicht, Eintrag im Log "Please define AttrVal(ReadingsVal('FK_name', first"



a) warum ist der Umweg über FK_name notwendig?
b) wie setze ich ein userattr in Perl auf einen neuen Wert, wenn ich eine Variable habe, die als Wert das device enthält?

denn durch die Zuweisung der neuen Attributwerte aus den vorhandenen Attributen im device $NAME
brauche ich nicht für jeden FK eine separate individuelle Anweisung, sondern setze nach dem open -und später auch closed -Event
die Aktionen aus $NAME Attributen die für im $NAME userattr myHZname aufgeführten Geräte.

Viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Beta-User am 13 Februar 2021, 14:21:35
setreading hast du nachgeschlagen?
Nochmal di Kurzform: Readings sind für sich wandelnde Inhalte besser geeignet als Attribute.
Und diese devspec ist auch nicht gut, wenn man schon alle Devices mitnimmt, dann wenigstens nur "state:open"...

Versuch' mal sowas: .*:open { fhem("setseading $SELF $NAME lieferte: $EVENT") }

Ungetestet, aber das sollte ein Reading mit dem auslösenden Gerät als Namen und dem Event als Inhalt an das notify schreiben.
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 16 Februar 2021, 21:49:45
Guten Abend,

alle Hinweise und Informationen, die ich bekommen habe, haben jezt zu einem, wie ich glaube, ganz gutem Ergebnis geführt.
Vielen Dank für die Unterstützung, one die ich sicherlich gescheitert wäre.
Ich habe im Bereich Heizung/Alarm 8 MAX! Heizungsthermostate, 1 PWMR Fussbodenregelung, 13 HM Fensterkontakte und 2 Selbstbau asksinPP HM Thermometer.
Für diese Konstellation habe ich jetzt das neue notify erstellt. Nach all den Hinweisen war klar, eine subroutine in 99-myUtils brauche ich nicht,
habe aber viel darüber gelernt für weitere Projekte.

Das notify sieht jetzt folgendermassen aus:


.*(open|closed) { 

# Die Fensterkontakte steuern die Heizkörperventile und den Alarm:
# Heizung:
# Beim Öffnen des Fensters / der Tuer wird, sofern eine Heizung im Raum ist, die aktuelle Temperatur gespeichert
# und der zugehörige Thermostat geschlossen.
# Beim Schliessen des Fensters wird die vorherige Temperatur wieder eingestellt und der Mode auf auto gesetzt.
# Alarm:
# Beim Öffnen eines Fensters / der Tuer im ganzen Haus wird der Alarm eingeschaltet wenn 'abwesend' eingestellt ist
# Beim Öffnen eines Fensters / der Tuer im Keller oder EG wird der Alarm eingeschaltet wenn Alarm auf 'on' gesetzt
#      d.h. oben im Schlafbereich können Fenster geöffnet sein.
#
# Vorbereitung:
# Die Subroutine DebianMail in die Datei 99_myUtils.pm kopieren / speichern (Emails schreiben).
# Die Device SIP_Client und myT25 in FHEM definieren (fuer Telefonanrufe).
# Zur Vereinfachung fuer das Eintragen von userattr und userreadings alle FK in attr <dev> group Fensterkontakte setzen.
# prüfen mit list a:group=Fensterkontakte
# fuer alle FKs userattr myHzSteu myAlSteu myAlText myHzDev setzen mit der Anweisung in der Command-Zeile
# attr a:group=Fensterkontakte userattr myHzSteu myAlSteu myAlText myHzDev
# fuer allr FKs die Attribute setzen mit: attr a:group=Fensterkontakte myAlSteu 1, für myAlText, myHZSteu, myHzDev wiederholen.
# Einzeln für jeden FK die konstanten Attributwerte einsetzen, für die ..Steu 0 oder 1, für Text eine Bezeichnung für den FK, myHzDev NAME des Thermostaten
# genauso mit den variablen userreadings verfahren: attr a:group=Fensterkontakte userReadings myprevTemp,myprevState   (komma beachten)
# und dann setreading a:group=Fensterkontakte myprevTemp 18.0  und  ... myprevState closed  als default Werte
# Dummy FK_name erstellen.


       fhem "set FK_name $NAME";;  #FK_name ist Dummy in fhem
                                   #FK_name1 .. FK_name3 Dummies
       my $myFKname = 'undef';;
       my $myFKevent = 'undef';;
       my $myHZname = 'undef';;
       my $myHZtemp = 0;;
       my $myHZtempneu = 5.0;;
       my $myoldState = 'undef';;
       my $myALtext = 'undef';;
       
       $myFKname = $NAME;; #Den Namen des auslösenden Fensterkontaktes in eine lokale Variable speichern
       $myFKevent = $EVENT;; #Das Ereignis in eine lokale Variable speichern
       Log 1, 'Das Event ist: '.$myFKname.' '.$myFKevent;;
       Log 1, 'Auslösendes Device aus ReadingsVal(FK_name...): '.AttrVal(ReadingsVal('FK_name', 'state', 'undef'),'alias','undef');;

       
       if ($myFKevent eq 'open' and ReadingsVal($myFKname, 'myprevState', 'undef') ne 'open') {    #Status von closed auf open geändert
          fhem ("setreading $myFKname myprevState open");;
          Log 1, 'das Ereignis ist '.$myFKname.'  '.$EVENT;;

          #Steuerung der Heizung:
          #Flag für HeizungSteuerung aus dem attr des FK lesen, falls 1, Ventil öffnen oder schließen
          if (AttrVal($myFKname, 'myHzSteu', '99') == 1) {                                                            #Heizkörpervenile
             Log 1, 'myHZSteu in '.$myFKname.' ist: '.AttrVal($myFKname, 'myHzSteu', 'undef'). ' Ventil schliessen';;
             $myHZname = AttrVal($myFKname, 'myHzDev', 'undef');; #Name des Thermostaten aus dem attr des FK lesen 
             Log 1, 'myHZname aus AttrVal(...) ist: '.$myHZname;;   
             $myHZtemp = ReadingsVal($myHZname, 'desiredTemperature', 0);; #die aktuelle Temperaturvorgabe lesen                   
             Log 1, 'die akt. Temperatur aus dem Thermostat '.$myHZname.' ist: '.$myHZtemp;;
             fhem ("setreading $myFKname myprevTemp $myHZtemp");; #Die akt. Vorgabe in den Fensterkontakt zur Wiederherstellung speichern
             Log 1, 'Vorherige Temperatur in '.$myFKname.' gespeichert: '.ReadingsVal($myFKname, 'myprevTemp', '0');;
             fhem ("setreading $myHZname desiredTemperature $myHZtempneu");; # Das Ventil schliessen
             Log 1, 'neue Temperatur in '.$myHZname.'  '.ReadingsVal($myHZname, 'desiredTemperature', '0');;
          }
          elsif (AttrVal($myFKname, 'myHzSteu', '99') == 2) {   #Fussbodenheizung FHEM-PWMR
             Log 1, 'myHZSteu in '.$myFKname.' ist: '.AttrVal($myFKname, 'myHzSteu', 'undef'). ' PWMR Solltemp reduzieren';;
             $myHZname = AttrVal($myFKname, 'myHzDev', 'undef');; #Name des Dummy für die Solltemp aus dem attr des FK lesen
             $myHZtemp = ReadingsVal($myHZname, 'state', 0);; #die aktuelle Solltemperatur lesen
             fhem ("setreading $myFKname myprevTemp $myHZtemp");; #Die akt. Vorgabe in den Fensterkontakt zur Wiederherstellung speichern
             fhem ("setreading $myHZname state $myHZtempneu");; # Die Vorgabetemperatur auf reduzierte Temperatur
             Log 1, 'neue Temperatur in '.$myHZname.'  '.ReadingsVal($myHZname, 'state', '0');;
          }
       

          #Steuerung des Alarms:
          Log 1, 'Der Dummy Anwesenheit steht auf: '.ReadingsVal('Anwesenheit', 'state', 'undef');;
          Log 1, 'Der Dummy Alarm_EA steht auf: '.ReadingsVal('Alarm_EA', 'state', 'undef').' die Alarmsteuerung steht auf '.AttrVal($myFKname, 'myAlSteu', 'undef');;

          if ((ReadingsVal('Anwesenheit', 'state', 'undef') eq 'abwesend') or
              (ReadingsVal('Alarm_EA', 'state', 'undef') eq 'on' and AttrVal($myFKname, 'myAlSteu', 'undef') == 1)) {
             #bei 'abwesend' wird der Alarm von FK im ganzen Haus ausgelöst, bei Alarm_EA 'on' nur im EG und Keller (attr myAlSteu 1) auslösen
       
             $myALtext = AttrVal($myFKname, 'myAlText', 'undef');;
             Log 1, $myALtext.' geoeffnet';;
             DebianMail('werner.XXXXXX@t-online.de',$myALtext.' geoeffnet',$myALtext.' geoeffnet');;
             fhem ("set SIP_Client call 0171XXXXXX 30 !fhem Fensteralarm $myALtext ist offen &");;  #Alarmmeldung auf Mobiltelefon
             #fhem ("set FLAMINGO_XXXXXX Testalarm");;                                               #Rauchmelder EG auslösen (Alarmsirene > 85 dBA
             #fhem ("set FLAMINGO_XXXXXX Testalarm");;                                               #Rauchmelder OG auslösen (Alarmsirene > 85 dBA
          }
       }
       elsif ($myFKevent eq 'closed' and ReadingsVal($myFKname, 'myprevState', 'undef') ne 'closed') { #Status von open auf closed geändert
          fhem ("setreading $myFKname myprevState closed");;
          Log 1, 'das Ereignis ist '.$myFKname.'  '.$EVENT;;
         
          #Steuerung der Heizung:
          # Beim Schließen des Fensters wird der Sollwert im Thermostat wieder auf den alten Wert zurückgesetzt.
          if (AttrVal($myFKname, 'myHzSteu', '99') == 1) {
             Log 1, 'myHZSteu in '.$myFKname.' ist: '.AttrVal($myFKname, 'myHzSteu', 'undef'). ' Hzg einschalten';;
             $myHZname = AttrVal($myFKname, 'myHzDev', 'undef');; #Name des Thermostaten aus dem attr des FK lesen 
             Log 1, 'myHZname aus AttrVal(...) ist: '.$myHZname;; 
             $myHZtemp = ReadingsVal($myFKname, 'myprevTemp', 0);; #vorheriger Wert aus dem FK
             fhem "setreading $myHZname desiredTemperature $myHZtemp";; #vorherigen Wert in den Thermostaten schreiben
             fhem "setreading $myHZname mode auto";;
             fhem "attr $myHZname scanTemp 1";;
             $myHZtemp = ReadingsVal($myHZname, 'desiredTemperature', 0);; #wiederhergestellt Temperatur
             Log 1, 'Die wieder hergestellte Temperatur in '.$myHZname.' ist '.$myHZtemp;;
             Log 1, 'Der ScanMode in '.$myHZname.' ist '.AttrVal($myHZname, 'scanTemp', 'undef');;
          }
          elsif (AttrVal($myFKname, 'myHzSteu', '99') == 2) {          #Fussbodenheizung FHEM-PWMR
             Log 1, 'myHZSteu in '.$myFKname.' ist: '.AttrVal($myFKname, 'myHzSteu', 'undef'). ' Hzg einschalten';;
             $myHZtemp = ReadingsVal($myFKname, 'myprevTemp', 0);; #vorheriger Wert aus dem FK
             $myHZname = AttrVal($myFKname, 'myHzDev', 'undef');; #Name des Dummy für die Solltemp aus dem attr des FK lesen
             Log 1, 'Temperatur zur Wiederherstellung ist: '.$myHZtemp;;
             fhem "setreading $myHZname state $myHZtemp";; #vorherigen Wert in den Sollwert Dummy schreiben
             $myHZtemp = ReadingsVal($myHZname, 'state', 0);; #wiederhergestellt Temperatur
             Log 1, 'Die wieder hergestellte Temperatur in '.$myHZname.' ist '.$myHZtemp;;
          }
       
          #Steuerung des Alarms:
          Log 1, 'Der Dummy Anwesenheit steht auf: '.ReadingsVal('Anwesenheit', 'state', 'undef');;
          if (ReadingsVal('Anwesenheit', 'state', 'undef') eq 'abwesend') {
             $myALtext = AttrVal($myFKname, 'myAlText', 'undef');;
             Log 1, $myALtext.' geschlossen';;
             DebianMail('werner.XXXXXX@t-online.de',$myALtext.' geschlossen',$myALtext.' geschlossen');;
          }
         
     }
}


Es ist vielleicht nicht die eleganteste Lösung, aber sie funktioniert.
Die Logs werde ich nach ein bisschen Probezeit auskommentieren bis auf die 2 oder 3 wesentlichsten.

Für weitere Verbesserungsvorschläge bin ich jederzeit offen.
Viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Beta-User am 18 Februar 2021, 10:25:18
Aua!

Zitateine subroutine in 99-myUtils brauche ich nicht,
Diese Schlussfolgerung erschließt sich mir nicht!

"Mehrzeiler" sind als myUtils-Routine immer leichter zu lesen, und du kannst auch ordentliche Doku als cref dazu packen.

Und wenn du sie schon nicht brauchst, brauchst du auch keine Variablen "umpacken". So macht "$myFKname = $NAME;" jedenfalls überhaupt keinen Sinn, dann kann man auch $NAME verwenden, dann weiß wenigstens jeder etwas eingearbeitete FHEM-User, was gemeint ist...

Und für maximale Verwirrung sind solche Konstruktionen gut:
my $myHZname = 'undef';Denn: Entweder, etwas ist nicht definiert, dann ist es undef OHNE QUOTES! (und das muss man m.E. nicht extra erwähnen, es reicht "my $myHZname;")Und "Log" ist auch "out", stattdessen sollte man m.E. Log3 verwenden...
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 18 Februar 2021, 23:05:49
man möge mir als Anfänger verzeihen, dass ich die geläufigen Standard nicht so gut kenne.
Ich war der Meinung wenn ein Code in der cfg steht, ist er gut aufgehoben und wird bei der Sicherung der fhem.cfg gleich mitgesichert.
Eine Subroutine oder Funktion oder Methode ist eigentlich für Code, den man wiederholt aufruft und benutzt und nicht mehrfach einsetzt. z.B. 13 mal für die einzelnen FKs.
Bei der Variablendeclaration wollte ich jeder Variablen einen Init-Wert mitgeben. Solange kein Wert in der Routine zugewiesen wird ist er halt undef.
"$myFKname = $NAME;"  : Leider funktionierte bei einigen Zeilen $NAME nicht als Parameter, es wurde dabei nicht der Name des Auslösers eingesetzt.
Warum, hab ich leider nicht herausgefunden. Aber über die Wertzuweisung in eine Variable ($myFKname) und diese dann eingesetzt, kam das korrekte Ergebnis.
Gerade wegen solcher Eigenarten habe ich die vielen LOgs eingebaut, um zu sehen was korrekt funktioniert und was nicht. Log3 kenne ich nicht.
Ich habe aber die LOgs inzwischen customLog eingesetzt, damit ich die Logs in dem zugehörigen Logfile zum notify habe und nicht immer im allgemeinen Logfile suchen muss.
Unterm Strich bin ich erstmal froh, dass es so funktioniert. Die Alternative, für jeden FK ein DOIF mit der zugehörigen einzusetzen,war für mich auf jeden Fall die schlechtere Lösung.
das wäre dann ungefähr so für jeden Fenstrkontakt extra:


[HM_XXXXXX:"open"] and [Anwesenheit:state] eq "abwesend")
    ({DebianMail('werner.XXXXXX@t-online.de',
                 'Buero-Fenster auf',
                 'Buerofenster ist auf')})
    (set SIP_Client call XXXXXX 30 !fhem Fensteralarm Buero &)
     (set FLAMINGO_XXXXXX Testalarm)
     (set FLAMINGO_XXXXXX Testalarm)
DOELSEIF
([HM_XXXXXX:"open"] and [Anwesenheit:state] eq "anwesend")
    (set HZ_Buero_Test [HZ_Buero:state:d])
    (set HZ_Buero desiredTemperature manual 5)
    (attr HZ_Buero scanTemp 0)
DOELSEIF
([HM_XXXXXX:"closed"] and [Anwesenheit:state] eq "abwesend")
    ({DebianMail('XXXXXX.schwetje@t-online.de',
                 'Buero-Fenster zu',
                 'Buerofenster ist zu')})
DOELSEIF
([HM_XXXXXX:"closed"] and [Anwesenheit:state] eq "anwesend")
    (set HZ_Buero desiredTemperature auto [HZ_Buero_Test:state])
    (attr HZ_Buero scanTemp 1)


und das 13 mal.
Viele Grüße
Werner
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: Wzut am 19 Februar 2021, 06:39:53
Zitat von: McShire am 16 Februar 2021, 21:49:45
8 MAX! Heizungsthermostate
Tipp : 10_MAX kennt seit einiger Zeit den TYP virtualShutterContakt, der lässt sich mit jedem HT / WT peeren.
Auf die Art kann man die MAX HTs dann direkt mit einem Fremdfabrikat steuern ohne notify :)
Titel: Antw:Ein device als Parameter an eine subroutine übergeben
Beitrag von: McShire am 19 Februar 2021, 10:46:25
Danke für den Hinweis