Making complex Rules reusable or simpler

Hi there,

If have complex sets of rules that repeat for each Room.
Probably I am making things way to complex :slight_smile:

I asked myself If there is an easier way to do things, especially for repeating tasks…
As an Example, If have Posted one Rule that manages the shading for 3 of my Rooms oriented to the south. This is only one Floor of 3.

My problem is if I want to change something for all rooms its a lot of effort. Mistakes are often made.

The comments an texts are a mixture of Geman and English. But I think it is obvious what I mean by complex if you look at my example. I put every floor in a Single rule as they all are triggert by azimuth change and i dont want so many rules to be triggert at the same time…

For a start it would help to somehow reuse the code for one shade for the others and just pass it the variables.

Feel free to criticise /help me or use my stuff. :slight_smile:

// Allgemeine Variablen
// ----------------------------------------------------------------------------------- 

var HeatingTolerance = 1 // Toleranz(Hysterese) für Entscheidung  Beschattung/Heizung Notwendig (in°C)
var SolarLightmax = 25500 // 2550 Is a good value for Summer maybe winter is different?



// ----------------------------------------------------------------------------------- 
// ----------------------------------------------------------------------------------- 
// Zimmer1 
// -----------------------------------------------------------------------------------
// Anpassbare Variablen
var DG_Zimmer1_EL_Start = 6              // Sonnenhöhe ab der Beschattung nötig wird
var DG_Zimmer1_AZ_Start = 70             // Azimuth (Sonnenstand)  ab der Beschattung nötig wird
var DG_Zimmer1_AZ_End = 230              // Azimuth (Sonnenstand)  ab der keine Beschattung mehr nötig ist
// Nicht anpassabare variablen
var DG_Zimmer1_SettempMinusTol = 0
var DG_Zimmer1_SettempPlusTol = 0
// ----------------------------------------------------------------------------------- 
// Zimmer2 
// -----------------------------------------------------------------------------------
// Anpassbare Variablen
var DG_Zimmer2_EL_Start = 6              // Sonnenhöhe ab der Beschattung nötig wird
var DG_Zimmer2_AZ_Start = 70             // Azimuth (Sonnenstand)  ab der Beschattung nötig wird
var DG_Zimmer2_AZ_End = 230              // Azimuth (Sonnenstand)  ab der keine Beschattung mehr nötig ist
// Nicht anpassabare variablen
var DG_Zimmer2_SettempMinusTol = 0
var DG_Zimmer2_SettempPlusTol = 0
// ----------------------------------------------------------------------------------- 
// Schlafzimmer Dachfenster 
// -----------------------------------------------------------------------------------
// Anpassbare Variablen
var DG_SLZ_DF_EL_Start = 6              // Sonnenhöhe ab der Beschattung nötig wird
var DG_SLZ_DF_AZ_Start = 70             // Azimuth (Sonnenstand)  ab der Beschattung nötig wird
var DG_SLZ_DF_AZ_End = 260              // Azimuth (Sonnenstand)  ab der keine Beschattung mehr nötig ist
// Nicht anpassabare variablen
var DG_Slz_SettempMinusTol = 0
var DG_Slz_SettempPlusTol = 0
// ----------------------------------------------------------------------------------- 
// Schlafzimmer Westfenster 
// -----------------------------------------------------------------------------------
// Anpassbare Variablen
var DG_SLZ_WF_EL_Start = 6              // Sonnenhöhe ab der Beschattung nötig wird
var DG_SLZ_WF_AZ_Start = 212             // Azimuth (Sonnenstand)  ab der Beschattung nötig wird
var DG_SLZ_WF_AZ_End = 260              // Azimuth (Sonnenstand)  ab der keine Beschattung mehr nötig ist



rule "DG_HeatingOrCooling"
when
    // For the user (convinience only)
    Item DG_Zimmer1_Beschattungsautomatik changed or
    Item DG_Zimmer2_Beschattungsautomatik changed or
    Item DG_Slz_Beschattungsautomatik changed or
    Item DG_Slz_SetTemp changed or 
    Item DG_Zimmer1_SetTemp changed or 
    Item DG_Zimmer2_SetTemp changed or 
    // Actual Trigger
    Item Azimuth changed
then
// Calculate Tolarances for all rooms
DG_Slz_SettempMinusTol     = DG_Slz_SetTemp.state as Number - HeatingTolerance
DG_Slz_SettempPlusTol     = DG_Slz_SetTemp.state as Number + HeatingTolerance
DG_Zimmer1_SettempMinusTol     = DG_Zimmer1_SetTemp.state as Number - HeatingTolerance
DG_Zimmer1_SettempPlusTol     = DG_Zimmer1_SetTemp.state as Number + HeatingTolerance
DG_Zimmer2_SettempMinusTol     = DG_Zimmer2_SetTemp.state as Number - HeatingTolerance
DG_Zimmer2_SettempPlusTol     = DG_Zimmer2_SetTemp.state as Number + HeatingTolerance


// ----------------------------------------------------------------------------------- 
// ----------------------------------------------------------------------------------- 
// Logik für Heizen und Kühlen durch Rafstor an Zimmer 1  
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
    //Here is determined if Solar Heating through the Windows would be a good idea
    // -----------------------------------------------------------------------------------
    if ((((IW_ForecastTodayMaxTemperature.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer1_SettempMinusTol)    &&       // Today's top temperature will be lower than the set Temp of the Thermostat
        ((DG_Zimmer1_ActTemp.state as Number ) < DG_Zimmer1_SetTemp.state as Number  )) 
    {
        if (((( Elevation.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_EL_Start) &&
            ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_AZ_Start) &&
            ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer1_AZ_End  )) 
        {
            if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Heizen durch öffnen der Verschattung macht momentan Sinn ')    }   
            if (( (DG_Zimmer1_Blind.state > 50 ) || (DG_Zimmer1_Blind.state == DOWN || (DG_Zimmer1_Blind.state == null))) &&
                (DG_Zimmer1_ShadeManuallyOperated.state != ON))
            {
                sendCommand(DG_Zimmer1_ShadeControl, 1) // Comand will trigger moving up of the shade in certain conditions
            }
            else
            {
                if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Kommando nicht gesendet weil Rolladen schon oben ist oder kürzlich von hand bewegt wurde') } 
            }
        }
        else
        {
            if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Heizen durch öffnen der Verschattung würde Sinn machen aber die Sonne steht zu tief ')    }   
        }
    }
    else{

     if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Heizen durch öffnen der Verschattung macht momentan KEINEN Sinn')}  
    }
    // Here s determined if Closing the Blinds mit be a good ideo to not overheat the room
    // -----------------------------------------------------------------------------------
    if ( 
        // First is Determined if the sun could actually shine in the window
        ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_AZ_Start) &&
        ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer1_AZ_End) &&
        ((( Elevation.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_EL_Start)        
        )
    {
        if (DG_Zimmer1_BeschattungDebugMode.state == ON) {  logInfo('Beschattung Zimmer1', 'Beschattung könnte sinvoll sein weil sonne einfallen könnte (Sonnenstand)') } 
        // Second is determined if there is actually solar radiation
        if ((Out_Weather_Lux2.state as Number ) > SolarLightmax)
        {
           if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Beschattung könnte sinvoll sein weil sonne Sonnenstrahlung Grenzwert übersteigt') } 
           // Third is determined if Temperature 
           if (((((IW_ForecastTodayMaxTemperature.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_SettempPlusTol) &&
           (DG_Zimmer1_ActTemp.state as Number  > DG_Zimmer1_SetTemp.state as Number  ))||
           (DG_Zimmer1_ActTemp.state as Number  > DG_Zimmer1_SettempPlusTol  ))
              {
                if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Beschattung ist sinvoll weil erwartete Aussentemperatur Solltemperatur übersteigt und Innentemperatur sollwert bereits überschreitet') } 
                    if (((DG_Zimmer1_Blind.state < 80 ) || (DG_Zimmer1_Blind.state == UP || (DG_Zimmer1_Blind.state === null))) &&
                        ((DG_Zimmer1_ShadeManuallyOperated.state != ON)||(DG_Zimmer1_ShadeManuallyOperated.state === null)))
                    {
                        sendCommand(DG_Zimmer1_ShadeControl, 10) // Comand will trigger moving down of the shade in certain conditions
                    }
                    else
                    {
                        if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Kommando nicht gesendet weil Rolladen schon unten ist oder kürzlich von hand bewegt wurde') } 
                    }
              }
           else
           {
            if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Beschattung ist Nicht sinvoll weil erwartete Aussentemperatur Solltemperatur nicht übersteigt oder innentemperatur sollwert nicht übersteigt') }               
           }
        }
        else
        {
        if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Beschattung ist nicht sinvoll weil der Grenzwert für Sonneneinstrahlung nicht überschritten wird') } 
        }
 
    }else
    {
    if (DG_Zimmer1_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer1', 'Beschattung ist blödsinn weil sonne nicht einfallen kann')} 
    }   

// ----------------------------------------------------------------------------------- 
// ----------------------------------------------------------------------------------- 
// Logik für Heizen und Kühlen durch Rafstor an Zimmer 2  
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
    //Here is determined if Solar Heating through the Windows would be a good idea
    // -----------------------------------------------------------------------------------
    if ((((IW_ForecastTodayMaxTemperature.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer2_SettempMinusTol)    &&       // Today's top temperature will be lower than the set Temp of the Thermostat
        ((DG_Zimmer2_ActTemp.state as Number ) < DG_Zimmer2_SetTemp.state as Number  )) 
    {
        if (((( Elevation.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer2_EL_Start)   &&
            ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer2_AZ_Start) &&
            ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer2_AZ_End  ))   
        {
            if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Heizen durch öffnen der Verschattung macht momentan Sinn ')    }   
            if (((DG_Zimmer2_Blind.state > 10 ) || (DG_Zimmer2_Blind.state == DOWN || (DG_Zimmer2_Blind.state === null))) &&
                ((DG_Zimmer2_ShadeManuallyOperated.state != ON)||(DG_Zimmer2_ShadeManuallyOperated.state === null)))
            {
                sendCommand(DG_Zimmer2_ShadeControl, 1) // Comand will trigger moving up of the shade in certain conditions
            }
            else
            {
                if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Kommando nicht gesendet weil Rolladen schon oben ist oder kürzlich von Hand bewegt wurde '+ DG_Zimmer2_ShadeManuallyOperated.state) } 
            }
        }
        else
        {
            if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Heizen durch öffnen der Verschattung würde Sinn machen aber die Sonne steht zu tief ')    }   
        }
    }
    else{

     if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Heizen durch öffnen der Verschattung macht momentan KEINEN Sinn')}  
    }
    // Here s determined if Closing the Blinds mit be a good ideo to not overheat the room
    // -----------------------------------------------------------------------------------
    if ( 
        // First is Determined if the sun could actually shine in the window
        ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer2_AZ_Start) &&
        ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer2_AZ_End) &&
        ((( Elevation.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer2_EL_Start)        
        )
    {
        if (DG_Zimmer2_BeschattungDebugMode.state == ON) {  logInfo('Beschattung Zimmer2', 'Beschattung könnte sinvoll sein weil sonne einfallen könnte (Sonnenstand)') } 
        // Second is determined if there is actually solar radiation
        if ((Out_Weather_Lux2.state as Number ) > SolarLightmax)
        {
           if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Beschattung könnte sinvoll sein weil sonne Sonnenstrahlung Grenzwert übersteigt') } 
           // Third is determined if Temperature 
           if (((((IW_ForecastTodayMaxTemperature.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer2_SettempPlusTol) &&
           (DG_Zimmer2_ActTemp.state as Number  > DG_Zimmer2_SetTemp.state as Number  ))||
           (DG_Zimmer2_ActTemp.state as Number  > DG_Zimmer2_SettempPlusTol  ))
              {
                if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Beschattung ist sinvoll weil erwartete Aussentemperatur Solltemperatur übersteigt und Innentemperatur sollwert bereits überschreitet') } 
                    if ( ( (DG_Zimmer2_Blind.state < 80) || (DG_Zimmer2_Blind.state == UP || (DG_Zimmer2_Blind.state === null)))  &&
                          (DG_Zimmer2_ShadeManuallyOperated.state != ON) )
                    {
                        sendCommand(DG_Zimmer2_ShadeControl, 10) // Comand will trigger moving down of the shade in certain conditions
                    }
                    else
                    {
                        if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Kommando nicht gesendet weil Rolladen schon unten ist oder kürzlich von Hand bewegt wurde '+ DG_Zimmer2_ShadeManuallyOperated.state +' Shade= ' + DG_Zimmer2_Blind.state ) } 
                    }
              }
           else
           {
            if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Beschattung ist Nicht sinvoll weil erwartete Aussentemperatur Solltemperatur nicht übersteigt oder innentemperatur sollwert nicht übersteigt') }               
           }
        }
        else
        {
        if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Beschattung ist nicht sinvoll weil der Grenzwert für Sonneneinstrahlung nicht überschritten wird') } 
        }
 
    }else
    {
    if (DG_Zimmer2_BeschattungDebugMode.state == ON) { logInfo('Beschattung Zimmer2', 'Beschattung ist blödsinn weil sonne nicht einfallen kann')} 
    }   


// ----------------------------------------------------------------------------------- 
// ----------------------------------------------------------------------------------- 
// Logik für Heizen und Kühlen durch Schlafzimmer Dachfenster 
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
    //Here is determined if Solar Heating through the Windows would be a good idea
    // -----------------------------------------------------------------------------------
    if ((((IW_ForecastTodayMaxTemperature.state as QuantityType<Number>).toBigDecimal ) < DG_Slz_SettempMinusTol)    &&       // Today's top temperature will be lower than the set Temp of the Thermostat
        ((DG_Slz_ActTemp.state as Number ) < DG_Slz_SettempPlusTol  )) 
    {
        if (((( Elevation.state as QuantityType<Number>).toBigDecimal ) > DG_SLZ_DF_EL_Start)&&
            ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_AZ_Start) &&
            ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) < DG_Zimmer1_AZ_End  ))      
        {
           if ( ((DG_Slz_Dachfenster_Roll.state > 10 ) || (DG_Slz_Dachfenster_Roll.state === null))  &&
                 ((DG_Slz_ShadeManuallyOperated.state != ON)||(DG_Slz_ShadeManuallyOperated.state === null)))
            {
                sendCommand(DG_Slz_ShadeControl, 1) // Comand will trigger moving down of the shade in certain conditions
            }
            else
            {
                if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung SLZ', 'Kommando nicht gesendet weil Rolladen schon oben ist oder kürzlich von Hand bewegt wurde') } 
            }
            if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Heizen durch öffnen der Verschattung macht momentan Sinn ')    }   
        }
        else
        {
            if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Heizen durch öffnen der Verschattung würde Sinn machen aber die Sonne steht zu tief ')    }   
        }
    }
    else{

     if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Heizen durch öffnen der Verschattung macht momentan KEINEN Sinn')}  
    }
    // Here s determined if Closing the Blinds mit be a good ideo to not overheat the room
    // -----------------------------------------------------------------------------------
    if ( 
        // First is Determined if the sun could actually shine in the window
        ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_SLZ_DF_AZ_Start) &&
        ((( Azimuth.state as QuantityType<Number>).toBigDecimal ) < DG_SLZ_DF_AZ_End) &&
        ((( Elevation.state as QuantityType<Number>).toBigDecimal ) > DG_SLZ_DF_EL_Start)        
        )
    {
        if (DG_Slz_BeschattungDebugMode.state == ON) {  logInfo('Beschattung Schlafzimmer', 'Beschattung könnte sinvoll sein weil sonne einfallen könnte (Sonnenstand)') } 
        // Second is determined if there is actually solar radiation
        if ((Out_Weather_Lux2.state as Number ) > 26000)
        {
           if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Beschattung könnte sinvoll sein weil sonne Sonnenstrahlung Grenzwert übersteigt') } 
           // Third is determined if Temperature 
           if (((((IW_ForecastTodayMaxTemperature.state as QuantityType<Number>).toBigDecimal ) > DG_Slz_SettempPlusTol) &&
           (DG_Slz_ActTemp.state as Number  > DG_Slz_SetTemp.state as Number  ))||
           (DG_Slz_ActTemp.state as Number  > DG_Slz_SettempPlusTol  ))
              {
                if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Beschattung ist sinvoll weil erwartete Aussentemperatur Solltemperatur übersteigt und Innentemperatur sollwert bereits überschreitet') } 
                    if ( (DG_Slz_Dachfenster_Roll.state < 80 )  &&
                        ((DG_Slz_ShadeManuallyOperated.state != ON) || (DG_Zimmer2_ShadeManuallyOperated.state === null)))
                    {
                        sendCommand(DG_Slz_ShadeControl, 10) // Comand will trigger moving down of the shade in certain conditions
                    }
                    else
                    {
                        if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Kommando nicht gesendet weil Rolladen schon unten ist oder kürzlich von Hand bewegt wurde') } 
                    }
              }
           else
           {
            if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Beschattung ist Nicht sinvoll weil erwartete Aussentemperatur Solltemperatur nicht übersteigt oder innentemperatur sollwert nicht übersteigt') }               
           }
        }
        else
        {
        if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Beschattung ist nicht sinvoll weil der Grenzwert für Sonneneinstrahlung nicht überschritten wird') } 
        }
 
    }else
    {
    if (DG_Slz_BeschattungDebugMode.state == ON) { logInfo('Beschattung Schlafzimmer', 'Beschattung ist blödsinn weil sonne nicht einfallen kann')} 
    }   

    //Check weather to open the Shades because sun can not enter again
    // Zimmer1            
    if (  (((Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer1_AZ_End) && 
          ( DG_Zimmer1_ShadeControl.state == 10 ) &&   // was automatically moved down
           (DG_Zimmer1_Beschattungsautomatik.state == ON) )
    {
         sendCommand(DG_Zimmer1_ShadeControl, 1) // Comand will trigger moving up of the shade in certain conditions   
         logInfo('Beschattung Zimmer1', 'Öffne weil sonne nicht mehr einfallen kann')   
    }

    // Zimmer2            
    if (  (((Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_Zimmer2_AZ_End) && 
          ( DG_Zimmer2_ShadeControl.state == 10 ) &&   // was automatically moved down
           (DG_Zimmer2_Beschattungsautomatik.state == ON) )
    {
         sendCommand(DG_Zimmer2_ShadeControl, 1) // Comand will trigger moving up of the shade in certain conditions   
         logInfo('Beschattung Zimmer2', 'Öffne weil sonne nicht mehr einfallen kann')   
    }

//    // SLZ            
//    if (  (((Azimuth.state as QuantityType<Number>).toBigDecimal ) > DG_SLZ_DF_AZ_End) && 
//          ( DG_Slz_ShadeControl.state == 10 ) &&   // was automatically moved down
//           (DG_SLZ_Beschattungsautomatik.state == ON) )
//    {
//         sendCommand(DG_Slz_ShadeControl, 1) // Comand will trigger moving up of the shade in certain conditions   
//         logInfo('Beschattung Schalfzimmer Dach', 'Öffne weil sonne nicht mehr einfallen kann')   
//    }



end


//Rolladen Komandoausführung DG Schlafzimmer
rule "DG_AutoShade  Roll Slz Command"
when 
    Item DG_Slz_ShadeControl     received command 
    // Ändern und in 3 Rules aufteilen oder revieved command auf 0 setzen (post update)
then
    if (DG_Slz_Beschattungsautomatik.state == ON) 
    {
        sendCommand(DG_SLZ_ShadeSupressManualDetection, ON)
        if (receivedCommand == 1)
        {
            sendCommand(DG_Slz_Dachfenster_Roll, UP)
        }
        if (receivedCommand == 10)
        {
            sendCommand(DG_Slz_Dachfenster_Roll, DOWN)
        }        
    }
end
//Rolladen Komandoausführung DG Schlafzimmer
rule "DG_AutoShade  Z1 Command"
when 
    Item DG_Zimmer1_ShadeControl received command
    // Ändern und in 3 Rules aufteilen oder revieved command auf 0 setzen (post update)
then
    if (DG_Zimmer1_Beschattungsautomatik.state == ON) 
    {
        postUpdate(DG_Zimmer1_ShadeSupressManualDetection, ON)
        if (receivedCommand == 1)
        {
            sendCommand(DG_Zimmer1_Blind, UP)
        }
        if (receivedCommand == 10)
        {
            sendCommand(DG_Zimmer1_Blind, DOWN)
        }        
    }
end

//Rolladen Komandoausführung DG Schlafzimmer
rule "DG_AutoShade Z2 Command"
when 
    Item DG_Zimmer2_ShadeControl received command
    // Ändern und in 3 Rules aufteilen oder revieved command auf 0 setzen (post update)
then
    if (DG_Zimmer2_Beschattungsautomatik.state == ON) 
    {
        postUpdate(DG_Zimmer2_ShadeSupressManualDetection, ON)
        if (receivedCommand == 1)
        {
            sendCommand(DG_Zimmer2_Blind, UP)
        }
        if (receivedCommand == 10)
        {
            sendCommand(DG_Zimmer2_Blind, DOWN)
        }        
    }
end

// ---------------------------------------------------------------------------------------------
// -------------Erkennung ob Rolladen von Hand bewegt wurde-------------------------------------
// ---------------------------------------------------------------------------------------------

rule "DetectManual Operation of Dachfenster"
when
    Item DG_Slz_Dachfenster_Roll  changed 
then
    if ((DG_Slz_Beschattungsautomatik.state == ON)          &&
        (DG_SLZ_ShadeSupressManualDetection.state != ON))
    {
                sendCommand(DG_Slz_ShadeManuallyOperated, ON)
    }
end 
rule "DetectManual Operation of Rafstor Zimmer1"
when
    Item DG_Zimmer1_Blind  changed
then
    if ((DG_Zimmer1_Beschattungsautomatik.state == ON)      &&
        (DG_Zimmer1_ShadeSupressManualDetection.state != ON)) 
    {
                sendCommand(DG_Zimmer1_ShadeManuallyOperated, ON)
    }
end

rule "DetectManual Operation of Rafstor Zimmer2"
when
    Item DG_Zimmer2_Blind  changed
then
    if ((DG_Zimmer2_Beschattungsautomatik.state == ON)      &&
        (DG_Zimmer2_ShadeSupressManualDetection.state != ON)) 
    {
                sendCommand(DG_Zimmer2_ShadeManuallyOperated, ON)
    }
end


//Ab Hier
// Präsensabhänige Funktionen und Manipulation der Beschattung bzw Automatik
//          1) Lammellen Öffnen wenn Raum betreten wird und Schließen wenn raum Verlassen wird nach Nachlaufzeit. 
//          2) Manuelle Unterdrückung der Automatik vorzeitig beenden wenn Raum Verlassen wurde nach Nachlaufzeit
// Gesteuert über Langzeitpräsens so das eine Bewegung nur max 1mal wenn der Raum für 15 min verlassen wurde ausgeführt wird.

rule "LamellenZ1"
when
    Item DG_Zimmer1_Presence_Long changed
then
    if(DG_Zimmer1_Presence_Long.state == ON){
        // Raum Betreten --> Lamellen auf
        if ((DG_Zimmer1_Beschattungsautomatik.state == ON)      &&
             (DG_Zimmer1_LamelleAufAuto.state == ON) &&
            ((DG_Zimmer1_Blind.state > 80))&&
             (DG_Zimmer1_Lamella.state >74)) 
        {
            sendCommand(DG_Zimmer1_Lamella, DG_Zimmer1_OpeningWidthOnEnter.state as Number) //
        }
    }
    if(DG_Zimmer1_Presence_Long.state == OFF){
        // Raum Verlassen --> Lamellen Zu
        if ((DG_Zimmer1_Beschattungsautomatik.state == ON)      &&
             (DG_Zimmer1_LamelleZuAuto.state == ON) &&
            ((DG_Zimmer1_Blind.state > 80))) 
        {
            sendCommand(DG_Zimmer1_Lamella, 90)
        }
        // Raum Verlassen --> Manuelle Unterdrückung der Automatikfunktion aus
        sendCommand(DG_Zimmer1_ShdaeManuallyOperated, OFF)

    }
end

rule "LamellenoeffnungZ2"
when
    Item DG_Zimmer2_Presence_Long changed
then
    if(DG_Zimmer2_Presence_Long.state == ON){
        // Raum Betreten --> Lamellen auf
        if ((DG_Zimmer2_Beschattungsautomatik.state == ON)      &&
             (DG_Zimmer1_LamelleAufAuto.state == ON) &&
            ((DG_Zimmer2_Blind.state > 80))                     &&
             (DG_Zimmer2_Lamella.state >74)) 
        {
            sendCommand(DG_Zimmer2_Lamella, DG_Zimmer2_OpeningWidthOnEnter.state as Number)
        }
    }
   if(DG_Zimmer2_Presence_Long.state == OFF){
        // Raum Verlassen --> Lamellen Zu
        if ((DG_Zimmer2_Beschattungsautomatik.state == ON)      &&
             (DG_Zimmer2_LamelleZuAuto.state == ON) &&
            ((DG_Zimmer2_Blind.state > 80))) 
        {
            sendCommand(DG_Zimmer2_Lamella, 90)
        }
        // Raum Verlassen --> Manuelle Unterdrückung der Automatikfunktion aus
        sendCommand(DG_Zimmer2_ShadeManuallyOperated, OFF)
    }
end

I would say in general for rules dsl it is easier to copy paste rules. It is not that easy to write generic functions in dsl and readability is quite poor. If you are looking into reusability I would look into some of the other rules engines such as python/habapp or js. If you are proficient in java you can take a look at jrule, which I’m currently maintaining.

Best regards s

1 Like

Hey Seaside,
Thanks for the reply. I did some basic stuff in java but i would not consider myself proficient.
Never the less I will have a look at all of the options you mentioned

1 Like

I would use arrays or maps to store your room based variables, e.g. var EL_Start = [6,6,6,6] and also store the names of items you want to change in same format, so that you can dynamically select the item you want to send a command to.

At the beginning of your rule you only need to check which room you want to update (e.g. based on triggering item name) and access the array by the relevant index or use the room name as key within a map.

There are a number of design pattern tutorials that should help:

Here are some thoughts:

  1. If your concern is running a bunch of rules all at the same time, instead of triggering them all based on every Azimuth change, create a single rule that monitors the Azimuth that then sets a flag (String Item like in Time of Day, Switch Items, you can even have that rule call other rules directly if using other rules languages). Then you can trigger your rules only at the times where the Azimuth has already been shown to be right.
    For a very simple example, I have an isCloudy Item which is set to ON when the cloudiness is above 50% and set to OFF when it’s below 50%. That Switch then triggers other rules or is checked by other rules to see if they should do something different based on cloudiness. If I decide I want to only worry if it’s 75% cloudy, I only need change that one rule.
    This will reduce how many rules you have running all the time and let you separate and consolidate the code that needs to decide whether the conditions are such that something should happen.
rule "Azimuth state machine"
when
    Item Elevation changed
then
    var newState = "STAY"
    switch(Azmuth.state) {
        case 6|°: newState = "EL_START"
        case 70|°: newState = "ZIMMER_AZ_START"
        case 230|°: newState = "ZIMMER_AZ_END"
        case 260|°: newState = "DF_AZ_END"
    }
    if(newState != "STAY" && Azumth_State.state != newState) {
        Azmuth_State.sendCommand(newState)
    }
end

Now you can trigger your rule using changes to Azmuth_State which will only change when it transitions from one state to another instead of every angle increment. And your conditions inside the rule become as simple as if(Azumth_State.state == "EL_START") instead of needing to check the raw numbers and check if the angle is between two states and the like.
NOTE: the states I listed are almost certainly not what you’d want, they are just for illustration purposes. I’m also assuming that Astro reports the Azmuth in whole number integer degrees. If it doesn’t you’ll need to use if statements to see if the Azmuth is greater than your threshold instead of the switch.

  1. Pay close attention to How to Structure a Rule (link above). This rule seems to follow a pattern that that design pattern was made for: a series of if/else statements that command the same Items in slightly different ways. Instead of repeating that over and over, set some variables to a default and your if/else if statements will modify those variables as necessary. Only at the end do you actually command the Item.
    This will greatly reduce the number of lines of code and consolidates where the commands are already sent which will make the rules easier to maintain.

  2. Long if conditions are hard to maintain. However, often you can greatly simplify things if you turn the conditions around. For example, in your “LamelleneffnugZ2” rule, if DG_Zimmer2_Beschattungsautomatik or DG_Zimmer1_LamelleAufAuto are OFF or DG_Zimmer2_Blind < 80, the rule does nothing. So put that test up front and the rule becomes:

then
    if(DG_Zimmer2_Beschattungsautomatik.state != ON 
       || DG_Zimmer1_LamelleAufAuto.state != ON
       || DG_Zimmer2_Blind <= 80) {
        // Nothing to do, exit
        return;
    }

    if(DG_Zimmer2_Presence_Long.state == ON && DG_Zimmer2_Lamella.state >74){
        DG_Zimmer2_Lamella.sendCommand(DG_Zimmer2_OpeningWidthOnEnter.state)
    }
    else {
        DG_Zimmer2_Lamella.sendCommand(90)
        DG_Zimmer2_ShadeManuallyOperated.sendCommand(OFF)
    }
end
  1. You will notice some differences in coding style between my version above and your version. Coding style can go a long way towards making code easier to read. For example, you’ve a lot of extraneous ( ) around your conditions that are simply not needed. It would be like writing math with parens around every number: (2) + (3) + (4). That’s not so bad when you’ve got a simple equation but it really gets in the way when things get more complex. See Coding Conventions in Rules for some additional advice.

  2. Some minor complexity is added because you use the sendCommand action instead of the method on the Item. When you use the action, the command sent needs to be a String or something Rules DSL can figure out how to convert to a String. When you use the method, it is much better at handling conversions for you which will let you eliminate a bunch of the as Number type statements.

  3. Use Associated Items to create one rule that can handle all of your “DetectManual Operation” rules. For example:

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "Detect Manual Operation"
when
    Member of Zimmer changed
then
    val beschattungsautomatic = ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName.replace('Blind', 'Beschattungsautomatik')
    val shadeSupressManualDetection = ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName.replace('Blind', 'ShadeSupressManualDetection')
    if(beschattungsautomatic.state == ON && shadeSupressMaunalDetection.state != ON) {
        sendCommand(triggeringItemName.replace('Blind', 'ShadeManuallyOperated'), ON)
    }
end

Put all your Blind Items into that Zimmer Group and this one rule will replace all the rest that you have in place for your manual detection across all your floors. Use the same approach and you can also consolidate your Lamellen rules too. It’s much much easier to maintain code that doesn’t get repeated all over the place. And as you can see, the one rule to replace them all isn’t really any more complicated than the originals.
If you get clever with your naming conventions, you might even be able to replace all your DetectManual rules with just a single rule. You just need to figure out how to identify the parts of the triggering Item’s name that is the same for all the associated Items and how to replace the rest with what’s different.
The same approach can be used to replace all the DG_AutoShade rules with one rule for all.

rule "AutoShade Command"
when
    Member of ShadeControl received command
then
    val beschattungsautomatik = ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName.replace('ShadeControl', 'Beschattungsautomatik')

    if(beschattungsautomatik != ON) {
        return;
    }

    sendCommand(triggeringItemName.replace('ShadeControl', 'ShadeSupressManualDetection'), ON)
    sendCommand(triggeringItemName.replace('ShadeControl', 'Dachfenster_Roll'), 
                                           if(receivedCommand == 1) UP else DOWN)
end

The code above would replace those three rules in the above plus any similar rules for your other floors.
Notice the use of the ternary operator to simplify the if/else and this is another example of exiting immediately if there’s nothing to do.

  1. Don’t fight against QuantityTypes. The code is easier to read and will actually work closer to what you want if you use them . Make DG_Zimmer1_El_Start an Angle using var DG_Zimmer1_EL_Start = 6 | °. I’ve shown this above in 1 also.

  2. If you put your constants into Items, not only can you use Associated Items to access them, you can adjust them through your UI. Just make sure they have restoreOnStartup.

  3. Pulling it all together:

import org.eclipse.smarthome.model.script.ScriptServiceUtil

val heatingTolerance = 1|°C // Toleranz(Hysterese) für Entscheidung  Beschattung/Heizung Notwendig (in°C)
val solarLightmax = 25500 // 2550 Is a good value for Summer maybe winter is different?
// If this is an Item you can create a rule that triggers on changes of the season to adjust it

rule "DG_HeatingOrCooling"
when
    Member of Beschattungautomatik changed or
    Member of SetTemp changed or
    Azimuth_State changed
then
    // Get the associated Items
    val rollorShutterNameParts = triggeringItemName.split('_')
    val rollorShutterName = rollorShutterNameParts.get(0) + '_' + rollorShutterNameParts.get(1)
    val setTemp = SetTemp.members.findFrist[ i | i.name = rollorShutterName+'_SetTemp'] // if you have a Group you don't need ScriptServiceUtil
    val actTemp = ScriptServiceUtil.getItemRegistry.getItem(rollorShutterName+'_ActTemp')
    val start = ScriptServiceUtil.getItemRegistry.getItem(rollorShutterName+'_Active') // holds a String matching one of the state of Azimuth_Start
    val blind = ScriptServiceUtil.getItemRegistry.getItem(rollorShutterName+'_Blind')
    val manuallyOperated = ScriptServiceUtil.getItemRegistry.getItem(rollorShutterName+'_ShadeManuallyOperated')
    val shadeControl = ScriptServiceUtil.getItemRegistry.getItem(rollorShutterName+'_ShadeControl')
    val beschattungsautomatik = ScriptServiceUtil.getItemRegistry.getItem(rollorShutterName+'_Beschattungsautomatik')
    
    // Calculate the Tolerances
    val setTempMinusTol = setTemp.state - heatingTolerance // assumes SetTemp is Number:Temperature
    val setTempPlusTol = setTemp.state + heatingTolerance

    // Check whether to open the Shades because sun cannot enter again
    if(AzimuthState.state != start && shadeControl.state == 10 && beschattungsautomatik.state == ON) {
        shadeControl.sendCommand(1)
        logInfo(rollorShutterName, 'Öffne weil sonne nicht mehr einfallen kann')
        return;
    }
    // Exit if there's nothing to do
    else if(AzimuthState.state != start) {
        logDebug(rollorShutterName, 'Nothing to do)
        return;
    }

    //Here is determined if Solar Heating through the Windows would be a good idea
    // -----------------------------------------------------------------------------------
    if(IW_ForecastTodayMaxTemperature.state < setTempMinusTol
       && actTemp.state < setTemp.state) {
        // Logging levels even for rules can be adjusted from the Karaf Console or log4j2.xml
        // change this to a logDebug and eliminate the DebugMode Item for further simplifications
        logDebug(rollorShutterName, 'Heizen durch öffnen der Verschattung macht momentan Sinn ')
            
        // I'm pretty certain a Dimmer Item can never have the state DOWN
        // The state can never be null, I think you mean NULL or UNDEF here
        if((blind.state > 50 || blind.state == NULL || blind.state == UNDEF)
           && shadeManuallyOperated.state != ON) {
            shadeControl.sendCommand(1)
        }
        else{
            logDebug(rollorShutterName, 'Kommando nicht gesendet weil Rolladen schon oben ist oder kürzlich von hand bewegt wurde')
        }
    }
    else {
        logDebug(rollorShutterName, 'Heizen durch öffnen der Verschattung macht momentan KEINEN Sinn')
    }

    // Here s determined if Closing the Blinds mit be a good ideo to not overheat the room
    // -----------------------------------------------------------------------------------
    logDebug(rollorShutterName, 'Beschattung könnte sinvoll sein weil sonne einfallen könnte (Sonnenstand)')
    // Determine if there is actually solar radiation, we already know the sun is in the right position
    // or else the rule would never get to this point.
    if(Out_Weather_Lux2.state > solarLightMax)) {
        logDebug(rollorShutterName, 'Beschattung könnte sinvoll sein weil sonne Sonnenstrahlung Grenzwert übersteigt')
        // determin if temperature is too hot
        // Use grouping and spacing of clauses to help explain what clauses go with which
        if((IW_ForecastTodayMaxTemperature.state > setTempPlusTol && actTemp.state > setTemp)
           || actTemp.state > setTempPlusTol) {
           logDebug(rollorShutterName, 'Beschattung ist sinvoll weil erwartete Aussentemperatur Solltemperatur übersteigt und Innentemperatur sollwert bereits überschreitet')
            if((blind.state < 80 || blind.state == NULL || blind.state === UNDEF)
               && (shadeManuallyOperated.state != ON || shadeManuallyOperated.state == NULL || shadeManuallyOperated.state == UNDEF)) {
                shadeControl.sendCommand(10)
            }
            else {
                logDebug(rollorShutterName, 'Kommando nicht gesendet weil Rolladen schon unten ist oder kürzlich von hand bewegt wurde')
            }
        }
        else {
            logInfo(rollorShutterName, 'Beschattung ist Nicht sinvoll weil erwartete Aussentemperatur Solltemperatur nicht übersteigt oder innentemperatur sollwert nicht übersteigt')
        }
    }
    // I'm not entirely certain the else log statements match up with the if statement any more
    else {
        logDebug(rollorShutterName, 'Beschattung ist blödsinn weil sonne nicht einfallen kann')
    }
end

Obviously I just typed in the above and there are likely many errors and typos. But it should be enough to illustrate how to go about creating reusable rules and how to simplify rules. I believe, using the approaches above, you can create just one of each of the rule types to manage all of your rollorshutters. You should not be copying and pasting and editing the same code over and over again.

In other rules languages, some of the above will be even simpler. For example, you don’t need to pull Items from the registry manually like that when you only have the Item’s name which will cut a bunch of that up front stuff.

1 Like

Sorry for not answering sooner, i was cought up in some work…

I really appreciate your effort. Thanks! that gives me a lot to think about!!!
It will take some time - to process and understand and act.

If i take the time to rewrite my rules would it be wise to change to something like Js scripting?