Fernsteuerung auf Sparflamme

Wenn das Internet eingeschränkt ist, kann man sich mit einem Bot behelfen

Unser Internet ist hier gut und billig, aber dank DS-Lite sieht es mit eingehenden Verbindungen schlecht aus. Damit sind Serverdienste weitgehend ausgeschlossen und abgesehen von kommerziellen Lösungen oder extremem Aufwand gibt es praktisch keinen Zugriff auf die CCU.

Wie gut, dass schon mal jemand einen CCU-Bot programmiert hat, mit dem man die CCU per Telegram steuern kann.

Auf dieser Seite soll es nur darum gehen, die Heizungssteuerung mit dem CCU-Bot umzusetzen. Wie man den Bot grundsätzlich einrichtet und wie er funktioniert, ist auf der CCU-Bot-Seite ausführlich beschrieben.

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.

String-Verlängerung

In den Scripten auf dieser Seite werden Strings verlängert (x = x # y). Dies führt oft zu Störungen bei der Ausführung von Programmen:

  • Scripte in Programmen werden nicht mehr ausgeführt
  • bei der Fehlerprüfung erscheinen unerklärliche Syntax-Fehler

Durch einen Neustart werden diese Probleme (vorübergehend) behoben. Auch hier hängt die Dauer, bis es zu Störungen kommt, davon ab, wie häufig diese Programmschritte ausgeführt werden.

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.

Ein Programm, vier Funktionen

Der gesamte Heizungsbot befindet sich in einem einzelnen WebUI-Programm. Ich bin meinem Konzept, alles in einzelne Programme zu packen, allerdings insoweit treu geblieben, dass die vier Funktionen, die es abdecken soll, in Einzelscripten abgelegt sind:

Die vier Scripte werden der Reihe nach ausgeführt. Wenn eines einen Befehl erkennt, für das es zuständig ist, macht es sein Ding und setzt die Variable zurück.

Das Script reagiert, so wie beim CCU-Bot beschrieben, auf die Systemvariable Telegram Command. Wenn diese nicht leer ist, werden die Scripte ausgeführt und setzen die Variable gegebenenfalls zurück.

Kompletter Status

Das erste Script dient nicht der Steuerung, sondern der Kontrolle: Mit dem Befehl /heizung status wird der Status aller Geräte und Variablen über das T-Framework zurückgeliefert, die für die Heizung relevant sind.

! HomeMatic-Script
! FERNSTEUERUNG AUF SPARFLAMME
! http://www.christian-luetgens.de/homematic/projekth/telegram-bot/Fernsteuerung.htm

! Heizungsstatus
! ==============

object o_cmd = dom.GetObject ("$src$");


if (o_cmd.Value() == "/HEIZUNG STATUS") {
  o_cmd.State ("");

  object o_chn;
  object o_dp;
  boolean b_status;
  string s_msg;
  integer i_diff;
  string s;

  s_msg = "*HEIZUNGSSTATUS*";

  s_msg = s_msg + "\t" + "gesamt: " + 
    dom.GetObject("Heizung gesamt").ValueList().StrValueByIndex (";", dom.GetObject ("Heizung gesamt").Value());
  s_msg = s_msg + "\n" + "Dachzimmer: " + 
    "absenken:heizen".StrValueByIndex (":", dom.GetObject ("Heizung Dachzimmer").Value().ToInteger());
  s_msg = s_msg + "\n" + "Kind: " + 
    dom.GetObject("Heizung Kind").ValueList().StrValueByIndex (";", dom.GetObject ("Heizung Kind").Value());

  o_dp = dom.GetObject ("Heizung Brenner").DPByHssDP ("STATE");

  b_status = o_dp.Value();
  i_diff = (system.Date ("%F %T").ToTime().ToInteger() - o_dp.Timestamp().ToInteger()) / 60;
  s_msg = s_msg # "\t" # "Brenner " # (("aus:ein").StrValueByIndex (":", b_status.ToInteger())) # 
    " seit " # i_diff # " Minute(n)";

  b_status = o_dp.LastValue();
  i_diff = (o_dp.Timestamp().ToInteger() - o_dp.LastTimestamp().ToInteger()) / 60;
  s_msg = s_msg # "\n" # "vorher " # (("aus:ein").StrValueByIndex (":", b_status.ToInteger())) # 
    " für " # i_diff # " Minute(n)";

  foreach (s, dom.GetObject("Heizung").EnumUsedNames()) {
    o_chn = dom.GetObject (s);

    o_dp = o_chn.DPByHssDP ("SET_TEMPERATURE");
    if (o_dp) { 
      s_msg = s_msg # "\t" # s # "\n" # "Soll: " # o_dp.Value().ToString (1) # "°C"; 
      o_dp = o_chn.DPByHssDP ("CONTROL_MODE");
      if (o_dp) { 
        s_msg = s_msg # " (" # (("Auto:Manu:Party:Boost").StrValueByIndex (":", o_dp.Value())) # ")"; 
      }
      o_dp = o_chn.DPByHssDP ("ACTUAL_TEMPERATURE");
      if (o_dp) { 
        s_msg = s_msg # "\n" # "aktuell: " # o_dp.Value().ToString (1) # "°C"; 
        o_dp = o_chn.DPByHssDP ("VALVE_STATE");
        if (o_dp) { 
          s_msg = s_msg # " (" # o_dp.Value() # "%)"; 
        }
      }
    }
  }
  
  o_chn = dom.GetObject ("Heizung Anforderung");
  s_msg = s_msg # "\t" # "Wärmebedarf: " # o_chn.Value().ToInteger();


  dom.GetObject ("Telegram").State (s_msg);

}

!  Ende des Scripts

Das Script ist etwas unübersichtlich, weil aus den verschiedensten Ecken Daten zusammengesucht werden. Ich schlüssele es hier mal etwas auf.

Systemvariablen

Zunächst werden die drei Systemvariablen herausgesucht und der Zustand zur ersten Telegram-Nachricht nach der Überschrift zusammengestellt.

Dieser Teil entspricht im Grunde weitgehend der Meldung des Heizungsstatus'.

Brennerstatus

Als nächstes wird der Brennerstatus hinzugefügt. Im Unterschied zur normalen Statusmeldung, die bei jeder Aktivität des Brenners gesendet wird, gibt es hier zwei Zeilen mit zwei Zeitangaben: Aktueller und vorheriger Zustand.

Thermostate

Nun kommen sämtliche Heizkörperthermostate im Gewerk Heizung. Es gibt für jeden Thermostaten den Namen, Modus, Solltemperatur, Ist-Temperatur sowie Ventil-Öffnungsgrad.

Wärmeanforderung

Zum Schluss wird der Wert der Wärmeanforderung ausgegeben, damit ich mir die Arbeit sparen kann, die Ventil-Werte aus der vorherigen Liste im Kopf zu addieren.

Alle Nachrichten werden mit Tabulatoren gretrennt in die Systemvariable Telegram geschrieben. Das Telegram-Framework mit Queue-Erweiterung kümmert sich darum, dass dann alles der Reihe nach gesendet wird.

Not-Aus

Eigentlich ist es übertrieben, von einem „Not-Aus“-Script zu sprechen, immerhin reagiert der CCU-Bot stets verzögert. Falls es um ein Software-Problem geht, weshalb ich die Heizung schnell stilllegen möchte, dann würde vermutlich dieser Teil der Software auch nicht mehr funktionieren.

Der richtige Weg, einen Not-Aus-Schalter zu implementieren, ist ein Heizungsschalter neben dem Brenner. Auf den kann man drücken und die Heizung ist aus. Gibt es hier nicht … und die Heizung hängt an derselben Sicherung wie das Deckenlicht im Wohnzimmer.

Egal.

! HomeMatic-Script
! FERNSTEUERUNG AUF SPARFLAMME
! http://www.christian-luetgens.de/homematic/projekth/telegram-bot/Fernsteuerung.htm

! Heizung Notaus
! ==============

object o_cmd = dom.GetObject ("$src$");


if (o_cmd.Value() == "/HEIZUNG AUS") {

  o_cmd.State ("");
  dom.GetObject ("Heizung Brenner").DPByHssDP ("STATE").State (false);
  dom.GetObject ("Heizung gesamt").State (2);

}

!  Ende des Scripts

Das Script ist höchst kompakt: Beim Befehl /heizung aus wird der Brenner ausgeschaltet und Heizung gesamt auf aus gesetzt.

Über die Statusmeldungen der Heizung bekomme ich die Rückmelung, dass es funktioniert.

Systemvariablen einstellen

Das Script, das ich für den Moment mal den Höhepunkt nennen will, kommt an dritter Stelle. Hier werden einzelne Parameter nach dem Befehl /heizung ausgewertet, um meine drei Systemvariablen einzustellen.

! HomeMatic-Script
! FERNSTEUERUNG AUF SPARFLAMME
! http://www.christian-luetgens.de/homematic/projekth/telegram-bot/Fernsteuerung.htm

! Heizung einstellen
! ==================

object o_cmd = dom.GetObject ("$src$");
string s_cmd_1 = o_cmd.Value().StrValueByIndex (" ", 0);
string s_cmd_2 = o_cmd.Value().StrValueByIndex (" ", 1);
string s_cmd_3 = o_cmd.Value().StrValueByIndex (" ", 2);


if (s_cmd_1 == "/HEIZUNG") {

  boolean b_cmd = false;

  if (s_cmd_2 == "HEIZEN") {
    dom.GetObject ("Heizung gesamt").State (1);
    b_cmd = true;
  }

  if (s_cmd_2 == "ABSENKEN") {
    dom.GetObject ("Heizung gesamt").State (0);
    b_cmd = true;
  }

  if (s_cmd_2 == "DACHZIMMER") {
 
    if (s_cmd_3 == "HEIZEN") {
      dom.GetObject ("Heizung Dachzimmer").State (1);
      b_cmd = true;
    }

    if (s_cmd_3 == "ABSENKEN") {
      dom.GetObject ("Heizung Dachzimmer").State (0);
      b_cmd = true;
    }

  }

  if (s_cmd_2 == "KIND") {
 
    if (s_cmd_3 == "FERIEN") {
      dom.GetObject ("Heizung Kind").State (2);
      b_cmd = true;
    }

    if (s_cmd_3 == "HEIZEN") {
      dom.GetObject ("Heizung Kind").State (1);
      b_cmd = true;
    }

    if (s_cmd_3 == "ABSENKEN") {
      dom.GetObject ("Heizung Kind").State (0);
      b_cmd = true;
    }

  }

  if (b_cmd) {
    o_cmd.State ("");
  }

}

!  Ende des Scripts

Als erstes wird Telegram Command aufgeteilt in seine zwei bis drei Wörter.

Das klingt unübersichtlich. Im nächsten Abschnitt ist ein Screenshot, der alles auf einen Schlag erklärt.

Sobald das Script eine Variable setzt, wird außerdem b_cmd auf true gesetzt. Damit kann ich dann ganz am Ende prüfen, ob irgendwas erfolgreich eingestellt wurde. Wenn ja, wird Telegram Command zurückgesetzt – wenn nein, ist das nächste Script dran.

Zusammen mit dem Programm zur Statusmeldung kann ich jetzt die Heizung aus der Ferne steuern und bekomme sofort eine passende Rückmeldung.

Ein paar Gedanken zur Sicherheit: Der einzige realistische Angriffsvektor ist es wohl, dass jemand die Zugangsdaten zu meinem CCU-Bot erfährt und die Heizung verstellt. Wenn er dann die Heizung ausschaltet, verdient er sich zwar eine Urkunde als Depp des Jahres, macht aber nichts kaputt. Wenn er in unserer Abwesenheit die Heizung einschaltet, kommt noch die Goldene Umweltsau am Band oben drauf.

Weitere Einbrüche sind schwer vorstellbar, denn die hin- und hergesendeten Texte werden an verschiedenen Stellen gefiltert und die CCU ist, wie bereits erwähnt, schon providerseitig von außen abgeschirmt. Das Restrisiko ist also hoffentlich gering.

Ich vertraue einfach darauf, dass eine winzige Heizungsinstallation kein lohnendes Angriffsziel ist. Man kann ja nicht mal mit dem Licht spielen.

Hilfe? Hilfe!

Das letzte Script ist die Hilfe. Da ich den Bot selbst programmiert habe, beschränke ich mich auf eine Gedächtnisstütze der Befehle.

Wenn ich nicht mehr weiß, was ich mit /heizung aus meine, ist eh alles zu spät.

! HomeMatic-Script
! FERNSTEUERUNG AUF SPARFLAMME
! http://www.christian-luetgens.de/homematic/projekth/telegram-bot/Fernsteuerung.htm

! Heizung Hilfe
! =============

object o_cmd = dom.GetObject ("$src$");
string s_cmd_1 = o_cmd.Value().StrValueByIndex (" ", 0);


if (s_cmd_1 == "/HEIZUNG") {
  o_cmd.State ("");

  dom.GetObject ("Telegram").State (

    "*Befehle /heizung*" #
    "\n/heizung status" #
    "\n/heizung heizen|absenken|aus" #
    "\n/heizung dachzimmer heizen|absenken" #
    "\n/heizung kind heizen|absenken|ferien"

  );

}

!  Ende des Scripts

Auch dieses Script reagiert nur, wenn das erste Wort des Befehls /heizung lautet. Es gibt noch eine weitere allgemeine Hilfe, so wie beim CCU-Bot beschrieben, die allerdings ähnlich kurz ist und mir nur zurückmeldet, dass der einzige verfügbare Befehl /heizung lautet.

Nachdem das geschafft ist, können wir beruhigt verreisen und rechtzeitig vor unserer Rückkehr schon mal die Wohnung wieder anheizen. Nur noch kurz warten, bis Corona vorbei ist.

Navigation