I’m having massive performance issues when dialing up the number of rules in OH 2.2.
I recently wrote a set of 4 rules to capture the state of my dumb screens (rollershutter items) based on timings of commands sent to the screens.
The screens are controlled by external button command (seen by OH).
What I do in essence is capturing those commands and capturing the timestamp they are sent. Based on that I can calculate what % the screen moved (e.g. it’s 50% open). There are some additional specificities (like the central control unit changes automatically from up/down to stop after eg. 60 sec or 90 sec, I covered that with the expire binding and I keep a current state and target state item for each screen which is inverse of OH shutterscreen % values for easy integration in homekit afterwards).
The rule works very nice for 1 screen => me very happy and very proud
However when I start applying the same rule on multiple screens the performance drops dramatically very fast. It’s literally the same set of 4 rules for another screen each time (no errors etc, tested them all separately).
For 2 screens it takes 3 minutes between the .rules refresh action and the .rules loading action and the rules actually only start responding after 8 minutes
For 4 screens time between refresh and loading is 5 minutes and rules only start responding after 12 minutes
For 8 screens OH never even gets to the stage of refreshing the rules based on the changed .rules file. When rebooting they never active. So either it takes more than hours or it doesn’t get there (not sure what because logging stops, no errors).
In either case this is not scalable at all (even when they would get through after hours) because I have 36 screens in total in my house to manage so no way I will ever get there.
So I have a few questions for the experts:
-
are rules in OH so resource intensive ? have rule performance issues come up anywhere else ? Is it the way OH deals with rules/rules logic => trying to understand what is underlying root cause
-
Would a different hardware help (currently raspbian on RPI 3). I’m sure it would improve performance but my fear is seen the exponential behavior on just a few screens any hardware how performant it might be would probably struggle just to do 36 stupid screens with 4 rules each
-
Would performance be better if I try to migrate the logic to node red, so keep the items in OH and do all rule logic in node red and send states back to OH?
I’m puzzled because imo 8 times 4 rules should not be something that kills OH.
@rlkoshak I know from other posts you don’t really believe in performance issues for home automation and I tended to agree with you but killing all by adding 32 rules (8x4 rules) made me change my view
Just FYI below the items and rules I’m using for 1 screen you’ll see it some calculations and info passing to items but it’s not like it’s rocket science or complex mathematical calculations. It’s fairly straighforward.
Items file
Group gScreen
Rollershutter screen_bureau "Screen bureau" (gScreen)
Number screen_bureau_current_position "current pos [%.1f]" (gScreen)
Number screen_bureau_target_position "target pos [%.1f]" (gScreen)
String screen_bureau_state "state [%s]" (gScreen) { expire="60s,STOPPED" }
DateTime screen_bureau_time (gScreen)
Rules
val Number screen_bureau_runtime = 30
var Number screen_bureau_gelopen = 0
var Number screen_bureau_newposition = 0
// Screen bureau
rule "screen bureau openhab item received command"
when
Item screen_bureau received command
then
if ((receivedCommand == UP) || (receivedCommand == 0))
{
VB4_2C_screen_bureau_op.sendCommand(ON)
}
if ((receivedCommand == DOWN) || (receivedCommand == 100))
{
VB4_2D_screen_bureau_neer.sendCommand(ON)
}
if ((receivedCommand == STOP) && (screen_bureau_state.state.toString !== "STOPPED"))
{
VB4_2C_screen_bureau_op.sendCommand(ON)
}
end
rule "screen bureau open"
when
Item NB_RB_bureaudeurlinks_BP8_A received update ON or
Item VB4_2C_screen_bureau_op received update ON
then
if ((screen_bureau_state.state.toString == "OPENING") || (screen_bureau_state.state.toString == "CLOSING"))
{
screen_bureau_state.postUpdate("STOPPED")
}
else
{
screen_bureau_state.postUpdate("OPENING")
screen_bureau_time.postUpdate(now.toString())
if (screen_bureau_current_position.state == NULL) { screen_bureau_current_position.postUpdate(0) }
screen_bureau_target_position.postUpdate(100)
}
end
rule "screen bureau dicht"
when
Item NB_RB_bureaudeurlinks_BP8_B received update ON or
Item VB4_2D_screen_bureau_neer received update ON
then
if ((screen_bureau_state.state.toString == "OPENING") || (screen_bureau_state.state.toString == "CLOSING"))
{
screen_bureau_state.postUpdate("STOPPED")
}
else
{
screen_bureau_state.postUpdate("CLOSING")
screen_bureau_time.postUpdate(now.toString())
if (screen_bureau_current_position.state == NULL) { screen_bureau_current_position.postUpdate(100) }
screen_bureau_target_position.postUpdate(0)
}
end
rule "screen bureau stopped"
when Item screen_bureau_state changed
then
if (screen_bureau_state.state.toString == "STOPPED")
{
if ((now.millis- (screen_bureau_time.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)/1000 > screen_bureau_runtime)
{
screen_bureau_current_position.postUpdate(screen_bureau_target_position.state)
screen_bureau.postUpdate(100 - (screen_bureau_current_position.state as DecimalType))
}
else
{
if ((screen_bureau_target_position.state as DecimalType) > (screen_bureau_current_position.state as DecimalType))
{
screen_bureau_gelopen = 100*((now.millis - (screen_bureau_time.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)/1000)/screen_bureau_runtime
screen_bureau_newposition = ((screen_bureau_current_position.state as DecimalType) + screen_bureau_gelopen).intValue
if (screen_bureau_newposition > 100) { screen_bureau_target_position.postUpdate(100) } else { screen_bureau_target_position.postUpdate(screen_bureau_newposition) }
screen_bureau_current_position.postUpdate(screen_bureau_target_position.state)
screen_bureau.postUpdate((100 - (screen_bureau_current_position.state as DecimalType)).intValue)
}
if ((screen_bureau_target_position.state as DecimalType) < (screen_bureau_current_position.state as DecimalType))
{
screen_bureau_gelopen = 100*((now.millis - (screen_bureau_time.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)/1000)/screen_bureau_runtime
screen_bureau_newposition = ((screen_bureau_current_position.state as DecimalType) - screen_bureau_gelopen).intValue
if (screen_bureau_newposition < 0) { screen_bureau_target_position.postUpdate(0) } else { screen_bureau_target_position.postUpdate(screen_bureau_newposition) }
screen_bureau_current_position.postUpdate(screen_bureau_target_position.state)
screen_bureau.postUpdate((100 - (screen_bureau_current_position.state as DecimalType)).intValue)
}
}
}
end