First of all, your rule as written is not possible. There are no arrays of Items you can reference like that and you certainly cannot trigger rules like that.
The simplest approach:
Here you will still have your 30 rules but the “logic” for each will be in a single and separately callable lambda.
The less simple approach which “might” work depending on timing is to use Groups:
Pay particular attention to triggering the rule with a Group and the sorting by lastUpdate to get access to the Item that triggered the rule.
If you choose to trigger the rule by the Group, be aware that your Group must have a type and function or else the Group will not receive updates. For example:
rlkoshak, Looks like this part works well, but I have issue with persistence,
I did all steps as you described in article.
Created mapdb.persist.
Strategies {
default = everyUpdate
}
Items {
// persist all items on every change and restore them from the db at startup
* : strategy = everyChange, restoreOnStartup
}
Rule looks like:
rule "Wall Button State Changed"
when
Item MCP23017Contact_B0 changed or
Item MCP23017Contact_B1 changed or
Item MCP23017Contact_B2 changed or
Item MCP23017Contact_B3 changed or
Item MCP23017Contact_B4 changed or
Item MCP23017Contact_B5 changed or
Item MCP23017Contact_B6 changed or
Item MCP23017Contact_B7 changed
then
Thread::sleep(50) // give persistence time to catch up
val wall_switch = gWallButtons.members.filter[s|s.lastUpdate("mapdb") != null].sortBy[lastUpdate("mapdb")].last as ContactItem
val lamp = gLights.members.filter[t | t.name == wall_switch.name+"_LED"].head as SwitchItem
if (wall_switch.state == OPEN) sendCommand(lamp, ON) else sendCommand(lamp, OFF)
end
I tried to do something like, but this doesn’t work:
rule "Initialize contact states"
when
System started
then
gWallButtons?.members.forEach(member|
postUpdate(member, member.historicState(now.minusSeconds(30)).state)
)
end
That isn’t how persistence works. Persistence only updates the state of the Items within openHAB. It does not go out through the bindings to the devices.
You have to write a System started rule to sendCommand the restored state to the Items for the command to go out to the GPIO pins.
You need to use sendCommand. postUpdate only updates the state of the Item in openHAB. You use sendCommand to trigger the binding to send the command out to the device.
Assuming restoreOnStartup is working fast enough, you can use:
2017-07-14 18:43:38.219 [ERROR] [ntime.internal.engine.RuleEngineImpl] -
Error during the execution of startup rule 'Initialize contact states': Could not invoke method:
org.eclipse.smarthome.model.script.actions.BusEvent.sendCommand(org.eclipse.smarthome.core.items.Item,java.lang.String) on instance: null
rule "Initialize contact states"
when
System started
then
gWallButtons.members.forEach(member |
member.sendCommand(member.state)
)
end
I found that approach Persist Contacts which you described, isn’t perfect for me.
I need rather persist actual state of Switch, because I can change Switch state from two different places
from PaperUI of from hardware buttons.
If I persist hardware buttons, i.e. Contacts ONLY
OH doesn’t restore light state correctly, it doesn’t take into account clicks which were made via PaperGUI.
I.e. after OH started if I control Switch ONLY from GUI, it won’t be restored! As soon as I store to mapdb only hardware inputs rather that actual state of Switch.
Read a different group then, change the “Initialize contact states” rule into a “Initialize switch states” by changing the group that it reads. You may need to make a gtoup of your switches, you may need to make sure switches are persisted and restored.
I’m afraid I really do not understand your question.
From a persistence perspective, there is no difference between a Contact and a Switch. If you have both Contacts and Switches then put the Contacts into another Group and loop through that Group in your System started rule just like you are for the Switches.
Assuming you really do mean PaperUI and not BasicUI (you mistakenly called BasicUI PaperUI above in your screenshot) then depending on the Binding, sending command straight to the Things through PaperUI may not be reflected in the Item states. Thus no updates get saved to the database so there is nothing to restore.
If you are wanting a situation where OH goes down and then you change the state of a light through some other means then when OH comes back up it doesn’t match, I can’t help with that. restoreOnStartup will only be able to handle restoring from historic data.
keep fighting with this:
I can’t figure out how to do null pointer check properly inside forEach lambda loop?
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B0_LED OFF
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B1_LED NULL
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B2_LED NULL
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B3_LED NULL
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B4_LED OFF
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B5_LED NULL
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B6_LED NULL
[INFO ] [.eclipse.smarthome.model.script.LAMP] - MCP23017Contact_B7_LED NULL
[ERROR] [ntime.internal.engine.RuleEngineImpl] -
Error during the execution of startup rule 'Initialize switch states': The argument 'command' must not be null or empty.
[INFO ] [se.smarthome.model.script.SwitchLED0] - OFF
It successfully update my first LED but then stucked on second NULL pointer and forEach loop breaks immediately without updating LED4. How to fix this?
Here is my rule:
rule "Initialize switch states"
when
System started
then
gLights?.members.forEach(lamp |
logInfo("LAMP", lamp.name + " " + lamp.state.toString)
)
gLights?.members.filter[lamp| lamp != null && lamp.state != null].forEach(lamp |
lamp.sendCommand(lamp.state.toString)
)
end
rule "Initialize switch states"
when
System started
then
gLights?.members.forEach[lamp |
logInfo("LAMP", lamp.name + " " + lamp.state.toString)
]
gLights?.members.forEach[lamp |
if (lamp.state != NULL) {
lamp.sendCommand(lamp.state.toString)
}
]
end
or if (lamp.state.toString != NULL)
note: I haven’t tested this… I wrote it fast using some other examples that I have found…
If it works… you can combine the first forEach with the second into one