[SOLVED] Variable timer on sitemap

I would like to set up a variable timer on my sitemap.

I have integrated my ceiling fan as an item via a Zigbee plug. Can I include an element in my sitemap where I can put in any amount of minutes (text box, drop down…) and then hit run to power the fan for set time?

As a compromise, 2 or three fixed scenarios (run for 15, 30 or 60 minutes) to choose from would be fine as well.

Any ideas?

Yes, you can do that. Try it :smiley: Let us know which parts you have trouble with.

Okay, I’ve stumbled about something similar in a coding example for an irrigation. It uses two sitemap items: one Selection and one Switch.

When the switch is changed to ON, the item is turned on and an OFF command is scheduled via
“createTimer(now.plusMinutes(Integer::parseInt(FanDuration.state.toString))) [|
Fan_State.sendCommand(OFF)]”

Is this the simplest way or is there a solution that only uses one line of the sitemap?

Well, if you use a Selection on your sitemap, you can have a rule that listens for commands to the selected Item from user clicking. That could take the place of a separate “go” button, it’s up to you.

Makes sense and works as well :wink:

Is there any way to kill previous timers? E.g., if I select 15 minutes and then change my mind and select 30, the 15 minute timer will still kill the fan, right?
Even from testing now, I probably have like 15 timers in line waiting to turn the fan off :wink:

val String filename = "fan.rules"

rule "Fan Timer"
when
	Item FF_Fan_Duration changed 
then
    if (FF_Fan_Duration != 0) {
        FF_Fan.sendCommand(ON)
		logInfo(filename,"Ventilator gestartet.")
			createTimer(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString))) [|
				FF_Fan.sendCommand(OFF)
				logInfo(filename,"Ventilator gestoppt.")
                FF_Fan_Duration = null]  
	}	
end

Yes, this is very common. The trick is to keep a “handle” to the Timer in a global variable, allowing you check if one is off already and then cancel or reschedule it.

Thank you, I’ll look into this later.

For the current code, I have an issue with the last line:

val String filename = "fan.rules"

rule "Fan Timer"
when
	Item FF_Fan_Duration changed 
then
if (FF_Fan_Duration == 0) {
        FF_Fan.sendCommand(OFF)
		logInfo(filename,"Ventilator gestoppt.")
	}	
    if (FF_Fan_Duration == 99) {
        FF_Fan.sendCommand(ON)
		logInfo(filename,"Ventilator gestartet.")
	}	
    if (FF_Fan_Duration != 0) {
        FF_Fan.sendCommand(ON)
		logInfo(filename,"Ventilator gestartet.")
			createTimer(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString))) [|
				logInfo(filename,"Ventilator gestoppt.")
                FF_Fan_Duration = 0]  
	}	
end

I would like to change FF_Fan_Duration (Number item) to 0 and thereby trigger the first part of the rule to stop the fan but:

Type mismatch: cannot convert from int to NumberItem

In the sitemap, I can assign values like 0 to the item just fine:

Selection item=FF_Fan_Duration mappings=[0="Aus",1="1 Min.",15="15 Min.",30="30 Min.",60="60 Min.",99="An"]

When you click in a sitemap, it sends commands to the Item. Very often (but not always) that can result in a state update as well.

If you want to change the state of an Item in a rule, use one of the provided postUpdate methods, which are smart enough to use type conversions in many cases.

SomeItem.postUpdate(newstate)

Yes, seems to work. Now the conditions don’t work as intended. Again my code:

val String filename = "fan.rules"

rule "Fan Timer"
when
	Item FF_Fan_Duration changed 
then
if (FF_Fan_Duration == 0) {
        FF_Fan.sendCommand(OFF)
		logInfo(filename,"Ventilator gestoppt.")
	}	
    else if (FF_Fan_Duration == 99) {
        FF_Fan.sendCommand(ON)
		logInfo(filename,"Ventilator gestartet.")
	}	
    else if (FF_Fan_Duration != 0) {
        FF_Fan.sendCommand(ON)
		logInfo(filename,"Ventilator gestartet.")
			createTimer(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString))) [|
				logInfo(filename,"Ventilator gestoppt.")
                FF_Fan_Duration.sendCommand(0)]
	}	
end

Starting the fan and timer with the Selection works:

2019-04-29 20:47:58.599 [ome.event.ItemCommandEvent] - Item 'FF_Fan_Duration' received command 1
2019-04-29 20:47:58.621 [vent.ItemStateChangedEvent] - FF_Fan_Duration changed from 0 to 1
2019-04-29 20:47:58.652 [ome.event.ItemCommandEvent] - Item 'FF_Fan' received command ON
==> /var/log/openhab2/openhab.log <==
2019-04-29 20:47:58.652 [INFO ] [pse.smarthome.model.script.fan.rules] - Ventilator gestartet.
==> /var/log/openhab2/events.log <==
2019-04-29 20:47:58.666 [nt.ItemStatePredictedEvent] - FF_Fan predicted to become ON
2019-04-29 20:47:58.676 [vent.ItemStateChangedEvent] - FF_Fan changed from OFF to ON

After one minute FF_Fan_Duration is set to 0 and should trigger the first if clause to turn the item off. However, it only puts the text of the first if clause into the log and apparently enters the last if clause once more…

19-04-29 20:48:58.660 [INFO ] [pse.smarthome.model.script.fan.rules] - Ventilator gestoppt.
==> /var/log/openhab2/events.log <==
2019-04-29 20:48:58.674 [ome.event.ItemCommandEvent] - Item 'FF_Fan_Duration' received command 0
2019-04-29 20:48:58.704 [vent.ItemStateChangedEvent] - FF_Fan_Duration changed from 1 to 0
==> /var/log/openhab2/openhab.log <==
2019-04-29 20:48:58.752 [INFO ] [pse.smarthome.model.script.fan.rules] - Ventilator gestartet.
==> /var/log/openhab2/events.log <==
2019-04-29 20:48:58.762 [ome.event.ItemCommandEvent] - Item 'FF_Fan' received command ON
2019-04-29 20:48:58.777 [nt.ItemStatePredictedEvent] - FF_Fan predicted to become ON
==> /var/log/openhab2/openhab.log <==
2019-04-29 20:48:58.777 [INFO ] [pse.smarthome.model.script.fan.rules] - Ventilator gestoppt.
==> /var/log/openhab2/events.log <==
2019-04-29 20:48:58.797 [ome.event.ItemCommandEvent] - Item 'FF_Fan_Duration' received command 0

Side note: is there another platform for this nitty gritty trouble shooting? I feel like I’m taking up the time and place here from bigger issues…

FF_Fan_Duration is an Item. You’l be interested in its .state (as opposed to its name or type etc.)

Nope, this is exactly the right place.

Riiiight, and it behaved like this because FF_Fan_Duration !=0 was always true.
Got it, thank you very much!

I’ve tried to implement the timer logic as per the previous link. Everything looks good up until the first createTimer.
The } after FF_Fan_Duration.sendCommand(0)] is marked with the comment “extraneous input ‘]’ expecting ‘}’(org.eclipse.xtext.diagnostics.Diagnostic.Syntax)”

var Timer fantimer = null
val String filename = "fan.rules"

rule "Fan Timer"
when
	Item FF_Fan_Duration changed 
then
if (FF_Fan_Duration.state == 0) {
        fantimer.cancel
        FF_Fan.sendCommand(OFF)
		logInfo(filename,"Ventilator gestoppt.")
	}	
    else if (FF_Fan_Duration.state == 99) {
        if(FF_Fan == ON) {
            fantimer.cancel
     	    logInfo(filename,"Timer gestoppt, Ventilator bleibt an.")
        }
        else {
            FF_Fan.sendCommand(ON)
		    logInfo(filename,"Ventilator gestartet.")
        }
	}
    else if (FF_Fan_Duration.state != 0) {
        if (fantimer === null) {
            FF_Fan.sendCommand(ON)
			fantimer = createTimer(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString))) [|
            FF_Fan_Duration.sendCommand(0)]
            }
        else {
            fantimer.reschedule(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString)))
        }
	}	
end

Remember, it’s the .state of Items that is of interest

Don’t forget you’ll want to set that back to null at some time, if you want the rule to run more than once.

Some versions of OH seem to be picky about square brackets. Try making sure there is whitespace separating them.

1 Like

Thank you very much! After a few changes it works like a charm now. I’m sure the code could be improved but the logic is not complicated at all once you get the syntax :wink:

If any other beginner is interested in a simple fan control:

Items:

Number	 FF_Fan_Duration            "Laufzeit Ventilator [%d Min.]" <time>
Switch   FF_Fan                     "Ventilator"               <fan_ceiling>   (FF_MasterBedroom, gFan)       ["Fan", "Switchable"]        {channel="zigbee:device:cd755e3f:84182600000fd1f8:84182600000FD1F8_3_switch"}

Sitemap:

Selection item=FF_Fan_Duration mappings=[0="Aus",1="1 Min.",15="15 Min.",30="30 Min.",60="60 Min.",99="An"]

Rules:

var Timer fantimer = null
val String filename = "fan.rules"

rule "Fan Timer"
when
	Item FF_Fan_Duration changed 
then
if (FF_Fan_Duration.state == 0) {
        FF_Fan.sendCommand(OFF)
        logInfo(filename,"Ventilator gestoppt.")
        if (fantimer !== null){
            fantimer.cancel
            fantimer=null
	    }
}	
else if (FF_Fan_Duration.state == 99) {
    if (FF_Fan.state == ON) {
        if (fantimer !== null){
            fantimer.cancel
            fantimer=null
        }	
        logInfo(filename,"Timer gestoppt, Ventilator bleibt an.")
    }
    else {
        FF_Fan.sendCommand(ON)
        logInfo(filename,"Ventilator gestartet.")
    }
}
else {
    if (fantimer === null) {
        if (FF_Fan.state == OFF) {
            FF_Fan.sendCommand(ON)
            logInfo(filename,"Ventilator gestartet mit "+FF_Fan_Duration.state+" Minuten Laufzeit.")}
        else {
            logInfo(filename,"Ventilator Laufzeit angepasst auf "+FF_Fan_Duration.state+" Minuten.")
        }
        fantimer = createTimer(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString))) [|
        FF_Fan_Duration.sendCommand(0)]
        }
    else {
        fantimer.reschedule(now.plusMinutes(Integer::parseInt(FF_Fan_Duration.state.toString)))
        logInfo(filename,"Ventilator Laufzeit angepasst auf "+FF_Fan_Duration.state+" Minuten.")
    }
}
end
1 Like