EnergySaving script. turn off specific light after x minutes. (in groovy)

Hello,

Its a little script that will turn off specific light after x minutes. The delay is specify for each switch.

If your want to reset the timer, you can just turn on the light again, no need to turn it off. (if you have those old switch that report their state after 25-30 seconds, this will not work very well)

edit: add optional timer show on the gui.

energy_saving.groovy

import org.openhab.core.jsr223.internal.shared.*
import org.openhab.core.items.ItemRegistry
import org.joda.time.DateTime
import org.openhab.core.library.types.*
import java.util.timer.*
import java.util.concurrent.locks.ReentrantLock

//this will turn off a light after x minutes, multiple light can be control with the same script.
//Pressing ON again on the switch will reset the timer
//the timer can be display to the gui when timeShow is specify
class EvergySavingImpl implements Rule {
    //var that take your configuration, see init in EvergySavingImpl()
    static switchToControl = []
    static ItemRegistry itemRegistry
    static Class pe
    static java.util.concurrent.locks.ReentrantLock lock  = new java.util.concurrent.locks.ReentrantLock()
    def logger = Openhab.getLogger('EvergySaving')
    
    EvergySavingImpl() {
        logger.info('EvergySaving init') 
        //You need to add your light to control here!
        //timeLimit: The light will turn off after this time in minutes
        //timerShow: give an item to show the timer on the GUI. (can be empty)
        //do not modify timer
        switchToControl << [mySwitch:'Light_A',\
                            timeLimit:15, \
                            timeShow: "", \
                            timer:null ]
        switchToControl << [mySwitch:'Light_B',\
                            timeLimit:15, \
                            timeShow: "Light_B_TimeOnGui", \
                            timer:null ]
        switchToControl << [mySwitch:'Light_Bathroom', \
                            timeLimit:45, \
                            timeShow: "", \
                            timer:null ]
    }

    java.util.List<EventTrigger> getEventTrigger() {
        def myTrigger = []
        switchToControl.each {
            myTrigger << new UpdatedEventTrigger(it.mySwitch)
            myTrigger << new ChangedEventTrigger (it.mySwitch)
        }
        return myTrigger
    }

    void execute(Event event) {
        lock.lock()
        def switchIndex = returnIndex(event.getItem().getName())
        if(event.getOldState()) {
            if(convertState(event.getNewState())) {
                makeTimer(switchIndex)
            }else {
                cancelTimer(switchIndex)
            }
        }else{
            //timer need updating
            if(convertState(event.getNewState()) && switchToControl[switchIndex].timer) {
                makeTimer(switchIndex)
            }
        }
        lock.unlock()
    }
    
    def returnIndex(String theSwitch) {
        def myIndex = null
        switchToControl.eachWithIndex { item, index ->
            if(item.mySwitch == theSwitch) {
                myIndex = index
            }
        }
        return myIndex
    }
    
    void makeTimer(def switchIndex) {
        cancelTimer(switchIndex)
        def myDelay = switchToControl[switchIndex].timeLimit
        if(switchToControl[switchIndex].timeShow) {
            makeTimerGui(switchIndex, myDelay*60)
        }else{
            switchToControl[switchIndex].timer = Openhab.createTimer(new DateTime().plusMinutes(myDelay),{
                    switchToControl[switchIndex].timer = null
                    Openhab.sendCommand(getSwitch(switchIndex), OnOffType.OFF)
                } as TimerTask )
        }
    }
    
    void makeTimerGui(def switchIndex, int time) {
        setTimerGui(switchIndex, time)
        def newTimerTime = time>60 ? 60 : 1
        switchToControl[switchIndex].timer = Openhab.createTimer(new DateTime().plusSeconds(newTimerTime),{
                if(time-1 == 0) {
                    switchToControl[switchIndex].timer = null
                    setTimerGui(switchIndex, 0)
                    Openhab.sendCommand(getSwitch(switchIndex), OnOffType.OFF)
                }else{
                    makeTimerGui(switchIndex,time-newTimerTime)
                }
            } as TimerTask )
    }
    
    void cancelTimer(def switchIndex) {
        switchToControl[switchIndex].timer?.cancel()
        switchToControl[switchIndex].timer = null
        if(switchToControl[switchIndex].timeShow!="") {
            setTimerGui(switchIndex, 0)
        }
    }
    
    void setTimerGui(def switchIndex, int time) {
        def text = ""
        if(time>60){
            text = time/60 + " minutes"
        }else{
            text = time>0 ? time + " seconds" : "0"
        }
        Openhab.sendCommand(getItem(switchToControl[switchIndex].timeShow), text)
    }
    
    def getSwitch(int index) {
        return itemRegistry.getItem(switchToControl[index].mySwitch)
    }
    
    def getItem(String name) {
        itemRegistry.getItem(name)
    }
    
    Boolean convertState(OnOffType status) {
        return (status == OnOffType.ON) ? true : false
    }
}

RuleSet getRules() {
    return new RuleSet(new EvergySavingImpl())
}
 
EvergySavingImpl.itemRegistry = this.ItemRegistry
EvergySavingImpl.pe = this.PersistenceExtensions

Here is a Rule’s DSL version for comparison.

#Items

Group gTimerLights
Group gTimerLightsTimeout

Switch Light_A (gTimerLights)
Number Light_A_Timeout (gTimerLightsTimeout)

Switch Light_B (gTimerLights)
Number Light_A_Timeout (gTimerLightsTimeout)

Switch Light_Batroom (gTimerLights)
Number Light_Batroom_Timeout (gTimerLightsTimeout)

#Rules

val Map<String, Timer> lightTimers = createHashMap

// You only need this rule the very first time this rules file is loaded to initially populate the Timeouts.
// Future updates can be done through the sitemap
rule "System Started"
when
    System started
then
    Light_A_Timeout.sendCommand(15)
    Light_B_Timeout.sendCommand(15)
    Light_Batroom_Timeout.sendCommand(45)
end

rule "Light Turned On"
when
    Item gTimerLights received update
then
    Thread::sleep(100) // give lastUpdate a chance to catch up
    val mostRecent = gTimerLights.members.sortBy[lastUpdate].last as SwitchItem

    // The light turned ON (or was updated to ON)
    if(mostRecent.state == ON) {

        // No timer, create one
        if(lightTimers.get(mostRecent.name) == null) {
            val mins = gTimerLightsTimeout.members.filter[n | n.name == mostRecent.name + "_Timeout"].head.state as DecimalType
            lightTimers.put(mostRecent.name, createTimer(now.plusMinutes(mins, [|
                mostRecent.sendCommand(OFF)
                lightTimers.put(mostRecent.name, null)
            ]))
        }

        // This is a new ON message that was received before the timer went off, reschedule the timer
        else {
            lightTimers.get(mostRecent.name).reschedule(now.plusMinutes(mins))
        }
    }
end

Cool, it can be useful to have option :slightly_smiling: