Ein device als Parameter an eine subroutine übergeben

Begonnen von McShire, 05 Februar 2021, 00:59:34

Vorheriges Thema - Nächstes Thema

McShire

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

Beta-User

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.
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

McShire

#17
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

Beta-User

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...
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

McShire

#19
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

Wzut

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 :)
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

McShire