How to show light timer as an item?

I have a timer that switches on some lights at a random interval. Is there a way of showing what time this will be scheduled for as an item in the openHAB GUI?

A sample section of the rule with the timer is shown below.

var int randomTime = (new java.util.Random).nextInt(2700)
tRandomLight_GF_Living_Room_OFF = createTimer(now.plusSeconds(randomTime)) [ |
                Living_Room_Lights?.members.forEach[item |
                            sendCommand(item, OFF)
                            try { Thread::sleep(5000) } catch(InterruptedException e) { }
                ]

Create your random time as a variable, capture it into a datetime Item, and also use it for the createTimer. Display the Item in your UI.

I thought you couldn’t set / create timers with a full specified dateTime. Hence why I was using the code:

createTimer(now.plusSeconds(randomTime))

But from what you’re saying the following is fine?

var int randomTime = (new java.util.Random).nextInt(2700)
var DateTime randomTimer = now.plusSeconds(randomTime)
LightsTimer.postUpdate(randomTimer)
tRandomLight_GF_Living_Room_OFF = createTimer(randomTimer) [ |
                Living_Room_Lights?.members.forEach[item |
                            sendCommand(item, OFF)
LightsTimer.postUpdate(null)
                            try { Thread::sleep(5000) } catch(InterruptedException e) { }
                ]

When you use now.plusSeconds(randomTime), that is creating a fully specified date time.

I think the above will work. If not you may have to do some manipulation of the randomTimer so LightsTimer can receive it as an update.

Also, the postUpdate(null) may not work. Let us know if it does though. It might work with NULL though if null doesn’t work. If not, I’m not sure it it is possible to set an Item back to Undefined from a Rule.

Thanks rikoshak. The documentation on github seems to imply that postupdate only accests strings…

Is there a simple way to convert whatever variable is passed to the postUpdate command into a string?

ie.

LightsTimer.postUpdate(randomTimer.type as String)

Actions are indeed limited to String. But calling LightsTimer.postUpdate is not calling the Action, it is calling the method on the Item which is able to handle a lot more than just Strings. This is why I always recommend using the method instead of the Action.

But in answer to your question, every object has a toString method.

LightsTime.postUpdate(randomTimer.toString)

Not sure why you are calling the type method. You should never need to call that.

I think you’re right as I seem to be getting quite a few errors when trying to set the variable to null.

Are there any work arounds?

Error during the execution of rule Living Room Lights OFF - Alarm Active
java.lang.IllegalStateException: Could not invoke method: org.openhab.model.script.actions.BusEvent.postUpdate(org.openhab.core.items.Item,java.lang.Number) on instance: null

You cannot set an Item’s state to null. I’m not even sure you can set it to Undefined (I.e. NULL), though you could give that a try. Typically you wouldn’t want to do this and most people go to great lengths to ensure their items are never Undefined.

Typically people will just leave it with its old value until it is needed again.

1 Like

I’ve made some tweaks to my rule to try and allow the timer to be set to an item in the GUI. But I’m getting an error about item / type not being declared.

[ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Turn on Living Room lights at random time after sunset
java.lang.RuntimeException: The name 'randomTimer_DateTime' cannot be resolved to an item or type.


rule "Turn on Living Room lights at random time after sunset"
    when   
        Item Sunset_Event received update ON
    	//Time cron "0 0/3 * 1/1 * ? *"
    then
    	logInfo("org.openhab","Sunsetting. Generating random time on for lights in living room", randomTimer_DateTime)

    	// Create a  timer with a random value in the range of 30 mins
        var int randomTime = (new java.util.Random).nextInt(1800)
        var DateTime randomTimer_DateTime = parse(now.plusSeconds(randomTime))
        dLivingroomLightsTimerON.postUpdate(randomTimer_DateTime)
        logInfo("org.openhab","Setting random timer to {} for tRandomLight_GF_Living_Room_ON.", randomTimer_DateTime)

        tRandomLight_GF_Living_Room_ON = createTimer(randomTimer_DateTime) [ |
                Living_Room_Lights?.members.forEach[item |
                		logInfo("org.openhab","Switching light state to ON for {}", item)
                        sendCommand(item, ON)
                        try { Thread::sleep(5000) } catch(InterruptedException e) { } //wait 5 seconds before switching on next light
                ]
        ]
end

“org.openhab” is a terrible name for the logger from this Rule. The name of the logger (first parameter) gets put at the end of the classname portion of the logging statement. For example logInfo("entry", "foo bar") will generate a log statement that looks like:

2016-10-13 13:16:22.549 [INFO ] [eclipse.smarthome.model.script.entry] - foo bar

Furthermore, logInfo only takes two arguments, the name and the message. You are calling logInfo with three arguments.

Finally, that last argument is randomTimer_DateTime which doesn’t exist in this rule yet. You don’t declare and define it until another four lines of code further down in the rule. You cannot reference a variable before it has been at least declared.

This is the cause of your current error.

This sentence doesn’t make sense. You don’t set a Timer to an Item. You set a Timer to execute some code in the future and assign it to a variable (if desired).

As I look a the code more closely I see more odd things. What is the parse for? now is a DateTime object and plusSeconds returns a DateTime so what is the parse even doing? It certainly isn’t necessary.

Is tRandomLight_GF_Libing_Room_ON a global variable or an Item? It makes no sense if it is an Item. Timers are variables. You cannot set an Item to a Timer. Furthermore, you cannot assign anything to an Item and have it work. You must postUpdate or sendCommand.

I keep seeing lots of weird things in this code that I don’t understand and it makes me wonder if you understand how Rules work.

Here is how I would implement a rule that turns on the Lights at a random time after sunset and turns them off again at Sunrise

Items:

Group Living_Room_Lights ...
Switch Light1 (Living_Room_Lights) ...
...

Switch Sunset_Event { astro="planet=sun, type=set, property=start"  }

DateTime dLivingrookLightsTimerON ... // presumably so you can see when the lights will turn on on your sitemap, it isn't needed for any other reason

Rule:

var Timer tRandomLight_GF_Living_Room_ON = null // global variable, not an Item

rule "Turn on Living Room lights at random time after sunset"
when
    Item Sunset_Event received update ON
then
    logInfo("Sunset Lights", "Sunsetting. Generating random time on for lights in living room") // only two arguments, reasonable logger name

    // Calculate a random time in the next 30 minutes to turn on the lights
    var int randomTime = (new java.util.Random).nextInt(1800)
    var DateTime randomTimer_DateTime = now.plusSeconds(randomTime) // no need for parse
    logInfo("Sunset Lights", "Living room lights will turn on at " + randomTimer_DateTime.toString)

    dLivingroomLightsTimerON.postUpdate(randomTimer_DateTime) // if this generates an error use postUpdate(new DateTimeType(randomTimer_DateTime.millis))
    
    // Create the timer
    tRandomLight_GF_Living_Room_ON = createTimer(randomTimer_DateTime, [|
        Living_Room_Lights.members.forEach[i | // I don't recommend using ? because typically you want an error if Living_Room_Lights has no members, avoid using reserved words like "item"
            logInfo("Sunset Lights". "Switching light state to ON for " + i.name) // Unless this is a pretty new addition, I don't think Java and by extension the Rules DSL supports formatting of Strings using {} notation, logInfo only allows two arguments
            i.sendCommand(ON) // I always recommend using i.sendCommand over the sendCommand action as it is better able to handle data type conversions
            try{ Thread::sleep(5000) } catch(InterruptedException e) { } 
        ]
    ])
end

@sentur and Rich,

At least for openHAB2 I know this is true:

A logXXXX() method accepts "{}"s in its second argument as placeholders for subsequent corresponding arguments. When I first started with openHAB2 I used that approach for logging object values – implicitly instance.toString() – within a logging format string and have yet to experience any problems with it.

Added after edit:

The method declaration for each of the logXXX() methods is:

    static public void logXXXX(String loggerName, String format, Object... args)

(found in smarthome/bundles/model/org.eclipse.smarthome.model.script/src/org/eclipse/smarthome/model/script/actions/LogAction.java)

SK

I suspect this might be new to OH 2. It is good to know. Though I would be lying if I said I wasn’t just a little dismayed by the proliferation of ways to construct and format Strings. It makes it more challenging for newer users. Oh well.

Thanks for your patience and advice rlkoshak. I really appreciate it.

Yes, I probably don’t understand how Rules work as well as I should. For the most part, I’ve cobbled together my own setup from posts in the OpenHAB Community and Wiki on Github. I often find some of the documentation and error messages quite cryptic and unclear. Which hasn’t helped. It’d be nice to see error line numbers as an example, but alas.

Using your suggested code edits I understand where I’ve gone wrong in the past. However I still seem to be getting a couple of errors.

//highlighted in your comments, this line of code may cause issues. Which it does. 
dLivingroomLightsTimerON.postUpdate(randomTimer_DateTime)

[ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Turn on Living Room lights at random time after sunset': Could not invoke method: org.openhab.model.script.actions.BusEvent.postUpdate(org.openhab.core.items.Item,java.lang.Number) on instance: null

dLivingroomLightsTimerON.postUpdate(new DateTimeType(randomTimer_DateTime.millis)) 

[ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Turn on Living Room lights at random time after sunset': Could not invoke constructor: org.openhab.core.library.types.DateTimeType.DateTimeType(java.util.Calendar)

//commenting out this update for now also causes further issues.
[ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Turn on Living Room lights at random time after sunset': The name 'i' cannot be resolved to an item or type.

For reference here’s all other global elements of the rules.

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.java.math.*
import org.joda.time.*

/* Global variables */

var Timer tRandomLight_GF_Living_Room_ON = null
var Timer tRandomLight_GF_Living_Room_OFF = null 
var Timer tRandomLight_FF_Bedroom_ON = null
var Timer tRandomLight_FF_Bedroom_OFF = null 

rule "Turn on Living Room lights at random time after sunset"
when
    Item Sunset_Event received update ON
then
    logInfo("Sunset Lights", "Sunsetting. Generating random time on for lights in living room") // only two arguments, reasonable logger name

    // Calculate a random time in the next 30 minutes to turn on the lights
    var int randomTime = (new java.util.Random).nextInt(1800)
    var DateTime randomTimer_DateTime = now.plusSeconds(randomTime) // no need for parse
    logInfo("Sunset Lights", "Living room lights will turn on at " + randomTimer_DateTime.toString)

    dLivingroomLightsTimerON.postUpdate(new DateTimeType(randomTimer_DateTime.millis)) // if this generates an error use postUpdate(new DateTimeType(randomTimer_DateTime.millis))
    
    // Create the timer
    tRandomLight_GF_Living_Room_ON = createTimer(randomTimer_DateTime, [|
        Living_Room_Lights.members.forEach[i | // I don't recommend using ? because typically you want an error if Living_Room_Lights has no members, avoid using reserved words like "item"
            logInfo("Sunset Lights". "Switching light state to ON for " + i.name) // Unless this is a pretty new addition, I don't think Java and by extension the Rules DSL supports formatting of Strings using {} notation, logInfo only allows two arguments
            i.sendCommand(ON) // I always recommend using i.sendCommand over the sendCommand action as it is better able to handle data type conversions
            try{ Thread::sleep(5000) } catch(InterruptedException e) { } 
        ]
    ])
end

I won’t ave access to a computer right now. Search the forum for examples of what people are doing to create a DateTimeType using a joda DateTime.