FHEM-Modus zu Perl-Modus umbauen um zu verschachteln?

Begonnen von OiledAmoeba, 17 Oktober 2025, 02:07:40

Vorheriges Thema - Nächstes Thema

OiledAmoeba

Moin,

zuerst: Ich habe bisher nur DOIF im FHEM-Modus eingesetzt. In den Perl-Modus lese ich mich gerade ein.

Ich habe derzeit ein recht kompliziertes DOIF im FHEM-Modus. Kompliziert, weil der Wartungsaufwand hoch ist. Bei der kleinsten Änderung, vor allem bei der Zeit, oder wenn ich merke, dass der Helligkeitswert doch unglücklich ist, muss ich jede Zeile anfassen:
([08:30-23:00]and not(5<[twilight:elevation]<25 and 225<[twilight:azimuth]<280) and [Stube_Motion:lux]>=20)
(set MQTT2_zigbee_Schlafen_Rollo_links pct 49;set MQTT2_zigbee_Schlafen_Rollo_rechts pct 47)
DOELSEIF ([08:30-23:00]and (5<[twilight:elevation]<25 and 225<[twilight:azimuth]<263) and [Stube_Motion:lux]>=20)
(set MQTT2_zigbee_Schlafen_Rollo_rechts pct 7)
DOELSEIF ([08:30-23:00]and (5<[twilight:elevation]<22 and 275<[twilight:azimuth]<280) and [Stube_Motion:lux]>=20)
(set MQTT2_zigbee_Schlafen_Rollo_links pct 7)
DOELSEIF ((5<[twilight:elevation]<7 and 225<[twilight:azimuth]<280)or([Stube_Motion:lux]<20))
(set MQTT2_zigbee_Schlafen_Rollo_(links|rechts) close)

Einfacher und übersichtlicher wäre es, wenn ich es in mehrere IFs verschachteln würde. (Sehr vereinfacht:)
if <Zeit> and <Helligkeit>
  if <Sonnenstand_1>
    <Rollo_rechts> abblenden
  elsif <Sonnenstand_2>
    <Rollo_links> abblenden
  else
    <Rollo_links> and <Rollo_rechts> normalauf
else
  <Rollo_links> and <Rollo_rechts> zu

Ganz angenehm im FHEM-Modus ist, dass der Befehl nur einmal ausgeführt wird, weil er ja über cmd_<n> erkennt, was er zuletzt gemacht hat. Wenn ich die Wiki richtig verstehe, funtioniert der Perl-Modus wie ein do:always, richtig? Also würde er z.B. außerhalb der <Zeit> zu jeder Minute oder bei jeder Helligkeitsänderung den Befehl "<Rollo_links> und <Rollo_rechts> zu" abfeuern?

Habe ich dann die Wiki richtig verstanden, dass ich z.B. den aktuellen Zustand in ein reading schreiben sollte und das an die Prüfung anhängen müsste, um Mehrfachausführungen zu verhindern? Also so zum Beispiel (wieder vereinfacht geschrieben):
...
elsif not <eigenesReading> = geschlossen
  <Rollo_links> and <Rollo_rechts> zu;
  <eigenesReading> geschlossen

Oder denke ich gerade komplett falsch?
Gruß
Florian

Jail auf XigmaNAS (freeBSD); CCU2 mit CULv3, nanoCUL868 und JeeLink-Clone; div. FS20-Komponenten; andFHEM; div. hm- und hmip-Komponenten; div. IT+

Prof. Dr. Peter Henning

#1
Zitat von: OiledAmoeba am 17 Oktober 2025, 02:07:40Oder denke ich gerade komplett falsch?
Ja.

Genau dieses, nämlich die schlichte Unwartbarkeit von solch komplexen DOIF-Strukturen, ist Grund genug, das in echte Perl-Programme in einer separaten Datei auszulagern.

Dort kann man wunderbar Arrays oder Hashes anlegen, die für jeden Einzelrollladen die relevanten Eckwerte bei der Beschattung enthalten. Kleines Beispiel:

#-- OSC_BrightnessSensor OSC_Shading_InOutAzimuth OSC_Shading_MinMax_Elevation
#  OSC_Min_OutsideTemperature OSC_Shading_Mode OSC_Shading_Pos OSC_Shading_StateChange_SunnyCloudy
#  OSC_Shading_WaitingPeriod
my %oscshadeattrs = (
    "AZ.Roll.K" => ["A.Light","95:265","5:90","5","always","{shaderoof()}","30000:20000","300"],
    "AZ.Roll.G" => ["A.Light","95:265","5:90","5","always","{shaderoof()}","30000:20000","300"],
    "GZ.Roll.F" => ["A.Light","190:290","5:90","5","always","{shadewestOG()}","60000:40000","300"],
    "BI.Roll.F" => ["A.Light","190:290","5:90","5","always","{shadewestOG()}","60000:40000","300"],
    "BI.Roll.T" => ["A.Light","170:220","5:90","5","always","{((shadesouth()/100)**2)*100}","60000:40000","300"],
    "SZ.Roll.0" => ["A.Light","100:220","5:90","5","always","{shadesouth()}","60000:40000","300"],
    "SZ.Roll.1" => ["A.Light","190:290","0:90","5","always","{shadewestOG()}","60000:40000","300"],
    "SZ.Roll.2" => ["A.Light","190:290","0:90","5","always","{((shadewestOG()/100)**2)*100}","60000:40000","300"],
    "SZ.Roll.3" => ["A.Light","190:290","0:90","5","always","{shadewestOG()}","60000:40000","300"],
    "WZ.Roll.0" => ["A.Light","100:220","5:90","5","always","{shadesouth()}","60000:40000","300"],
    "WZ.Roll.1" => ["A.Light","190:280","0:90","5","always","{shadewestEG()}","60000:40000","300"],
    "WZ.Roll.2" => ["A.Light","200:220","5:90","5","always","{shadesouth()}","60000:40000","300"],
    "WZ.Roll.3" => ["A.Light","90:270","0:90","5","never","100","60000:40000","300"]
    );

Die entsprechenden Shading-Funktionen berechnen den für die Beschattung nötigen Stand des Rollladens in Abhängigkeit vom Sonnenstand.
LG

pah

OiledAmoeba

#2
Dein Code-Beispiel sieht für mich super aus. Allerdings ist das für einen absoluten Perl-Dummy wie mich noch Zukunftsmusik... Erinnert mich an die Wiki Automatisierung/Beschattung. Da will ich irgendwann sicher hin, aber derzeit sind dazu meine Kenntnisse nicht ausreichend.

Ich hatte heute Nacht mit meinen bescheidenen Perl-Kenntnissen noch ein wenig gebastelt. Herausgekommen ist das:

define di_Nobodyfenster_Rollo_links DOIF {\
    if ([[Steuerdaten_n:time_begin]]-[[Steuerdaten_n:time_end]] and [Stube_Motion:lux]>=[Steuerdaten_n:lux_min])\
    {\
        if ([Stube_Motion:lux]>=[Steuerdaten_n:lux_blinding] and [$SELF:ele_from]<=[twilight:elevation]<=[$SELF:ele_to] and [$SELF:azi_from]<=[twilight:azimuth]<=[$SELF:azi_to] and [$SELF] ne "geblendet")\
        {\
            fhem_set (AttrVal("$SELF","actor","")." pct ".AttrVal("$SELF","pct_shading",0));;;;\
            set_State ("geblendet");;;;\
        }\
        elsif ([$SELF] ne "normal")\
        {\
            fhem_set (AttrVal("$SELF","actor","")." pct ".AttrVal("$SELF","pct_open",0));;;;\
            set_State ("normal");;;;\
        }\
    }\
    elsif ([$SELF] ne "geschlossen")\
    {\
            fhem_set (AttrVal("$SELF","actor","")." pct ".AttrVal("$SELF","pct_close",0));;;;\
            set_State ("geschlossen");;;;\
    }\
}
attr di_Nobodyfenster_Rollo_links userattr actor ele_from ele_to azi_from azi_to pct_shading pct_open pct_close
attr di_Nobodyfenster_Rollo_links DbLogExclude .*
attr di_Nobodyfenster_Rollo_links actor MQTT2_zigbee_Nobody_Rollo
attr di_Nobodyfenster_Rollo_links azi_from 240
attr di_Nobodyfenster_Rollo_links azi_to 359
attr di_Nobodyfenster_Rollo_links ele_from 0
attr di_Nobodyfenster_Rollo_links ele_to 5
attr di_Nobodyfenster_Rollo_links pct_close 0
attr di_Nobodyfenster_Rollo_links pct_open 4
attr di_Nobodyfenster_Rollo_links pct_shading 0
attr di_Nobodyfenster_Rollo_links room Rollo
#   DEF        {
#    if ([[Steuerdaten_n:time_begin]]-[[Steuerdaten_n:time_end]] and [Stube_Motion:lux]>=[Steuerdaten_n:lux_min])
#    {
#        if ([Stube_Motion:lux]>=[Steuerdaten_n:lux_blinding] and [$SELF:ele_from]<=[twilight:elevation]<=[$SELF:ele_to] and [$SELF:azi_from]<=[twilight:azimuth]<=[$SELF:azi_to] and [$SELF] ne "geblendet")
#        {
#            fhem_set (AttrVal("$SELF","actor","")." pct ".AttrVal("$SELF","pct_shading",0));;
#            set_State ("geblendet");;
#        }
#        elsif ([$SELF] ne "normal")
#        {
#            fhem_set (AttrVal("$SELF","actor","")." pct ".AttrVal("$SELF","pct_open",0));;
#            set_State ("normal");;
#        }
#    }
#    elsif ([$SELF] ne "geschlossen")
#    {
#            fhem_set (AttrVal("$SELF","actor","")." pct ".AttrVal("$SELF","pct_close",0));;
#            set_State ("geschlossen");;
#    }
#}
#   FUUID      68f1931c-f33f-2b54-136a-62c1de2606838abf
#   MODEL      Perl
#   NAME       di_Nobodyfenster_Rollo_links
#   NOTIFYDEV  di_Nobodyfenster_Rollo_links,global,Stube_Motion,Steuerdaten_n,twilight
#   NR         1127
#   NTFY_ORDER 50-di_Nobodyfenster_Rollo_links
#   STATE      geschlossen
#   TYPE       DOIF
#   VERSION    29460 2024-12-29 20:25:48
#   eventCount 36
#   Helper:
#     DBLOG:
#       mode:
#         logdb:
#           TIME       1760662300.50141
#           VALUE      enabled
#   READINGS:
#     2025-10-17 12:28:49   Device          Stube_Motion
#     2025-10-17 12:28:49   block_01        executed
#     2025-10-17 12:20:18   e_Steuerdaten_n_lux_blinding 500
#     2025-10-17 12:20:24   e_Steuerdaten_n_lux_min 100
#     2025-10-17 12:28:49   e_Stube_Motion_lux 1332
#     2025-10-17 08:30:02   e_di_Nobodyfenster_Rollo_links_STATE geschlossen
#     2025-10-17 12:28:28   e_twilight_azimuth 169.88
#     2025-10-17 12:28:28   e_twilight_elevation 26.63
#     2025-10-17 03:52:07   mode            enabled
#     2025-10-17 08:30:02   state           geschlossen
#     2025-10-17 12:21:02   timer_01_c01    18.10.2025 08:30:00
#     2025-10-17 03:52:07   timer_02_c01    17.10.2025 23:00:00
#   Regex:
#     accu:
#     bar:
#     barAvg:
#     collect:
#     cond:
#       Steuerdaten_n:
#         0:
#           lux_blinding ^Steuerdaten_n$:^lux_blinding:
#           lux_min    ^Steuerdaten_n$:^lux_min:
#       Stube_Motion:
#         0:
#           lux        ^Stube_Motion$:^lux:
#       di_Nobodyfenster_Rollo_links:
#         0:
#           &STATE     ^di_Nobodyfenster_Rollo_links$
#           azi_from   ^di_Nobodyfenster_Rollo_links$:^azi_from:
#           azi_to     ^di_Nobodyfenster_Rollo_links$:^azi_to:
#           ele_from   ^di_Nobodyfenster_Rollo_links$:^ele_from:
#           ele_to     ^di_Nobodyfenster_Rollo_links$:^ele_to:
#       twilight:
#         0:
#           azimuth    ^twilight$:^azimuth:
#           elevation  ^twilight$:^elevation:
#     itimer:
#       Steuerdaten_n:
#         itimer:
#           time_begin ^Steuerdaten_n$:^time_begin:
#           time_end   ^Steuerdaten_n$:^time_end:
#   condition:
#     0         
#    if (::DOIF_time_once($hash,0,$wday)-::DOIF_time_once($hash,1,$wday) and ::ReadingValDoIf($hash,'Stube_Motion','lux')>=::ReadingValDoIf($hash,'Steuerdaten_n','lux_min'))
#    {
#        if (::ReadingValDoIf($hash,'Stube_Motion','lux')>=::ReadingValDoIf($hash,'Steuerdaten_n','lux_blinding') and ::ReadingValDoIf($hash,'di_Nobodyfenster_Rollo_links','ele_from')<=::ReadingValDoIf($hash,'twilight','elevation')<=::ReadingValDoIf($hash,'di_Nobodyfenster_Rollo_links','ele_to') and ::ReadingValDoIf($hash,'di_Nobodyfenster_Rollo_links','azi_from')<=::ReadingValDoIf($hash,'twilight','azimuth')<=::ReadingValDoIf($hash,'di_Nobodyfenster_Rollo_links','azi_to') and ::InternalDoIf($hash,'di_Nobodyfenster_Rollo_links','STATE') ne "geblendet")
#        {
#            fhem_set (AttrVal("di_Nobodyfenster_Rollo_links","actor","")." pct ".AttrVal("di_Nobodyfenster_Rollo_links","pct_shading",0));;
#            set_State ("geblendet");;
#        }
#        elsif (::InternalDoIf($hash,'di_Nobodyfenster_Rollo_links','STATE') ne "normal")
#        {
#            fhem_set (AttrVal("di_Nobodyfenster_Rollo_links","actor","")." pct ".AttrVal("di_Nobodyfenster_Rollo_links","pct_open",0));;
#            set_State ("normal");;
#        }
#    }
#    elsif (::InternalDoIf($hash,'di_Nobodyfenster_Rollo_links','STATE') ne "geschlossen")
#    {
#            fhem_set (AttrVal("di_Nobodyfenster_Rollo_links","actor","")." pct ".AttrVal("di_Nobodyfenster_Rollo_links","pct_close",0));;
#            set_State ("geschlossen");;
#    }
#
#   days:
#   helper:
#     NOTIFYDEV  di_Nobodyfenster_Rollo_links,global,Stube_Motion,Steuerdaten_n,twilight
#     event      update_installed_version: 16777316,lux: 1332,occupancy: false,batterymV: 2500,illuminance_raw: 31245,battery: 56,last_seen: 2025-10-17T12:28:49+02:00,update_state: idle,linkquality: 29,update_latest_version: 16777316,batteryVoltage: 2.5
#     globalinit 1
#     last_timer 2
#     sleeptimer -1
#     triggerDev Stube_Motion
#     triggerEvents:
#       update_installed_version: 16777316
#       lux: 1332
#       occupancy: false
#       batterymV: 2500
#       illuminance_raw: 31245
#       battery: 56
#       last_seen: 2025-10-17T12:28:49+02:00
#       update_state: idle
#       linkquality: 29
#       update_latest_version: 16777316
#       batteryVoltage: 2.5
#     triggerEventsState:
#       update_installed_version: 16777316
#       lux: 1332
#       occupancy: false
#       batterymV: 2500
#       illuminance_raw: 31245
#       battery: 56
#       last_seen: 2025-10-17T12:28:49+02:00
#       update_state: idle
#       linkquality: 29
#       update_latest_version: 16777316
#       batteryVoltage: 2.5
#   internals:
#     all         di_Nobodyfenster_Rollo_links:STATE
#   interval:
#   intervalfunc:
#   intervaltimer:
#   localtime:
#     0          1760769000
#     1          1760734800
#   perlblock:
#     0          block_01
#   readings:
#     all         Stube_Motion:lux Steuerdaten_n:lux_min Steuerdaten_n:lux_blinding di_Nobodyfenster_Rollo_links:ele_from twilight:elevation di_Nobodyfenster_Rollo_links:ele_to di_Nobodyfenster_Rollo_links:azi_from twilight:azimuth di_Nobodyfenster_Rollo_links:azi_to
#   realtime:
#     0          08:30:00
#     1          23:00:00
#   time:
#     0          [Steuerdaten_n:time_begin]
#     1          [Steuerdaten_n:time_end]
#   timeCond:
#     0          0
#     1          0
#   timer:
#     0          0
#     1          0
#   timers:
#     0           0  1
#   trigger:
#   triggertime:
#     1760734800:
#       localtime  1760734800
#       hash:
#     1760769000:
#       localtime  1760769000
#       hash:
#   uiState:
#   uiTable:
#
setstate di_Nobodyfenster_Rollo_links geschlossen
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:28:49 Device Stube_Motion
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:28:49 block_01 executed
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:20:18 e_Steuerdaten_n_lux_blinding 500
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:20:24 e_Steuerdaten_n_lux_min 100
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:28:49 e_Stube_Motion_lux 1332
setstate di_Nobodyfenster_Rollo_links 2025-10-17 08:30:02 e_di_Nobodyfenster_Rollo_links_STATE geschlossen
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:28:28 e_twilight_azimuth 169.88
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:28:28 e_twilight_elevation 26.63
setstate di_Nobodyfenster_Rollo_links 2025-10-17 03:52:07 mode enabled
setstate di_Nobodyfenster_Rollo_links 2025-10-17 08:30:02 state geschlossen
setstate di_Nobodyfenster_Rollo_links 2025-10-17 12:21:02 timer_01_c01 18.10.2025 08:30:00
setstate di_Nobodyfenster_Rollo_links 2025-10-17 03:52:07 timer_02_c01 17.10.2025 23:00:00


Der "Steuerung" Dummy sieht so aus:
define Steuerdaten_n dummy
attr Steuerdaten_n DbLogExclude .*
attr Steuerdaten_n readingList lux_min lux_blinding time_begin time_end
attr Steuerdaten_n room Rollo
attr Steuerdaten_n setList lux_min lux_blinding time_begin time_end
#   FUUID      68f10b5c-f33f-2b54-0190-48562f6c99312096
#   NAME       Steuerdaten_n
#   NR         1064
#   STATE      ???
#   TYPE       dummy
#   eventCount 23
#   READINGS:
#     2025-10-17 12:20:18   lux_blinding    500
#     2025-10-17 12:20:24   lux_min         100
#     2025-10-17 12:21:02   time_begin      08:30
#     2025-10-17 03:51:34   time_end        23:00
#
setstate Steuerdaten_n 2025-10-17 12:20:18 lux_blinding 500
setstate Steuerdaten_n 2025-10-17 12:20:24 lux_min 100
setstate Steuerdaten_n 2025-10-17 12:21:02 time_begin 08:30
setstate Steuerdaten_n 2025-10-17 03:51:34 time_end 23:00


Der setstate mit der Uhrzeit sieht so aus, weil ich heute schon mit der Uhrzeit gespielt habe. Denn: Er schaltet nicht. Obwohl, er schaltet schon. Er geht auf "geschlossen" und das war es dann.

Zur Fehlersuche habe ich die Uhrzeit rausgenommen und er schaltet aktuell nach "normal", weil der Sonnenstand noch nicht erreicht ist. Meiner Meinung nach hat er ein Problem mit der Uhrzeit, denn "zwischen morgen 8:30 und heute 23:00" kann nicht wahr werden.
Andererseits habe ich hier im Forum auch eine Nachricht gefunden, dass er das Datum in der Zeitangabe ignoriert. Aber dann verstehe ich nicht, warum er mit Zeitangabe nicht funktioniert.

Für mich, wenn ich nicht einen Verständnisfehler habe, steht im ersten if (nach Übersetzung der Variablen) "wenn es zwischen 8:30 und 23:00 ist und die Helligkeit größer/gleich 100 ist". Es funktioniert aber nur, wenn ich das if verändere zu "wenn die Helligkeit größer/gleich 100 ist"


##### Update 16:12 #####
Klammerfehler. Die Zeitsteuerung mit indirekten Zeitangaben klappt schon, wenn man die Klammern richtig setzt. Nachdenken ist besser als kopieren ;-)
falsch:[[Steuerdaten_n:time_begin]]-[[Steuerdaten_n:time_end]]richtig:[[Steuerdaten_n:time_begin]-[Steuerdaten_n:time_end]]Die erste eckige Klammer leitet die Zeitfunktion ein und die zweite Klammer fragt das Device. So wie die Klammern vorher gesetzt waren, wurde von der Beginnzeit die Endzeit abgezogen, was kein verwertbares Ergebnis lieferte.
Gruß
Florian

Jail auf XigmaNAS (freeBSD); CCU2 mit CULv3, nanoCUL868 und JeeLink-Clone; div. FS20-Komponenten; andFHEM; div. hm- und hmip-Komponenten; div. IT+

Damian

#3
Dein Code funktioniert wohl für einen Aktor. Das nächste Ziel sollte sein, diese Automatik für mehrere Aktoren zu realisieren.

Ich mache es so ähnlich, allerdings nutze ich neben den Zeiten und der Sonneneinstrahlung auch die Raumtemperatur als Kriterium.

Dazu habe ich die Steuerung soweit generalisiert, dass sie für beliebige Aktoren funktioniert.

Hier die Lösung ohne GUI:

https://wiki.fhem.de/wiki/DOIF/Automatisierung#Beschattungssteuerung_abh%C3%A4ngig_von_der_Zimmertemperatur_und_Sonneneinstrahlung_f%C3%BCr_mehrere_Szenarien

Und das ist eine mit GUI:

https://wiki.fhem.de/wiki/DOIF/Automatisierung#Beschattungssteuerung_abh%C3%A4ngig_von_der_Zimmertemperatur_und_Sonneneinstrahlung_f%C3%BCr_mehrere_Szenarien_mit_Visualisierung

In beiden Fällen reicht eine Definitionszeile mit Parametern, um eine unabhängige Steuerung pro Fenster zu definieren. Das Besondere ist, dass hier sogenannte Templates genutzt wurden. Der Vorteil gegenüber einer Perl-Funktion ist, dass im Programmcode des Templates die Trigger automatisch gesetzt werden.


Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Per

Zitat von: Prof. Dr. Peter Henning am 17 Oktober 2025, 10:36:07Genau dieses, nämlich die schlichte Unwartbarkeit von solch komplexen DOIF-Strukturen, ist Grund genug, das in echte Perl-Programme in einer separaten Datei auszulagern.
Oder einfach Perl DOIF nehmen, ist auch echtes Perl, lässt sich aber noch einfacher warten und debuggen.
Hier habe ich nämlich Trigger und Variablen alle an einem Platz. Und die Syntaxprüfung gibt es obendrauf. Und ich muss keine extra Datei pflegen.