Presence Simulation

historicState is stored in what ever persistence provider you have configured. See the Persistence wiki page for details on which ones are supported and how to configure them.

The tl;dr is that openHAB will save the values of your Items into a database and historicState queries that database for the past values.

Thanks Rich.

Added persistence to the list of things to learn more about. :smile:

Hi

I am implementing this.
Can I have some help to change the script so it just send the command if the state is different.

/Mike

var newState = //something
if(MyItem.state != newState) {
    // do stuff
}

@rlkoshak

How do i use that in the forEach loop?

/mike

val newState = // something
MyGroup.members.forEach[item |
    if(item.state != newState {
        // do stuff
    }
]

You can only reference final variables from a forEach. Declaring newState as a val instead of var does that. However, final means that once it is assigned you cannot change newState.

All,
I am trying to implement this but seem to have (simple?) syntax problems:
I get:
Incompatible types. Expected java.lang.String but was org.openhab.core.types.State
for
G_Lights.members.forEach[light | light.sendCommand(light.historicState(now.minusDays(days)).state)]

What am I missing to transform string into state!?

Not sure why it’s not working, but try adding .toString:

G_Lights.members.forEach[light | light.sendCommand(light.historicState(now.minusDays(days)).state.toString)]
1 Like

Thanks!
The error is gone. now I need to check fucntionality. :slightly_smiling:

1 Like

Hi, could you tell me how you do presence detection with dd-wrt?

I’m using Owntracks, wich is great, but never hurts to add another source.

Thanks

/mike

Resurrecting this thread (I also moved it to the Scripts & Rules category :)) to ask some simple “technical” questions (for me to understand better the state stuff):

Why do I have to use state.toString in the following rule:

var int days = 7

rule "Simulate Lights on Vacation"
when
        Time cron "0 0/5 * * * ?"
then
        if (Vacation_Mode.state == ON) {
                gGF_Lights?.members.forEach[item |
                        logInfo("Vacation",item + " got " + item.historicState(now.minusDays(days)).state)
                        item.sendCommand(item.historicState(now.minusDays(days)).state.toString)
                ]
        }
end

The above rule works fine (with .state.toString). It cycles through all (21) members of the gGF_Lights group and “replays” the state from 1 week behind (stored in my default persistence = mapdb)

Without .toString in the sendCommand line, I get:

2017-07-14 11:05:00.144 [INFO ] [ipse.smarthome.model.script.Vacation] - P03_Wh_Light (Type=SwitchItem, State=OFF, Label=P03 Wh. Light, Category=switch, Groups=[gGF_P03_Warehouse, gGF_Lights, gKNX]) got OFF
2017-07-14 11:05:00.145 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule Simulate Lights on Vacation: Could not invoke method: org.eclipse.smarthome.model.script.actions.BusEvent.sendCommand(org.eclipse.smarthome.core.items.Item,java.lang.String) on instance: null

Interestingly enough, the logInfo entry displays past week’s state without adding .toString :slight_smile: (but it adds alot of not needed info for the item (like Type= etc)… is there a way to filter those out?)

Ps: Also, should I use: gGF_Lights?.members.forEach or gGF_Lights.members.forEach in my rule? (I think that with and/or without the question mark, it works)

Ps2: Another type of question here: The rule fires every 5 mins. Is there a way to “enable” such rule only when I flick to “Vacation_Mode” switch to ON? Meaning that the when trigger condition would change to Item Vacation_Mode changed from OFF to ON… afterwards… maybe find a way to run the cron job.
I am just thinking to avoid the recurrent execution of the rule when I don’t really need it.

Ps3: OH 2.2.0 Snapshot Build #985

Ps4: Thanx to @rlkoshak & @watou for the examples they provided in this thread. I build my rule based on their inputs.

I found 1 answer :slight_smile:

and enhanced a bit the rule (added a small delay timer between each command to avoid spamming the KNX Bus):

var int days = 7

rule "Simulate Lights on Vacation"
when
	Time cron "0 0/5 * * * ?"
then
	if (Vacation_Mode.state == ON) {
		gGF_Lights.members.forEach(light,i |
			createTimer(now.plusMillis(i*50)) [|
			logInfo("Vacation",light + " got " + light.historicState(now.minusDays(days)).state)
			light.sendCommand(light.historicState(now.minusDays(days)).state.toString)
			]
		)
	}
end
Previous rule result:
2017-07-14 13:55:00.001 [ItemCommandEvent          ] - Item 'P03_Wh_Light' received command OFF
2017-07-14 13:55:00.002 [ItemCommandEvent          ] - Item 'P04_LED' received command OFF
2017-07-14 13:55:00.003 [ItemCommandEvent          ] - Item 'P04_Mirror' received command OFF
2017-07-14 13:55:00.003 [ItemCommandEvent          ] - Item 'P05_Light' received command OFF
2017-07-14 13:55:00.004 [ItemCommandEvent          ] - Item 'P06_Large_LED' received command OFF
2017-07-14 13:55:00.005 [ItemCommandEvent          ] - Item 'P06_Small_LED' received command OFF
2017-07-14 13:55:00.005 [ItemCommandEvent          ] - Item 'P07_Large_LED' received command OFF
2017-07-14 13:55:00.009 [ItemCommandEvent          ] - Item 'P07_Small_LED' received command OFF
2017-07-14 13:55:00.009 [ItemCommandEvent          ] - Item 'P08_LED' received command OFF
2017-07-14 13:55:00.010 [ItemCommandEvent          ] - Item 'P08_Ventilator' received command ON
2017-07-14 13:55:00.010 [ItemCommandEvent          ] - Item 'P08_Light' received command OFF
2017-07-14 13:55:00.013 [ItemCommandEvent          ] - Item 'P08_Terrace_Large_LED' received command OFF
2017-07-14 13:55:00.013 [ItemCommandEvent          ] - Item 'P08_Terrace_Small_LED' received command OFF
2017-07-14 13:55:00.013 [ItemCommandEvent          ] - Item 'P09_Light' received command OFF
2017-07-14 13:55:00.014 [ItemCommandEvent          ] - Item 'P10_Light' received command OFF
2017-07-14 13:55:00.016 [ItemCommandEvent          ] - Item 'P11_Small_LED' received command OFF
2017-07-14 13:55:00.016 [ItemCommandEvent          ] - Item 'P11_Large_LED' received command OFF
2017-07-14 13:55:00.019 [ItemCommandEvent          ] - Item 'P11_Light' received command OFF
2017-07-14 13:55:00.020 [ItemCommandEvent          ] - Item 'P11_Terrace_Light' received command OFF
2017-07-14 13:55:00.021 [ItemCommandEvent          ] - Item 'P01_Exterior_Light' received command OFF
2017-07-14 13:55:00.021 [ItemCommandEvent          ] - Item 'mP3_03_01_O' received command OFF
New, with small delay result:
2017-07-14 14:00:00.079 [ItemCommandEvent          ] - Item 'P03_Wh_Light' received command OFF
2017-07-14 14:00:00.129 [ItemCommandEvent          ] - Item 'P04_LED' received command OFF
2017-07-14 14:00:00.180 [ItemCommandEvent          ] - Item 'P04_Mirror' received command OFF
2017-07-14 14:00:00.233 [ItemCommandEvent          ] - Item 'P05_Light' received command OFF
2017-07-14 14:00:00.283 [ItemCommandEvent          ] - Item 'P06_Large_LED' received command OFF
2017-07-14 14:00:00.336 [ItemCommandEvent          ] - Item 'P06_Small_LED' received command OFF
2017-07-14 14:00:00.385 [ItemCommandEvent          ] - Item 'P07_Large_LED' received command OFF
2017-07-14 14:00:00.434 [ItemCommandEvent          ] - Item 'P07_Small_LED' received command OFF
2017-07-14 14:00:00.486 [ItemCommandEvent          ] - Item 'P08_LED' received command OFF
2017-07-14 14:00:00.537 [ItemCommandEvent          ] - Item 'P08_Ventilator' received command ON
2017-07-14 14:00:00.588 [ItemCommandEvent          ] - Item 'P08_Light' received command OFF
2017-07-14 14:00:00.636 [ItemCommandEvent          ] - Item 'P08_Terrace_Large_LED' received command OFF
2017-07-14 14:00:00.687 [ItemCommandEvent          ] - Item 'P08_Terrace_Small_LED' received command OFF
2017-07-14 14:00:00.738 [ItemCommandEvent          ] - Item 'P09_Light' received command OFF
2017-07-14 14:00:00.789 [ItemCommandEvent          ] - Item 'P10_Light' received command OFF
2017-07-14 14:00:00.842 [ItemCommandEvent          ] - Item 'P11_Small_LED' received command OFF
2017-07-14 14:00:00.898 [ItemCommandEvent          ] - Item 'P11_Large_LED' received command OFF
2017-07-14 14:00:00.949 [ItemCommandEvent          ] - Item 'P11_Light' received command OFF
2017-07-14 14:00:01.000 [ItemCommandEvent          ] - Item 'P11_Terrace_Light' received command OFF
2017-07-14 14:00:01.050 [ItemCommandEvent          ] - Item 'P01_Exterior_Light' received command OFF
2017-07-14 14:00:01.102 [ItemCommandEvent          ] - Item 'mP3_03_01_O' received command OFF

Unbelievable accuracy… I am in love with my openHAB system :slight_smile:
21 commands send within 21 milliseconds (first version without the delay)
21 commands send within 21*(1+50)=1071ms (second version with the 50ms delay between them)

I don’t know that it is required. It may not be any longer. NCO was having errors just using .state and following watou’s advice and adding the .toString seemed to fix it.

A lot of times things do not work, we try something, and that fixes it and we never really find out why.

Theoretically, it should work without the .toString but perhaps there is one or more Item types that will not accept generic State objects as an argument. Passing a String is universally supported though.

The ? means to skip past the line if the value is null. I personally avoid its use most of the time because if something is null I want there to be an error.

The best you can do is add a Switch and an if statement in your rule so the body of the existing rule only executes when the Switch is ON.

The Experimental Rules Engine will be able to handle this use case though.

1 Like

Small update/improvement to the Presence Simulation rule for anyone who want to re-use it:

Added the 3 variables on top to be easier to adjust the settings.

  • days = 7 (in days of course) is how long back in time the rule will go to pull state information from the items to be “replayed” to simulate presence
  • delay = 50 (in milliseconds) is the time gap between each sendCommand (to avoid overloading the bus)
  • persistence = "influxdb" (text) is the selected persistence service from which to pull the historical data. This is useful if you use a default persistence like rrd4j or mapdb and have influxdb as a 2nd tier persistence service.
var int days = 7
var int delay = 50
var String persistence = "influxdb"

rule "Simulate Lights on Vacation"
when
        Time cron "0 0/5 * * * ?"
then
        if (Vacation_Mode.state == ON) {
                gGF_Lights.members.forEach(light,i |
                        createTimer(now.plusMillis(i*delay)) [|
                        logInfo("Vacation",light.name + " got " + light.historicState(now.minusDays(days), persistence).state)
                        light.sendCommand(light.historicState(now.minusDays(days), persistence).state.toString)
                        ]
                )
        }
end
5 Likes

Hi @Dim,
I was looking at your code, and I’m probably going to use it for my presence simulation (so thank you!).

However, I do have some questions:
If I understand your code correctly, your code will set all lights (in group gGF_Lights) to their historic state. So if a light was OFF two weeks ago, and it is still OFF now, the light will still get the command “OFF”. Correct? I don’t know how many lights you have in your group, but wouldn’t it generate a lot of obsolete commands every five minutes?

The same remark goes for your logInfo. It would also write something to the log when there was no difference between the historic state and now.

Would it be possible to adapt to this code (I still need to set up a separate group and store in persistence, so I can’t verify for myself)?

        if (Vacation_Mode.state == ON) {
                gGF_Lights.members.forEach(light,i |
                        if (light.historicState(now.minusDays(days), persistence).state != light.state) {
                               createTimer(now.plusMillis(i*delay)) [|
                               logInfo("Vacation",light.name + " got " + light.historicState(now.minusDays(days), persistence).state)
                               light.sendCommand(light.historicState(now.minusDays(days), persistence).state.toString)
                        ]
                }
                )
        }

Hi @Dries !

I copied it from @rlkoshak, so he owns the “Intellectual Property Rights” :stuck_out_tongue:

Absolutely! This is my next step: to implement an if {} statement to check the current state versus the historical and to suppress the command if equal. Based on: (again… @rlkoshak recommendation :slight_smile:). I will post soon an update for this.

I believe that this will work… I will test it out and let you know!

what I tried to do (but failed so far) is to declare a variable(?) at the start of the rule for the “historicState(now.minusDays(days), persistence).state.toString” since it repeats in the rule and I wanted to use a “oldState” definition to clean up the statements :slight_smile:

1 Like

This is great Work. Thank you for sharing.

A quick question. I’m pesisting Lights Switches as well as Dimmers in a Group I call GLight.

Will the above turn on/off lights as well as set the Dimmers to the historical state?

It should be able to do this without any problem.

If there are issues (due to different type of items in the Group), you can split the items in subgroups
Then, you can create a new .forEach block and run it on the Dimmers subgroup.

This code seems to works, including only sending changes when there are changes between the current state and the historical state.

However can anyone tell me how the i in ... forEach(lamp, i | ... works? Is this some automagical incrementer? I’ve not been able to find anything in the documentation about this.

var int days = 7
var int delay = 50

rule "Simulate_lights"
when
    Time cron "0 0/1 * * * ?"
then
    if (Away_Button.state == ON)
    {
        GLights.members.forEach(lamp, i | 

            if (lamp.state.toString != lamp.historicState(now.minusDays(days)).state.toString)
            {
                createTimer(now.plusMillis(i * delay)) [|
                    if (lamp instanceof DimmerItem) 
                    {
                        logInfo("Dimmer",  "Changing dimmer: " + lamp.name + " from " + lamp.state.toString + " to " + lamp.historicState(now.minusDays(days)).state.toString)
                        lamp.sendCommand(lamp.historicState(now.minusDays(days)).state as PercentType)
                    }
                    else
                    {
                        logInfo("Switch",  "Changing switch: " + lamp.name + " to " + lamp.historicState(now.minusDays(days)).state.toString)
                        lamp.sendCommand(lamp.historicState(now.minusDays(days)).state.toString)
                    }

                ]
            }         
        )
    }
end