Timer Value


(Thomas) #3

Hello Rich,
thank you for the your explanation.
I tried to implement you “countdown” code but I received the following message from OpenHab Designer. "Couldn’t resolve reference to JvmIdentifiableElement ‘Countdown’.
No idea what i did wrong.

As you can see my programming knowledge is not brilliant.

So any help is much appreciated


(Rich Koshak) #4

Have you defined Countdown in a .items file anywhere? Did you save that .items file?

You need to define all Items that you use in a .items file.

Number Countdown "My Countdown [%d]"

(Thomas) #5

Thank you for the fast replay!
Yes I defined the Countdown in the .items file. (like all the outer Items).
I restarted OpenHAB Designer and then the error messages dissipated. It is now working for me :smile: Tknaks for your help!


(Jonas) #6

Hi,

i got it working for me too but i want a number (set from habpanel) as the timer value so i just replaced the 180 with number.state and it does not work.
It updates my current time once but then is stuck.
i cant see why because the while-loop should be still active.


(Udo Hartmann) #7

Please use

(number.state as DecimalType)

instead


(Jonas) #8

Hi!

Thanks for your reply!

still no luck - same issue … here my rules-file:

rule "Countdown"
when
    Item WoZi_Schalter_Countdown received command ON
then
    var count = (WoZi_Nummer_Countdown_Wunsch.state as DecimalType)
    while(count >= 0) {
        WoZi_Nummer_Countdown_Ist.postUpdate(count)
        count = count - 1
        Thread::sleep(1000)
    }
    WoZi_Licht_Couchtisch_Alarm.sendCommand("LSELECT")
end

and the items:

/* Timer */
Number WoZi_Nummer_Countdown_Wunsch "Wohnzimmer-Countdown [%d]"
Number WoZi_Nummer_Countdown_Ist "Istzustand Countdown [%d]"
Switch WoZi_Schalter_Countdown "Starte Countdown Wohnzimmer"

(Rich Koshak) #9

Try

var count = (WoZi_Nummer_Countdown_Wunsch.state as Number).intValue

(Jonas) #10

working like charm! thank you! can you link me to an article where i can see some commands? or is this java script? then i know where to read :wink:

thank you


(Rich Koshak) #11

http://docs.openhab.org/configuration/rules-dsl.html


(Jonas) #12

Tanks a lot!
Now i have another issue. I kind of understand why but i don’t know how to solve it:

Everytime i push the Switch the timer function (rule) is called again and this messes up the current timer (it posts updates from each instance of functions that is called).
to solve this i wanted to reset the counter to 0. When the rule is called (i assume in a new instance) the var counter is of course not the same as before so i cant just write:

var counter = 0

after the when.
So i decided to create a global variable in the items as a number. (just added:

Number counter “Zaehler”

to items. I don’t quite understand why this does not solve my problem and how to write to number variables.
i used sendCommand in the past but this won’t help if i use math-functions as in this case.

Why can i not just assign a value to the Number-Variable like:
counter = 0 (counter defined in items files as described above).

Thank you!


(Rich Koshak) #13

There is nothing “Number-Variable like” about a Number Item. Items are complex Objects with lots of moving parts and the Number Item just happens to carry a State that is a Number.

To change the state of an Item you must use either sendCommand or postUpdate. This is the only way to change the state of an Item.

I don’t understand this statement. I and others do math with and update the results from calculations using postUpdate and sendCommand all the time. Please review:

and if that doesn’t solve your problem post your code.


(Raf Daems) #14

Hello, my very first post after 6 months learning OH.

A small improvement on this script:
==> purpose is to make the timer restartable when is it still running.

I uploaded a v2 version, using switches instead of global variables, and applying some formatting.
Please note, this script is just a snippet, it contains (at least I think) reusable code - but it needs tweaking; Mind for instance the dummy if-then-else to test the display of days, when needed. Of course, this is no real life case, but my code to test the display of days (easily).

Lastly, I reused code I found here on the forum to:

  • create the countdown timer
  • display it properly with leading zero’s, when needed.
    So my contribution is the assembly of those parts to a ‘restartable’ countdown timer.
var count = 180


rule "Countdown"
when
    Item broodKlaar changed to ON
then
    //logInfo("countdown", "countdownRunning.state :"+countdownRunning.state.toString)
    
//initialise when needed
    if (countdownRunning.state == NULL) sendCommand(countdownRunning,OFF)
    if (countdownStop.state == NULL) sendCommand(countdownStop,OFF)
    
    if (countdownRunning.state==ON) //is timer already running --> start the logic to restart it
    {
        sendCommand(countdownStop,ON) // see below, this breaks the while loop
        Thread::sleep(2500) // needed to be sure the while loop condition evaluated at least once
        count = 180 //reinitialise to value of choice
        sendCommand(countdownRunning,OFF) 
        sendCommand(countdownStop,OFF)
        sendCommand(broodKlaar,OFF) // to act as a push button iso a two-state switch
        Thread::sleep(2500) // setting switches here above takes some time, so stalling a bit.
    }
    
    if (countdownRunning.state==OFF) // no timer is running (yet)
    {
        sendCommand(countdownRunning,ON)  
        sendCommand(broodKlaar,OFF) // to act as a push button iso a two-state switch
        while(count >= 0 && countdownStop.state == OFF) { 
            var sec = count % 60
            var min = (count / 60) % 60
            var hrs = (count / (60*60)) % 24
            var day = count / (60*60*24)
            var String stringToSend 
            
            if ( count  < 150 && count > 100)  //dummy code to test the display of days, when needed
                {
                    day=5
                //stringToSend = day.toString("02d") + " days, " + hrs.toString("02d") + ":" + min.toString("02d") + ":" + sec.toString("02d") 
                stringToSend = String::format("%1$02d days, %2$02d:%3$02d:%4$02d", day, hrs, min, sec)
                }
                else 
                //stringToSend = hrs.toString("02d") + ":" + min.toString("02d") + ":" + sec.toString("02d")
                stringToSend = String::format("%1$02d:%2$02d:%3$02d", hrs, min, sec)
            
            countdown.postUpdate(stringToSend)
            
            count = count - 1
            Thread::sleep(1000)    
        }
        
    }
    
end

Hoping I use the correct formatting.

Cheers,

Raf


(Rich Koshak) #15

All of those long Thread::sleeps give me concern. Especially there long Thread::sleep in the while loop. See Why have my Rules stopped running? Why Thread::sleep is a bad idea for why and alternatives.


(Raf Daems) #16

Hello, thanks for the heads-up;

maybe this looks better in the sight of ‘no sleep’ :wink:

Remark: don’t pay attention to the (if 1==1), just a part of the development track of this organic code;

var Integer startvalue = 30

rule "Countdown running"
when
    Item countdownStart changed to ON
    or
    Item countdownKeeprunning changed to ON
    or
    Item countdownReset changed to ON
then
    
    if (countdownRunning.state == NULL) sendCommand(countdownRunning,OFF)
    if (countdownReset.state == NULL) sendCommand(countdownReset,OFF)
    if (countdownKeeprunning.state == NULL) sendCommand (countdownKeeprunning,OFF)
    if (countdownStart.state == NULL) sendCommand (countdownStart,OFF)
    if (count.state == NULL || (count.state as Number).intValue==0) count.postUpdate(startvalue)
    
    
    if (countdownStart.state == ON)
    {
        if(1==1) { //showing the result
            logInfo("Countdown","showing the result")
            var sec = (count.state as Number).intValue  % 60
            var min = (((count.state as Number).intValue) / 60) % 60
            var hrs = (((count.state as Number).intValue) / (60*60)) % 24
            var day = ((count.state as Number).intValue) / (60*60*24)
            var String stringToSend 
            
            if ( count.state  < 25 && count.state > 20) 
                {
                    day=5
                    stringToSend = String::format("%1$02d days, %2$02d:%3$02d:%4$02d", day, hrs, min, sec)
                }
                else 
                {
                    stringToSend = String::format("%1$02d:%2$02d:%3$02d", hrs, min, sec)
                }   
            
            
            
            countdown.postUpdate(stringToSend)
        }

        if (count.state >0 && countdownReset.state==OFF)
        {
            createTimer(now.plusSeconds(1),  [ |
               logInfo ("Countdown","if count.state > 0 - start == ON")
               count.postUpdate((count.state as Number).intValue -1)
               sendCommand(countdownKeeprunning, OFF)
               sendCommand(countdownKeeprunning, ON)
            ])
        }
        if ((count.state as Number).intValue == 0) sendCommand(countdownStart,OFF)

         
    }

    if (countdownReset.state==ON)
    {
        count.postUpdate(startvalue)
        countdown.postUpdate("resetting")
        sendCommand(countdownKeeprunning,OFF)
        sendCommand(countdownReset,OFF)
        sendCommand(countdownStart,OFF)
        createTimer(now.plusMillis(1200),  [ |
               count.postUpdate (startvalue)
               sendCommand(countdownStart,ON) 
        ])   
    }
    
    
end

rule "Countdown cleaning up"
when
    Item countdownStart changed to OFF
then
    
    if (countdownRunning.state == NULL) sendCommand(countdownRunning,OFF)
    if (countdownReset.state == NULL) sendCommand(countdownReset,OFF)
    if (countdownKeeprunning.state == NULL) sendCommand (countdownKeeprunning,OFF)
    if (countdownStart.state == NULL) sendCommand (countdownStart,OFF)
    if (count.state == NULL) count.postUpdate(startvalue)
    
    createTimer(now.plusMillis(1200),  [ |
               sendCommand(countdownKeeprunning,OFF)
               sendCommand(countdownReset,OFF)   
               countdown.postUpdate ("timer stopped")
               count.postUpdate (startvalue) 
    ])
    
end    

(Vincent Regaud) #17

I quickly perused your code:
In your last rule, the trigger is countdownStart changed to OFF so there is no need to test if NULL
Also, when then items are know, use the sendCommand method.
If these items have no binding then use the postUpdate method instead (It’s quicker because the sendCommand will send a command and then post an update but you don’t need the command because there is no physical device involved with a binding)

So a quick clean up:

rule "Countdown cleaning up"
when
    Item countdownStart changed to OFF
then
    if (countdownRunning.state == NULL) countdownRunning.postUpdate(OFF)
    if (countdownReset.state == NULL) countdownReset.postUpdateOFF)
    if (countdownKeeprunning.state == NULL) countdownKeeprunning.postUpdate(OFF)
    if (count.state == NULL) count.postUpdate(startvalue)
    
    createTimer(now.plusMillis(1200),  [ |
               countdownKeeprunning.postUpdate(OFF)
               countdownReset.postUpdate(OFF)   
               countdown.postUpdate ("timer stopped")
               count.postUpdate (startvalue) 
    ])
    
end    

You can do the same on your other rules


(Rich Koshak) #18

I’ll second all of Vincent’s recommendations. I’m much happier with this version that lacks the Thread::sleeps.


(Raf Daems) #20

update with your hints:

What I’ve learned:

  • difference between sleep and createtimer
  • difference between sendcommand and postupdate
var Integer startvalue = 10

rule "Countdown initialise"  
when 
    System started
then
    if (countdownRunning.state == NULL) countdownRunning.postUpdate(OFF)
    if (countdownReset.state == NULL) countdownReset.postUpdate(OFF)
    if (countdownKeeprunning.state == NULL) countdownKeeprunning.postUpdate(OFF)
    if (countdownStart.state == NULL) countdownStart.postUpdate(OFF)
    if (count.state == NULL) count.postUpdate(startvalue)
   
end

rule "Countdown running"
when
    Item countdownStart changed to ON
    or
    Item countdownKeeprunning changed to ON
    or
    Item countdownReset changed to ON
then

    if(1==1) //formatting and outputting count to stringToSend 
    { 
        logInfo("Countdown","formatting and outputting count to stringToSend --> Countdown ")
        var sec = (count.state as Number).intValue  % 60
        var min = (((count.state as Number).intValue) / 60) % 60
        var hrs = (((count.state as Number).intValue) / (60*60)) % 24
        var day = ((count.state as Number).intValue) / (60*60*24)
        var String stringToSend 
            
        if ( count.state  < 25 && count.state > 20) 
            {
                day=5
                stringToSend = String::format("%1$02d days, %2$02d:%3$02d:%4$02d", day, hrs, min, sec)
            }
            else 
            {
                stringToSend = String::format("%1$02d:%2$02d:%3$02d", hrs, min, sec)
            }   
            
            
        countdown.postUpdate (stringToSend)        
    }

    
     
     
    if (countdownStart.state == ON)  //Start is ON
    {
        logInfo("Countdown","Start is on")
        if ((count.state as Number).intValue > 0 && countdownReset.state==OFF)
        {
            logInfo("Countdown","countdown value in seconds: " + count.state.toString)
            createTimer(now.plusSeconds(1),  [ |
               if (countdownStart.state == ON) //if start is STILL on this second later
               {
                    logInfo ("Countdown","((count.state as Number).intValue > 0 && countdownReset.state==OFF)")
                    count.postUpdate((count.state as Number).intValue -1)
                    countdownKeeprunning.postUpdate(OFF)
                    countdownKeeprunning.postUpdate(ON)
               }
            ])
        }
         
         
    }

    if (countdownReset.state==ON)
    {
        logInfo("Countdown","if (countdownReset.state==ON)")
        countdown.postUpdate("resetting")
        countdownKeeprunning.postUpdate(OFF)
        countdownReset.postUpdate(OFF)
        countdownStart.postUpdate(OFF)
        createTimer(now.plusMillis(1500),  [ |
                count.postUpdate (startvalue)
                countdownStart.postUpdate(ON) 
        ])   
    }
    
    if ((count.state as Number).intValue == 0) 
    {
        logInfo("Countdown"," if ((count.state as Number).intValue == 0)")
        countdownStart.postUpdate(OFF)
    }
end

rule "Countdown cleaning up"
when
    Item countdownStart changed to OFF
then
    logInfo("Countdown"," Item countdownStart changed to OFF")
        
    createTimer(now.plusMillis(1200),  [ |
               countdownKeeprunning.postUpdate(OFF)
               countdownReset.postUpdate(OFF)   
               countdown.postUpdate ("timer stopped")
               count.postUpdate (startvalue) 
    ])
    
end        

Creating rules: how to make them general ? Shoudl I use proxy-items, or variables?
(Rune) #21

@Raf_Daems Could you please post the complete example including items/sitemap? It looks very interesting, so I’d try it out :slight_smile:


(Raf Daems) #22

Very sorry for my late reply.

I’m afraid I did nor do have a lot more than this.

I use habpanel.

Let me know if that answers more or less your question?

Best regards

Raf


(Rune) #23

ok, I’ll give it one more try :slight_smile: