Tipps zu homematic Skripts

Nachfolgende Tipps und CODE-Snipets geben meine persönlichen Erfahrungen bei der Script-Erstellung für die CCU wieder. Sie dürfen gerne kritisch hinterfragt werden …
Mancher Anwender mit Erfahrungen in modernen Skript-Sprachen dürfte sich über Eigenarten und fehlende Features bei der CCU-Skript-Sprache geärgert haben – lohnt sich nicht. Dieser erratische Block besteht zu lange schon.

Vorsicht:
Kapselung existiert nicht. Die in einem Skript deklarierten Variablen sind global „vorhanden“ – mit teilweise unerwarteten Seiteneffekten. Werden dieselben Variablen-Namen in verschiedenen Skripts verwendet und laufen diese Skripts „gleichzeitig“ kann es schon mal zu Geistereffekten kommen.

Vorsicht
Durch die Änderung der Beschreibung (!) von Systemvariablen im WebUI geht nebenbei deren Inhalt (Wert) verloren. (32.4 -> 0; true -> false; „hallo Otto“ -> „???“)
Sicherlich schon lange bekannt.

Vorsicht:
Die Evaluation von Ausrücken erfolgt stets von rechts nach links und NICHT nach den allgemeinen Regeln der Mathematik, wie diese üblicherweise in den verbreiteten Skript-Sprachen angewendet werden.
Dies ist sehr ungewohnt und führt zu ebenso ungewohnten Ergebnissen.
Quintessenz: immer ausreichend Klammern setzen.
Nur k enthält den erwarteten Wert 18.

integer i = 1 +  2*2  +  3*3  + 4;
integer j = 1 +  2*2  + (3*3) + 4;
integer k = 1 + (2*2) + (3*3) + 4;   

Diese gilt auch für boolsche Ausdrücke wie z.B. Bedingungen bei einem if.
Quintessenz erneut: immer ausreichend Klammern setzen.

boolean b1= (!false && !true);     ! b1 wird true -> eher unerwartet
boolean b2= (!true && !false);     ! b1 wird false 
boolean b3= ((!true) && (!false)); ! b3 wird false
boolean b4= ((!false) && (!true)); ! b4 wird false

Vorsicht
Trotz Deklaration als integer ist die Variable currentHour im folgenden Beispiel ein String (Type Nr. 4). Dies kann bei Vergleichen (je nach Reihenfolge in einem logischen Ausdrucks) zu merkwürdigen Resultaten führen.

integer currentHour= system.Date("%H");
WriteLine("Type of currentHour: "#currentHour.VarType());
WriteLine("(currentHour >= 2): "#(currentHour >= 2));
WriteLine("(2 < currentHour ): "#(2 < currentHour));


Im Zweifelsfall ist eine explizite Typen-Umwandlung .ToInteger() angezeigt.

integer currentHour= system.Date("%H").ToInteger();
WriteLine("Type of currentHour: "#currentHour.VarType());
WriteLine("(currentHour >= 2): "#(currentHour >= 2));
WriteLine("(2 < currentHour ): "#(2 < currentHour));

Objekt-Referenzen:
Objekte, deren Zustand (z.B. Temperatur eines Sensors, Schaltzustand eines Aktors etc.) von der CCU abgefragt werden müssen, sollten VOR dem eigentlichen Zugriff im Skript überprüft werden. Ansonsten kann es zu unerwünschten Nebeneffekten kommen. Man sollte sich nicht darauf verlassen, dass ein Zugriff stets klappt. Aufgrund z.B. von EMV-Problemen können Störungen auftreten.

Beispiel einer Aktorabfrage
string ActuatorLine1="HmIP-RF.00259D89A4DE32:6.STATE";
string ActuatorLine2="HmIP-RF.00259D89A4DE32:10.STATE";
string ActuatorLine3="HmIP-RF.00259D89A4DE32:14.STATE";

var L1OnObj = dom.GetObject(ActuatorLine1); ! boolean
var L2OnObj = dom.GetObject(ActuatorLine2); ! boolean
var L3OnObj = dom.GetObject(ActuatorLine3); ! boolean

if (L1OnObj && L2OnObj && L3OnObj) { ! alle Objekte da?

	integer linesOn = L1OnObj.Value().ToInteger() + L2OnObj.Value().ToInteger() + L3OnObj.Value().ToInteger();

	if ((linesOn < 0) || (linesOn>3)) {
		quit; ! Ausstieg, da keine plausiblen Werte vorhanden
	}

} else {
	quit; ! ungültige Objektreferenzen
}

WriteLine(" --- linesOn:" # linesOn); 
  • Systemvariablen, die in Skripten verwendet werden, sollten eher nicht manuell angelegt werden, sondern besser in einem Skript. Im Falle eines Falles können diese einfach per Skript wieder hergestellt werden.
Beispiel eines Setup-Skripts zu Erstellung von Systemvariablen
string persistentTempNames="Mean_Temp_Sum,Mean_Temp,max_TempDay,min_TempDay,max_TempAbs,min_TempAbs";

object sysVar;
object sysVarObjects = dom.GetObject(ID_SYSTEM_VARIABLES);
string persistentVarName;

foreach(persistentVarName, persistentTempNames.Split(",")) {
	sysVar  = dom.GetObject(persistentVarName);
	if (sysVar  ) {
		WriteLine("System Variable "#persistentVarName#" already exists!");
	} else {
		sysVar = dom.CreateObject(OT_VARDP);
		sysVarObjects.Add(sysVar.ID());
		sysVar.Name(persistentVarName);   
		sysVar.ValueType(ivtFloat);
		sysVar.ValueSubType(istGeneric);
		sysVar.DPInfo("Temperaturdaten in Grad Celsius");
		sysVar.ValueMin(0);
		sysVar.ValueMax(65000);
		sysVar.ValueUnit("°C");  
		sysVar.DPArchive(false); !do not log changes of this system var
		sysVar.State(0.0); ! initial value
		sysVar.Internal(false); !not an internal var
		sysVar.Visible(true);
		WriteLine(persistentVarName#" created.");		
	}
}