Auf meinem neuen Display-Taster (HmIP-WRCD) wollte ich gerne die Anzahl der angeschalteten Lampen darstellen. Ich hatte mir sowas ähnliches ganz einfach für meine Fenster geschrieben, doch ist das für Lampen kaum geeignet:

!Check Fenster------------------------
var WZ_Fenster = "HmIP-RF.xxxxxxxxxxxxxx:1";
var KU_Fenster = "HmIP-RF.xxxxxxxxxxxxxx:1";
var BAD_Fenster = "HmIP-RF.xxxxxxxxxxxxxx:1";
var SZ_Fenster = "HmIP-RF.xxxxxxxxxxxxxx:1";
var SZ_Tuer = "HmIP-RF.xxxxxxxxxxxxxx:1";

var OpenWindows = 
    dom.GetObject(WZ_Fenster + ".STATE").Value() + 
    dom.GetObject(KU_Fenster + ".STATE").Value() + 
    dom.GetObject(BAD_Fenster + ".STATE").Value() + 
    dom.GetObject(SZ_Fenster + ".STATE").Value() + 
    dom.GetObject(SZ_Tuer + ".STATE").Value();

if(dom.GetObject("Check_FensterOffen").State() != OpenWindows ){
    dom.GetObject("Check_FensterOffen").State(OpenWindows);
}

Ich habe also ganz einfach alle Fenster/Türen aufgelistet (haben ja nicht so viele einen Sensor). Den Auf/Zu Wert habe ich dann einfach summiert, und diese Zahl wird dann in eine Systemvariable "Check_FensterOffen" gespeichert. Das Skript ist einfach, schnell geschrieben, aber hat den entscheidenen Nachteil, das man die Geräte alle einzeln "hart verdrahten" muss. Das hätte ich nun bei den Lampen ebenso machen können, aber davon besitze ich einige mehr. Zum Hintergrund, ich setze auch ein "Philips Hue" System ein, da ist jede Glühbirne ein Gerät.

Also kam mir die Idee, einfach ein Skript zu schreiben, welches alle Lampen durchparst. Am sinnvollsten geht man über die "Gewerke" (also dem Funktionstyp des Gerätes). Ich habe also mein Skript mit:

var i = dom.GetObject("Licht");

 begonnen, wie man es in so manchen Tutorial sieht. Funktioniert aber nicht, da es kein Objekt mit diesem Namen gibt. Ich hab also so mal verschiedenes ausprobiert, aber ich konnte das "Licht"-Objekt einfach nicht finden. Hmm, dann habe ich mal folgendes probiert:

!Alle Gewerke--------------------------
object lFunctions = dom.GetObject(ID_FUNCTIONS);
string lFunction;
WriteLine("Gewerke:");
foreach(lFunction,lFunctions.EnumNames())
{
  WriteLine(lFunction);
}

Ok, Ok, die heißen nun also:

Gewerke:
funcButton
funcCentral
funcClimateControl
funcEnergy
funcEnvironment
funcHeating
funcLight
funcLock
funcSecurity
funcWeather

Ja, sinnvoll, müsste man aber erst wissen... Nun gut dann gehts also weiter:

!Check Lampen innerhalb Gewerk----------------------------------
var i = dom.GetObject("funcLight");
var noLightsOn = 0;
string itemID;

foreach(itemID, i.EnumUsedIDs()) {
    var oLight = dom.GetObject(itemID);
    WriteLine("---");
    WriteLine("n:"+oLight.Name()); 
    WriteLine("s:"+oLight.State().ToString()); 
}

WriteLine("Ende");

Da kommt ja schon so einiges raus, aber eigentlich brauch ich ja nur immer ein Kanal, aber wie kann ich Ihn greifen? Ich habe ein bißchen gesucht, und folgende Lösung gefunden:

!Check Lampen innerhalb Gewerk----------------------------------
var i = dom.GetObject("funcLight");
var noLightsOn = 0;
string itemID;

foreach(itemID, i.EnumUsedIDs()) {
    var oLight = dom.GetObject(itemID);
    var dev = dom.GetObject(oLight.Device());
    var oCh1Light = dev.Channels().GetAt(1);
    !is channel1?
    if(oLight.Name() == oCh1Light.Name()){
         WriteLine("---");
         WriteLine("n:"+oLight.Name()); 
         WriteLine("d:"+dev.HssType());
         WriteLine("s:"+oLight.State().ToString());
    }
}

WriteLine("Ende");

Es wird also zu jedem ermittelten Kanal das Gerät ermittelt, und von dort der Kanal 1 ermittelt. Nur wenn es sich um Kanal 1 handelt erfolgt nun eine Ausgabe.
Das sieht schonmal besser aus. Doch einige Lampen geben gar kein Ergebnis aus :-(. Also habe ich mal kurz mit dem XMP-API-CCU Addon recherchiert, und folgendes "für mich" gefunden:

Ich besitze die folgenden Lampentypen:

  • -VIR-LG-RGBW-DIM, das sind Hue-RGB-Lampen, welche vom Philips Hue Addon kommen (über CUxD importiert)
  • -VIR-LG-WHITE-DIM, das gleiche wie oben, nur sind das Weiße Lampen
  • -VIR-LG-ONOFF, eine Steckdose, wie oben Hue über CuxD importiert.
  • -HmIP-BSM, einige Lampen habe ich nicht über Hue, sonder klassisch über Homematic Ip angebunden.

Nun verhalten sich die Lampentypen unterschiedlich: Bei VIR-LG-ONOFF konnte man noch einfach den An/Aus-Wert finden. Er ist im Kanal 1 unter STATE abgespeichert, man muss ".toString()" verwenden, das der Wert auch mittels WriteLine() angezeigt wird. Bei VIR-LG-RGBW-DIM kommt ein Float raus, es handelt sich um den Dimmwert. Beim Weißen Licht VIR-LG-WHITE-DIM befinden sich die Werte abwechslungsweise mal im Kanal 2, auch hier als Float mit Dimmwert. Bei HmIP-BSM wundert sich der Anfänger: Bei Homematic-IP Aktoren befinden sich normalerweise auf:

  • -Kanal 1: Allgemeine Daten (allgemein üblich bei Homematic)
  • -Kanal 2: Taster unten (ja bei Homematic oben und unten immer vertauscht)
  • -Kanal 3: Taster oben 
  • -Kanal 4: Aktor
  • -Kanal 5: weiterer Aktorkanal
  • -Kanal 6: weiterer Aktorkanal
  • -Kanal 7: Strommesser
  • -Kanal 8: Benachrichtigung vom Strom abhängig
  • -Kanal 9: Timer

Der "interessante" Ein/Aus Wert befindet sich also auf Kanal 4. Nun gut, also muss noch für jeden Schaltertyp eine eigene "Extrawurst" gebraten werden, um einheitlich die Anzahl der angeschalteten Lampen zu ermitteln. Hier also das komplette Skript:

!Check Lampen innerhalb Gewerk----------------------------------
var i = dom.GetObject("funcLight");
var noLightsOn = 0;
string itemID;

foreach(itemID, i.EnumUsedIDs()) {
    var oLight = dom.GetObject(itemID);
    var dev = dom.GetObject(oLight.Device());
    var oCh1Light = dev.Channels().GetAt(1);
    var oCh2Light = dev.Channels().GetAt(2);
    var oCh4Light = dev.Channels().GetAt(4);
    !is channel1?
    if(oLight.Name() == oCh1Light.Name()){
      if(dev.HssType() == "VIR-LG-RGBW-DIM"){
         WriteLine("---");
         WriteLine("n:"+oLight.Name()); 
         WriteLine("d:"+dev.HssType());
         var tmpState = false;
         if(oLight.State() > 0){
           tmpState = true;
           noLightsOn = noLightsOn + 1;
         }
         WriteLine("s:"+tmpState.ToString()); 
      }
      if(dev.HssType() == "VIR-LG-ONOFF"){
         WriteLine("---");
         WriteLine("n:"+oLight.Name()); 
         WriteLine("d:"+dev.HssType());
         WriteLine("s:"+oLight.State().ToString());
         if(oLight.State()){
            noLightsOn = noLightsOn + 1;
         }
      } 
    }
    if(oLight.Name() == oCh2Light.Name()){
      if(dev.HssType() == "VIR-LG-WHITE-DIM"){
         WriteLine("---");
         WriteLine("n:"+oLight.Name()); 
         WriteLine("d:"+dev.HssType());
         var tmpState = false;
         if(oLight.State() > 0){
           tmpState = true;
           noLightsOn = noLightsOn + 1;
         }
         WriteLine("s:"+tmpState.ToString()); 
      }
    }
    if(oLight.Name() == oCh4Light.Name()){
      if(dev.HssType() == "HmIP-BSM"){
         var tmplightname = "HmIP-RF." + oLight.Address() + ".STATE";
         var oLightState = dom.GetObject(tmplightname).Value();
         WriteLine("---");
         WriteLine("n:"+oLight.Name()); 
         WriteLine("d:"+dev.HssType());
         WriteLine("a:"+tmplightname);
         WriteLine("s:"+oLightState.ToString());
         if(oLightState){
            noLightsOn = noLightsOn + 1;
         }
      }
    }
}

if(dom.GetObject("Check_LichtAn").State() != noLightsOn){
    dom.GetObject("Check_LichtAn").State(noLightsOn);
}

WriteLine("Ende");

Was ist noch dazugekommen? Ich habe hier für jeden Typ nach dem entsprechenden Kanal gefiltert. Bei VIR-LG-RGBW-DIM und VIR-LG-WHITE-DIM habe ich mittels 

if(oLight.State() > 0){
   noLightsOn = noLightsOn + 1;
}

den Wert in 0 und 1 umgewandelt. Beim HmIP-BSM kommt nach der bewährten Methode nur "null" raus, also habe ich direkt den Datenkanal angezapft:

var tmplightname = "HmIP-RF." + oLight.Address() + ".STATE";
var oLightState = dom.GetObject(tmplightname).Value();
if(oLight.State()){
     noLightsOn = noLightsOn + 1;
}

Dazu habe ich die vorhandene Adresse verwendet, das Protokoll noch davorgeschrieben, dann klappt mit dem ".STATE". Zur Veranschaulichung der Zugriff auf den Datenkanal:

"HmIP-RF.xxxxxxxxxxxxxx:4.STATE"

Die ganzen Daten werden wie hier nun in Systemvariablen geschrieben. Hier im Beispiel:

if(dom.GetObject("Check_LichtAn").State() != noLightsOn){
    dom.GetObject("Check_LichtAn").State(noLightsOn);
}

Die zusätzliche Abfrage verhindert ein aktualisieren des Zugriffes, und damit eine Aktualisierung der Variable. In meinem separaten Skript zum Display, benutze ich dann die Aktualisierungszeit der Systemvariable. Wer sich hierrüber wundert: Ja das ist eine der zahlreichen undokumentierten Funktionen von Homematic-Skript.

Hier mal das Display-Skript:

var cntMinutes = 1;
var FLDisplay = "HmIP-RF.xxxxxxxxxxxxxx:3";
var CurrTimestamp = system.Date().ToTime().ToInteger();
var oFensterOffen= dom.GetObject(ID_SYSTEM_VARIABLES).Get("Check_FensterOffen");
var oHeizungAuto = dom.GetObject(ID_SYSTEM_VARIABLES).Get("Check_HeizungAuto");
var oLichtAn = dom.GetObject(ID_SYSTEM_VARIABLES).Get("Check_LichtAn");
var uhrzeit = system.Date("%d.%m %H:%M");

var dt = "{DDBC=WHITE,DDTC=BLACK,DDA=CENTER,DDS=Lich/Fen: ";
dt = dt+      oLichtAn.Value().ToInteger()#"/"#oFensterOffen.Value().ToInteger()#",DDID=2},";
dt = dt+"{DDBC=WHITE,DDTC=BLACK,DDA=CENTER,DDS=Heiz Auto:"#oHeizungAuto.Value().ToInteger()#",DDID=3},";
dt = dt+"{DDBC=WHITE,DDTC=BLACK,DDA=CENTER,DDS="#uhrzeit#",DDID=4,DDC=true}";

if((CurrTimestamp  - oFensterOffen.Timestamp().ToInteger()) < 60*cntMinutes){
   dom.GetObject(FLDisplay+ ".COMBINED_PARAMETER").State(dt);
   WriteLine("Fenster");
}elseif((CurrTimestamp  - oHeizungAuto .Timestamp().ToInteger()) < 60*cntMinutes){
   dom.GetObject(FLDisplay+ ".COMBINED_PARAMETER").State(dt);
   WriteLine("Heizung");
}elseif((CurrTimestamp  - oLichtAn.Timestamp().ToInteger()) < 60*cntMinutes){
   dom.GetObject(FLDisplay+ ".COMBINED_PARAMETER").State(dt);
   WriteLine("Licht");
}
WriteLine("Ende");

Es werden alle Daten in einem Rutsch übermittelt, dazu kann man das Format:

var FLDisplay = "HmIP-RF.xxxxxxxxxxxxxx:3";
dom.GetObject(FLDisplay+ ".COMBINED_PARAMETER").State("{DDBC=WHITE,DDTC=BLACK,DDI=1,DDA=CENTER,DDS=Zeile1,DDID=1},{DDBC=WHITE,DDTC=BLACK,DDI=2,DDA=CENTER,DDS=Zeile2,DDID=2},{DDBC=WHITE,DDTC=BLACK,DDI=3,DDA=CENTER,DDS=Zeile3,DDID=3},{DDBC=WHITE,DDTC=BLACK,DDI=5,DDA=CENTER,DDS=Zeile4,DDID=4},{DDBC=WHITE,DDTC=BLACK,DDI=3,DDA=CENTER,DDS=Zeile5,DDID=5,DDC=true},{R=1,IN=5,ANS=4}");

Wichtig ist hierbei, keine Zeilenumbrüche einzubauen, sonst kommt nicht mehr alles auch dem Display an. Hier eine Erklärung zu obiger Zeichenkette:

DDBC = BackgroundColor, (WHITE, BLACK)
DDTC = TextColor (WHITE, BLACK)
DDI =  Image (s.Liste)
DDA = TextAlign (CENTER, LEFT, RIGHT)
DDS = Textstring max 14 Zeichen ohne Bild, danach wird automatisch hinten abgeschnitten. Bei Bild in der Zeile: max.11, danach wird automatisch vorne abgeschnitten
DDID = Textzeile (1,2,3,4,5)
DDC = Commit = Ausführung

R = Repetiton Wiederholung
IN = Intervall Abstand Töne

DDI Image
 0 = Nicht benutzt
 1 = Lampe aus
 2 = Lampe ein
 3 = Schloss auf
 4 = Schloss zu
 5 = X
 6 = Häckchen
 7 = Info
 8 = Briefumschlag
 9 = Schraubenschlüssel
 10 = Sonne
 11 = Mond
 12 = Wind
 13 = Wolke
 14 = Wolke/Blitz
 15 = Wolke/leichter Regen
 16 = Wolke/Mond
 17 = Wolke/Regen
 18 = Wolke/Schnee
 19 = Wolke/Sonne
 20 = Wolke/Sonne/Regen
 21 = Wolke/Schneeflocke
 22 = Wolke/Regentropfen
 23 = Flamme
 24 = Fenster auf
 25 = Rollladen
 26 = Eco
 27 = Unscharf
 28 = Hüllschutz
 29 = Vollschutz
 30 = Benachrichtigung
 31 = Uhr

ANS Sound
-1 = Nicht benutzt
0 = Batterie leer, kurz-kurz-kurz
1 = Alarm aus,  lang-kurz
2 = Externer Alarm aktiviert, lang-kurz-kurz
3 = Interner Alarm aktiviert, lang-kurz
4 = Externer Alarm verzögert aktiviert, kurz-kurz
5 = Interner Alarm verzögert aktiviert, kurz
7 = Fehler, lang
6 = Event, mittel

Eine Systemvariable ließt man nun etwas indirekter:

var oFensterOffen= dom.GetObject(ID_SYSTEM_VARIABLES).Get("Check_FensterOffen");
var Fensterwert = oFensterOffen.State();
var LastAccess =  oFensterOffen.Timestamp();

anstatt des simplen "dom.GetObject("varname").State()". Ich ermittele also wann zum letzten mal der Wert aktualisieret wurde, und nur dann, wird das Display überhaupt aktualisiert. Bei läuft das Skript jede Minute, und mein DutyCycle liegt derzeit bei 6%, also alles problemlos. Leider Kommt bei mir die "Burst-Limit" Meldung. Ich überlege ob ich die nicht abschalte (unter "Geräte" auch "Netzbetrieb" umschalten) allerdings würde dann der Schalter mehr Strom verbrauchen, und ich müsste öfters die Batterie wechseln. Ich teste erstmal so.

Ich hoffe der ein oder andere konnte was lernen. Das Skript ist nicht die "feinste" Art des Programmierens (ich habe öfters z.B. das DRY-Prinzip verletzt, aber der Editor von Homeatic ist beschränkt und auch die Skriptsprache umständlich zu debuggen. Ich hoffe Ihr könnt drüber hinwegsehen :-)

 


Keine Kommentare

Kommentar hinterlassen

Als Antwort auf Some User