I am trying to set up a rule/script within the OH 3 UI which is intended to do the following:
Set room state to occupied when motion sensor state changes to ON.
Set room state to unoccupied 5 minutes after motion sensor state changes to OFF
Unless it changes back ON during that 5 minute period.
My prefered method would be to use Blockly and/or JavaScript as a means of setting up the control structure I require and adding in the necessary code. However it would appear that setTimeout is not available within the OH environment so if resorted to using Rule DSL and createTimer.
the script I have written is:
val String Occupied = "Occupied"
val String Unoccupied = "Unoccupied"
val String MovingToUnoccupied = "Moving To Unoccupied"
var boolean LoggingEnabled = true
var int TimerPeriod = 2 // Time in minuted
var Timer OccupationTimer
if( StudyMotionSensor1_MotionIntrusion.state == ON )
{
if(LoggingEnabled) logInfo("Study", "Motion: ON, OccupancyStatus: " + OccupancyStatus.state)
if(OccupancyStatus.state == MovingToUnoccupied)
{
if(LoggingEnabled) logInfo("Study", "Occupancy Delay Timer Cancelled - Motion")
OccupationTimer.cancel()
}
OccupancyStatus.postUpdate(Occupied)
}
if (StudyMotionSensor1_MotionIntrusion.state == OFF)
{
OccupancyStatus.postUpdate(MovingToUnoccupied)
if(LoggingEnabled) logInfo("Study", "Occupancy Delay Timer Started")
OccupationTimer = createTimer(now.plusMinutes((TimerPeriod)), // Wait TimerPeriod until setting room unoccupied
[|
OccupancyStatus.postUpdate(Unoccupied) // No motion then switch light off
])
}
The issue I’ve run up against is that because OccupationTimer is not global it is set to null each time the rule is invoked which leads to the error:
Script execution of rule with UID ‘162f1cdbb3’ failed: cannot invoke method public abstract boolean org.openhab.core.model.script.actions.Timer.cancel() on null
I know I can get around this by using a .rules file as in OH 2 but given the way the OH developement if moving I would prefer to do it via the UI.
So my questions are:
Is there an an alternative to setTimeout in can use in JavaScript within the OH environment. If so could you please point me to where I can find details.
Is there a way of defining global variables for a rule other than defining the rule within a .rule file or creating a dummy item to hold the timerID which something I don’t see as an elegant solution since it will make the model more confusing.
I was struggling with this myself today, and cobbled together bits of knowledge from a number of posts. Much thanks to @rlkoshak for his very helpful posts and knowledge!
There is a reason, although not necessarily a good one, ignorance I knew the Expire binding didn’t exist in OH3, but didn’t realise it was now an item parameter.
That said, I’m not sure it will do what I want as the device in question can be operated in multiple ways with different time-out requirements, including no time-out. My first thought is that I could use virtual Items to achieve this, but I don’t really want to clutter my model with virtual Items.
Obviously I need to give this further thought and explore the Expire option further.
Many thanks for the pointer and expanding my knowledge a little bit further.
Indeed id also like to have more conditions on the off.
For example I want to turn on accent light via motion. But if other light sources in the room are on, I do NOT want to turn it off again.
I use node red for stuff like that, but it would be nice to cover it in rules already.
Or in the bathroom I want to dim the light after 3mins and then it off 2mins later, instead of going directly to off.
For equipments where I want a delayed power off I have a rule that sends an ON command to a item (non-linked to a channel) that expires after some time
In some cases I want a variable expiration (for example, towel radiators shall power OFF 5h in winter and 4h in summer) so I group several “expirable” items in a group
Another rule detects when the expired item (or group) goes to OFF and takes necessary actions
I use this method to:
Switch off TV box 30min after TV is shutoff between midnight and sunrise
Switch off towel radiators 3h/4h/5h after being ON (depending of several factors)
Switch off auxiliary resistance in my solar panel
Expire is good but it should not be combined with channel links because most bindings poll devices and are continuously sending ON commands, so timer never expires. I’ve raised this as an issue in github but so far it has not been actioned.
For “critical power off” situations (such as irrigation) I prefer pulsetime instead, but not all devices support it.
Am I right in thinking for the towel rail example you basically have 3 virtual items (1 each with a 3, 4, or 5hr expire) which act as proxies for the actual towel rail switch Item and you decide which one to use to switch on the towel rail depending on a combination of factors.
I can see how this would be beneficial in some circumstances and will certainly be experimenting with ‘expire’ but I currently envisage, maybe incorrectly, that their are circumstances where I would still want to use my own timer. Eg where circumstances change during the operation of the device which necessitate a different mode of operation, or the timeout needs to change dynamically during operation.
Once again thanks for providing me with an alternative way forward and plenty of food for thought.
rule "Ligar toalheiro Suite"
when
Item GF_LivingDining_TV changed from ON to OFF
then
if (gPresence.state == ON && GlobalSetting.state == "ONW" && now.getHourOfDay() < 4){
FF_MasterBedroom_Power.sendCommand(ON)
MasterBedroom_Power_Timer_5h.sendCommand(ON)
} else if (gPresence.state == ON && GlobalSetting.state == "ONS" && now.getHourOfDay() < 4){
FF_MasterBedroom_Power.sendCommand(ON)
MasterBedroom_Power_Timer_4h.sendCommand(ON)
}
end
rule "Iniciar timer toalheiro Suite"
when
Item FF_MasterBedroom_Power changed from OFF to ON
then
if (GlobalSetting.state == "ONW"){
MasterBedroom_Power_Timer_4h.sendCommand(ON)
} else if (GlobalSetting.state == "ONS"){
MasterBedroom_Power_Timer_3h.sendCommand(ON)
}
end
rule "Desligar toalheiro Suite"
when
Item gMasterBedroom_Power_Timer changed from ON to OFF
then
FF_MasterBedroom_Power.sendCommand(OFF)
end
As you point out, the drawback of expire is that time must be predefined. There are times where you need more granularity. In such cases I use either pulsetime (for single events) or createTimer(now.plusSeconds(1), ... (for a chain of events with delay between them).
True. But MQTT and TP-Link bindings do it. In my case this represents 85% of the devices that I turn ON/OFF using rules. I think that this is not documented anywhere, hence my suggestion to not use expire with linked items.
Many thanks to everyone who replied, in particular @Confectrician and @scotthraban, I now have prototype timers working in the way I envisaged. @garyfree I will also be investigating “expire” further as I can envisage a number of situations where I would find it useful.
One final question, is there a list of available/recomended Javascript includes that can be used with OpenHAB?
Most of the relevant stuff from openHAB itself is already included by default. In those rare cases where that is not the case, most of the classes at https://openhab.org/javadoc/latest/.