[SOLVED] Same When - Different Then

Hi everyone, I very much appreciate the support given to me.
I have just set up a Xiaomi Cube which works fine for the test actions I have put into the rule so far. The question here could relate to any switch not specifically the cube.
From the below rule you can see that when the cube is rotated left or right the mood lamps change to a specific color. Is it possible to somehow create in a rule the ability for the action, in this case, rotate left to cycle through a list of different responses? By that I mean, If I repeat the rotate left trigger action say 3 times, I get a different colour for each rotate left action. I would like to have perhaps 8 coulors available which cycle through for the repeated rotate left.
I think that explains it.
Here is the current cube rule:

rule "Aqara Cube Controller"
when
    Channel 'mihome:sensor_cube:04cf8c9783a3:158d00029bc4f8:action' triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        case "MOVE": {
            All_Living.sendCommand(OFF)
            Mood_Lamps.sendCommand(ON)
            TV_Light_Color.sendCommand("300,50,50")
	        L3_Color.sendCommand("300,50,50")
	        L4_Color.sendCommand("300,50,50")
        }
        case "ROTATE_RIGHT": {
            TV_Light_Color.sendCommand("27,46,33")
	        L3_Color.sendCommand("27,46,33")
	        L4_Color.sendCommand("27,46,33")
        }
        case "ROTATE_LEFT": {
            TV_Light_Color.sendCommand("218,94,77")
	        L3_Color.sendCommand("218,94,77")
	        L4_Color.sendCommand("218,94,77")
        }
        case "FLIP90": {
            SW3_02_3.sendCommand(ON)
        }
        case "FLIP180": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(ON)
        }
        case "TAP_TWICE": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(OFF)
        }
        case "SHAKE_AIR": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(ON)
        }
        case "FREE_FALL": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(ON)
        }
    //    case "ALERT": {
    //        <ACTION>
    //    }
    }
end

As allways, any help much appreciated.
J

I thing this topic should be moved to “Setup, configuration & use” as this section is meant for working examples I guess.

To your question. Yes, just make a variable (e.g. boolean) which is triggered when the cube is rotated left. In the rule just ask if the boolean is stil true and then send the other mood setting. If the cube is meanwhile rotated right, set in the rule that the boolean is false. This works for two different colors, if you want more just use an integer which increases.
This is my plain and easy approach :wink:

"This is my plain and easy approach "
Thanks for the response but I am a total noob at this stuff and have no idea what you are saying. Could you please give an example using the case “ROTATE LEFT” EXAMPLE above.
Cheers

Yes. You need to keep track of the last state in another Item or in a global variable and then cycle to the next one when the event occurs. When you get to the end cycle back to the beginning. Using a number and the % is great for this.

This Rule could also benefit from Design Pattern: How to Structure a Rule. I’ll leave that for later.

var currState = 0
var leftColors = newArrayList("218,94,77", "<some other color>", "<some other color>")

rule "Aqara Cube Controller"
when
    Channel 'mihome:sensor_cube:04cf8c9783a3:158d00029bc4f8:action' triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        ...
        case "ROTATE_LEFT": {
            currState = currState + 1 % leftColors.size
            color = leftColors.get(currState)
            TV_Light_Color.sendCommand(color)
	        L3_Color.sendCommand(color)
	        L4_Color.sendCommand(color)
        }        
    ...

I just typed in the above, there may be errors.

I’m not an expert in rules DSL (I write my rules in Python) but I think you need parentheses:

currState = (currState + 1) % leftColors.size

When I was already in bed I realized there’s a small mistake here (and I was too lazy to get out again :wink: ).

The following will always evaluate to 1:

currState = (currState + 1) % leftColors.size

The corrected rule:

var currState = 0
var leftColors = newArrayList("218,94,77", "<some other color>", "<some other color>")

rule "Aqara Cube Controller"
when
    Channel 'mihome:sensor_cube:04cf8c9783a3:158d00029bc4f8:action' triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        ...
        case "ROTATE_LEFT": {
            currState = currState + 1
            color = leftColors.get(currState % leftColors.size)
            TV_Light_Color.sendCommand(color)
	        L3_Color.sendCommand(color)
	        L4_Color.sendCommand(color)
        }        
    ...

If you want to do the same thing for rotate right using the same array then it becomes a bit more complicated because the value of currState at some point will become negative. When that happens you need to reset the currState to the size of the leftColors array (which when you use it for both left and right rotate may be better called colors).

var currState = 0
var colors = newArrayList("218,94,77", "300,50,50", "27,46,33")

rule "Aqara Cube Controller"
when
    Channel 'mihome:sensor_cube:04cf8c9783a3:158d00029bc4f8:action' triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        ...
        case "ROTATE_LEFT": {
            if (currState <= 0) currState = colors.size
            currState = currState - 1
            color = colors.get(currState % colors.size)
            TV_Light_Color.sendCommand(color)
	    L3_Color.sendCommand(color)
	    L4_Color.sendCommand(color)
        }        
        case "ROTATE_RIGHT": {
            if (currState <= 0) currState = -1
            currState = currState + 1
            color = colors.get(currState % colors.size)
            TV_Light_Color.sendCommand(color)
	    L3_Color.sendCommand(color)
	    L4_Color.sendCommand(color)
        }        
    ...

Like the code from Rich, this is untested code. So if there’s a little mistake in there then at least it should give you a good direction.

I’ve made a minor change that the makes it all behave a bit more intuitive in that if you now rotate to the left, the color in the array to the left of the current color will now become active and if you rotate to the right, the color in the array to the right of the current color will now become active.
So, let’s say you have “red”, “white” and “blue” defined in your array and the current selected color is “white” then rotating to the left would select “red” and rotating to the right would select “blue”.

If you want the “move” action to also use the array:

        ...
        case "MOVE": {
            All_Living.sendCommand(OFF)
            Mood_Lamps.sendCommand(ON)
            currState = 1 
            color = colors.get(currState)
            TV_Light_Color.sendCommand(color)
	    L3_Color.sendCommand(color)
	    L4_Color.sendCommand(color)
        }
    ...

Arrays use zero based indexes hence currState is 1 for the second color in the array.

Again, this is all untested code but should point you into the right direction.

I. Don’t think so. Initialize currState to 0 and we get

CurrState = (0 + 1) mod 3 = 1

Next time we get

CurrState = (1 + 1) mod 3 = 2

Then

CurrState = (2 + 1) mod 3 = 0

And then we loop again.

CurrState = (0 + 1) mod 3 = 1

So we end up going to 1 first instead of 0 (you can initialize currState to -1 if stressing at 0 is important) but the expression doesn’t always evaluate to 1. It loops through 0 to n-1 where n is the number of entries in the last.

Hi Rich, you’re correct. Let’s say it was late last night and early this morning :wink:

Thanks Rich & Marcel, Ill have a play. This is exciting as it also expands the possibilities of many other devices such as turn a mood lamp on to a revolving color or a single switch to fire multiple other devices in order.
I very much appreciate the help and Ill get back to you when I have tried both solutions as I’m not sure which is right or if both will work. Not looking at rotate right yet as that will perform some other function, but the difference between your codes is in the first lines under Rotate Left which say the same but in different ways.
Cheers

This is what I have to get me started.

var currState = 0
var leftColors = newArrayList("175,100,100", "200,100,100", "225,100,100", "250,100,100", "275,100,100", "300,100,100", "325,100,100", "350,100,100", "15,100,100", "40,100,100", "65,100,100", "90,100,100", "115,100,100", "140,100,100", "165,100,100" )


rule "Aqara Cube Controller"
when
    Channel 'mihome:sensor_cube:04cf8c9783a3:158d00029bc4f8:action' triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        case "MOVE": {
            All_Living.sendCommand(OFF)
            Mood_Lamps.sendCommand(ON)
            
        }
        case "ROTATE_RIGHT": {
            TV_Light_Color.sendCommand("27,46,33")
	        L3_Color.sendCommand("27,46,33")
	        L4_Color.sendCommand("27,46,33")
        }
        case "ROTATE_LEFT": {
            currState = currState + 1
            color = leftColors.get(currState % leftColors.size)
            TV_Light_Color.sendCommand(color)
	        L3_Color.sendCommand(color)
	        L4_Color.sendCommand(color)
        }
        case "FLIP90": {
            SW3_02_3.sendCommand(ON)
        }
        case "FLIP180": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(ON)
        }
        case "TAP_TWICE": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(OFF)
        }
        case "SHAKE_AIR": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(ON)
        }
        case "FREE_FALL": {
            SW3_02_3.sendCommand(OFF)
            SW3_02_1.sendCommand(ON)
        }
    //    case "ALERT": {
    //        <ACTION>
    //    }
    }
end

I get this error
“The method color(String) is undefined” in lines 23-26. Should this be var color = leftColors…
Cheers

Yes, since it appears that color is not previously defined, you must declare it as a variable. I’d use val instead of var since you never assign anything new to that variable. val will make it a constant.

Hi Guys, I tested it last night and it worked beautifully. I only then realized the value of Marcel’s suggestion to do the left and right. I have just now added left and right option and expanded the array list to increments of 10 which will give a much broader and subtle selection. I will test this tonight.

Using the same logic in the same rule, I would also like to assign an Array List to the FLIP90 action but instead of cycling through commands I would like to cycle through items. An example would be on 1st flip turn Light_1 ON. Second Flip would turn light_1 OFF and Light_2 ON, 3rd flip turn Light_2 OFF and Light_3 ON.
Without any real understanding Would this work?

var items = newArrayList2("SW3_02_1.sendCommand(ON)", "SW3_02_1.sendCommand(OFF) && SW3_02_2.sendCommand(ON)", "SW3_02_2.sendCommand(OFF) && SW3_02_3.sendCommand(ON)" )

And then for the case action this,

case "FLIP90": {
            currState = currState + 1
            val action = items.get(currState % items.size)
            action
}

I know it cant be that simple and errors with “The method newArrayList2(String, String, String) is undefined” and “The field Xiaomi_cubeRules.items refers to the missing type Object”,

Your help much appreciated.
James

The way I might do it is to make the array be an array of lambdas.

var items = newArrayList2([ | SW3_02_1.sendCommand(ON) ], 
    [| SW3_02_1.sendCommand(OFF)
       SW3_02_2.sendCommand(ON) ],

And so on.

Thanks Rich, but what does the execution of the list look like in the case “FLIP90”?
And I think I need “newArrayList” not newArrayList2 as I first listed and need it under FLIP90 and not at top. Does that make sense?
Cheers

You get the lambda the same way you get the color from the other array. Then just call .apply()

currState = currState + 1
func = items.get(currState % leftColors.size)
func.apply()

Ah thank you, apply for the execution. Would that be

currState = currState + 1
val func = items.get(currState % items.size)
func.apply()

Again, Thanks Rich and Marcel, this is what I finished with. Currently untested but no errors found.

var currState = 0
// List of colors to cycle on left and right turn
var colors = newArrayList("170,100,100", "180,100,100", "190,100,100", "200,100,100", "210,100,100", "220,100,100", "230,100,100", "240,100,100", "250,100,100", "260,100,100", "270,100,100", 
 "280,100,100", "290,100,100", "300,100,100", "310,100,100", "320,100,100", "330,100,100", "340,100,100", "350,100,100", "360,100,100", "10,100,100", "20,100,100", "30,100,100", "40,100,100", 
 "50,100,100", "60,100,100", "70,100,100", "80,100,100", "90,100,100", "100,100,100", "110,100,100", "120,100,100", "130,100,100", "140,100,100", "150,100,100", "160,100,100" )
// List of Lights to cycle on Flip90 in lounge
var lounge = newArrayList([ | SW3_02_1.sendCommand(ON) ], [ | SW3_02_1.sendCommand(OFF) SW3_02_2.sendCommand(ON) ], [ | SW3_02_2.sendCommand(OFF) SW3_02_3.sendCommand(ON) ], [ | SW3_02_3.sendCommand(OFF) White_Lamps.sendCommand(ON)], [ | White_Lamps.sendCommand(OFF)] )
// List of Lights to cycle on Flip180 in dining
var dining = newArrayList([ | SW3_01_1.sendCommand(ON) ], [ | SW3_01_1.sendCommand(OFF) SW3_01_2.sendCommand(ON) ], [ | SW3_01_2.sendCommand(OFF) SW3_01_3.sendCommand(ON) ], [ | SW3_01_2.sendCommand(ON) SW3_01_3.sendCommand(ON)] )

rule "Aqara Cube Controller"
when
    Channel 'mihome:sensor_cube:04cf8c9783a3:158d00029bc4f8:action' triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        case "MOVE": {
            All_Living.sendCommand(OFF)
            Mood_Lamps.sendCommand(ON)
            //Lounge_TV.sendCommand(ON)
            
        }
        case "ROTATE_RIGHT": {
            if (currState <= 0) currState = -1
            currState = currState + 1
            val color = colors.get(currState % colors.size)
            TV_Light_Color.sendCommand(color)
	        L3_Color.sendCommand(color)
	        L4_Color.sendCommand(color)
        }
        case "ROTATE_LEFT": {
            if (currState <= 0) currState = colors.size
            currState = currState - 1
            val color = colors.get(currState % colors.size)
            TV_Light_Color.sendCommand(color)
	        L3_Color.sendCommand(color)
	        L4_Color.sendCommand(color)
        }
        case "FLIP90": {
            currState = currState + 1
            val lfunc = lounge.get(currState % lounge.size)
            lfunc.apply() 

        }
        case "FLIP180": {
            currState = currState + 1
            val dfunc = dining.get(currState % dining.size)
            dfunc.apply() 
        }
        //checks the status of all door and wondow sensors
        case "TAP_TWICE": {
            HouseCheck.sendCommand(ON)
            
        }
        //begins and anounces bedtime routine
        case "SHAKE_AIR": {
            Main_Lights_Out.sendCommand(ON)
        }
    //case "FREE_FALL": {
    //        SW3_02_3.sendCommand(OFF)
    //        SW3_02_1.sendCommand(ON)
    //    }
    //    case "ALERT": {
    //        <ACTION>
    //    }
    }
end

Cheers Guys

1 Like

Hi Rich & Marcel, It works a treat. Left rotation cycles left or down in color value and right turn cycles right or up in color value. Light on/off cycles also work great but in practice are not so useful; but was still a lot of fun.
I used the same rule functions to control a Xiaomi switch which has single press, double press and hold options to turn on/off bedroom lamps, cycle through color range and turn on bedtime routine.
I very much appreciate your help.
James

Good to hear you got it working :slight_smile:

Glad you got it working. I’ve taken a note to implement something like this for the Helper Libraries. In the future when using the NGRE or JSR223 Rules you shouldn’t need to code it and can just use code that already does it.