Presence Simulation

Thanks. With this approach I was able to identify the problem. It seems that one of my KNX switches didn’t return a proper status update. Very strange, since these KNX switches have been set up at least 5 years ago and I never noticed the problem. It seems my presence simulation is working now. :slight_smile:

Another question: the cron job is scheduled every 5 minutes (in the example code). Would you consider that as the recommended setting? I’m thinking of setting this to every minute. With the job set every five minutes, it might become obvious that lights are always turned on/off at 18h00, 18h05, 18h10… Or would it put too much of a burden on the OH system (I have approx. 20 switches in my presence simulation group)?

A minute is an eternity for a computer. I wouldn’t worry about any performance problems.

You could add a bit of noise to when they start by adding a random number to the timer so nothing starts exactly on any minute and the lights don’t go on all at the same time.

2 Likes

Ok, clear!

I have made some adaptations now:

  • Job runs every minute instead of every five minutes;
  • I have changed the delay from “50” to “1000”. I realize the initial purpose of “delay” was to avoid flooding OH with many commands at the same time. But with a delay of 1000 also the lights are turned on slightly more spread in time. If you have 20 lights, the last light will be switched 20 seconds after the rule has started. I know it’s not random, but someone trying to break in needs to be pretty smart to see a pattern. :slight_smile:
// ***************************
// Global variables
// ***************************

var int presence_days = 1
var int presence_delay = 1000
var String persistence = "influxdb"

// ***************************
// Presence Simulation
// ***************************

rule "Presence Simulation"
when
	Time cron "0 0/1 * 1/1 * ? *"
then
	if (Virtual_PresenceSimulation.state == ON) {
		gPresenceSimulation.members.forEach(light,i |
			if(light.historicState(now.minusDays(presence_days), persistence).state != light.state) {
				createTimer(now.plusMillis(i*presence_delay)) [|
					logInfo("Pres_Sim",light.name + " schakelt " + light.historicState(now.minusDays(presence_days), persistence).state)
					light.sendCommand(light.historicState(now.minusDays(presence_days), persistence).state.toString)
				]
			}
		)
	}
end

4 Likes

Just setting this up for my home.
I want to use your script Dries.

The only thing which I need to change is the gPresenceSimulation with my lights group i want to manage right ? In my case that would be gLight.

I also got the Virtual_PresenceSimulation switch item.

Did I miss anything which I need to add to my .items file etc. ?

regards

Hi,
Assuming you are using influxdb as persistence, that’s the only thing you would need to adjust, indeed.

Good luck,
Dries

1 Like

Just a small question: does this work in OH3 as well? And where exactly do I enter this code?
If I do it via Rules --> Code, the code always “disappears” .
If i do it via Scripts --> the EXMAScript and the Rule DSL doesn’t do anything… :roll_eyes:

It should work in OH 3.

You need to save them to a .rules file in the conf/rules folder. The code above will not work in Ui created rule without changes.

Came here as the previous method I used (google calendar scheduler) is no longer supported in OH3.

Tried the great method in this thread but had a little difficulty at first but quickly found the answer here

In short, change this (from above code):
createTimer(now.plusMillis(i*presence_delay))

To this:
createTimer(now.toInstant().plusMillis(i*presence_delay).atZone(now.zone))

For completeness and for anyone else who is interested in this method here is what I have now which I can confirm works with OH3.

Make sure influxDB is setup, ether manually or via openhabian

Add following:

.items

Switch Swi_PreSim  // used in sitemap to turn simulation on/off

Group  gSim        // add this group to items you wish to be included

example light to be included:

Dimmer Dim_Lounge_Rear "Lounge Rear" <light> (gSim) { channel="xyz" }

.persist

Items {
gSim* : strategy = everyChange
}

.rules

// **************************
// Global variables
// ***************************
var int presence_days = 1
var int presence_delay = 1000
var String persistence = "influxdb"
// ***************************
// Presence Simulation
// ***************************
rule "Presence Simulation"

when

    //Time cron "0 0/1 * 1/1 * ? *" //every 60 sec
    Time cron "0/30 0/1 * 1/1 * ? *" //every 30 sec

then

    if (Swi_PreSim.state == ON) {
        gSim.members.forEach(light,i |
            if(light.historicState(now.minusDays(presence_days), persistence).state != light.state) {
                //createTimer(now.plusMillis(i*presence_delay)) [|  OH2
                createTimer(now.toInstant().plusMillis(i*presence_delay).atZone(now.zone)) [| //OH3
                    logInfo("Pres_Sim",light.name + " state " + light.historicState(now.minusDays(presence_days), persistence).state)
                    light.sendCommand(light.historicState(now.minusDays(presence_days), persistence).state.toString)
                ]
            }
        )
    }
end

Pleased I found this as it is so much simpler than the Google method, no messing about with their API and all done locally, wish I discovered this a while ago!

2 Likes

What would this rule look like if it was ported to javascript on OH3?

Let me share with you how far I got so far.
For timers some investigation is needed:

triggers:
  - id: "1"
    configuration:
      cronExpression: 0 * * * * ? *
    type: timer.GenericCronTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var logger =
        Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Experiments");

        var PersistenceExtensions = Java.type("org.openhab.core.persistence.extensions.PersistenceExtensions");

        var ZonedDateTime = Java.type("java.time.ZonedDateTime");


        logger.info("Running presence simulation");

        var presence = ir.getItem("PresenceSimulation").state

        var presenceOffset = 7


        if (presence == ON) {
          var group = ir.getItem("gPresenceSimulation");
          group.allMembers.forEach(function (item) {

            logger.info("processing gPresenceSimulation member {}", item.name);

            var historicState = PersistenceExtensions.historicState(item, ZonedDateTime.now().minusDays(presenceOffset),"rrd4j").state;
            if (historicState != item.state) {

              logger.info("Pres_Sim {} set to {}", item.name, historicState);
              events.sendCommand(item.name, historicState)
            }
          })
        };
    type: script.ScriptAction
1 Like

Move the check to see if PresenceSimulation is ON in a Script Condition instead of the Script Action. That will make the Action a lot simpler.

Beyond that everything looks as I would expect. Is there anything not working?

What do you need the timers for here? Or are you just needing to look into Timers in general?

Thank you for the feedback on script condition.

I included timers to avoid overflow of the binding interface with simultaneous "sendCommand"s
I had to do so on my old installation, but I am not sure whether I still need with OH3 on raspi4 and newer v2 bindings.

triggers:
  - id: "1"
    configuration:
      cronExpression: 0 * * * * ? *
    type: timer.GenericCronTrigger
conditions:
  - inputs: {}
    id: "3"
    configuration:
      itemName: PresenceSimulation
      state: ON
      operator: =
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var logger =
        Java.type("org.slf4j.LoggerFactory").getLogger('org.openhab.rule.' +
        ctx.ruleUID);// "org.openhab.model.script.PresenceSimulation");

        var PersistenceExtensions = Java.type("org.openhab.core.persistence.extensions.PersistenceExtensions");

        var ZonedDateTime = Java.type("java.time.ZonedDateTime");

        var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");


        var presenceOffset = 7

        var presenceDelay = 100000000


        var group = ir.getItem("gPresenceSimulation");

        group.allMembers.forEach(function (item, index) {

          logger.info("processing gPresenceSimulation member {}", item.name);

          var historicState = PersistenceExtensions.historicState(item, ZonedDateTime.now().minusDays(presenceOffset),"rrd4j").state;
          if (historicState != item.state) {

            logger.debug("Pres_Sim {} set to {}", item.name, historicState);
            ScriptExecution.createTimer(ZonedDateTime.now().plusNanos(index*presenceDelay), function(){
              events.sendCommand(item.name, historicState)
            })                             
          }
        });
    type: script.ScriptAction

OK, if that’s the case it looks like your latest code looks reasonable. Another approach that could work, assuming that you don’t have too many Items that makes the rule run longer than an minute, is to use java.lang.Thread.sleep() to pause inline instead of scheduling a timer. The problems with long running rules is way less pronounced in OH 3. You can still cause problems if your one rule takes too long but the problems will be limited to that one rule and no longer starve out all your rules. So the recommendations against using Thread.sleep in rules is relaxed somewhat.

Have you tried to use this only for a certain time of the day? How would this rule look like then? Because the only-if function of the “new” rule engine doesn’t seem to work…: [Rules] Issues regarding only-if and Dimmer-value (new OH3 Rule-Engine) · Issue #2124 · openhab/openhab-core · GitHub

That issue is specific to Dimmers. It does not mean that conditions in general do not work. I’ve successfully used them in many of my rules.

But if that doesn’t work for you, add an if condition to exit in your Script Action if it’s not the right time.

1 Like

Okay great Thanks =)

what is “presence delay” for?

1 Like

presenceDelay is actually misnamed. Meant is the delay between commands to avoid flooding interface with too many simultaneous sendCommands.

2 Likes

Ohhhh okay that’s nice! Thanks for the explanation :slight_smile:

maybe could be renamed to smth lik ‘replayTimeShift’ or smth like that