Help with order of execution within a rule

HI,
I would like the last 3 lines of the rule to be executed only after he went through 4 x if conditions. I guess Thread::sleep or Timer will work, but is there a smarter way?

rule "Set AC Limit"
when
    Item TimeOfDay changed to 1 or
    System started
then
      val currMonth = now.getMonthValue
    if(currMonth == 4 || currMonth == 11)
      {ACLimit.sendCommand(24.9)}
    if(currMonth == 5 || currMonth == 10)
      {ACLimit.sendCommand(25.4)}
    if(currMonth == 6 || currMonth == 9)
      {ACLimit.sendCommand(25.7)}
    if(currMonth == 7 || currMonth == 8)
      {ACLimit.sendCommand(25.9)}
      Thread::sleep(500) 
      var Number aclimit = ACLimit.state as DecimalType
      var Number aclimitrooms = aclimit + 0.3
      postUpdate(ACLimitRooms, aclimitrooms)
end

You will have to emaplin your use case a with a whole lot more detail. What are you trying to accomplish?

Note, you already know what ACLimit’s state is supposed to be, so just use that and then you don’t need to sleep or use a timer at all.

rule "Set AC Limit"
when
    Item TimeOfDay changed to 1 or
    System started
then
    val currMonth = now.getMonthValue
    var currACLimit = ACLimit.state as Number
    if(currMonth == 4 || currMonth == 11) 
      {currACLimit = 24.9}
    else if(currMonth == 5 || currMonth == 10)
      {currACLimit = 25.4}
    else if(currMonth == 6 || currMonth == 9)
      {currACLimit = 25.7}
    if(currMonth == 7 || currMonth == 8)
      {currACLimit = 25.9}
    ACLimit.sendCommand(currACLimit)
    val aclimitrooms = currACLimit + 0.3
    ACLimitRooms.postUpdate(aclimitrooms)
end

If you use a switch statement this becomes much cleaner

rule "Set AC Limit"
when
    Item TimeOfDay changed to 1 or
    System started
then
    val currMonth = now.getMonthValue
    var currACLimit = ACLimit.state as Number
    switch(currMonth) {
        case currMonth == 4 || currMonth == 11: currACLimit = 24.9
        case currMonth == 5 || currMonth == 10: currACLimit = 25.4
        case currMonth == 6 || currMonth == 9:  currACLimit = 25.7
        case currMonth == 7 || currMonth == 8:  currACLimit = 25.9
    }
    ACLimit.sendCommand(currACLimit)
    val aclimitrooms = currACLimit + 0.3
    ACLimitRooms.postUpdate(aclimitrooms)
end

Some other differences demonstrating Rules DSL best practices:

  • cast the state of Number Items to Number instead of DecimalType
  • do not force the type of a variable except where absolutely necessary
  • use the postUpdate method on the Item, not the postUpdate action

I dont know what is the AC limit until all 4 IF statements are complete, this is what I was trying to accomplish. Wait until ACLimit is defined and only THEN calculate ACLimitRooms.
As I understand the part of the rule that is outside of IF loops executes simultaneously to IF, it doesnt wait for IF to complete as he doesnt care about the results? It knows it has to be executed in either case when the rule was fired.

But your example with cases is perfect and it will work exactly as I intended, thanks!

That is not correct and not how it works. Each line of code executes in sequence from top to bottom. So the line var Number aclimit = ACLimit.state as DecimalType does wait until the four if statements complete before running.

The problem is that sending a command to an Item gets processed in the background. It takes some time for the Item to change after sending a command to it and the rule doesn’t wait around until the Item changes before running the next line of code. All of that happens in the background so if you try to check the state of an Item immediately after sending a command to it, the Item is unlikely to have finished processing that command and won’t be in it’s new state.

To complicate things further, there is no guarantee that an Item will change in response to a command at all. But that’s not something to worry about here, but soemthing to keep in mind.

The Thread::sleep(500) was basically giving enough time for the command to be processed in the background and for the Item to be updated in response to the command.

In my version of the rule, we don’t need to wait because we already know what state the Item will be in eventually. So we just use that.

This could also be written as:

switch(currMonth) {
        case 4, case 11: currACLimit = 24.9
        case 5, case 10: currACLimit = 25.4
        // or split into separate lines
        case 6,
        case 9:  currACLimit = 25.7
        case 7, case 8:  currACLimit = 25.9
    }

Or even better:

val currACLimit = switch(currMonth) {
  case 4, case 11: 24.9
  case 5, case 10: 25.4
  case 6, case 9: 25.7
  case 7, case 8: 25.9
}

There’s probably other ways of doing it, e.g. using a Hashmap

See Xtend - Expressions

2 Likes
rule "Set AC Limit"
when
  Item TimeOfDay changed to 1 or
  System started
then
  val currACLimit = switch(now.getMonthValue) {
    case 4, case 11: 24.9
    case 5, case 10: 25.4
    case 6, case 9: 25.7
    case 7, case 8: 25.9
  }
  ACLimit.sendCommand(currACLimit)
  ACLimitRooms.postUpdate(currACLimit + 0.3)
end
1 Like

That must be relatively new (meaning within the last five years or so). One of the annoying things with Rules DSL/Xtend switch statements was there didn’t used to be a fall through. I’m glad to see they eventually added it.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.