Rules: Help with bitwise operations

Hi OH community,

Can anyone help with syntax on the following rule? This rule is for an inovelli zwave switch and was written by sovapatr on the inovelli message boards. The variable “currentEffect” adjusts the onboard LED on the device to show different visual indicators.

case 3.0: {
      var int currentEffect = (BRE_Dimmer_01_LEDStripEffect.state as Number).intValue
      var int e = currentEffect.bitwiseAnd(255 << 24) >> 24   //effect 1-5
      var int d = currentEffect.bitwiseAnd(255 << 16) >> 16   //duration 0-255
      var int b = currentEffect.bitwiseAnd(255 << 8) >> 8     //brightness 1-10
      var int c = currentEffect.bitwiseAnd(255)               //color 0-255

      c = (c + 16) % 255        //Increment color
      var hue = c * 360 / 255   //Convert 0-255 to 0-360
      var HSBType color = new HSBType(new DecimalType(hue),new PercentType(100),new PercentType(100))
      var newEffect = currentEffect.bitwiseAnd(-256).bitwiseOr(c) //replace color
      BRE_Dimmer_01_LEDStripEffect.sendCommand(newEffect)
      BRE_LightBulb_01_Color.sendCommand(color)

      logInfo("Config Button Pressed", "Effect: " + e.toString + ", Duration: " + d.toString + ", Brightness: " + b.toString + ", Color: " + c.toString)
}

It takes the existing 4 bit value, reads the current settings, changes the first bit, and updates the value (color).

I’ve tried various things for this line, but can’t seem to get it to update to the proper value.

var newEffect = currentEffect.bitwiseAnd(-256).bitwiseOr(c) //replace color

I would like to accomplish the same, but change the last bit (effect) instead of the first (color).

Thanks so much!

Looks to me like the dimmer is returning an integer with the Effect stored in the leftmost byte (the leftmost eight bits of the integer), the Duration in the next byte, the Brightness in the next byte, and the Color in the rightmost byte. If the goal is to change the Effect (the leftmost byte of the integer) I believe the following should work, but it’s untested.

var int currentEffect = (BRE_Dimmer_01_LEDStripEffect.state as Number).intValue
var int newStatus = (currentStatus << 8) >> 8 // shift 8 bits to the left, then 8 to the right to set the leftmost 8 bits to zeros
var int newEffect = 4       // whatever value you would like for the new effect (1 - 5)
newStatus = newStatus.bitwiseOr(newEffect << 24)  // "<< 24" shifts the new Effect's value to the leftmost bits of the integer
BRE_Dimmer_01_LEDStripEffect.sendCommand(newStatus)

HTH

Jim

2 Likes

@JimH Thanks so much for your help!

My mistake, I should have been more clear on the description.

You’re right, the dimmer returns an integer with all four values stored (8-bit unsigned), however the order seems to be reversed.

Inovelli provides a spreadsheet calculator to manually check which values you should use for various colors and effects. According to the spreadsheet, Effect should be the right-most value: https://docs.google.com/spreadsheets/d/1HS1OZl1VFQiNcjug6AKV3jG3PYa-NwEF0yph9qB7xE0/edit?usp=sharing

I tried various combinations of the code above, but I’ve only managed to get very strange values.

var int newStatus = (currentEffect << 8) >> 8 // shift 8 bits to the left, then 8 to the right to set the leftmost 8 bits to zeros
var int newEffect = 5       // whatever value you would like for the new effect (1 - 5)
newStatus = newStatus.bitwiseOr(newEffect << 24)  // "<< 24" shifts the new Effect's value to the leftmost bits of the integer

logInfo("Config Button Pressed", "Effect: " + e.toString + ", Duration: " + d.toString + ", Brightness: " + b.toString + ", Color: " + c.toString + ", Old Effect:" + currentEffect.toString + ", New Effect:" + newStatus.toString)

[INFO ] [e.model.script.Config Button Pressed] - Effect: 3, Duration: 255, Brightness: 3, Color: 255, Old Effect:67044351, New Effect:-64513

Example: change effect from 5 (slow blink) to 2 (chase)
Original Status: 100598783
Expected: 50267135
Returns: -64513

Inovelli’s spreadsheet is misleading. They have Color on the Left and effect on the right but if you look at the formula that generates the “Numeric value” cells you will see that they are multiplying the Effect by 16,777,216, effectively shifting the Effect three bytes to the left. The Duration is multiplied by 65,536 and the Level by 256.

The source of our problem is that the bit shift operators (<<, >>) are arithmetic shifts, not logical shifts. The sign bit is causing the issue. However the following code is working for me. Perhaps someone with Xtend of Java expertise can provide a way to do logical shifts…

var int currentStatus = 100598783  // white, level 3, indefinitely, pulse
logInfo("Inovelli", "currentStatus before setting the Effect to 2 is {}", currentStatus.toString)
var Integer mask = 16777215
var Integer newStatus = currentStatus.bitwiseAnd(mask)  // set the leftmost 8 bits to zeros
var Integer newEffect = 2
newStatus = newStatus.bitwiseOr(newEffect * 16777216)
logInfo("Inovelli", "newStatus with Effect = 2 is {}", newStatus.toString)

currentStatus = newStatus
logInfo("Inovelli", "currentStatus before setting the Effect to 5 is {}", currentStatus.toString)
newStatus = currentStatus.bitwiseAnd(mask)  // set the leftmost 8 bits to zeros
newEffect = 5
newStatus = newStatus.bitwiseOr(newEffect * 16777216)
logInfo("Inovelli", "newStatus with Effect = 5 is {}", newStatus.toString)
2020-07-07 22:20:09.180 [INFO ] [ipse.smarthome.model.script.Inovelli] - currentStatus before setting the Effect to 2 is 100598783
2020-07-07 22:20:09.183 [INFO ] [ipse.smarthome.model.script.Inovelli] - newStatus with Effect = 2 is 50267135
2020-07-07 22:20:09.184 [INFO ] [ipse.smarthome.model.script.Inovelli] - currentStatus before setting the Effect to 5 is 50267135
2020-07-07 22:20:09.186 [INFO ] [ipse.smarthome.model.script.Inovelli] - newStatus with Effect = 5 is 100598783

HTH
Jim

1 Like

@jimh Yes, you were absolutely right. I was confused by the spreadsheet layout. You’ve definitely added some much needed clarity.

This is now working perfectly. Thanks so much!!! I thought I was losing my mind :joy:

This is a more straightforward way (IMO) to change the switch’s effect. The key is using the logical shift right (>>>) instead of the arithmetic shift right (>>).

    var int currentStatus  = (BRE_Dimmer_01_LEDStripEffect.state as Number).intValue
    logInfo("Inovelli4", "currentStatus: {}", currentStatus.toString)

    // get the value of the leftmost eight bits
    var int effect = currentStatus.bitwiseAnd(255 << 24) >>> 24     
    logInfo("Inovelli4", "current effect is {}", effect.toString)

    // change the value here
    effect = 4             //effect 1-5
    logInfo("Inovelli4", "new effect is {}", effect.toString)

    // create the new status
    var int newStatus = 0
    // set the leftmost eight bits to zero, then overlay them with the new value
    newStatus = (currentStatus << 8 ) >>> 8
    newStatus = newStatus.bitwiseOr(effect << 24)

    logInfo("Inovelli4", "newStatus: {}", newStatus.toString)
    BRE_Dimmer_01_LEDStripEffect.sendCommand(newEffect)
2020-07-08 16:35:20.314 [INFO ] [pse.smarthome.model.script.Inovelli4] - currentStatus: 100598783
2020-07-08 16:35:20.317 [INFO ] [pse.smarthome.model.script.Inovelli4] - current effect is 5
2020-07-08 16:35:20.318 [INFO ] [pse.smarthome.model.script.Inovelli4] - new effect is 4
2020-07-08 16:35:20.320 [INFO ] [pse.smarthome.model.script.Inovelli4] - newStatus: 83821567

The following will allow you to change any/all of the values for the switch.

    var int currentStatus  = (BRE_Dimmer_01_LEDStripEffect.state as Number).intValue
    logInfo("Inovelli3", "currentStatus: {}", currentStatus.toString)
    // parse the status value into individual variables
    var int effect = currentStatus.bitwiseAnd(255 << 24) >>> 24     
    var int duration = currentStatus.bitwiseAnd(255 << 16) >>> 16   
    var int level = currentStatus.bitwiseAnd(255 << 8) >>> 8        
    var int color = currentStatus.bitwiseAnd(255)                  
    logInfo("Inovelli3", "current values: effect {}, duration {}, level {}, color {}", 
            effect.toString, duration.toString, level.toString, color.toString)

    // update any/all values you would like to change
    effect = 4             // effect 1 - 5
    duration = 30          // duration 0 - 255 
    level = 1              // brightness 1 - 10
    color = 170            // color 0 - 255
    logInfo("Inovelli3", "new: effect {}, duration {}, level {}, color {}", 
            effect.toString, duration.toString, level.toString, color.toString)

    // combine the individual values into one status variable        
    var int newStatus = 0
    newStatus = newStatus.bitwiseOr(effect << 24)
    newStatus = newStatus.bitwiseOr(duration << 16)
    newStatus = newStatus.bitwiseOr(level << 8) 
    newStatus = newStatus.bitwiseOr(color)
    logInfo("Inovelli3", "newStatus: {}", newStatus.toString)
    BRE_Dimmer_01_LEDStripEffect.sendCommand(newEffect)
2020-07-08 16:29:36.525 [INFO ] [pse.smarthome.model.script.Inovelli3] - currentStatus: 100598783
2020-07-08 16:29:36.531 [INFO ] [pse.smarthome.model.script.Inovelli3] - current values: effect 5, duration 255, level 3, color 255
2020-07-08 16:29:36.533 [INFO ] [pse.smarthome.model.script.Inovelli3] - new: effect 4, duration 30, level 1, color 170
2020-07-08 16:29:36.536 [INFO ] [pse.smarthome.model.script.Inovelli3] - newStatus: 69075370

If this works for you please mark this as solved so that others can find it quickly

Jim

2 Likes

Thanks! I’ll try this out tonight.

Yes, the ultimate goal is to build a rule which can adjust all attributes as needed, so this definitely seems close. Just a quick check though, the last line should read

  BRE_Dimmer_01_LEDStripEffect.sendCommand(newStatus)

right?