Rollershutter Group positioning

Hello, I want to further develop my rollershutter control in a way that all rollershutters of the living room (4 rollershutters in total) can be closed or opened simultaneously to the same degree by a single Group(Voice)Command, e.g., lets say, all rollershutters shall be closed simultaneously to 50%. At the moment that „only“ works by individual commands to every single rollershutter. The respective rule is shown below and works timer-based, because a direct positioning of our rollershutters doesn’t work unfortunately:

val Number WZ_Rollo_01_100 = 20 // measured time in seconds for the rollershutter to move from fully open to fully closed position (here 20sec)
var Number RolloTime_01 = -1 // that will be the time length for the movement from current position towards the desired position
var Number RolloDifference = NULL // delta of rollershutter move 
var String RolloDirection = NULL // rollershutter movement up oder down

rule "WZ_Rollo_01_Alexa"
when
Item WZ_Rollo_01_Alexa received command
then
logInfo("Rollo 1 Alexa", "Actual Rollo Position: " + WZ_Rollo_01_Pos.state)
logInfo("Rollo 1 Alexa", "receivedCommand: " + receivedCommand)

var Timer timer = null

if ((receivedCommand as DecimalType).intValue == 100) {
// when command 100%, then rollershutter shall close completely, without Timer (for Reset)
RolloDirection = "DOWN"
RolloTime_01 = NULL
}

if ((receivedCommand as DecimalType).intValue == 0) {
// when command 0%, then rollershutter shall open completely, without Timer (for Reset)
RolloDirection = "UP"
RolloTime_01 = NULL
}

if (receivedCommand > WZ_Rollo_01_Pos.state) {
// we compare the desired new position with the current position: if „New“ is bigger than „Current“, the rollershutter has to move down
RolloDifference = ((receivedCommand as DecimalType).intValue - (WZ_Rollo_01_Pos.state as DecimalType).intValue) // we calculate the difference or distance between the current position and the desired new position (as Percentage Value)
logInfo("Rollo 1 Alexa", "Rollo Difference: " + RolloDifference)
RolloDirection = "DOWN"
}

if (receivedCommand < WZ_Rollo_01_Pos.state) {
// we compare the desired new position with the current position: if „New“ is smaller than „Current“, the rollershutter has to move up
RolloDifference = ((WZ_Rollo_01_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue) // we calculate the difference or distance between the current position and the desired new position (as Percentage Value)
logInfo("Rollo 1 Alexa", "Rollo Difference: " + RolloDifference)
RolloDirection = "UP"
}

if (RolloTime_01 === NULL) {
logInfo("Rollo 1 Alexa", "Rollo received command: " + RolloDirection)
WZ_Rollo_01.sendCommand(RolloDirection)
WZ_Rollo_01_Pos.sendCommand(receivedCommand) // we give the actual position to the Item
RolloTime_01 = -1
logInfo("Rollo 1 Alexa", "Rollo Time: " + RolloTime_01)
} else {
RolloTime_01 = (RolloDifference/100) * WZ_Rollo_01_100 // we calculate the driving-time in seconds
logInfo("Rollo 1 Alexa", "Rollo received command: " + RolloDirection) 
WZ_Rollo_01.sendCommand(RolloDirection)
logInfo("Rollo 1 Alexa", "Rollo Time: " + RolloTime_01)
timer = createTimer(now.plusSeconds(RolloTime_01.intValue), [|
logInfo("Rollo 1 Alexa", "Timer expired and Rollo set to STOP") // if Timer has expired, the rollershutter stops at the desired new position
WZ_Rollo_01.sendCommand(STOP)
])
WZ_Rollo_01_Pos.sendCommand(receivedCommand) // we give the actual position to the Item
}
end

The above rule I have programmed for all 4 rollershutters in an analogue way. Because all rollershutters have a different size and thus also different driving-times, I have measured and assigned the individual times for each rollershutter. Now the wish arises, to have the possibility, to position all 4 rollershutters simultaneously to a dedicated position, e.g. all rollershutters simultaneously to 50%. How can I solve that in an elegant way based on the above rule(s)? I thought about a Lambda approach, would that be a suitable way forward? Unfortunately my programming skills are exhausted here, so I would be very happy for any kind of coding support.

I think Design Pattern: Associated Items coupled with Design Pattern: Encoding and Accessing Values in Rules will be your best bet to make the Rule generic for all your Rollershutters. Trigger the Rule using a Member of trigger, use the name of the triggeringItem to get access to the Rollershutter Item and an Item that holds the measured time it takes to close the shutter and possible another Item to store your RolloTime variable as well.

Once you have done all that, the logic can be the same for all your Rollershutters.

One thing of note. NULL is not the same thing as null. NULL is a state that an Item can have when it is uninitialized. null is a value a variable can have when it is uninitialized. In pretty much all the cases I see with a quick scan, you want to be using null, not NULL.

Hello,

I tried to solve the same problem. On my test system (without the actual hardware) it runs already like a charm.

On my production system it fails. I assume because to many timers running in parallel.

I was not able to debug it further. Dayjob. Vacation. You know those things, don’t you?

I share my code here because I solved most of the questions you had. I hope you find it useful and bring it to life.

import java.util.Map

val logName = "Blinds_Pos"

// Mappings
val Map<String, Number> lengthOfShutter = newHashMap(
  "Shutter_GF_Wohnen"   -> 23.0,
  "Shutter_GF_Arbeiten" -> 12.0,
  "Shutter_GF_Essen"    -> 23.0,
  "Shutter_FF_Schlafen" -> 23.0,
  "Shutter_FF_Kind1"    -> 23.0,
  "Shutter_FF_Kind2"    -> 23.0,
  "Shutter_GF_Test"     -> 23.0
)

val Map<String, Number> positionOfShutter = newHashMap(
  "Shutter_GF_Wohnen"   -> 0.0,
  "Shutter_GF_Arbeiten" -> 0.0,
  "Shutter_GF_Essen"    -> 0.0,
  "Shutter_FF_Schlafen" -> 0.0,
  "Shutter_FF_Kind1"    -> 0.0,
  "Shutter_FF_Kind2"    -> 0.0,
  "Shutter_GF_Test"     -> 0.0
)

var Map<String, Number> currentDirectionOfShutter = newHashMap(
  "Shutter_GF_Wohnen"   -> 0,
  "Shutter_GF_Arbeiten" -> 0,
  "Shutter_GF_Essen"    -> 0,
  "Shutter_FF_Schlafen" -> 0,
  "Shutter_FF_Kind1"    -> 0,
  "Shutter_FF_Kind2"    -> 0,
  "Shutter_GF_Test"     -> 0
)

var Map<String, Timer> timerOfShutter = newHashMap(
  "Shutter_GF_Wohnen"   -> null,
  "Shutter_GF_Arbeiten" -> null,
  "Shutter_GF_Essen"    -> null,
  "Shutter_FF_Schlafen" -> null,
  "Shutter_FF_Kind1"    -> null,
  "Shutter_FF_Kind2"    -> null,
  "Shutter_GF_Test"     -> null
)

rule "Update Blinds Position"
when
    Member of gShutters received command 
then
  val trigger = triggeringItem
  
  // var positionName = trigger.name + "_Position"
  // var positionItem = gBlindPositions.members.findFirst[ t | t.name == positionName ]
  var positionBefore = positionOfShutter.get(trigger.name)
  
  var currentDirection = currentDirectionOfShutter.get(triggeringItem.name)

	logInfo(logName, "A member of gShutters received a command: " + trigger.name + ", state=" + receivedCommand)
  logInfo(logName, "  Length of Shutter is {}, current direction is {}, current pos is {}", 
    lengthOfShutter.get(trigger.name), 
    currentDirection,
    positionBefore)
  
  if (receivedCommand != UP && receivedCommand != DOWN && receivedCommand != STOP) {
      logInfo(logName, "  No UP/DOWN/STOP received, received {} instead", receivedCommand)
      var deltaPercent = (trigger.state as DecimalType) - positionBefore 
      var deltaT = deltaPercent/100 * lengthOfShutter.get(trigger.name)
      var desiredDirection = "UP"
      if (deltaPercent > 0) {desiredDirection="DOWN"} else {desiredDirection="UP"}
      if (deltaT <0) {deltaT = -deltaT}
      deltaT = deltaT + 0.5
      logInfo(logName, "  Delta% {} DeltaT {} Direction {}", deltaPercent, deltaT, desiredDirection)
      logInfo(logName, "  {} should run for {} seconds in direction {}", trigger.name, deltaT, desiredDirection)
      
      return

      if (receivedCommand ==   0) {trigger.sendCommand(UP  ); return}
      if (receivedCommand == 100) {trigger.sendCommand(DOWN); return}
      
      if(timerOfShutter.get(triggeringItem.name) !== null) {
			    timerOfShutter.get(triggeringItem.name).cancel()
      } 
      
      if (desiredDirection == "UP") triggeringItem.sendCommand(UP)
      if (desiredDirection == "DOWN") triggeringItem.sendCommand(DOWN)

      logInfo(logName,"  Set STOP timer after deltaT run.")
      timerOfShutter.put(triggeringItem.name,createTimer(now.plusSeconds(deltaT.intValue)) [|
        trigger.sendCommand(STOP)
        logInfo(logName,"  STOP timer after deltaT run.")
      ])
      return
  }
    
         if (receivedCommand == UP)   { currentDirectionOfShutter.put(triggeringItem.name,-1)
  } else if (receivedCommand == DOWN) { currentDirectionOfShutter.put(triggeringItem.name, 1)
  } else if (receivedCommand == STOP) { currentDirectionOfShutter.put(triggeringItem.name, 0)}

  if(timerOfShutter.get(triggeringItem.name + "_STOP") !== null) {
      timerOfShutter.get(triggeringItem.name + "_STOP").cancel()
  } 
  if (receivedCommand != STOP) {
      var length = (lengthOfShutter.get(trigger.name) + 3)
      logInfo(logName, "  Set STOP Timer after length run.")
      timerOfShutter.put(triggeringItem.name + "_STOP",
      createTimer(now.plusSeconds(length.intValue)) [|
        trigger.sendCommand(STOP)
        logInfo(logName, "  STOP Timer after length run.")
      ]
      )
  }

  var deltaT = (now.millis - triggeringItem.lastUpdate.millis) / 1000.0
  var deltaProcent = deltaT /  lengthOfShutter.get(triggeringItem.name) * currentDirection * 100
  var positionAfter  = positionBefore + deltaProcent

  if (positionAfter > 100) positionAfter= 100;
  if (positionAfter < 0) positionAfter= 0;

  // positionItem.sendCommand(positionAfter)
  positionOfShutter.put(triggeringItem.name, positionAfter)
  triggeringItem.postUpdate(positionAfter)

  logInfo(logName,"  Blind {} run for {} seconds in direction {}, was on {}% and is now on {}%", triggeringItem.name, deltaT, currentDirection, positionBefore, positionAfter)
end

Hello Ralf,

Thanks a lot for sharing the code. I will try to bring it to life :slight_smile:
May I ask you some more questions to better understand the code?

  • are the strings like “Shutter_GF_Wohnen” etc. in the Mappings the names of the rullershutter items?
  • is “gShutters” the name of your Rollershutters-Group that you have defined elsewhere (e.g. in an .items file?
  • how do you “fire” the rule to run? By voice command? I would like to use an Alexa voice command like “Alexa, setze alle Rollos im Wohnzimmer auf 50%”. How can I “connect” the respective Alexa voice command item to the rule?

Depending on the technology involved, there can also be issues with rapidfire spamming of commands to several devices.
Sometimes you need the added complication of short delays between commands.

/1/ yes those are the item names
/2/ yes this is the group
/3/ you can send the command to the item in any rule. If you name the item correctly and Alexa works it should be fairly simple saying “Wohnzimmer Rolladen 50%”
Shutter_GF_Wohnen.sendCommand(50)
/4/ technology is knx

Hi Ralf,

I did not get it, how the Alexa-item and this rule can be “connected”.
I have defined the following Alexa-item:

//--> Alle Rollos im Wohnzimmer
Rollershutter WZ_Rollos_Alexa "Wohnzimmer Alle Rollos" ["Switchable"]

But when I say “Alexa, Wohnzimmer Alle Rollos auf 50%”, nothing happens. Where is my failure? For the rule to run, I think, one item (or member) of my Rollershutter group must receive a command, right? But how can I make it with my Alexa-item? Maybe you can help me.

If you look in your events.log, you should see commands from Alexa. That would be useful to confirm that part works.

If you give your Group the “label” and [tag] of your test Item (and get rid of the test Item) I think that should work.

I do not controll my rollershutter item (yet) via alexa. Cant test right now.

You might want to look at „PercentageController“ here
https://www.openhab.org/docs/ecosystem/alexa/

Another way is adding rule

Rule “you name it”
When
  Item WhatItem changed from off to on
Then
  YourRollershutterGroup.sendCommand(50)
End

Hi Ralf,

can you please describe in short words to me, how your rule exactly works and what it does (in order to see wether it is exactly that what I want to realize)? That would be totally fine. By the way: would it be possible for you to add some comments in the rule in order to better see and understand, what exactly is happening at the respective line? That would indeed be a great help for me too.

Thanks and best regards.

Hello,

I have a rollershutter KNX actor which has no percentage function at all. Its „dump“. It can run up/down thats it. It does not even know at which position it is. It runs up/down until you press stop or until a max runtime is reached. You can set a max time per shutter.

The rule presented reacts on any up/down and stop. Each time any rollershutter is moving (up or down) the rule checks for how long it ran and updates an percentage value.

If the shutter was at the top (0%) and its total runtime is 30s but it ran only 15s down it is at 50%. Got it? Thats one part.

The other part is when it receives a percentage value (set). If so, it checks in which direction it must run (say it was at 50% and you set it to 25%. up if the desired direction bc the value is smaller than the current percentage) and calculates for how long it must run (50%-25% times 30s which is 7.5s) to reach the desired position. It than starts a timer to run (up in that case) for the desired number of milli seconds.

Last thing is when the shutter ran longer than max time up it sets position to 0% (down 100%).

It does all of this with one rule for any shutter which is member of the group.

I hope this helps.

OK, understood. But I want to move all 4 rollershutters at once. Currently I found a solution, but maybe it is not perfect from coding perspective because it consists of some “double” code lines, i.e. four more or less analogue rules (one for each shutter) running in a serial manner… I’m sure that code can be shortened, but how to do it best? Would be nice to get some help on it. Sorry, but I’m a noob…

val Number WZ_Rollo_01_100 = 20 // measured moving time from 0% to 100% in seconds (20sec)
val Number WZ_Rollo_02_100 = 16
val Number WZ_Rollo_03_100 = 16
val Number WZ_Rollo_04_100 = 16

var Number RolloTime_01 = -1 // lengths of movement
var Number RolloTime_02 = -1
var Number RolloTime_03 = -1
var Number RolloTime_04 = -1

var Number RolloDifference = NULL // delta of movement
var String RolloDirection = NULL // up or down


rule "WZ_Alle_Rollos"
when
	Item WZ_Rollos_Alexa received command
then
	logInfo("Rollos Alexa", "Alexa command: " + receivedCommand) // you'll need this for comparison

//Rollo 1

	logInfo("Rollo 1 Alexa", "Actual Rollo Position: " + WZ_Rollo_01_Pos.state)
    logInfo("Rollo 1 Alexa", "receivedCommand: " + receivedCommand)
    
	var Timer_1 timer_1 = null

    if ((receivedCommand as DecimalType).intValue == 100) {
    // wenn ganz runter, ohne Timer (zum Reset)
    RolloDirection = "DOWN"
    RolloTime_01 = NULL
}
    if ((receivedCommand as DecimalType).intValue == 0) {
    // wenn ganz hoch, ohne Timer (zum Reset)
    RolloDirection = "UP"
    RolloTime_01 = NULL
}
    if (receivedCommand > WZ_Rollo_01_Pos.state) {
    // es muss also runter (down)
    RolloDifference = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_01_Pos.state as DecimalType).intValue)
      logInfo("Rollo 1 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "DOWN"
}
    if (receivedCommand < WZ_Rollo_01_Pos.state) {
    // es muss also rauf (up)
    RolloDifference = ((WZ_Rollo_01_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
      logInfo("Rollo 1 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "UP"
}
    if (RolloTime_01 === NULL) {
    logInfo("Rollo 1 Alexa", "Rollo received command: " + RolloDirection)
     WZ_Rollo_01.sendCommand(RolloDirection)
     WZ_Rollo_01_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
     RolloTime_01 = -1
      logInfo("Rollo 1 Alexa", "Rollo Time: " + RolloTime_01)
    } else
    {
    RolloTime_01 = (RolloDifference/100) * WZ_Rollo_01_100 // wir errechnen die Fahrtlänge in Sekunden
      logInfo("Rollo 1 Alexa", "Rollo received command: " + RolloDirection) 
    WZ_Rollo_01.sendCommand(RolloDirection)
      logInfo("Rollo 1 Alexa", "Rollo Time: " + RolloTime_01)
     timer_1 = createTimer(now.plusSeconds(RolloTime_01.intValue), [|
       logInfo("Rollo 1 Alexa", "Timer expired and Rollo set to STOP")
     WZ_Rollo_01.sendCommand(STOP)
     ])
    WZ_Rollo_01_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
}

//Rollo 2

    logInfo("Rollo 2 Alexa", "Actual Rollo Position: " + WZ_Rollo_02_Pos.state)
    logInfo("Rollo 2 Alexa", "receivedCommand: " + receivedCommand)

    var Timer_2 timer_2 = null

    if ((receivedCommand as DecimalType).intValue == 100) {
    // wenn ganz runter, ohne Timer (zum Reset)
    RolloDirection = "DOWN"
    RolloTime_02 = NULL
}
    if ((receivedCommand as DecimalType).intValue == 0) {
    // wenn ganz hoch, ohne Timer (zum Reset)
    RolloDirection = "UP"
    RolloTime_02 = NULL
}
    if (receivedCommand > WZ_Rollo_02_Pos.state) {
    // es muss also runter
    RolloDifference = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_02_Pos.state as DecimalType).intValue)
      logInfo("Rollo 2 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "DOWN"
}
    if (receivedCommand < WZ_Rollo_02_Pos.state) {
    // es muss also rauf
    RolloDifference = ((WZ_Rollo_02_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
      logInfo("Rollo 2 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "UP"
}
    if (RolloTime_02 === NULL) {
    logInfo("Rollo 2 Alexa", "Rollo received command: " + RolloDirection)
     WZ_Rollo_02.sendCommand(RolloDirection)
     WZ_Rollo_02_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
     RolloTime_02 = -1
      logInfo("Rollo 2 Alexa", "Rollo Time: " + RolloTime_02)
    } else
    {
    RolloTime_02 = (RolloDifference/100) * WZ_Rollo_02_100 // wir errechnen die Fahrtlänge in Sekunden
      logInfo("Rollo 2 Alexa", "Rollo received command: " + RolloDirection) 
    WZ_Rollo_02.sendCommand(RolloDirection)
      logInfo("Rollo 2 Alexa", "Rollo Time: " + RolloTime_02)
     timer_2 = createTimer(now.plusSeconds(RolloTime_02.intValue), [|
       logInfo("Rollo 2 Alexa", "Timer expired and Rollo set to STOP")
     WZ_Rollo_02.sendCommand(STOP)
     ])
    WZ_Rollo_02_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
}

//Rollo 3

    logInfo("Rollo 3 Alexa", "Actual Rollo Position: " + WZ_Rollo_03_Pos.state)
    logInfo("Rollo 3 Alexa", "receivedCommand: " + receivedCommand)
	
    var Timer_3 timer_3 = null

    if ((receivedCommand as DecimalType).intValue == 100) {
    // wenn ganz runter, ohne Timer (zum Reset)
    RolloDirection = "DOWN"
    RolloTime_03 = NULL
}
    if ((receivedCommand as DecimalType).intValue == 0) {
    // wenn ganz hoch, ohne Timer (zum Reset)
    RolloDirection = "UP"
    RolloTime_03 = NULL
}
    if (receivedCommand > WZ_Rollo_03_Pos.state) {
    // es muss also runter
    RolloDifference = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_03_Pos.state as DecimalType).intValue)
      logInfo("Rollo 3 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "DOWN"
}
    if (receivedCommand < WZ_Rollo_03_Pos.state) {
    // es muss also rauf
    RolloDifference = ((WZ_Rollo_03_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
      logInfo("Rollo 3 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "UP"
}
    if (RolloTime_03 === NULL) {
    logInfo("Rollo 3 Alexa", "Rollo received command: " + RolloDirection)
     WZ_Rollo_03.sendCommand(RolloDirection)
     WZ_Rollo_03_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
     RolloTime_03 = -1
      logInfo("Rollo 3 Alexa", "Rollo Time: " + RolloTime_03)
    } else
    {
    RolloTime_03 = (RolloDifference/100) * WZ_Rollo_03_100 // wir errechnen die Fahrtlänge in Sekunden
      logInfo("Rollo 3 Alexa", "Rollo received command: " + RolloDirection) 
    WZ_Rollo_03.sendCommand(RolloDirection)
      logInfo("Rollo 3 Alexa", "Rollo Time: " + RolloTime_03)
     timer_3 = createTimer(now.plusSeconds(RolloTime_03.intValue), [|
       logInfo("Rollo 3 Alexa", "Timer expired and Rollo set to STOP")
     WZ_Rollo_03.sendCommand(STOP)
     ])
    WZ_Rollo_03_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
}

//Rollo 4

    logInfo("Rollo 4 Alexa", "Actual Rollo Position: " + WZ_Rollo_04_Pos.state)
    logInfo("Rollo 4 Alexa", "receivedCommand: " + receivedCommand)
	
    var Timer_4 timer_4 = null

    if ((receivedCommand as DecimalType).intValue == 100) {
    // wenn ganz runter, ohne Timer (zum Reset)
    RolloDirection = "DOWN"
    RolloTime_04 = NULL
}
    if ((receivedCommand as DecimalType).intValue == 0) {
    // wenn ganz hoch, ohne Timer (zum Reset)
    RolloDirection = "UP"
    RolloTime_04 = NULL
}
    if (receivedCommand > WZ_Rollo_04_Pos.state) {
    // es muss also runter
    RolloDifference = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_04_Pos.state as DecimalType).intValue)
      logInfo("Rollo 4 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "DOWN"
}
    if (receivedCommand < WZ_Rollo_04_Pos.state) {
    // es muss also rauf
    RolloDifference = ((WZ_Rollo_04_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
      logInfo("Rollo 4 Alexa", "Rollo Difference: " + RolloDifference)
    RolloDirection = "UP"
}
    if (RolloTime_04 === NULL) {
    logInfo("Rollo 4 Alexa", "Rollo received command: " + RolloDirection)
     WZ_Rollo_04.sendCommand(RolloDirection)
     WZ_Rollo_04_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
     RolloTime_04 = -1
      logInfo("Rollo 4 Alexa", "Rollo Time: " + RolloTime_04)
    } else
    {
    RolloTime_04 = (RolloDifference/100) * WZ_Rollo_04_100 // wir errechnen die Fahrtlänge in Sekunden
      logInfo("Rollo 4 Alexa", "Rollo received command: " + RolloDirection) 
    WZ_Rollo_04.sendCommand(RolloDirection)
      logInfo("Rollo 4 Alexa", "Rollo Time: " + RolloTime_04)
     timer_4 = createTimer(now.plusSeconds(RolloTime_04.intValue), [|
       logInfo("Rollo 4 Alexa", "Timer expired and Rollo set to STOP")
     WZ_Rollo_04.sendCommand(STOP)
     ])
    WZ_Rollo_04_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
}

end

I’m thinking of creating a special binding that supports a percentage command and also tracks the position of s shutter where the hardware only supports up/down/stop.

Since you can assign multiple bindings to an item that might be doable. Sadly I have very little time to do that right now but if there is any reason why that would not work I’d like to hear it before investing time.

Count me in for testing.

Me too :slight_smile:

Hello, maybe someone can help me with my rule. The rule shall do the following. If the Item “WZ_Rollos_Alexa” received command than the 4 rollershutter of our living room shall move to a certain position depending on the voice command (i.e. 50%). To achive that, I:

  1. compare the current position of the shutters with the desired position
  2. determine the direction of the move (based on step 1.)
  3. calculate the delta between current and desired position and then the driving time based on that delta
  4. define 4 individual timers (one for each shutter) that stop the movement of the respective shutter after the timer has expired

The problem is that only 2 of the 4 timers work as expected, the other timers seem to do nothing and the 2 assigned shutters don’t stop moving at all whereas the other 2 shutters stop as expected after their timer has expired. Does anybody has a clue what the problem is and can help me to solve it.

Thanks and best regards.

val Number WZ_Rollo_01_100 = 20 // measured time in seconds for move from fully open to fully closed
val Number WZ_Rollo_02_100 = 16
val Number WZ_Rollo_03_100 = 16
val Number WZ_Rollo_04_100 = 16

var Number RolloTime_01 = -1 // das wird die Länge der Fahrt
var Number RolloTime_02 = -1
var Number RolloTime_03 = -1
var Number RolloTime_04 = -1

var Number RolloDifference_1 = NULL // Delta der Rollofahrt
var Number RolloDifference_2 = NULL // Delta der Rollofahrt
var Number RolloDifference_3 = NULL // Delta der Rollofahrt
var Number RolloDifference_4 = NULL // Delta der Rollofahrt

var String RolloDirection = NULL // rauf/runter

rule "WZ_Alle_Rollos"
when
	Item WZ_Rollos_Alexa received command
then
		logInfo("Rollos Alexa", "Alexa command: " + receivedCommand) // you'll need this for comparison

	var Timer WZ_timer_1 = null
	var Timer WZ_timer_2 = null
	var Timer WZ_timer_3 = null
	var Timer WZ_timer_4 = null

		logInfo("Rollo 1 Alexa", "Actual Rollo Position: " + WZ_Rollo_01_Pos.state)
		logInfo("Rollo 2 Alexa", "Actual Rollo Position: " + WZ_Rollo_02_Pos.state)
		logInfo("Rollo 3 Alexa", "Actual Rollo Position: " + WZ_Rollo_03_Pos.state)
		logInfo("Rollo 4 Alexa", "Actual Rollo Position: " + WZ_Rollo_04_Pos.state)
		logInfo("Rollo 1 Alexa", "receivedCommand: " + receivedCommand)	
		logInfo("Rollo 2 Alexa", "receivedCommand: " + receivedCommand)	
		logInfo("Rollo 3 Alexa", "receivedCommand: " + receivedCommand)	
		logInfo("Rollo 4 Alexa", "receivedCommand: " + receivedCommand)	

    if ((receivedCommand as DecimalType).intValue == 100) {
    // wenn ganz runter, ohne Timer (zum Reset)
    RolloDirection = "DOWN"
    RolloTime_01 = NULL
	RolloTime_02 = NULL
	RolloTime_03 = NULL
	RolloTime_04 = NULL
}
    if ((receivedCommand as DecimalType).intValue == 0) {
    // wenn ganz hoch, ohne Timer (zum Reset)
    RolloDirection = "UP"
    RolloTime_01 = NULL
	RolloTime_02 = NULL
	RolloTime_03 = NULL
	RolloTime_04 = NULL
}
    if ((receivedCommand > WZ_Rollo_01_Pos.state) || (receivedCommand > WZ_Rollo_02_Pos.state) || (receivedCommand > WZ_Rollo_03_Pos.state) || (receivedCommand > WZ_Rollo_04_Pos.state)) {
    // es muss also runter
    RolloDifference_1 = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_01_Pos.state as DecimalType).intValue)
	RolloDifference_2 = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_02_Pos.state as DecimalType).intValue)
	RolloDifference_3 = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_03_Pos.state as DecimalType).intValue)
	RolloDifference_4 = ((receivedCommand as DecimalType).intValue-(WZ_Rollo_04_Pos.state as DecimalType).intValue)
	
		logInfo("Rollo 1 Alexa", "Rollo Difference: " + RolloDifference_1)
		logInfo("Rollo 2 Alexa", "Rollo Difference: " + RolloDifference_2)
		logInfo("Rollo 3 Alexa", "Rollo Difference: " + RolloDifference_3)
		logInfo("Rollo 4 Alexa", "Rollo Difference: " + RolloDifference_4)
		
    RolloDirection = "DOWN"
}
    if ((receivedCommand < WZ_Rollo_01_Pos.state) || (receivedCommand < WZ_Rollo_02_Pos.state) || (receivedCommand < WZ_Rollo_03_Pos.state) || (receivedCommand < WZ_Rollo_04_Pos.state)) {
    // es muss also rauf
    RolloDifference_1 = ((WZ_Rollo_01_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
	RolloDifference_2 = ((WZ_Rollo_02_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
	RolloDifference_3 = ((WZ_Rollo_03_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
	RolloDifference_4 = ((WZ_Rollo_04_Pos.state as DecimalType).intValue-(receivedCommand as DecimalType).intValue)
	
		logInfo("Rollo 1 Alexa", "Rollo Difference: " + RolloDifference_1)
		logInfo("Rollo 2 Alexa", "Rollo Difference: " + RolloDifference_2)
		logInfo("Rollo 3 Alexa", "Rollo Difference: " + RolloDifference_3)
		logInfo("Rollo 4 Alexa", "Rollo Difference: " + RolloDifference_4)
		
    RolloDirection = "UP"
}
    if ((RolloTime_01 === NULL) || (RolloTime_02 === NULL) || (RolloTime_03 === NULL) || (RolloTime_04 === NULL)) {
		logInfo("Rollo 1 Alexa", "Rollo received command: " + RolloDirection)
		logInfo("Rollo 2 Alexa", "Rollo received command: " + RolloDirection)
		logInfo("Rollo 3 Alexa", "Rollo received command: " + RolloDirection)
		logInfo("Rollo 4 Alexa", "Rollo received command: " + RolloDirection)
		
    WZ_Rollo_01.sendCommand(RolloDirection)
	WZ_Rollo_02.sendCommand(RolloDirection)
	WZ_Rollo_03.sendCommand(RolloDirection)
	WZ_Rollo_04.sendCommand(RolloDirection)
	 
    WZ_Rollo_01_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
	WZ_Rollo_02_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
	WZ_Rollo_03_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
	WZ_Rollo_04_Pos.sendCommand(receivedCommand) // wir geben die aktuelle Position ans Item.
	 
    RolloTime_01 = -1
	RolloTime_02 = -1
	RolloTime_03 = -1
	RolloTime_04 = -1
	 
		logInfo("Rollo 1 Alexa", "Rollo Time: " + RolloTime_01)
		logInfo("Rollo 2 Alexa", "Rollo Time: " + RolloTime_02)
		logInfo("Rollo 3 Alexa", "Rollo Time: " + RolloTime_03)
		logInfo("Rollo 4 Alexa", "Rollo Time: " + RolloTime_04)
	  
    } else {
	
    RolloTime_01 = (RolloDifference_1/100) * WZ_Rollo_01_100 // wir errechnen die Fahrtlänge in Sekunden
	RolloTime_02 = (RolloDifference_2/100) * WZ_Rollo_02_100 // wir errechnen die Fahrtlänge in Sekunden
	RolloTime_03 = (RolloDifference_3/100) * WZ_Rollo_03_100 // wir errechnen die Fahrtlänge in Sekunden
	RolloTime_04 = (RolloDifference_4/100) * WZ_Rollo_04_100 // wir errechnen die Fahrtlänge in Sekunden
				
		logInfo("Rollo 1 Alexa", "Rollo received command: " + RolloDirection)
		logInfo("Rollo 2 Alexa", "Rollo received command: " + RolloDirection)
		logInfo("Rollo 3 Alexa", "Rollo received command: " + RolloDirection)
		logInfo("Rollo 4 Alexa", "Rollo received command: " + RolloDirection)
	  
    WZ_Rollo_01.sendCommand(RolloDirection)
	WZ_Rollo_02.sendCommand(RolloDirection)
	WZ_Rollo_03.sendCommand(RolloDirection)
	WZ_Rollo_04.sendCommand(RolloDirection)
	
		logInfo("Rollo 1 Alexa", "Rollo Time: " + RolloTime_01)
		logInfo("Rollo 2 Alexa", "Rollo Time: " + RolloTime_02)
		logInfo("Rollo 3 Alexa", "Rollo Time: " + RolloTime_03)
		logInfo("Rollo 4 Alexa", "Rollo Time: " + RolloTime_04)
	
    WZ_timer_1 = createTimer(now.plusSeconds(RolloTime_01.intValue), [|
		logInfo("Rollo 1 Alexa", "Timer expired and Rollo set to STOP")
    WZ_Rollo_01.sendCommand(STOP)
    ])
	 
	WZ_timer_2 = createTimer(now.plusSeconds(RolloTime_02.intValue), [|
		logInfo("Rollo 2 Alexa", "Timer expired and Rollo set to STOP")
    WZ_Rollo_02.sendCommand(STOP)
    ])
	 
	WZ_timer_3 = createTimer(now.plusSeconds(RolloTime_03.intValue), [|
		logInfo("Rollo 3 Alexa", "Timer expired and Rollo set to STOP")
    WZ_Rollo_03.sendCommand(STOP)
    ])
	 
	WZ_timer_4 = createTimer(now.plusSeconds(RolloTime_04.intValue), [|
		logInfo("Rollo 4 Alexa", "Timer expired and Rollo set to STOP")
    WZ_Rollo_04.sendCommand(STOP)
    ])
	 
    WZ_Rollo_01_Pos.sendCommand(receivedCommand)	// wir geben die aktuelle Position ans Item
	WZ_Rollo_02_Pos.sendCommand(receivedCommand)	// wir geben die aktuelle Position ans Item
	WZ_Rollo_03_Pos.sendCommand(receivedCommand)	// wir geben die aktuelle Position ans Item
	WZ_Rollo_04_Pos.sendCommand(receivedCommand)	// wir geben die aktuelle Position ans Item
}
end

I wouldn’t set yout times to NULL, these are not Item states. Maybe 0 or null.

Do you see the messages each timer should make?

In the log I see:

2019-08-10 15:40:25.132 [INFO ] [.smarthome.model.script.Rollos Alexa] - Alexa command: 50
2019-08-10 15:40:25.132 [INFO ] [smarthome.model.script.Rollo 1 Alexa] - Actual Rollo Position: 0
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 2 Alexa] - Actual Rollo Position: 0
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 3 Alexa] - Actual Rollo Position: 0
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 4 Alexa] - Actual Rollo Position: 0
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 1 Alexa] - receivedCommand: 50
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 2 Alexa] - receivedCommand: 50
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 3 Alexa] - receivedCommand: 50
2019-08-10 15:40:25.148 [INFO ] [smarthome.model.script.Rollo 4 Alexa] - receivedCommand: 50
2019-08-10 15:40:25.165 [INFO ] [smarthome.model.script.Rollo 1 Alexa] - Rollo Difference: 50
2019-08-10 15:40:25.165 [INFO ] [smarthome.model.script.Rollo 2 Alexa] - Rollo Difference: 50
2019-08-10 15:40:25.165 [INFO ] [smarthome.model.script.Rollo 3 Alexa] - Rollo Difference: 50
2019-08-10 15:40:25.165 [INFO ] [smarthome.model.script.Rollo 4 Alexa] - Rollo Difference: 50
2019-08-10 15:40:25.182 [INFO ] [smarthome.model.script.Rollo 1 Alexa] - Rollo received command: DOWN
2019-08-10 15:40:25.182 [INFO ] [smarthome.model.script.Rollo 2 Alexa] - Rollo received command: DOWN
2019-08-10 15:40:25.182 [INFO ] [smarthome.model.script.Rollo 3 Alexa] - Rollo received command: DOWN
2019-08-10 15:40:25.182 [INFO ] [smarthome.model.script.Rollo 4 Alexa] - Rollo received command: DOWN
2019-08-10 15:40:25.187 [INFO ] [smarthome.model.script.Rollo 1 Alexa] - Rollo Time: 10.00000000
2019-08-10 15:40:25.187 [INFO ] [smarthome.model.script.Rollo 2 Alexa] - Rollo Time: 8.00000000
2019-08-10 15:40:25.187 [INFO ] [smarthome.model.script.Rollo 3 Alexa] - Rollo Time: 8.00000000
2019-08-10 15:40:25.187 [INFO ] [smarthome.model.script.Rollo 4 Alexa] - Rollo Time: 8.00000000
2019-08-10 15:40:33.197 [INFO ] [smarthome.model.script.Rollo 4 Alexa] - Timer expired and Rollo set to STOP
2019-08-10 15:40:35.202 [INFO ] [smarthome.model.script.Rollo 1 Alexa] - Timer expired and Rollo set to STOP

I see two of the timers only?? Any idea?

Hmm, three 8 second timers created in the same millisecond. Only the last one gets executed. I wonder …


I did not think it was granular to the millisecond, but makes sense.

I think you could try staggering your times just by 10mS or so

Hi rossko57,

what does it mean and how can I do it? Sorry, but I‘m not an expert.