The following is from PaperUI
If you bind an item to the Motion Off Timer you can use this to set how long it stays in the ON state (I use 15 secs).
The Last Activity channel will show the last time it tripped and also I use a standard item name and capture the last tripped time separately based on the ON/OFF state, but you may not need to do that.
My system is a little more complex than most as I have over 360 Things and about 7800 items.
I use node-red for my rule engine with OH 2.3 but I’m looking at javascript through JSR233 for OH 2.4.
As you can imagine the migration is a big task.
What I do is use groups to trigger a proxy item on each light that I want automated (the group would have motion, door/window sensors, and anything else particular to that light).
Then the logic is done at the light level and compares the time the trigger was last tripped, adds the timer value for the light (each light has a configurable timer value in another proxy item) and compares to the current time.
I have a single function that checks every minute for all lights that are configured to allow automation (another proxy item) and send an OFF command if it is now past the timer for any particular light.
This has the advantage that it will not “forget” to turn off lights following a system restart or anything else that may make the current states/timers unreliable. It also means that I don’t have to right separate rules for each light. But it does require a strict naming convention for all lights, this is my definition for one light:
Group GRoom3MainLight "%RoomName% Main Light" <light> (GRoom3)
Group GRoom3MainLightMisc "%RoomName% Main Light Misc" <light> (GRoom3MainLight)
Switch Room3MainLightSwitch "%RoomName% Main Light" <light> (GRoom3,GRoom3MainLight,Lights,GPersist) {channel="zwave:device:8ff2027c:node75:switch_binary"}
Dimmer Room3MainLightDimmer "%RoomName% Main Light [%d %%]" <light> (GRoom3,GRoom3MainLight,GPersist)
Color Room3MainLightColour "%RoomName% Main Light Colour" <colorwheel> (GRoom3MainLight,GPersist)
Dimmer Room3MainLightColourTemp "%RoomName% Main Light Colour Temp [%d]" <colorwheel> (GRoom3MainLight,GPersist)
Switch Room3MainLightWhite "%RoomName% Main Light White" <light> (GRoom3MainLight,GPersist) { expire="1s, state=OFF" }
Number Room3MainLightWatts "%RoomName% Main Light Watts [%.1f W]" <energy> (GRoom3MainLight,GPersist,GRestore) {channel="zwave:device:8ff2027c:node75:meter_watts"}
Number Room3MainLightPower "%RoomName% Main Light Power [%d W]" <energy> (GRoom3MainLightMisc,GPowerLevels,GPersist,GRestore)
Number Room3MainLightkWh "%RoomName% Main Light kWh [%.1f kWh]" <energy> (GRoom3MainLight,GPersist,GRestore) {channel="zwave:device:8ff2027c:node75:meter_kwh"}
Number Room3MainLightYesterdaykWh "%RoomName% Main Light Yesterday kWh [%.1f kWh]" <energy> (GRoom3MainLight,GPersist,GRestore)
Number Room3MainLightTodaykWh "%RoomName% Main Light Today kWh [%.1f kWh]" <energy> (GRoom3MainLight,GPersist,GRestore)
Switch Room3MainLightAuto "%RoomName% Main Light Auto" <selfAutoMode> (GRoom3MainLight,GPersist,GRestore)
Number Room3MainLightAutoOn "%RoomName% Main Light Auto On" <selfAutoMode> (GRoom3MainLight,GPersist,GRestore)
Number Room3MainLightTimer "%RoomName% Main Light Timer [%d min]" <clock> (GRoom3MainLight,GTimers,GPersist,GRestore) [ MinutesTimer ]
Number Room3MainLightNightTimer "%RoomName% Main Light NightTimer [%d min]" <clock> (GRoom3MainLightMisc,GTimers,GPersist,GRestore) [ MinutesTimer ]
Number Room3MainLightEveningTimer "%RoomName% Main Light EveningTimer [%d min]" <clock> (GRoom3MainLightMisc,GTimers,GPersist,GRestore) [ MinutesTimer ]
Number Room3MainLightDayTimer "%RoomName% Main Light DayTimer [%d min]" <clock> (GRoom3MainLightMisc,GTimers,GPersist,GRestore) [ MinutesTimer ]
Number Room3MainLightThreshold "%RoomName% Main Light Threshold [%d lux]" <sun> (GRoom3MainLight,GPersist,GRestore) [ GSetpoint_0_1000_1 ]
Switch Room3MainLightSolar "%RoomName% Main Light Solar" <sun> (GRoom3MainLight,GPersist,GRestore)
Switch Room3MainLightDimmable "%RoomName% Main Light Dimmable" <light> (GRoom3MainLightMisc,GPersist,GRestore)
Switch Room3MainLightArmed "%RoomName% Main Light Armed" <security> (GRoom3MainLightMisc,GPersist,GRestore)
Switch Room3MainLightTrigger "%RoomName% Main Light Trigger" <selfMotion> (GRoom3MainLightMisc) { expire="15s, state=OFF" }
DateTime Room3MainLightLastChanged "%RoomName% Main Light Changed [%1$ta, %1$te/%1$tm %1$tR]" <calendar> (GRoom3MainLight,GPersist,GRestore)
DateTime Room3MainLightLastUpdate "%RoomName% Main Light Update [%1$ta, %1$te/%1$tm %1$tR]" <calendar> (GRoom3MainLight,GPersist,GRestore)
Number Room3MainLightScene "%RoomName% Main Light Scene [%d]" <calendar> (GRoom3MainLight,GPersist)
For what you are doing the relevant items are the Room3MainLightSwitch, Room3MainLightTimer, Room3MainLightTrigger and Room3MainLightLastChanged fields.
Room3MainLightLastChanged is updated whenever the the trigger or switch items change and also when the rule decides the last change was older than the timer.
The timer is in minutes.
From some older code I did the comparison would be something like this:
if (( swtimer.state > 0 ) && (now.isAfter((switchlastchanged.state as DateTimeType).calendar.timeInMillis + ((swtimer.state as DecimalType).intValue * 1000 * 60)))) myswitch.sendCommand(OFF)
So only if the timer item is set to greater than 0 will the check be done, then it checks if now is after the timer in milliseconds (minutes * 1000 * 60) and then turns off the light.
Hope this helps.