Unable to use Private Cache in Rules DSL

Using OpenHAB 3.4.1 on Ubuntu 20.04 Intel 64-bit, Java 11.0.7+8.

I’m attempting to use the new privateCache in the Rules DSL and only finding errors. My goal is to have a security motion sensor that OPENs momentarily update a presence Switch with a 30 second minimum hold time. I do not want to create a timer var for each possible target item. I just want to set a timer and fetch it later if the rule needs to update that timer.

Items:

Group:Contact:OR(OPEN,CLOSED) gMotion_Sensors "Motion Sensors" (gSecurity)
Group:Contact:OR(OPEN,CLOSED) gIndoor_Motion_Sensors "Indoor Motion Sensors" (gMotion_Sensors)

Group:Switch:OR(ON, OFF) gEntry_Presence "Entry Presence"
Contact BedroomsUp_Motion "Bedrooms PIR" <motion> (DSCAlarmZones, DSCAlarmMotion, gIndoor_Motion_Sensors) { channel="dscalarm:zone:megahaus:zone1:zone_status" }
Switch BedroomsUp_Presence (gEntry_Presence)

Rule (not all {name}_Presence exist, so there is a check to avoid errors)

rule "Motion to Presence"
when
  Member of gIndoor_Motion_Sensors changed
then
  val triggeringItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName)
  val actionItemName = triggeringItem.name.toString.replace("_Motion","_Presence")
  if (ScriptServiceUtil.getItemRegistry.getItems(actionItemName).length < 1) return
  var actionItem = ScriptServiceUtil.getItemRegistry.getItem( actionItemName )
  var newValue = if (triggeringItem.state == OPEN) ON else OFF
  logWarn("security", "transforming {}={} to {}={}", triggeringItem.name, triggeringItem.state, actionItem.name, newValue)
  var presenceTimer = privateCache.get(actionItem.name.toString)
  if (newValue == ON) {
    actionItem.sendCommand(newValue)
    if (presenceTimer === null) privateCache.put(actionItem.name.toString,
      createTimer(now.plusSeconds(30)) [|
        logWarn("presence", "{} timeout", actionItem.name.toString)
        actionItem.sendCommand(OFF)
        privateCache.remove(actionItem.name.toString)
      ]
    ) else presenceTimer.reschedule(now.plusSeconds(30))
  }
end

Logs indicate privateCache is not defined in this scope.

2023-01-19 14:12:38.978 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'BedroomsUp_Motion' changed from CLOSED to OPEN
2023-01-19 14:12:38.984 [WARN ] [g.openhab.core.model.script.security] - tranforming BedroomsUp_Motion=OPEN to BedroomsUp_Presence=ON
2023-01-19 14:12:38.985 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'security-14' failed: The name 'privateCache' cannot be resolved to an item or type; line 214, column 23, length 12 in security

The code looks like it should be in scope here. What am I doing wrong?

That code is part of a commit from two weeks ago, after 3.4 was released. The private and shared cache variables may not be available for Rules DSL until OH 4. Or it might be possible that it’s only available in UI rules. You don’t really need it in text based rules, just declare a variable outside the rule at the top of the .rules file.

You don’t need to. Create a Map.

val timers = createHashmap

It has the same put and get methods that the privateCache uses (since it’s a Map that backs that as well).

Ah, thanks! I wanted to use a map, but couldn’t find the proper search incantation. The solution below seems to be working (note newHashMap not createHashMap :wink:). To be clear, if I actually wanted the global scope of sharedCache to make a value available between rules, I would need to run a nightly, move to another scripting engine, or wait for a release that includes it for Xtend? No other workaround for global scope?

import java.util.Map
val Map<String, Timer> timers = newHashMap

rule "Motion to Presence"
when
  Member of gIndoor_Motion_Sensors changed
then
  val triggeringItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName)
  val actionItemName = triggeringItem.name.toString.replace("_Motion","_Presence")
  if (ScriptServiceUtil.getItemRegistry.getItems(actionItemName).length < 1) return
  var actionItem = ScriptServiceUtil.getItemRegistry.getItem( actionItemName )
  var newValue = if (triggeringItem.state == OPEN) ON else OFF
  logWarn("security", "transforming {}={} to {}={}", triggeringItem.name, triggeringItem.state, actionItem.name, newValue)
  var presenceTimer = timers.get(actionItem.name.toString)
  if (newValue == ON) {
    actionItem.sendCommand(newValue)
    if (presenceTimer === null) timers.put(actionItem.name.toString,
      createTimer(now.plusSeconds(30)) [|
        logWarn("presence", "{} timeout", actionItem.name.toString)
        actionItem.sendCommand(OFF)
        timers.remove(actionItem.name.toString)
      ]
    ) else presenceTimer.reschedule(now.plusSeconds(30))
  }
end
2023-01-19 14:23:37.847 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'BedroomsUp_Motion' changed from OPEN to CLOSED
2023-01-19 14:23:37.853 [WARN ] [g.openhab.core.model.script.security] - transforming BedroomsUp_Motion=CLOSED to BedroomsUp_Presence=OFF
2023-01-19 14:23:41.638 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'BedroomsUp_Motion' changed from CLOSED to OPEN
2023-01-19 14:23:41.643 [WARN ] [g.openhab.core.model.script.security] - transforming BedroomsUp_Motion=OPEN to BedroomsUp_Presence=ON
2023-01-19 14:23:45.871 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'BedroomsUp_Motion' changed from OPEN to CLOSED
2023-01-19 14:23:45.877 [WARN ] [g.openhab.core.model.script.security] - transforming BedroomsUp_Motion=CLOSED to BedroomsUp_Presence=OFF
2023-01-19 14:24:11.646 [WARN ] [g.openhab.core.model.script.presence] - BedroomsUp_Presence timeout
2023-01-19 14:24:11.647 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'BedroomsUp_Presence' received command OFF
2023-01-19 14:24:11.648 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'BedroomsUp_Presence' changed from ON to OFF
2023-01-19 14:24:11.648 [INFO ] [hab.event.GroupItemStateChangedEvent] - Item 'gEntry_Presence' changed from ON to OFF through BedroomsUp_Presence

I’m not sure what “run a nightly” means.

I believe the sharedCache should already be available in OH 4 for Rules DSL, but again, I don’t know if it has universal support or only UI rules support.

And of course, another option is to move the rules to the UI if that is in fact the limitation here.

Yet another option is to put all your rules into one file.

Hello,

I try to use the sharedCache feature in my DSL rules but I have errors on OH 4.1.1 (via Docker).

My rule:

rule "Test SharedCache in OH 4.1.1 / DSL Engine"
when
    Time cron "*/30 * * * * ?"   // Every 30 secondes
then
    var testSharedCache = sharedCache.get('foo')
    logInfo("testSharedCache", "testSharedCache = " + testSharedCache)
end

openhab.log:

2024-01-11 14:31:00.132 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'intrusion_extension-3' failed: The name 'sharedCache' cannot be resolved to an item or type; line 104, column 27, length 11 in intrusion_extension
2024-01-11 14:31:30.131 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'intrusion_extension-3' failed: The name 'sharedCache' cannot be resolved to an item or type; line 104, column 27, length 11 in intrusion_extension

The relevant pull request https://github.com/openhab/openhab-core/pull/3187 from @J-N-K has been merged in december 2022.

What’s something wrong ?

Thank you

I can’t say what’s wrong. I just ran the following code in 4.1.1 and it worked as expected.

sharedCache.get('foo')
sharedCache.put('foo', 'bar')
logInfo('Rules DSL Scratchpad', 'Foo is ' + sharedCache.get('foo'))

I don’t use .rules files so maybe there is a difference there? There shouldn’t be but who knows.

I copied your code to a rule and it works as expected too.

Thank you @rlkoshak or your response :slight_smile:
Indeed, if it works on your DSL engine on OH 4.1.1, that’s curious.
I will try to isolate this rule in a new simpler file in order to see the behavior on my OH 4.1.1 instance.