Time based shutter control

Hello,

I’ve spent a lot of time searching for a time based shutter control, although I found bits and pieces I made a code myself - with half success. As there are no sensors to know at which positions my shutters are (and I can’t mount a rotary encoder on them) the only thing I can use is “time” to track the positions of the shutters.
I’m using currently Openhab 1.8, I’ve tried 2.0 but there none of my rules (code) work.

A short summary how my system is made.
Raspberry PI connected to two Arduinos.
One Arduino is for inputs (eg. tracks which switch was pressed and for how long). Returns a value to openhab:

  • when shutter switch pressed: s-3-1
  • when shutter switch released s-3-0-542223

s stands for shutter, I have thermostats (t) and switches (b), that’s why I differentiate between them.
-3- stand for the button which is pressed
1 or 0 if it’s pressed or released
542223 is the milliseconds of how long that button was pressed, with this I can calculate a percentage (basically the position of my shutter)

The other Arduino controls the outputs (eg. Relays).

I can control my shutter from my tablet, or by pressing a switch. The position is displayed in percentage.

My code:
It might seem long, basically there are 7 shutters, so the code is repeated for each. Functions could simplify it, but I did not have time to look it up, how to make it.

import java.util.concurrent.locks.ReentrantLock
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.binding.exec.*
import org.joda.time.DateTime
import org.openhab.core.library.types.*
import org.joda.time.*
import org.openhab.model.script.actions.Timer
import java.util.List
import java.util.ArrayList
import java.math
import java.util.Date

var java.util.concurrent.locks.ReentrantLock lock  = new java.util.concurrent.locks.ReentrantLock()
val List<Integer> shutterOldState = newArrayList(0,0,0,0,0,0,0)
var Integer presstime = 0
var Integer pinID = 0
var Integer onoff = 0
var Number[] SHUTTER_FULL_UP_TIME = newArrayList(20000, 20000, 20000, 20000, 20000, 20000, 20000)
var Number[] SHUTTER_FULL_DOWN_TIME = newArrayList(20000, 20000, 20000, 20000, 20000, 20000, 20000)
var Timer timer0 = null
var Timer timer10 = null
var Timer timer2 = null
var Timer timer30 = null
var Timer timer4 = null
var Timer timer50 = null
var Timer timer6 = null
var boolean run = true
val List<boolean> shutw = newArrayList(true, true, true, true, true, true, true)


rule "Arduino input handler" // what this does is it breaks up the "s-3-0-542223" code
when   
          Item Arduino_input changed 
     then  
     
var String[] check = Arduino_input.state.toString.split("-")
if (check.get(0) == "b")
		{
     	pinID = new Integer (check.get(1))
		val String itemName = "LightSwitch" + pinID
		Lights?.allMembers.filter(s | s.name.equals(itemName)).forEach[item|
			if(item.state==OFF) postUpdate(itemName, "ON")
			else postUpdate(itemName, "OFF")] 	
		}

if (check.get(0) == "t")
		{
     	pinID = new Integer (check.get(1))
		val String itemName = "Thermostat" + pinID
		val String itemValue = check.get(2)
		Thermostat?.allMembers.filter(s | s.name.equals(itemName)).forEach[item|
			if(itemValue=="1") postUpdate(itemName, "OFF")
			else postUpdate(itemName, "ON")] 	
		}
      	  
if (check.get(0) == "s") //while running from web, functions can not be executed
		{
		run = false //will not enter the shutter run program
		pinID = new Integer (check.get(1))
		onoff = new Integer (check.get(2))
		if (onoff == 0) presstime = new Integer (check.get(3))
			else presstime = 0
		
		if (pinID <= 9/*  && shutterOldState.get(pinID-3) != 0*/)
			{
      	  		 //aka shutter in living room button to move up
      	    if (onoff == 1)	sendCommand (Arduino ,pinID + "-1s") //starts moving up
    	  		else 
    	  		{
    	  		sendCommand (Arduino ,pinID + "-0s")
    	  		run = true //stops
    	  		
    	  		}
			}
      	  
		if (pinID >= 10 /*&& shutterOldState.get(pinID-10) != 100*/)
			{
      	  		//aka shutter in living room button to move down
      	  	if (onoff == 1)	sendCommand (Arduino ,pinID + "-1s") //starts moving down
    	  		else 
    	  		{
    	  		sendCommand (Arduino ,pinID + "-0s")
    	  		run = true //stops
    	  		
    	  		}	
			}
//	
		check = null	
      	
// below code calculates the percentage (position) of the shutter, when the command was received from a switch

		if (pinID < 10) shutterOldState.add(pinID-3, Math::round((shutterOldState.get(pinID-3) - presstime / SHUTTER_FULL_UP_TIME.get(pinID-3) * 100).intValue))
			else if (pinID >= 10) shutterOldState.add(pinID - 10, Math::round((shutterOldState.get(pinID-10) + presstime / SHUTTER_FULL_DOWN_TIME.get(pinID-10) * 100).intValue))
if (pinID < 10)
		if (shutterOldState.get(pinID-3) <= 0) shutterOldState.add(pinID-3, 0)
if (pinID >= 10) 
		if (shutterOldState.get(pinID-10) >= 100) shutterOldState.add(pinID-10, 100)
		if (onoff == 0) {
				val String itemName2 = "Shutter_" + (if (pinID<10) pinID.intValue else if (pinID>=10) (pinID-7).intValue)
				gShutter?.allMembers.filter(s | s.name.equals(itemName2)).forEach[item|
				postUpdate(itemName2, (shutterOldState.get(if (pinID<10) pinID-3 else if (pinID>=10) pinID-10)).toString)] 
		}
	  		
}    	 
end


rule "Shutter Living Room 3"       
when
	Item Shutter_3 received command
	then
	if (run) {
		
		lock.lock() try { 
			
	if 	(timer0 ==null) {

                
      	if ((shutterOldState.get(0).intValue - (Shutter_3.state as DecimalType).intValue) > 0 && shutw.get(0)) {
      		val upMin = ((shutterOldState.get(0) - (Shutter_3.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(0) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"3-1s") //goes up
      		shutw.add(0, false)
        	timer0 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"3-0s")
        		timer0 = null
        			shutw.add(0, true)

      		 ]
		}
		  
		 if ((shutterOldState.get(0).intValue - (Shutter_3.state as DecimalType).intValue) < 0 && shutw.get(0)) {
      		val dnMin = (((Shutter_3.state as DecimalType).intValue - shutterOldState.get(0))* SHUTTER_FULL_DOWN_TIME.get(0) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"10-1s") //goes down
      		shutw.add(0, false)
       		timer0 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"10-0s")
       			timer0 = null
       				shutw.add(0, true)
       		 ]
		}
        
      	shutterOldState.add(0, (Shutter_3.state as DecimalType).intValue)
		}  
		
		else  { postUpdate(Shutter_3, shutterOldState.get(0).intValue) shutw.add(0, false)}
		
	} finally { lock.unlock() }

}

end	

rule "Shutter Living Room 4"       
when
	Item Shutter_4 received command
	then
	if (run) {
		
		lock.lock() try { 
				
		if 	(timer10 ==null) {
	
        
      	if ((shutterOldState.get(1).intValue - (Shutter_4.state as DecimalType).intValue) > 0 && shutw.get(1)) {
      		val upMin = ((shutterOldState.get(1) - (Shutter_4.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(1) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"4-1s") //goes up
      		shutw.add(1, false)
        	timer10 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"4-0s")
        		timer10 = null
        		shutw.add(1, true)
      		 ]
		}
		  
		 if ((shutterOldState.get(1).intValue - (Shutter_4.state as DecimalType).intValue) < 0 && shutw.get(1)) {
      		val dnMin = (((Shutter_4.state as DecimalType).intValue - shutterOldState.get(1))* SHUTTER_FULL_DOWN_TIME.get(1) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"11-1s") //goes down
      		shutw.add(1, false)
       		timer10 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"11-0s")
       			timer10 = null
       			shutw.add(1, true)
       		 ]
		}
        
    	 shutterOldState.add(1, (Shutter_4.state as DecimalType).intValue)
		}   
		else  {	postUpdate(Shutter_4, shutterOldState.get(1).intValue) shutw.add(1, false)}

	} finally { lock.unlock() }
}

end	

rule "Shutter Living Room 5"       
when
	Item Shutter_5 received command
	then
	if (run) {
		
		lock.lock() try { 
						
		if 	(timer2 ==null) {

        
      	if ((shutterOldState.get(2).intValue - (Shutter_5.state as DecimalType).intValue) > 0 && shutw.get(2)) {
      		val upMin = ((shutterOldState.get(2) - (Shutter_5.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(2) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"5-1s") //goes up
      		shutw.add(2, false)
        	timer2 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"5-0s")
        		timer2 = null
        		shutw.add(2, true)
      		 ]
		}
		  
		 if ((shutterOldState.get(2).intValue - (Shutter_5.state as DecimalType).intValue) < 0 && shutw.get(2)) {
      		val dnMin = (((Shutter_5.state as DecimalType).intValue - shutterOldState.get(2))* SHUTTER_FULL_DOWN_TIME.get(2) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"12-1s") //goes down
      		shutw.add(2, false)
       		timer2 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"12-0s")
       			timer2 = null
       			shutw.add(2, true)
       		 ]
		}
        
     shutterOldState.add(2, (Shutter_5.state as DecimalType).intValue)
     
		}     
  		else  { postUpdate(Shutter_5, shutterOldState.get(2).intValue) shutw.add(2, false) }

	} finally { lock.unlock() }
  }
 
end


rule "Shutter Living Room 6"       
when
	Item Shutter_6 received command
	then
	if (run) {
		
		lock.lock() try { 
							
		if 	(timer30 ==null) {
	
        
      	if ((shutterOldState.get(3).intValue - (Shutter_6.state as DecimalType).intValue) > 0 && shutw.get(3)) {
      		val upMin = ((shutterOldState.get(3) - (Shutter_6.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(3) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"6-1s") //goes up
      		shutw.add(3, false)
        	timer30 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"6-0s")
        		timer30 = null
        		shutw.add(3, true)
      		 ]
		}
		  
		 if ((shutterOldState.get(3).intValue - (Shutter_6.state as DecimalType).intValue) < 0 && shutw.get(3)) {
      		val dnMin = (((Shutter_6.state as DecimalType).intValue - shutterOldState.get(3))* SHUTTER_FULL_DOWN_TIME.get(3) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"14-1s") //goes down
      		shutw.add(3, false)
       		timer30 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"14-0s")
       			timer30 = null
       			shutw.add(3, true)
       		 ]
		}
        
        shutterOldState.add(3, (Shutter_6.state as DecimalType).intValue)	
		}     
  		else  { postUpdate(Shutter_6, shutterOldState.get(3).intValue) shutw.add(3, false) }
  	
	} finally { lock.unlock() }
  }
  
end


rule "Shutter Living Room 7"       
when
	Item Shutter_7 received command
	then
	if (run) {
		
		lock.lock() try { 
				
		if 	(timer4 ==null) {

        
      	if ((shutterOldState.get(4).intValue - (Shutter_7.state as DecimalType).intValue) > 0 && shutw.get(4)) {
      		val upMin = ((shutterOldState.get(4) - (Shutter_7.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(4) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"7-1s") //goes up
      		shutw.add(4, false)
        	timer4 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"7-0s")
        		timer4 = null
        		shutw.add(4, true)
      		 ]
		}
		  
		 if ((shutterOldState.get(4).intValue - (Shutter_7.state as DecimalType).intValue) < 0 && shutw.get(4)) {
      		val dnMin = (((Shutter_7.state as DecimalType).intValue - shutterOldState.get(4))* SHUTTER_FULL_DOWN_TIME.get(4) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"15-1s") //goes down
      		shutw.add(4, false)
       		timer4 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"15-0s")
       			timer4 = null
       			shutw.add(4, true)
       		 ]
		}
        
     shutterOldState.add(4, (Shutter_7.state as DecimalType).intValue)
		}     
  		else  { postUpdate(Shutter_7, shutterOldState.get(4).intValue) shutw.add(4, false) }
 	} finally { lock.unlock() }
  }
  
end

 		
rule "Shutter Living Room 8"       
when
	Item Shutter_8 received command
	then
	if (run) {
		
		lock.lock() try { 
				
		if 	(timer50 ==null) {

        
      	if ((shutterOldState.get(5).intValue - (Shutter_8.state as DecimalType).intValue) > 0 && shutw.get(5)) {
      		val upMin = ((shutterOldState.get(5) - (Shutter_8.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(5) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"8-1s") //goes up
      		shutw.add(5, false)
        	timer50 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"8-0s")
        		timer50 = null
        		shutw.add(5, true)
      		 ]
		}
		  
		 if ((shutterOldState.get(5).intValue - (Shutter_8.state as DecimalType).intValue) < 0 && shutw.get(5)) {
      		val dnMin = (((Shutter_8.state as DecimalType).intValue - shutterOldState.get(5))* SHUTTER_FULL_DOWN_TIME.get(5) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"16-1s") //goes down
      		shutw.add(5, false)
       		timer50 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"16-0s")
       			timer50 = null
       			shutw.add(5, true)
       		 ]
		}
        
     shutterOldState.add(5, (Shutter_8.state as DecimalType).intValue)
		}     
  		else  { postUpdate(Shutter_8, shutterOldState.get(5).intValue) shutw.add(5, false) }
  		
	} finally { lock.unlock() }
  }
  
end
  	
  	
rule "Shutter Living Room 9"       
when
	Item Shutter_9 received command
	then
	if (run) {
		
		lock.lock() try { 
				
		if 	(timer6 ==null) {
    
        
      	if ((shutterOldState.get(6).intValue - (Shutter_9.state as DecimalType).intValue) > 0 && shutw.get(6)) {
      		val upMin = ((shutterOldState.get(6) - (Shutter_9.state as DecimalType).intValue)* SHUTTER_FULL_UP_TIME.get(6) / 100).intValue
      		println(upMin)
      		sendCommand (Arduino ,"9-1s") //goes up
      		shutw.add(6, false)
        	timer6 = createTimer(now.plusMillis(upMin)) [|
        		sendCommand (Arduino ,"9-0s")
        		timer6 = null
        		shutw.add(6, true)
      		 ]
		}
		  
		 if ((shutterOldState.get(6).intValue - (Shutter_9.state as DecimalType).intValue) < 0 && shutw.get(6)) {
      		val dnMin = (((Shutter_9.state as DecimalType).intValue - shutterOldState.get(6))* SHUTTER_FULL_DOWN_TIME.get(6) / 100).intValue
      		println(dnMin)
      		sendCommand (Arduino ,"17-1s") //goes down
      		shutw.add(6, false)
       		timer6 = createTimer(now.plusMillis(dnMin)) [|
        		sendCommand (Arduino ,"17-0s")
       			timer6 = null
       			shutw.add(6, true)
       		 ]
		}
        
     shutterOldState.add(6, (Shutter_9.state as DecimalType).intValue)
		}
  		else  { postUpdate(Shutter_9, shutterOldState.get(6).intValue) shutw.add(6, false) }
	} finally { lock.unlock() }
  }
  
end


And now comes my problem, which I could not resolve and I’d like to ask for your help.
The problem I face is “simple”. My code works, but not for every shutter rule… and I do not get it why. It’s the same code with different variables.

Ex:

2017-05-07 07:43:06 - Shutter_3 received command 53
2017-05-07 07:43:09 - Arduino received command 10-1s
2017-05-07 07:43:20 - Arduino received command 10-0s

…works fine…

2017-05-07 07:44:03 - Shutter_4 received command 38

…does nothing…

If someone has some time to give me a hint how to make this with a function, to simplify my code, it would be cool.

Thanks in advance!

What specifically are your problems? There are a few differences (i.e. you no longer import or reference org.openhab.*) but overall they should work as is. Have you looked at the Migration Tutorial?

This code seems overly complicated for what it does. I indeed, you should use lambdas to avoid the repeated code. Beyond that I have the following recommendations:

  • Switch statements can bring down your line count and make the code a little less complicated looking.
  • Don’t be afraid to use lambdas to break up some of your rules. In particular your Arduino Input rule.
  • If you reuse a value in all of your if statements, calculate it up front instead of repeating it in each statement (e.g.g pinID in Arduino input handler
  • Consider creating a lock for each shutter rather than reusing the same lock for all shutters. You could be facing a race condition or lockout.
  • Use a hashmap and the name of the Item to store your locks and Timers.
  • Consider just using Thread::sleep instead of Timers.
  • Add a catch. I’ve seen cases where an exception thrown in a rule, despite what is supposed to happen, ends up skipping the finally so your lock never gets unlocked.
  • Consider using persistence instead of a local ArrayList to store and access the old states.
  • Consider storing your shutter times in an Item instead of the global ArrayList, allowing you to fine tune it through a sitemap rather than through code

Your code might end up looking something like the following which, given the fewer lines and the more broken up nature of it might result in revealing the source of your problem. NOTE: I’m just typing this in. It likely contains errors.

import org.eclipse.xtext.xbase.lib.Functions
import java.util.ReentrantLock
import java.util.Map
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.model.script.actions.Timer

var Boolean run = true
Map<String, Integer> shutterOldState = newHashMap
Map<String, ReentrantLock> locks = newHashMap
Map<String, Timer> timers = newHashMap
Map<String, Boolean> shutw = newHashMap

val Functions$Function1<String, Boolean> processLights = [ button |
    Lights.allMembers.filter[s|s.name == "LightSwitch"+button].forEach[light |
        light.postUpdate(if(item.state == ON) OFF else ON)
    ]
    true
]

val Functions$Function2<String, String, Boolean> processTemp = [ button, value |
    Thermostat.allMembers.filter[s | s.name == "Thermostat"+button].forEach[thermostat |
        thermostat.postUpdate(if(value == "1") OFF else ON)
    ]
]

val Functions$Function4<Integer, Integer, Integer, Integer> calculatePercent = [ oldState, pressTime, fullTime |
    var press = if(button < 10) pressTime * -1 else pressTime
    var returnVal = Math::round(oldState + presstime / fullTime * 100).intValue

    if(returnVal < 0) returnVal = 0
    returnVal%100 // result of last statement gets returned
]

val Functions$Function6<Boolean, RollerShutter, Map<String, ReentrantLock>, Map<String, Timers>, Map<String, Integer>, Map<String, Boolean>, Boolean> processShutter = [run, shutter, locks, timers, oldStates, shutw |

if(run){
    if(locks.get(shutter.name) == null) locks.put(new ReentrantLock())

    locks.get(shutter.name).lock
    try {
        if(timers.get(shutter.name) == null ) {

            val oldState = oldStates.get(shutter.name)
            // Default to up values
            var fullTime = (gShutterTimes.members.filter[time|time.name = shutterID+"Up"].head.state as DecimalType).intValue
            var min = (oldState - (shutter.state as DecimalType)) * fullTime / 100
            var pin = Integer::parseInt(shutter.name.charat(shutter.name.length - 1))

            // change to dwn values if necessary
            if(oldState - (shutter.state as DecimalType)) < 0){
                fullTime = (gShutterTimes.members.filter[time|time.name = shutterID+"Dwn"].head.state as DecimalType).intValue
                min = ((shutter.state as DecimalType) - oldState) * fullTime / 100
                pin = Integer::parseInt(shutter.name.charat(shutter.name.length - 1)) + 7
            }

            if( shutw.getOrDefault(shutter.name, true) {
                
                Arduino.sendCommand(shutterNum"-1s")

                shutw.put(shutter.name, false)

                timers.put(shutter.name, createTimer(now.plusMillis(upMin), [|
                    Arduino.sendCommand(shutterNum+"-0s")
                    shutw.put(shutter.name, true)
                    timers.put(shutter.name, null)
                ])
            }

            oldStates.put(shutter.name, (shutter.state as DecimalType).intValue)
        }
        else {
            shutter.postUpdate(oldStates.get(shutter.name)
            shutw.add(shutter.name, false)
        }
    }
    catch(Throwable t) {
        logError("Shutters", "Error in processShutter lambda: " + t.toString)
    }
    finally {
        locks.get(shutter.name).unlock
    }
}
]

rule "Handle Arduino Input"
when
    Item Arduino_input changed
then

    val check = Arduino_input.state.toString.split("_")
    val type = check.get(0)
    val button = check.get(1)
    val value = if(check.length >=3) check(2) else "-1"

    switch type{
        case "b": processLights.apply(button)
        case "t": processTemp.apply(button, value)
        case "s": {
            run = true // not certain this is needed, I think same thing can be achieved by just checking that the Timer exists

            val buttonInt = Integer::parseInt(button)
            val pressTime = if(value == "0") check.get(3) else 0
            val shutterID = "Shutter_" + (buttonInt - 7)
            val fullTime = (gShutterTimes.members.filter[time|time.name = shutterID+"Up"].head.state as DecimalType).intValue

            if(buttonInt >= 10) {
                pressTime = pressTime * -1
                shutterID = "Shutter_+ buttonInt
                fullTime = (gShutterTimes.members.filter[time|time.name = shutterID+"Dwn"].head.state as DecimalType).intValue
            }
            
            // In your code these pinID >=10 and <=10 are identical so I reworked.
            Arduino.sendCommand(button + if(value == "1") "-1s" else "-0s")
            if(value != "1") run = false

            val percent = calculatePercent.apply(shutterOldState.getOrDefault(shutterID, 0), pressTime * -1, fullTime)
            shutterOldState.put(shutterID, percent)
            if(value == "0") {
                gShutter.allMembers.filter[s | s.name == shutterID].foreEach[ shutter |
                    shutter.postUpdate(percent)
                ]
            }
        }
    }

end

rule "Shutter Living Room 3"
when
    Item Shutter_3 received command
then
        processShutter.apply(run, Shutter_3, locks, timers, oldStates, shutw)
end

...

Hello Richard,

Thanks for your time, code and quick answer. I’ve been checking the code while trying to understand it how everything works. There are not many changes compared to your original. It also showed me how bad I am at programming this properly. :slight_smile:

I hade to change this part of the code, as the “RollerShutter” variable was not a good variable, or I do not know how to declare it. I’ve changed it to “org.openhab.core.items.GenericItem”[quote=“rlkoshak, post:2, topic:27996”]
val Functions$Function6<Boolean, RollerShutter, Map<String, ReentrantLock&gt…;
[/quote]

So far I corrected the syntax errors, and ran into a problem while running the code.

2017-05-12 16:04:12.726 [ERROR] [.openhab.model.script.Shutters] - Error in processShutter lambda: java.lang.NullPointerException: 
cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null

My items and rules:

Group	LivingRoom	"Living Room"
Group	KitchenRoom	"Living Room"
Group	Rooms	"Bedrooms"
Group	Bathrooms	"Bathrooms"
Group	Entrance	"Corridors"
Group	Thermostat	"Thermostat [%s]"
Group	Tempstate

String	Arduino	"Arduino [%s]"	{ serial="/dev/ttyACM1", autoupdate="false" }
String	Arduino_input	"Arduino_input [%s]"	{ serial="/dev/ttyACM0", autoupdate="false" }

Number	Arduino_humidity	"Inner Humidity [%.1f %%]"	<temperature>	(LivingRoom)	
Number	Arduino_temperature	"Inner Temperature [%.1f °C]"	<temperature>	(LivingRoom,Tempstate)	

String	Thermostat20	"Living room thermostat [%s]"	<temperature>	(Thermostat,LivingRoom)	
String	Thermostat21	"Kitchen thermostat [%s]"	<temperature>	(Thermostat)	
String	Thermostat22	"Room 1 thermostat [%s]"	<temperature>	(Thermostat)	
String	Thermostat23	"Room 2 thermostat [%s]"	<temperature>	(Thermostat)	
String	Thermostat24	"Room 3 thermostat [%s]"	<temperature>	(Thermostat)	
String	Thermostat25	"Room 4 thermostat [%s]"	<temperature>	(Thermostat)	
String	Thermostat26	"Small bathroom thermostat [%s]"	<temperature>	(Thermostat)	
String	Thermostat27	"Master Bathroom thermostat [%s]"	<temperature>	(Thermostat)	

Group	gShutter	"All shutters [%d %%]"	<rollershutter>
Group	LShutter	"Living shutters [%d %%]"	<rollershutter>
Rollershutter	Shutter_3	"Shutter Living Room Door  [%d %%]"	<rollershutter>	(gShutter,LShutter,LivingRoom)	
Rollershutter	Shutter_4	"Shutter Living Room Window [%d %%]"	<rollershutter>	(gShutter,LShutter,LivingRoom)	
Rollershutter	Shutter_5	"shutter Living Room Window [%d %%]"	<rollershutter>	(gShutter,LShutter,LivingRoom)	
Rollershutter	Shutter_6	"Bedroom 1 Shutter  [%d %%]"	<rollershutter>	(gShutter)	
Rollershutter	Shutter_7	"Bedroom 2 Shutter  [%d %%]"	<rollershutter>	(gShutter)	
Rollershutter	Shutter_8	"Bedroom 3 Shutter  [%d %%]"	<rollershutter>	(gShutter)	
Rollershutter	Shutter_9	"Bedroom 4 Shutter  [%d %%]"	<rollershutter>	(gShutter)	

Number Shutter_3Up (gShutterTimes)
Number Shutter_4Up (gShutterTimes)
Number Shutter_5Up (gShutterTimes)
Number Shutter_6Up (gShutterTimes)
Number Shutter_7Up (gShutterTimes)
Number Shutter_8Up (gShutterTimes)
Number Shutter_9Up (gShutterTimes)
Number Shutter_3Down (gShutterTimes)
Number Shutter_4Down (gShutterTimes)
Number Shutter_5Down (gShutterTimes)
Number Shutter_6Down (gShutterTimes)
Number Shutter_7Down (gShutterTimes)
Number Shutter_8Down (gShutterTimes)
Number Shutter_9Down (gShutterTimes)

Group	gShutterTimes


String	Shutterswitch1
String	Shutterswitch2
String	Shutterswitch3
String	Shutterswitch4
String	Shutterswitch5
String	Shutterswitch6
String	Shutterswitch7

Group:Switch:OR(ON,OFF)	Lights	"All lights [%d]"	(all)	
Group:Switch:OR(ON,OFF)	Kitchen	"Kitchen"	(all)	
Group:Switch:OR(ON,OFF)	Living	"Living Room"	(all)	
Group:Switch:OR(ON,OFF)	Mood	"Mood Lights"	(all)	

Switch	LightSwitch28	"Living - Mood Light"	(Lights,Living,Mood,LivingRoom)	
Switch	LightSwitch29	"Living - LED Spots"	(Lights,Living,LivingRoom)	
Switch	LightSwitch30	"Kitchen - Entrée"	(Lights,Kitchen)	
Switch	LightSwitch31	"Kitchen - Mood Light"	(Lights,Kitchen,Mood)	
Switch	LightSwitch32	"Kitchen - LED Spots"	(Lights,Kitchen)	
Switch	LightSwitch33	"Kitchen - Above the table"	(Lights,Kitchen)	
Switch	LightSwitch34	"Kitchen - @ window"	(Lights,Kitchen)	
Switch	LightSwitch35	"Room 1"	(Lights)	
Switch	LightSwitch36	"Room 2"	(Lights)	
Switch	LightSwitch37	"Room 3"	(Lights)	
Switch	LightSwitch38	"Room 4"	(Lights)	
Switch	LightSwitch39	"Master Bathroom"	(Lights)	
Switch	LightSwitch40	"Small Bathroom"	(Lights)	
Switch	LightSwitch41	"Entrance"	(Lights)	
Switch	LightSwitch42	"Entrance Outside"	(Lights)	
Switch	LightSwitch43	"Living - Studio Lamps"	(Lights,Living,LivingRoom)	
Switch	LightSwitch44	"Living - Chandelier"	(Lights,Living,LivingRoom)	
Number	LightT

Switch	WashingMachine45	"Washing Machine [%s]"
DateTime	Date	"Current date [%1$tA, %1$td.%1$tm.%1$tY]"	{ ntp="Europe/Bucharest" }

Group	Weather	"Weather information"
Number	Humidity	"Outer Humidity [%d %%]"	{ weather="locationId=home, type=atmosphere, property=humidity" }
Number	Pressure	"Pressure [%.2f mb]"	{ weather="locationId=home, type=atmosphere, property=pressure" }
String	Temperatur_MinMax	"Min/Max Temperature [%s °C]"	{ weather="locationId=home, forecast=0, type=temperature, property=minMax, roundingMode=down, scale=0" }
String	Temperatur_MinMax2	"Min/Max Temperature [%s °C]"	{ weather="locationId=home, forecast=1, type=temperature, property=minMax, roundingMode=down, scale=0" }
Number	Clouds	"Clouds [%.0f %%]"	{ weather="locationId=home, type=clouds, property=percent" }
String	Condition	"Condition [%s]"	{ weather="locationId=home, type=condition, property=text" }
String	Condition_ID	"Condition id [%s]"	{ weather="locationId=home, type=condition, property=id" }
DateTime	ObservationTime	"Observation time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"	{ weather="locationId=home, type=condition, property=observationTime" }
DateTime	LastUpdate	"Last update [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"	{ weather="locationId=home, type=condition, property=lastUpdate" }
String	CommonId	"Common id [%s]"	{ weather="locationId=home, type=condition, property=commonId" }
Number	Rain	"Rain fall [%.2f mm/h]"	{ weather="locationId=home, type=precipitation, property=rain" }
Number	Snow	"Snow fall [%.2f mm/h]"	{ weather="locationId=home, type=precipitation, property=snow" }
Number	Precip_Probability	"Precipitation probability [%d %%]"	{ weather="locationId=home, forecast=0, type=precipitation, property=probability" }
Number	Precip_Probability_FC	"Precipitation probability [%d %%]"	{ weather="locationId=home, forecast=1, type=precipitation, property=probability" }
Number	Temperature	"Outer Temperature [%.2f °C]"	<w>	(Tempstate)		{ weather="locationId=home, type=temperature, property=current" }
Number	Temp_Feel	"Temperature feel [%.2f °C]"	{ weather="locationId=home, type=temperature, property=feel" }
Number	Wind_Speed	"Windspeed [%.2f km/h]"	{ weather="locationId=home, type=wind, property=speed" }

Switch	Watering_Zone1	"Back garden Zone 1"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone2	"Back garden Zone 2"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone3	"Back garden Zone 3"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone4	"Back garden Zone 4"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone5	"Back garden Zone 5"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone6	"Front Garden Zone 6"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone7	"Front Garden Zone 7"	<selfWater>	(Watering_Circuits)	
Switch	Watering_Zone8	"Front Garden Zone 8"	<selfWater>	(Watering_Circuits)	

Number	Watering_Program_Duration_Zone1	"Watering time Zone1 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone2	"Watering time Zone2 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone3	"Watering time Zone3 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone4	"Watering time Zone4 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone5	"Watering time Zone5 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone6	"Watering time Zone6 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone7	"Watering time Zone7 [%d]"	<clockon>	(Watering_Program_Duration)	
Number	Watering_Program_Duration_Zone8	"Watering time Zone8 [%d]"	<clockon>	(Watering_Program_Duration)	

Group	Watering_Program_Duration	<selfRuntime>	(Watering)	
Switch	Watering_Program_Start	"Program start"	<selfAutoMode>	(Watering)	
String	Watering_Program_State	"Status [%s]"	<selfInfo>	(Watering)	
String	Air_Garden_Message	"Actual status [%s]"	<Watering>
Group	Watering	"Irrigation [%d]"	<selfWater>	(All)	
Group:Switch:OR(ON,OFF)	Watering_Circuits	"Irrigation [%d]"	<selfWater>	(Watering)	

Group	Clock	<clock>	(All)	

Switch	weckerMontag	"Monday"	<calendar>	(gWeckerWochentage)	
Switch	weckerDienstag	"Tuesday"	<calendar>	(gWeckerWochentage)	
Switch	weckerMittwoch	"Wednesday"	<calendar>	(gWeckerWochentage)	
Switch	weckerDonnerstag	"Thursday"	<calendar>	(gWeckerWochentage)	
Switch	weckerFreitag	"Friday"	<calendar>	(gWeckerWochentage)	
Switch	weckerSamstag	"Saturday"	<calendar>	(gWeckerWochentage)	
Switch	weckerSonntag	"Sunday"	<calendar>	(gWeckerWochentage)	
String	weckerZeitMessage	"%s"
Number	weckerZeitStunde	"Stunde [%d]"	<clock>	(gWeckerZeit)	
Number	weckerZeitMinute	"Minute [%d]"	<clock>	(gWeckerZeit)	

import org.eclipse.xtext.xbase.lib.Functions
import java.util.ReentrantLock
import java.util.concurrent.locks.Lock
import java.util.HashMap
import java.util.Map
import java.util.LinkedHashMap

import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.binding.exec.*
import org.joda.time.*
import org.openhab.model.script.actions.*
import org.openhab.model.script.actions.Timer
import java.util.List
import java.math
import java.util.Date
import java.lang.Integer.*

var boolean run = true

var Map<String, Integer> shutterOldState = newHashMap
var Map<String, Integer> oldStates = newHashMap
var Map<String, Lock> locks = newHashMap
var Map<String, Timer> timers = newHashMap
var Map<String, Boolean> shutw = newHashMap
var org.openhab.core.items.GenericItem shutter
var String shutterID
var Integer fullTime
var Integer percent
var Integer pressTime


val org.eclipse.xtext.xbase.lib.Functions$Function1 <String, Boolean> processLights = [ button |
    Lights.allMembers.filter[s|s.name == "LightSwitch"+button].forEach[item |
        item.postUpdate(if(item.state == ON) "OFF" else "ON")
    ]
    true
]

val org.eclipse.xtext.xbase.lib.Functions$Function2 <String, String, Boolean> processTemp = [ button, value |
    gShutter.allMembers.filter[s | s.name == "Thermostat"+button].forEach[item |
        item.postUpdate(if(value == "1") "OFF" else "ON")
    ]
    true
]

val org.eclipse.xtext.xbase.lib.Functions$Function4 <Integer, Integer, Integer, Integer, Integer> calculatePercent = [ button, oldState, pressTime, fullTime |
    var press = if(button < 10) pressTime * -1 else pressTime
    var returnVal = Math::round((oldState + press / fullTime * 100).intValue)

    if (returnVal < 0) returnVal = 0
    if (returnVal > 100) returnVal = 100
    return returnVal // result of last statement gets returned
    
]



val org.eclipse.xtext.xbase.lib.Functions$Function6<Boolean, org.openhab.core.items.GenericItem , Map<String, Lock>, Map<String, Timer>, Map<String, Integer>, Map<String, Boolean>, Boolean> processShutter = [run, shutter, locks, timers, oldStates, shutw |
//if(run){
//   if(locks.get(shutter.name) == null) locks.put(new Lock())
//
//    locks.get(shutter.name).lock
   try {
    if (timers.get(shutter.name) == null ) {

            var oldState = oldStates.get(shutter.name)
            // Default to up values
            var fullTime = (gShutterTimes.members.filter[s | s.name == "Shutter_" + shutter.name + "Up"].head.state as DecimalType).intValue
            var min = (oldState - (shutter.state as DecimalType)) * fullTime / 100
            var pin = Integer::parseInt(shutter.name.charAt(shutter.name.length - 1).toString)

            // change to dwn values if necessary
            if (oldState - (shutter.state as DecimalType) < 0) {
                fullTime = (gShutterTimes.members.filter[s | s.name == "Shutter_" + shutter.name + "Down"].head.state as DecimalType).intValue
                min = ((shutter.state as DecimalType) - oldState) * fullTime / 100
                pin = Integer::parseInt(shutter.name.charAt(shutter.name.length - 1).toString) + 7
           }

            if (shutw.getOrDefault(shutter.name, true)) {
                
                Arduino.sendCommand(shutter + "-1s")

                shutw.put(shutter.name, false)

                timers.put(shutter.name, createTimer(now.plusMillis(min), [|
                    Arduino.sendCommand(shutter + "-0s")
                    shutw.put(shutter.name, true)
                    timers.put(shutter.name, null)
                ])
            )

            oldStates.put(shutter.name, (shutter.state as DecimalType).intValue)
		}
        else {
            shutter.postUpdate(oldStates.get(shutter.name))
            shutw.put(shutter.name, false)
        }
//      
    }
}
    catch (Throwable t) {
        logError("Shutters", "Error in processShutter lambda: " + t.toString)
    }
    
    
//    finally {
//        locks.get(shutter.name).unlock
 //   }

true
]

rule "Startup"
when 
	System started
then {
	
	Shutter_3Up.postUpdate(20000)
	Shutter_4Up.postUpdate(20000)
	Shutter_5Up.postUpdate(20000)
	Shutter_6Up.postUpdate(20000)
	Shutter_7Up.postUpdate(20000)
	Shutter_8Up.postUpdate(20000)
	Shutter_9Up.postUpdate(20000)
	Shutter_3Down.postUpdate(20000)
	Shutter_4Down.postUpdate(20000)
	Shutter_5Down.postUpdate(20000)
	Shutter_6Down.postUpdate(20000)
	Shutter_7Down.postUpdate(20000)
	Shutter_8Down.postUpdate(20000)
	Shutter_9Down.postUpdate(20000)
	
//	//Thread::sleep(10000) 
//	sendCommand (Arduino_input , "TMP")
//	//sets shutter to starting position
//shutterOldState.add(0, 100)
//sendCommand(Shutter_3, 0)
//shutterOldState.add(1, 100)
//sendCommand(Shutter_4, 0)
//shutterOldState.add(2, 100)
//sendCommand(Shutter_5, 0)
//shutterOldState.add(3, 100)
//sendCommand(Shutter_6, 0)
//shutterOldState.add(4, 100)
//sendCommand(Shutter_7, 0)
//shutterOldState.add(5, 100)
//sendCommand(Shutter_8, 0)
//shutterOldState.add(6, 100)
//sendCommand(Shutter_9, 0)
}
end



//rule "Request TEMP"
//
//
//when 
//	Time cron "0 0/15 * 1/1 * ? *"
//	then 
//		sendCommand (Arduino_input , "TMP")
//end


rule "Arduino input handler"
when   
          Item Arduino_input changed 
     then  
     
val String[] check = Arduino_input.state.toString.split("-")
val type = check.get(0)
val button = check.get(1)
val value = if(check.size >= 3) check.get(2) else "-1"


switch type{
	
case "b": processLights.apply(button)
case "t": processTemp.apply(button, value)
case "tmp":
		{
		var Double humidityDouble = new Double (check.get(1))
		Arduino_humidity.postUpdate(humidityDouble)
		
		var Double temperatureDouble = new Double (check.get(2))
		Arduino_temperature.postUpdate(temperatureDouble)
		}
      	  
case "s": {
//            run = true // not certain this is needed, I think same thing can be achieved by just checking that the Timer exists

            val buttonInt = Integer::parseInt(button)
            pressTime = if (value == "0") Integer::parseInt(check.get(3)) else 0
            shutterID = "Shutter_" + (buttonInt - 7)
            fullTime = (gShutterTimes.members.filter[s|s.name == "Shutter_" + shutter.name + "Up"].head.state as DecimalType).intValue

            if (buttonInt >= 10) {
              //  pressTime = pressTime  -1
            shutterID = "Shutter_"+ buttonInt
            fullTime = (gShutterTimes.members.filter[s|s.name == "Shutter_" + shutter.name + "Down"].head.state as DecimalType).intValue
            }
            
            // In your code these pinID >=10 and <=10 are identical so I reworked.
            Arduino.sendCommand(button + if(value == "1") "-1s" else "-0s")
            if(value != "1") //run = false

            percent = calculatePercent.apply(buttonInt, shutterOldState.getOrDefault(shutterID, 0), pressTime * -1, fullTime)
            shutterOldState.put(shutterID, percent)
            if(value == "0") {
                gShutter.allMembers.filter[s | s.name == shutterID].forEach[ sh |
                    sh.postUpdate(percent)]
                
            }
        }


} 	 
end

rule "Shutter Living Room 3"
when
    Item Shutter_3 received command
then
        processShutter.apply(run, Shutter_3, locks, timers, oldStates, shutw)
end

rule "Shutter Living Room 4"
when
    Item Shutter_4 received command
then
        processShutter.apply(run, Shutter_4, locks, timers, oldStates, shutw)
end

rule "Shutter Living Room 5"
when
    Item Shutter_5 received command
then
        processShutter.apply(run, Shutter_5, locks, timers, oldStates, shutw)
end

rule "Shutter Room 1"
when
    Item Shutter_6 received command
then
        processShutter.apply(run, Shutter_6, locks, timers, oldStates, shutw)
end


rule "Light28_FROM WEB" //used when clicking

when 
	Item LightSwitch28 changed
	then 
	if (LightSwitch28.state==ON) sendCommand (Arduino ,"28-1s")		
	else sendCommand (Arduino ,"28-0s")
end	

Should be RollerShutterItem.

The error indicates that shutter is null in the lambda.

Add logging statements to the rules and the lambdas to print out the values of the Item’s state and other values you use to narrow down where the problem may be.