Random Rollershutter triggered, after astro set#event ? How?

Hello Guys,

i have set up a new project. I will trigger my blinds randomly after the astro set#event is triggered. So after the sunset, oh should start to trigger random rooms to move down the blinds. Different offset times different order evere day. The time for the offset should in a range of some minutes after astro set#event

Is there a way to do that ?
For now after the astro binding set#event was triggered, i let move the blinds from the first room, on the same time i let create timers with some minutes different from the other rooms. So eacht room is go down a little bit different. But every day at the same time, same order and same distance.

So i will have it a little bit random.

Maybe someone can help me create a rule or what ever to become this to happen.
I have read something about java util random, but im new at openhab this matter and have no idea how to deal with it.

Greet’s
D3ltoro

Did you see my answer in https://openhabforum.de/viewtopic.php?f=15&t=355&start=10#p7882 ?

Group:Rollershutter gShutters "All shutters"
var Timer tShutters = null
val java.util.concurrent.ThreadLocalRandom random = (new java.util.concurrent.ThreadLocalRandom)

rule "close Shutters"
when
    Channel 'astro:sun:home:set#event' triggered START
then
    tShutters?.cancel                                                       // cancel existing timer, if there is one
    val int randomTime = random.nextInt(60) + 20                            // minimum 20 Seconds, maximum 80 Seconds
    tShutters = createTimer(now.plusSeconds(randomTime),[ |
        val iGroup = gShutters.members.filter[r|(r.state as Number) < 100]  // all open shutters
        if(iGroup.size > 0) {                                               // any open Shutters left?
            val int randomTime = random.nextInt(60) + 20                    // minimum 20 Seconds, maximum 80 Seconds
            val int randomShutter = random.nextInt(iGroup.size -1) +1       // from 1 to iGroup.size
            iGroup.get(randomShutter).sendCommand(DOWN)                     // or 100 for absolute position
            iGroup.get(randomShutter).postUpdate(100)                       // ensure state is set to 100 immediately
            tShutters.reschedule(now.plusSeconds(randomTime))               // next close @ 
        }
    ])
end

for this I use a generic function:

import java.util.HashMap

var Timer tAlarmanlageStartTimer = null
var Timer tSireneTimer = null
var Timer tshadowTimer = null
var Timer tProxyTimer = null
var HashMap<String, Timer> 		tRandomTimers = newHashMap()
var HashMap<String, Timer> 		tTimersWithCancel = newHashMap()
var HashMap<String, Timer> 		tEscalationTimer = newHashMap()
var HashMap<String, Integer> 	iEscalationTimer = newHashMap()
var HashMap<String, String> 	stEscalationTimer = newHashMap()

// this function is used to wait for a random interval and then set the item to given state
// GenericItem = item
// Hashmap of the Timer
// Numberitem with the maximum of delay
// String with new state after interval is reached
val Procedures$Procedure4<GenericItem, HashMap<String, Timer>, NumberItem, String> startRandomTimerWithAction= [ relatedItem, tRandomTimers, nuRandomTimerTime, newitemstate |

    var int randomTime

	// if the times still exists, delete it first, no reschedule implemented
    if(tRandomTimers.get(relatedItem.name) !== null)
    {
        tRandomTimers.get(relatedItem.name).cancel()
        tRandomTimers.put(relatedItem.name, null)
        tRandomTimers.remove(relatedItem.name)
    }

    // if no timer in needed because interval <= 30
    if((nuRandomTimerTime.state as Number).intValue <= 30)
    {
        logInfo("Logger", "RandomTimer for Item " + relatedItem.label + " not needed. Action " + newitemstate.toString + " executed.")
        relatedItem.sendCommand(newitemstate)
        return;
    }

    // ensure that random value is greater then 0
    randomTime = (new java.util.Random).nextInt((nuRandomTimerTime.state as Number).intValue) + 5

    // wait a little bit, if two timers end on the same time not all of them are execute the commands
    Thread::sleep(100)
    
    // create the timer and do the action at the end of the timer
    logInfo("Logger", "RandomTimer for Item " + relatedItem.label + " started with " + randomTime + " sec.")
    tRandomTimers.put(relatedItem.name, createTimer(now.plusSeconds(randomTime))
    [ |
        tRandomTimers.get(relatedItem.name).cancel()
        tRandomTimers.put(relatedItem.name, null)
        tRandomTimers.remove(relatedItem.name)
        logInfo("Logger", "RandomTimer for Item " + relatedItem.label + " reached end. Action " + newitemstate.toString + " executed.")
        relatedItem.sendCommand(newitemstate)
	])
]

A rollershutter item (RolloRuntergefahren) is triggered by the astro binding, that know the rollershutter should go up or down.

In addition for every rollershutter there is a switch to exclude from automatic up or down (SW_RollerShutter_01_AutoMode, …)

tRandomTimers is a number item for the maximum delay for the random in sec.

RS_RollerShutter_0 is the rollershutter item itself.

rule "RolloRuntergefahren"
	when 
		Item RolloRuntergefahren changed from OFF to ON
	then
		if(SW_RollerShutter_01_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_01, tRandomTimers, nuRandomValueRollerShutter, "25")}
		if(SW_RollerShutter_02_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_02, tRandomTimers, nuRandomValueRollerShutter, "30")}
		if(SW_RollerShutter_03_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_03, tRandomTimers, nuRandomValueRollerShutter, "25")}
		if(SW_RollerShutter_04_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_04, tRandomTimers, nuRandomValueRollerShutter, "0")}
		if(SW_RollerShutter_05_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_05, tRandomTimers, nuRandomValueRollerShutter, "0")}
		if(SW_RollerShutter_06_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_06, tRandomTimers, nuRandomValueRollerShutter, "25")}
		if(SW_RollerShutter_07_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_07, tRandomTimers, nuRandomValueRollerShutter, "25")}
		if(SW_RollerShutter_08_AutoMode.state == ON){startRandomTimerWithAction.apply(RS_RollerShutter_08, tRandomTimers, nuRandomValueRollerShutter, "25")}
end

The function is generic and can be used for every random need.

Hi
I am trying to use your rule but on the Visual studio Code i get this error,
The constructor ThreadLocalRandom() is not visible. from this: val java.util.concurrent.ThreadLocalRandom random = (new java.util.concurrent.ThreadLocalRandom)

Do you have an idea why?
Thanks

Hmm. No idea… It should be available since Java 7