Lifx animations

Post scriptum:

I also tried to simply fade the strip off, as the button in the app does. But I want to stretch the fade duration to 10 instead of 1 seconds. How could one achieve this? My first approach was to define each of the 16 color zones as an individual item, and to fade them out separated, based on this Universaldimmer implementation.

Items:

// TODO: Deduplicate using reflection?
Color Strip_C_CouchBackgroundZone0 "Couchbeleuchtung (Zone 0)" <lightbulb> { channel="lifx:colormzlight:D073D52FE9DB:colorzone0" }
Color Strip_C_CouchBackgroundZone1 "Couchbeleuchtung (Zone 1)" <lightbulb> { channel="lifx:colormzlight:D073D52FE9DB:colorzone1" }
//...
Color Strip_C_CouchBackgroundZone15 "Couchbeleuchtung (Zone 15)" <lightbulb> { channel="lifx:colormzlight:D073D52FE9DB:colorzone15" }

String universaldimmer "Trigges a rule to fade in/out a light"

Rules:

rule "Button_C_FadeBackground"
when
	Item Button_C_FadeBackground changed
then
	val zones = (0..15).map [int i |
		ScriptServiceUtil.getItemRegistry.getItem('Strip_C_CouchBackgroundZone' + i)]
	zones.forEach [zone |
		universaldimmer.sendCommand('Color,0,10000,50,' + zone.name)]
end

Dimmer rules (widely copied from PeterK’s linked version; but use a mapping to allow for multiple timers):

import java.util.HashMap
import org.eclipse.smarthome.model.script.ScriptServiceUtil

val HashMap<GenericItem, Timer> timers = newHashMap()

// will dimm the item and report back the new brightness
var dimm = [ GenericItem myitem, String mytype, Number mytarget, Number mystep  |
	var int mybrightness = 0
	
	mybrightness = (if (mytype == "Color")
		(myitem.state as HSBType).getBrightness()
	else
		(myitem.state as DecimalType)).intValue
	
	if (mytarget.intValue > mybrightness) {
		mybrightness = mybrightness + mystep.intValue
		if (mybrightness > mytarget.intValue) mybrightness = mytarget.intValue
	} else {
		mybrightness = mybrightness - mystep.intValue
		if (mybrightness < mytarget.intValue) mybrightness = mytarget.intValue
	}
	
	myitem.sendCommand(mybrightness)
	
	mybrightness
]


rule "Universaldimmer"
when
	Item universaldimmer received command // expect e.g. Color,100,120000,12,col_E1_ES_Hue_Go_Farbe
then
	val buffer			= receivedCommand.toString.split(",")
	val mytype			= receivedCommand.toString.split(',').get(0)					// Color or Dimmer
	var int mytarget	= Integer::parseInt(buffer.get(1))								// where to go to 
	val int myfadetime	= Integer::parseInt(buffer.get(2))					 			// time for fading
	val int mystep		= Integer::parseInt(buffer.get(3))								// step for fading
	val myitem_name		= receivedCommand.toString.split(',').get(4)					// Item to handle
	val myitem			= ScriptServiceUtil.getItemRegistry.getItem(myitem_name)
	var int mytime		= 0
	
	logInfo("Universaldimmer", "Going to dim item " + myitem + "to target " + mytarget)
	
	if (timers.containsKey(myitem)) {
		logInfo("trying to cancel timer for " + myitem)
		timers.get(myitem).cancel()
		logInfo("canceled timer for " + myitem)
	}
	
	if		(mytarget > 100)	mytarget = 100
	else if (mytarget < 0)		mytarget = 0
	
	// fade
	var int mybrightness = dimm.apply(myitem, mytype, mytarget, mystep)
	
	mytime = Math::round(myfadetime * mystep /
		if (mybrightness > mytarget)
			mybrightness + mystep - mytarget
		else
			mytarget - (mybrightness + mystep))
	
	timers.put(myitem, createTimer(now.plusMillis(mytime)) [|
		// Check if finishes
		if (mybrightness != mytarget )  {
			// if not finished dim again 
			mybrightness = dimm.apply(myitem, mytype, mytarget, mystep)
			
			// rescheduling
			timers.get(myitem).reschedule(now.plusMillis(mytime))
			logInfo("dimmed " + myitem + " to " + myitem.state.toString)
		} else {
			timers.remove(myitem)
			myswitch.sendCommand(OFF)
			logInfo("Universaldimmer", "Completed dimming of " + myitem + "to target " + mytarget)
		}
	])
end

Unfortunately, this works very unreliably. I get tons of BasicIndexOutOfBoundsExceptions raised in XbaseInterpreter.evaluateArgumentExpressions and only few color zones are dimmed completely:

Any ideas what could I could have done wrong? What does this error mean? Anyway, I wonder whether the strip indeed does not provide a more specific interface …