Lichtszenen per Script schalten

Mit diesem Script können Leuchten zu Lichtszenen zusammengeschaltet werden

Ich gebe es zu: Ich übertreibe es manchmal etwas mit der Beleuchtung. In meinem Wohnzimmer gibt es knapp ein Dutzend Leuchten und fast alle sind einzeln zu schalten. Das ist toll, wenn man zum Fernsehen, zum Essen, zum Lesen usw. jeweils eigene Szenen entwerfen kann – aber die Programmierung jeder einzelnen Leuchte ist eine Qual.

Eine Zeit lang hatte ich eine 12-Tasten-Fernbedienung dafür belegt – jede Taste eine Leuchte. Mittlerweile reicht auch die nicht mehr, um alles einzeln zu schalten. Vor die Wahl gestellt, viel Geld für die große 19-Tasten-Fernbedienung auszugeben und ein Dutzend neuer direkter Verknüpfungen anzulegen oder ein Script zu schreiben, mit dem ich einfach verschiedenste Lichtszenen ansteuern kann, nehme ich natürlich das Script.

Variablen in HomeMatic-Scripten

Die CCU hatte früher ein Limit von maximal 200 Variablen, das mit Firmware-Version 2.29.18 aufgehoben wurde. Dieses Limit bezog sich auf alle Variablen, die in allen Scripten verwendet werden. Gemeint sind Variablen, die direkt im Script definiert werden: object x; object y; var z;

Wenn Scripte nicht mehr funktionieren und bei der Prüfung unerklärliche Syntax-Fehler auftreten, sollte versuchsweise dieses Programm wieder gelöscht oder deaktiviert werden – oder, noch besser, auf die aktuelle Firmware-Version aktualisiert werden.

Strings in HomeMatic-Scripten

Durch String-Verwendung in HomeMatic-Scripten kann die CCU fehlerhaft arbeiten, instabil werden oder sogar abstürzen. Grundsätzlich gilt: Je öfter mit Strings hantiert wird, desto eher führt dies zu Problemen.

Ich empfehle daher, nach Umsetzung dieser Anleitung die CCU unter Beobachtung zu halten.

Benennung von Systemvariablen

Prinzipiell kann man Systemvariablen – so wie allen Objekten in der CCU – beliebige Namen geben, also z. B. auch Umlaute und Sonderzeichen verwenden. Ich empfehle jedoch, sich auf reguläre Buchstaben (a-z, A-Z) zu beschränken: Bei Umlauten und Sonderzeichen besteht die Gefahr, dass Systemvariablen in Scripten nicht überall gefunden werden.

Das WebUI-Programm

In der WebUI brauche ich nur ein relativ einfaches Programm. Es reagiert auf alle Tastendrücke meiner Fernbedienung und startet dann ein Script.

Wie man im Screenshot sieht, gibt es hier für jeden Kanal der Tasterschnittstelle einen Eintrag, alle mit „oder“ verknüpft. Auch „Tastendruck lang“ werte ich aus. So kann ich mit acht Tasten an der Wand acht verschiedene Lichtszenen steuern.

Bei der 12-fach-Fernbedienung, die nun keine direkten Verknüpfungen für die einzelnen Aktoren mehr enthält, sieht der „Wenn … “-Teil etwas umfangreicher aus, ist aber ebenfalls simpel gestrickt.

Bei genauem Hinsehen fällt auf, dass Taste 9 fehlt. Hier habe ich eine andere Funktion auf die Fernbedienung gelegt – man muss also den Sender nicht komplett für die Lichtszenen reservieren.

Der „Dann … “-Teil ist entsprechend kompakt: Hier wird nur das Script gestartet, in dem die Magie passiert.

Einfaches Script

In der ersten Version schaltet das Script lediglich die Leuchten in meinem Wohnzimmer.

! HomeMatic-Script
! LICHTSZENEN PER SCRIPT SCHALTEN
! http://www.christian-luetgens.de/homematic/programmierung/beleuchtung/lichtszenen/Lichtszenen.htm

! Fernbedienungstaste bestimmen
! Tasten sind paarweise angeordnet, aber wir brauchen es linear

  string s_src = dom.GetObject($src$).Name();
  integer i_chn = s_src.StrValueByIndex(".", 1).StrValueByIndex(":", 1).ToInteger();

  if (s_src.StrValueByIndex(".", 2) == "PRESS_LONG") {
    i_chn = i_chn + 12; ! für langen Tastendruck bei 12-Tasten-FB
  }

  integer i_btn = 0;
  while (i_chn > 1) {
    i_chn = i_chn - 1;
    i_btn = i_btn + 1;
  }

! Kanäle
  string tab = "\t";
  string prefix = "Wohnzimmer.Licht.";
  string s_channels =  
    prefix # "Fluter Fernseher" # tab #
    prefix # "Fluter Sofa"      # tab #
    prefix # "Esstisch"         # tab #
    prefix # "Geist"            # tab #
    prefix # "Plattenregal"     # tab #
    prefix # "Lavalampen"       # tab #
    prefix # "Lumibaer"         # tab #
    prefix # "Sofabeleuchtung"  # tab #
    prefix # "Sonne"            # tab #
    prefix # "Vitrine 1"        # tab #
    prefix # "Vitrine 2";


! Lichtszenen
  string s_scenes =  
    "Wohnzimmer     :1:1:0:1:1:1:1:1:0:1:1;" #
    "Esszimmer      :0:0:1:1:0:0:1:0:0:1:1;" #
    "Couchtisch     :0:0:0:0:1:1:1:1:1:1:1;" #
    "alles aus      :0:0:0:0:0:0:0:0:0:0:0;" #
    "Ambiente dunkel:0:0:0:1:0:1:0:1:0:0:0;" #
    "Ambiente hell  :0:0:0:1:1:1:1:1:0:1:1;" #
    "Effekte        :0:0:0:1:0:1:1:0:0:0:0;" #
    "Plattenregal   :0:0:0:1:1:1:1:0:0:0:0;" #
    "alles ein      :1:1:1:1:1:1:1:1:1:1:1;" #
    "alles aus      :0:0:0:0:0:0:0:0:0:0:0;";

! Lichtszene auswählen
  string s_scene = s_scenes.StrValueByIndex(";", i_btn);
  
! durch Aktoren iterieren
  string s_channels_idx;
  object o_item;
  object o_dp;
  integer i_active;
  integer i_channel;
  integer i_count = 2;
  
  while (i_count > 0) {
    i_channel = 0;
    i_count = i_count - 1;
    foreach (s_channels_idx, s_channels) {
      i_channel = i_channel + 1;
      o_item = dom.GetObject(s_channels_idx);
      i_active = s_scene.StrValueByIndex(":", i_channel).ToInteger();
      if (i_active == i_count) {
        if (o_item.HssType() == "DIMMER") {
          o_item.DPByHssDP("RAMP_TIME").State(2.0); ! schnell einschalten
          o_dp = o_item.DPByHssDP("LEVEL");
        } else {
          o_dp = o_item.DPByHssDP("STATE");
        }
        if (o_dp.Value() != i_active) {
          o_dp.State(i_active);
        }
      }
    }
  }

!  Ende des Scripts

Eigentlich ziemlich selbsterklärend, oder? Na gut – ein, zwei Sätze vielleicht doch.

Fernbedienungstaste bestimmen

$src$ liefert in Scripten die ID des Objekts, von dem das Programm ausgelöst wurde. Bei einer Fernbedienungstaste ist das genau der Kanal, der betätigt wurde, mit dem Zustand „langer“ oder „kurzer“ Tastendruck.

Die Fernbedienungstasten sind paarweise angeordnet. Es gibt also nicht Taste 1, Taste 2, Taste 3 usw., sondern Paar 1 – Taste 1, Paar 1 – Taste 2, Paar 2 – Taste 1 usw. Für den weiteren Programmablauf ist es einfacher, wenn die Tasten durchnummeriert sind. Die Zählung beginnt dabei mit 0.

Bei der Gelegenheit wird auch der lange vom kurzen Tastendruck getrennt: 0-11 für kurzen Tastendruck, 12-23 für langen.

Ein kurzer Tastendruck auf die linke Taste des zweiten Tastenpaares liefert also „2“, ein langer Tastendruck „14“.

Kanaldefinition

Als nächstes folgt eine Liste der Kanalnamen, unter denen die einzelnen Aktoren für die Beleuchtung angesprochen werden können.

Den Lumibär gibt es bei Flötotto, der Geist kommt von IKEA und die Lavalampen natürlich von Mathmos. Nur, falls sich jemand wundert.

Lichtszenen definieren

Die Lichtszenen sind ebenfalls eine Zeichenkette: Für jede Taste der Fernbedienung (0-11) gibt es eine Lichtszene. In jeder Lichtszene wiederum ist der Zustand des jeweiligen Kanals definiert, wobei die Reihenfolge der Kanaldefiniton entspricht.

Beispiel: Bei „Couchtisch“ werden die sechste bis elfte Leuchte eingeschaltet, also alles von „Lavalampen“ bis „Vitrine 2“.

Die einzelnen Lichtszenen sind mit Semikolons abgetrennt, die einzelnen Kanäle durch Doppelpunkte. Der Name am Anfang der Zeile dient nur der Übersicht – im Programm hat er keine Funktion.

Lichtszene auswählen

An dieser Stelle wird die Fernbedienungstaste einer Lichtszene zugeordnet.

Beispiel: Die linke Taste des zweiten Tastenpaares liefert Szene Nummer 2, wobei die Zählung bei 0 beginnt. Mit anderen Worten: „Couchtisch“.

Schalten

Schließlich der Programmablauf: Das Script geht jeden Kanal zweimal durch. Beim ersten Durchlauf wird geprüft, ob der Kanal in der ausgewählten Szene aktiv sein soll und ggf. eingeschaltet. Beim zweiten Durchlauf werden entsprechend die nicht benötigten Kanäle ausgeschaltet.

Für Dimmer wird zusätzlich eine Rampenzeit von 2 Sekunden eingestellt, um das Licht etwas sanfter ein- und auszuschalten.

Anschließend werden alle Kanäle geschaltet, die noch nicht den richtigen Zustand haben.

Automatischer Dimmer

Beim einfachen Script werden die Dimmer wie Schaltaktoren behandelt. Lediglich für das sanfte Ein- und Ausschalten wird die Dimmerfunktion genutzt. Das ist natürlich langweilig.

Es wäre doch cool, wenn bei einigen Lichtszenen die gedimmten Leuchten eine sanfte Hintergrundbeleuchtung geben, und zwar automatisch an die Gesamthelligkeit angepasst. Bei anderen sollen sie ausgeschaltet bleiben – ich mag es nicht, wenn mein Deckenfluter sich im Fernseher spiegelt – oder mit voller Helligkeit leuchten.

Zeit für ein verbessertes Script.

! HomeMatic-Script
! LICHTSZENEN PER SCRIPT SCHALTEN
! http://www.christian-luetgens.de/homematic/programmierung/beleuchtung/lichtszenen/Lichtszenen.htm

! Fernbedienungstaste bestimmen
! Tasten sind paarweise angeordnet, aber wir brauchen es linear

  string s_src = dom.GetObject($src$).Name();
  integer i_chn = s_src.StrValueByIndex(".", 1).StrValueByIndex(":", 1).ToInteger();

  if (s_src.StrValueByIndex(".", 2) == "PRESS_LONG") {
    i_chn = i_chn + 12; ! für langen Tastendruck bei 12-Tasten-FB
  }

  integer i_btn = 0;
  while (i_chn > 1) {
    i_chn = i_chn - 1;
    i_btn = i_btn + 1;
  }

! Kanäle
  string tab = "\t";
  string prefix = "Wohnzimmer.Licht.";
  string s_channels =  
    prefix # "Fluter Fernseher" # tab #
    prefix # "Fluter Sofa"      # tab #
    prefix # "Esstisch"         # tab #
    prefix # "Geist"            # tab #
    prefix # "Plattenregal"     # tab #
    prefix # "Lavalampen"       # tab #
    prefix # "Lumibaer"         # tab #
    prefix # "Sofabeleuchtung"  # tab #
    prefix # "Sonne"            # tab #
    prefix # "Vitrine 1"        # tab #
    prefix # "Vitrine 2";


! Lichtszenen
  string s_scenes =  
    "Wohnzimmer     :1:1:0:1:1:1:1:1:0:1:1;" #
    "Esszimmer      :0:0:1:1:0:0:1:0:0:1:1;" #
    "Couchtisch     :2:2:0:0:1:1:1:1:1:1:1;" #
    "alles aus      :0:0:0:0:0:0:0:0:0:0:0;" #
    "Ambiente dunkel:0:2:0:1:0:1:0:1:0:0:0;" #
    "Ambiente hell  :2:2:0:1:1:1:1:1:0:1:1;" #
    "Effekte        :0:0:0:1:0:1:1:0:0:0:0;" #
    "Plattenregal   :2:2:0:1:1:1:1:0:0:0:0;" #
    "alles ein      :1:1:1:1:1:1:1:1:1:1:1;" #
    "alles aus      :0:0:0:0:0:0:0:0:0:0:0;";

! Lichtszene auswählen
  string s_scene = s_scenes.StrValueByIndex(";", i_btn);
  
! durch Aktoren iterieren
  string s_channels_idx;
  object o_item;
  object o_dp;
  integer i_active;
  integer i_channel;
  integer i_count = 2;
  real r_dimmer;
  
  while (i_count > -1) {
    i_channel = 0;
    i_count = i_count - 1;
    foreach (s_channels_idx, s_channels) {
      i_channel = i_channel + 1;
      o_item = dom.GetObject(s_channels_idx);
      i_active = s_scene.StrValueByIndex(":", i_channel).ToInteger();
      if (i_active == i_count) {
        if (o_item.HssType() == "DIMMER") {
          o_dp = o_item.DPByHssDP("LEVEL");
          if (o_dp.Value() != i_active) {
            ! schnell einschalten, langsam ausschalten
            o_item.DPByHssDP("RAMP_TIME").State(2.0 + (3.0 * (1 - i_active))); 
            o_dp.State(i_active);
          }
        } else {
          o_dp = o_item.DPByHssDP("STATE");
          if (o_dp.Value() != i_active) {
            o_dp.State(i_active);
          }
          r_dimmer = r_dimmer + (0.05 * i_active);
        }
      }
      if ((i_active == 2) && (i_count == -1) && (o_item.HssType() == "DIMMER")) {
          o_item.DPByHssDP("RAMP_TIME").State(10.0); ! langsam Helligkeit ändern
          o_item.DPByHssDP("LEVEL").State(r_dimmer);
      }
    }
  }

!  Ende des Scripts

Der größte Teil ist unverändert. Hier die Änderungen.

Lichtszenen

In der Definition der Lichtszenen gibt es jetzt nicht mehr nur „1“ für „ein“ und „0“ für „aus“, sondern auch „2“ für „gedimmt“.

Die Deckenfluter (ja, schon wieder IKEA) sind in der „Wohnzimmer“-Szene eingeschaltet, in der „Esszimmer“-Szene aus und beim „Couchtisch“ gedimmt.

Aktoren schalten

Das Programm hat jetzt die Variable r_dimmer hinzubekommen. Außerdem gibt es nun drei Durchläufe.

Beim ersten Durchlauf werden wie bisher die gewünschten Aktoren eingeschaltet. r_dimmer wird für jeden eingeschalteten Aktor um 5 % erhöht. Beim zweiten Durchlauf werden ebenfalls wie bisher die nicht benötigten Aktoren ausgeschaltet. Wer genau hinschaut, kann hier auch eine Neuerung bei den Rampenzeiten der Dimmer sehen: Sie schalten sich schneller ein als aus.

Der dritte Durchlauf schließlich setzt die Dimmer auf den mit r_dimmer bestimmten Wert, wenn „2“ in der Lichtszene ausgewählt ist. Die Rampenzeit sind hier gemächliche 10 Sekunden.

Systemvariable schalten

Meine Vitrinen haben Tür-/Fensterkontakte, mit denen das Licht automatisch beim Öffnen ein- und ausgeschaltet wird. Wenn sie in Lichtszenen vorkommen, sollen sie aber nicht ihre Beleuchtung selbsttätig ausschalten. Darum habe ich für sie die Sperrung eingerichtet und schalte sie indirekt über Systemvariable.

Die letzte Änderung des Scripts berücksichtigt daher auch Objekte, die Systemvariable sind und keine Schaltaktoren oder Dimmer.

! HomeMatic-Script
! LICHTSZENEN PER SCRIPT SCHALTEN
! http://www.christian-luetgens.de/homematic/programmierung/beleuchtung/lichtszenen/Lichtszenen.htm

! Fernbedienungstaste bestimmen
! Tasten sind paarweise angeordnet, aber wir brauchen es linear

  string s_src = dom.GetObject($src$).Name();
  integer i_chn = s_src.StrValueByIndex(".", 1).StrValueByIndex(":", 1).ToInteger();

  if (s_src.StrValueByIndex(".", 2) == "PRESS_LONG") {
    i_chn = i_chn + 12; ! für langen Tastendruck bei 12-Tasten-FB
  }

  integer i_btn = 0;
  while (i_chn > 1) {
    i_chn = i_chn - 1;
    i_btn = i_btn + 1;
  }

! Kanäle
  string tab = "\t";
  string prefix = "Wohnzimmer.Licht.";
  string s_channels =  
    prefix # "Fluter Fernseher" # tab #
    prefix # "Fluter Sofa"      # tab #
    prefix # "Esstisch"         # tab #
    prefix # "Geist"            # tab #
    prefix # "Plattenregal"     # tab #
    prefix # "Lavalampen"       # tab #
    prefix # "Lumibaer"         # tab #
    prefix # "Sofabeleuchtung"  # tab #
    prefix # "Couchtisch"       # tab #
    prefix # "Vitrinen";


! Lichtszenen
  string s_scenes =  
    "Wohnzimmer     :1:1:0:1:1:1:1:1:0:1;" #
    "alles aus      :0:0:0:0:0:0:0:0:0:0;" #
    "Couchtisch     :2:2:0:0:1:1:1:1:1:1;" #
    "Esszimmer      :0:0:1:1:0:0:1:0:0:1;" #
    "Ambiente dunkel:2:0:0:1:0:1:0:1:0:0;" #
    "Ambiente hell  :2:2:0:1:1:1:1:1:0:1;" #
    "Effekte        :0:0:0:1:0:1:1:0:0:0;" #
    "Plattenregal   :2:2:0:1:1:1:1:0:0:0;" #
    "alles ein      :1:1:1:1:1:1:1:1:1:1;" #
    "alles aus      :0:0:0:0:0:0:0:0:0:0;";

! Lichtszene auswählen
  string s_scene = s_scenes.StrValueByIndex(";", i_btn);
  
! durch Aktoren iterieren
  string s_channels_idx;
  object o_item;
  object o_dp;
  integer i_active;
  integer i_channel;
  integer i_count = 2;
  real r_dimmer;
  
  while (i_count > -1) {
    i_channel = 0;
    i_count = i_count - 1;
    foreach (s_channels_idx, s_channels) {
      i_channel = i_channel + 1;
      o_item = dom.GetObject(s_channels_idx);
      i_active = s_scene.StrValueByIndex(":", i_channel).ToInteger();
      if (i_active == i_count) {
        if (o_item.IsTypeOf(OT_VARDP)) {
          if (o_item.Value() != i_active) {
            o_item.State(i_active);
          }
        } else {
            if (o_item.HssType() == "DIMMER") {
              o_dp = o_item.DPByHssDP("LEVEL");
              if (o_dp.Value() != i_active) {
                ! schnell einschalten, langsam ausschalten
                o_item.DPByHssDP("RAMP_TIME").State(2.0 + (3.0 * (1 - i_active))); 
                o_dp.State(i_active);
              }
            } else {
              o_dp = o_item.DPByHssDP("STATE");
              if (o_dp.Value() != i_active) {
                o_dp.State(i_active);
              }
              r_dimmer = r_dimmer + (0.05 * i_active);
            }
        }
      }
      if ((i_active == 2) && (i_count == -1) && (o_item.HssType() == "DIMMER")) {
          o_item.DPByHssDP("RAMP_TIME").State(10.0); ! langsam Helligkeit ändern
          o_item.DPByHssDP("LEVEL").State(r_dimmer);
      }
    }
  }

!  Ende des Scripts

Auch hier nur kleine Änderungen.

Kanaldefinition und Lichtszenen

Die Kanaldefiniton verweist nicht mehr auf die einzelnen Aktoren („Vitrine 1“ und „Vitrine 2“), sondern auf die Systemvariable („Vitrine“ – einfallslos, ich weiß).

Auch die Lichtszenen wurden entsprechend angepasst: Es gibt überall eine Leuchte weniger.

Aktoren schalten

Beim Schalten der Aktoren wird nicht nur auf Schaltaktoren und Dimmer geprüft, sondern auch auf Systemvariable. Für Schaltaktoren und Dimmer werden die entsprechenden Datenpunkte geschaltet – für Systemvariable halt die Systemvariable.

Zusätzliche Sender

Um Lichtszenen aus anderen Programmen auszulösen, kann man nun einfach die entsprechende Fernbedienungstaste per Programm betätigen und das Script als Unterprogramm starten.

Wenn ich lange auf den Taster für meine Treppenhausbeleuchtung drücke, geht im Wohnzimmer das Licht aus. Auf ähnliche Art kann ich auch z. B. bei Abwesenheit das Licht ausschalten oder die Lichtszenen mit anderen Ereignissen verknüpfen.

Navigation