Presence Simulation

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

You can’t do it at the beginning of the rule because you don’t have access to the specific Light Item until you are inside the for loop. the best you can do is (I also included the check to prevent it sending the same state if the light is already at a given state):

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 |
                        val oldState = light.historicState(now.minusDays(days), persistence).state
                        if(oldState != light.state) {
                            createTimer(now.plusMillis(i*delay)) [|
                            logInfo("Vacation",light.name + " got " + oldState)
                            light.sendCommand(oldState.toString)
                        }
                        ]
                )
        }
end
3 Likes

Hi @rlkoshak,

I was trying to set up presence simulation. I practically copied your entire code (renamed some groups and variables).

But I get an error when the rule is saved.

My rule:

// ***************************
// Global variables
// ***************************

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

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

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

The error:

2017-07-19 19:36:37.585 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'presence_simulation.rules' has errors, therefore ignoring it: [19,25]: no viable alternative at input 'val'
[24,25]: extraneous input '}' expecting ']'

I cant’ find out what is wrong with the code.
I assumed the “}” and “]” got switched. So I adapted the last bit in the rule to:

                            light.sendCommand(oldState.toString)
                        ]
                        }
                )
        }
end

But it didn’t solve anything…

for some reason, the val within the forEach loop doesn’t work… we need to try other methods to shorten the code :slight_smile:

Is there a way to define a “free” text (something that is not evaluated at the moment of declaration) in the beginning that can be re-used?
something like:
(my_free_text variable?) oldState = “historicState(now.minusDays(presence_days), persistence).state”
and then use: light.oldState within the forEach loop ?
In this way, the evaluation will be done within the loop.

This works fine: (but with too much text :slight_smile:)
(I added a 2nd group loop for the group First Floor Lights also (gFF))

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 |
                        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)
                                ]
                        }
                )
                gFF_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)
                                ]
                        }
                )
        }
end

Note: I didn’t use the main group “gLights” because it would send a command to all its members, including the subgroups gGF_lights and gFF_lights. If a command was to be issued against the subgroup, this would turn ON/OFF all lights in the subgroup. I wanted to avoid this, so I created 2 forEach loops for each subgroup.

Related items:

/* Control Groups */
Group	gAll
Group	gInfluxDB
Group	gMapDB
Group:Switch:OR(ON, OFF)	gLights		"All Lights [(%d)]"				<light>		(gAll,gMapDB,gInflux)
Group:Switch:OR(ON, OFF)	gGF_Lights	"Ground Floor Lights [(%d)]"	<light>		(gLights)
Group:Switch:OR(ON, OFF)	gFF_Lights	"First Floor Lights [(%d)]"		<light>		(gLights)
Group:Switch:OR(ON, OFF)	gVacation	"Vacation Group"				<present>	(gAll,gMapDB,gInflux)

/* and of course the famous Vacation_Mode switch item */
/* Switch it ON to enable Vacation Mode and let the rule do its thing */
/* You can trigger this switch from a presence detection solution also */
Switch					Vacation_Mode	"Vacation Mode"					<present>	(gVacation)

Maybe using a lambda to avoid repetition of code:

// ***************************
// Global variables
// ***************************

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

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

val org.eclipse.xtext.xbase.lib.Functions$Function2<GenericItem, Integer, ?> updateLight = [
	light, i |
    val oldState = light.historicState(now.minusDays(presence_days), persistence).state
    if (oldState != light.state) {
        createTimer(now.plusMillis(i*presence_delay)) [|
            logInfo("Pres_Sim",light.name + " got " + oldState)
            light.sendCommand(oldState.toString)
        ]
    }
	true
]

rule "Presence Simulation"
when
        Time cron "0 0/5 * * * ?"
then
    if (Vacation_Mode.state == ON) {
        gGF_Lights.members.forEach[light,i |
			updateLight.apply(light as GenericItem, i)
		]
        gFF_Lights.members.forEach[light,i |
			updateLight.apply(light as GenericItem, i)
		]
   }
end
2 Likes

much, much better… thank you ! :slight_smile:

I’m all for doing this if you want to as a challenge or a learning opportunity. But from a practical perspective, there is a lot of effort being put in here to do something that ultimately will save maybe one line of code.

That is weird. Try using val to see if it likes that better. It should work. I know I’ve used vars in foreach loops like that before.

You could use anything that stores data but that you can define as a val. For example:

  • StringBuilder
  • HashMap
  • ArrayList

But again, that seems like a lot of work for ultimately little to no real savings in code simplicity.

1 Like

nice approach!
In this way, I can use the main gLights group and filter for SwitchItem types to avoid sending commands to the subgroups.

Tip: instead of historicState you could send a random percent value to the dimmers to simulate presence (…although nothing beats real historical data).
some variation of the rand used in:

You got me :slight_smile:
I also want to break it down to simple steps for noobs like me to understand these rules better :blush: