Daraus ist eine Methode entstanden, mit der man via dem Messenger Telegram sich nicht nur Statusmeldungen der CCU zusenden lassen kann, sondern der CCU auch Abfrage-Befehle und ebenfalls Schaltbefehle senden kann.
Voraussetzungen
- Telegram-Bot (Beschreibung von dtp zur Erstellung weiter unten)
- CCU mit CUxD (CUxD-Exec und CUxD-Timer)
- vorzugsweise einen Raspberry Pi (Abfrage des Bots und Entlastung der CCU)
- Statusabfragen (dann wenn man sie haben möchte)
- Die CCU prüft zur Erhöhung der Sicherheit die Chat-ID des Absenders
- Der CCU Schaltbefehle senden
- Schaltbefehle sind durch eine einmalige mTAN geschützt (erzeugt aus einem Teil der Nachrichtenzeit "date", multipliziert mit einer Zufallszahl)
- Definition wie lange die mTAN (Angabe in Sekunden) gültig ist. Default: 300 Sekunden.
- Passwortgeschützte Hilfe-Anforderung (als Antwort kommt eine Auflistung aller verfügbaren Befehle)
Telegram-Bot erstellen (Danke an dtp)
Das Einrichten eines Bots gestaltet sich recht einfach.
- Sofern man noch keinen Telegram-Account hat, läd man sich die Telegram-App oder das Telegram-Programm für sein OS herunter (es werden eigentlich alle gängigen mobilen und Desktop-OS unterstützt), gibt eine Telefonnummer an, an die die SMS mit dem Freischaltcode geschickt werden soll und schaltet sich einen Account frei.
- In der InApp-Suche gibt man dann einfach BotFather ein und startet anschließend mit einen Chat mit ihm. Über
Code: Alles auswählen
/start
richtet man dann den Bot ein. Dazu fragt der Botfather nach einem Namen für den Bot (z.B. "HomeMatic") und anschließend nach einem Usernamen, der immer auf "bot" oder "Bot" enden muss (z.B. "MeinHMBot"). Das war's. Nun ist der Bot eingerichtet und man erhält vom BotFather eine Nachricht mit dem API-Schlüssel des Bots und mit einem Link (z.B. "telegram.me/MeinHMBot").Code: Alles auswählen
/newbot
- Damit der Bot Einem über die CCU eine Nachricht schicken kann, muss man noch die chat_id des Empfängers, also von sich selbst, herausfinden. Dazu geht man zunächst in der App auf den vom BotFather gesenden Link, gibt entweder "/start" ein oder drückt unten auf den Start-Button und sendet eine kleine Nachricht an ihn (z.B. "Hallo Bot"). Im Anschluss startet man einen Browser und gibt folgendes ein: Achtung, die "<>" dienen lediglich der optischen Abgrenzung und sind folglich wegzulassen. Unter Linux wäre die Eingabe übrigens
Code: Alles auswählen
https://api.telegram.org/bot<API-Schlüssel des Bots>/getUpdates
Nun sucht man in dem angezeigten String nach der ID des Chats. Diese steht in der Regel hinterCode: Alles auswählen
curl -s -X POST https://api.telegram.org/bot<API-Schlüssel des Bots>/getUpdates
Das war's.Code: Alles auswählen
"chat"; {"id":
ACHTUNG: Dieses Script läuft NICHT mehr unter der CCU-Firmware 2.29.18
robsdobs hat es bereits hierfür modifiziert und auch um Funktionen erweitert. >>Klick<<
CCU-Script ACHTUNG:
Code: Alles auswählen
! ### Telegram-Bot Version 0.1.3 ###
!
! ########## Info START ##########
! - das Script erstellt automatisch die benötigten SysVars, wenn diese nicht vorhanden sind. !!!NICHT selber erstellen!!!
! - Du benötigst UNBEDINGT einen eingerichteten CUxD-Timer und einen CUxD-Exec.
! - der CUxd-Timer läuft komplett im Hintergrund ohne extra Programm und wird lediglich beim nächsten Aufruf dieses Scripts auf Status geprüft.
! ########## Info ENDE ##########
! ########## Config START ##########
string chatid = "1XXXXXXX1"; ! chat_id des Empfängers
string botAPI = "DasistmeinebotAPIunddieverrateichnicht"; ! API des eingerichteten Test-Bots
var url = "https://api.telegram.org/bot"#botAPI#"/getUpdates";
var cURL="LD_LIBRARY_PATH=/usr/local/addons/cuxd /usr/local/addons/cuxd/curl";
var cuxd_dev="CUX2801001:10"; ! Adresse des benutzten CUxD-Exec-Devices für Telegram !!!und NUR dafür!!!
var cuxd_rand="CUX2801001:16"; ! Adresse des benutzten CUxD-Exec-Devices für die Zufallszahl
var cuxd_timer="CUX2800001:1"; ! Adresse des benutzten CUxD-Timer-Devices für den Timer
var password="123456"; ! Passwort für sichere Abfragen (z.B. Ausgabe der verfügbaren Befehle)
var heizung="BidCos-RF.MEQ0272050:1"; ! Beispiel-Aktor für Status- und Schalt-Befehle
integer timerTAN_max = "300"; ! maximale Gültigkeit der TAN in Sekunden
! ########## Config ENDE ##########
! ########## Script START ##########
! +++++ Deklaration weiterer interner Variablen +++++
string message; string message_tmp; string message_index; string message_state;
integer timerTAN = dom.GetObject("CUxD."#cuxd_timer#".TIMER_GET").State();
if (timerTAN > 0) {
var mTAN = dom.GetObject("Telegram-TAN").Value();
}
else {
dom.GetObject("Telegram-TAN").State("");
var mTAN = "";
}
! ### Telegram-Meldungen START ###
string svName = "Telegram-Text";
object svObj = dom.GetObject(svName);
if (!svObj){
object svObjects = dom.GetObject(ID_SYSTEM_VARIABLES);
svObj = dom.CreateObject(OT_VARDP);
svObjects.Add(svObj.ID());
svObj.Name(svName);
svObj.ValueType(ivtString);
svObj.ValueSubType(istChar8859);
svObj.DPInfo("Telegram-Text der übertragen der soll");
svObj.ValueUnit("");
svObj.State("");
svObj.Internal(false);
svObj.Visible(true);
dom.RTUpdate(false);
}
else {
string message = dom.GetObject("Telegram-Text").Value();
if (message != "") {
! +++++ Umwandlung von Sonderzeichen gem. UTF-8 (Code by dtp)+++++
foreach(message_index,message.Split(" ")){message_tmp = message_tmp + message_index + "%20";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("Ä")){message_tmp = message_tmp + message_index + "%C3%84";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("Ö")){message_tmp = message_tmp + message_index + "%C3%96";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("Ü")){message_tmp = message_tmp + message_index + "%C3%9C";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ä")){message_tmp = message_tmp + message_index + "%C3%A4";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ö")){message_tmp = message_tmp + message_index + "%C3%B6";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ü")){message_tmp = message_tmp + message_index + "%C3%BC";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ß")){message_tmp = message_tmp + message_index + "%C3%9F";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("°")){message_tmp = message_tmp + message_index + "%C2%B0";}
message = message_tmp; message_tmp = "";
! Kürzen der Nachricht um die jeweils am Ende angefügten Umwandlungscodes
message = message.Substr(0, message.Length()-51);
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d parse_mode='HTML' -d text='"#message#"' -d chat_id="#chatid);
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
message = "";
dom.GetObject("Telegram-Text").State("");
}
}
! ### Telegram-Meldungen Ende ###
dom.GetObject("CUxD."#cuxd_dev#".CMD_SETS").State(cURL#" -s -k -L " # url);
dom.GetObject("CUxD."#cuxd_dev#".CMD_QUERY_RET").State(1);
string rueckgabe = dom.GetObject("CUxD."#cuxd_dev#".CMD_RETS").State();
integer anfang_chatid = rueckgabe.Find("chat")+12;
integer ende_chatid = rueckgabe.Find("chat")+21;
string ausgabe_chatid = rueckgabe.Substr(anfang_chatid,ende_chatid-anfang_chatid);
if (ausgabe_chatid != chatid) {
integer anfang_update_id = rueckgabe.Find("update_id")+11;
integer ende_update_id = rueckgabe.Find("update_id")+20;
string ausgabe_update_id = rueckgabe.Substr(anfang_update_id,ende_update_id-anfang_update_id);
ausgabe_update_id=(ausgabe_update_id.ToInteger()+1); ! update_id um 1 erhöhen (um später die Nachricht zu löschen)
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="#ausgabe_update_id);
string rueckgabe= "";
quit;
}
integer anfang_cpx = rueckgabe.Find("update_id");
if (anfang_cpx != "-1") {
integer anfang_update_id = rueckgabe.Find("update_id")+11;
integer ende_update_id = rueckgabe.Find("update_id")+20;
string ausgabe_update_id = rueckgabe.Substr(anfang_update_id,ende_update_id-anfang_update_id);
ausgabe_update_id=(ausgabe_update_id.ToInteger()+1); ! update_id um 1 erhöhen (um später die Nachricht zu löschen)
integer anfang_text = rueckgabe.Find("text")+7;
integer ende_text = rueckgabe.Find("}}]}")-1;
string ausgabe_text = rueckgabe.Substr(anfang_text,ende_text-anfang_text);
if (ausgabe_text == "Hallo Bot") {
string message = "Hallo Manu ;-)\nTest Umlaute: Ä Ö Ü ä ö ü ß °";
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d text='"#message#"' -d chat_id="#chatid);
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
}
if (ausgabe_text == "Status Heizung") {
if ((dom.GetObject(heizung#".STATE").Value()) == "true") { string message_state = "eingeschaltet"; } else { string message_state = "ausgeschaltet"; }
string message = "Heizung ist "#message_state;
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d text='"#message#"' -d chat_id="#chatid);
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
}
if (ausgabe_text == "Hilfe") {
string message = "Na, wie heisst das Zauberwort?";
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d text='"#message#"' -d chat_id="#chatid);
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
}
if (ausgabe_text == "Hilfe "#password) {
string message = "Status-Befehle:\n<b>Status Heizung</b><i> Heizungsstatus abfragen</i>\n<b>Status Fenster</b><i> welche Fenster sind offen?</i>\n<b>Status Alarmanlage</b><i> ein- oder ausgeschaltet?</i>\n\nSchalt-Befehle:\n<b>Heizung ein|aus</b><i> Heizung ein- oder ausgeschalten</i>\n<b>Alarmanlage ein|aus</b><i> Alarmanlage ein- oder ausgeschalten</i>";
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d parse_mode='HTML' -d text='"#message#"' -d chat_id="#chatid);
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
}
if (ausgabe_text == "getTAN") {
dom.GetObject("CUxD."#cuxd_rand#".RAND").State(255);
integer rand = dom.GetObject("CUxD."#cuxd_rand#".RAND").State().ToInteger();
integer anfang_date = rueckgabe.Find("date")+11;
integer ende_date = rueckgabe.Find("date")+16;
string ausgabe_date = rueckgabe.Substr(anfang_date,ende_date-anfang_date);
integer bTAN=(ausgabe_date.ToInteger()*rand);
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d text='"#bTAN#"' -d chat_id="#chatid);
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
string svName = "Telegram-TAN";
object svObj = dom.GetObject(svName);
if (!svObj){
object svObjects = dom.GetObject(ID_SYSTEM_VARIABLES);
svObj = dom.CreateObject(OT_VARDP);
svObjects.Add(svObj.ID());
svObj.Name(svName);
svObj.ValueType(ivtString);
svObj.ValueSubType(istChar8859);
svObj.DPInfo("aktuelle Telegram-TAN"); ! Beschreibung darf nicht leer ("") sein!
svObj.ValueUnit("");
svObj.Internal(false);
svObj.Visible(true);
dom.RTUpdate(false);
}
svObj.State(bTAN);
dom.GetObject("CUxD."#cuxd_timer#".TIMER_SET").State(timerTAN_max);
quit;
}
! was jetzt kommt, wird NUR ausgeführt, wenn eine mTAN vorhanden ist
if (mTAN != "") {
if (ausgabe_text == "Test "#mTAN) {
string message = "Na, geht doch! SELBSTZERSTÖRUNG eingeleitet *rofl*";
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d text='"#message#"' -d chat_id="#chatid);
! dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
dom.GetObject("Telegram-TAN").State("");
dom.GetObject("CUxD."#cuxd_timer#".TIMER_SET").State(0);
}
if (ausgabe_text == "Heizung ein "#mTAN) {
dom.GetObject(heizung#".STATE").State(1);
if ((dom.GetObject(heizung#".STATE").Value()) == "true") { string message_state = "eingeschaltet"; } else { string message_state = "ausgeschaltet"; }
string message = "Heizung ist "#message_state;
}
if (ausgabe_text == "Heizung aus "#mTAN) {
dom.GetObject(heizung#".STATE").State(0);
if ((dom.GetObject(heizung#".STATE").Value()) == "true") { string message_state = "eingeschaltet"; } else { string message_state = "ausgeschaltet"; }
string message = "Heizung ist "#message_state;
}
}
else {
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="#ausgabe_update_id);
}
! Ende der Schalt-Befehle
if (message != "") {
! +++++ Umwandlung von Sonderzeichen gem. UTF-8 (Code by dtp)+++++
foreach(message_index,message.Split(" ")){message_tmp = message_tmp + message_index + "%20";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("Ä")){message_tmp = message_tmp + message_index + "%C3%84";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("Ö")){message_tmp = message_tmp + message_index + "%C3%96";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("Ü")){message_tmp = message_tmp + message_index + "%C3%9C";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ä")){message_tmp = message_tmp + message_index + "%C3%A4";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ö")){message_tmp = message_tmp + message_index + "%C3%B6";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ü")){message_tmp = message_tmp + message_index + "%C3%BC";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("ß")){message_tmp = message_tmp + message_index + "%C3%9F";}
message = message_tmp; message_tmp = "";
foreach(message_index,message.Split("°")){message_tmp = message_tmp + message_index + "%C2%B0";}
message = message_tmp; message_tmp = "";
! Kürzen der Nachricht um die jeweils am Ende angefügten Umwandlungscodes
message = message.Substr(0, message.Length()-51);
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k https://api.telegram.org/bot"#botAPI#"/sendMessage -d parse_mode='HTML' -d text='"#message#"' -d chat_id="#chatid);
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="# ausgabe_update_id);
message = "";
}
else {
dom.GetObject("CUxD."#cuxd_dev#".CMD_EXEC").State(cURL#" -s -k -L " # url #"?offset="#ausgabe_update_id);
}
}
Befehle in diesem Script
- Hallo Bot Der Bot begrüßt Dich nett zurück
- Hilfe <password> wenn das Passwort richtig ist, erhälst Du eine Auflistung aller vorhandenen Befehle (hier nur Beispiele aufgelistet)
- Status Heizung Beispiel einer reellen Abfrage eines HM-Aktors
- getTAN hiermit erhaltet ihr die benötigte TAN, um danach Schaltbefehle auszuführen
- Test <TAN> ist nur mein Test-Befehl
- Heizung ein <TAN> | Heizung aus <TAN>Beispiel einer reellen Schaltung eines HM-Aktors
Script-Trigger
Es gibt zwei Varianten, wie das Script getriggert werden kann:
- Zeitmodul in der CCU: die CCU führt zyklisch -z.B. jede Minute- das Script aus !!!Nicht zu empfehlen, hohe Last der CCU!!!
Ich habe meine CCU2 mal damit "gefoltert". Läuft das Zeitmodul alle 15 Sekunden, stirbt das ReGa der CCU!!!
. - Raspberry Pi prüft den Bot nach neuen Nachrichten: wenn eine neue Nachricht vorliegt, "drückt" er die virtuelle Taste 40 der CCU (Danke an curiosity)
Code: Alles auswählen
#!/bin/bash
if echo $(curl -s -X POST https://api.telegram.org/bot<Bot-ID>/getUpdates) |grep <Chat-ID> &>/dev/null;
then
curl http://<CCU-IP>:8181/Text.exe?Antwort=dom.GetObject%28%22BidCos-RF.BidCoS-RF:40.PRESS_SHORT%22%29.State%28true%29; >/dev/null 2>&1
fi
Curiosity hat das Ganze mal telegram.sh genannt und zu Testzwecken erst einmal ins Homeverzeichnis gelegt.
Wichtig: erstellt das obige Script nicht mit einem Windows-Editor (erst recht nicht Notepad). Linux erwartet am Ende der Zeile nur einen LF, Windows jedoch CR+LF. Das Script läuft dann nicht bzw. bombardiert euch mit Fehlern, die ihr nicht "sehen" könnt.
Erstellt es am Besten mit vi telegram.sh direkt auf dem Raspberry Pi (nachdem ihr per cd in /home/pi/ seid).
Nun muss das Script noch ausführbar gemacht werden:
Code: Alles auswählen
sudo chmod +x telegram.sh
Mittels crontab -e folgende Zeile einfügen.
Code: Alles auswählen
*/1 * * * * /home/pi/telegram.sh
To-Do
- Ich plane, das ganze noch weiter zu vereinfachen. Angedacht ist, alles im Config-Bereich einzutragen und den Rest macht das Script:
Bei Hilfe <password> würden dann diese Parameter automatisch mit ausgegeben werden.
Code: Alles auswählen
status1 = "Status Heizung"; beschr1 = "Heizungsstatus abfragen"; abfrage1 ="BidCos-RF.KEQ0XXXXX1:1"; status2 = "Status Fenster"; beschr2 = ""welche Fenster sind offen?"; abfrage2 ="BidCos-RF.KEQ0XXXXX1:1"; status3 = "Status Alarmanlage"; beschr3 = ""ein- oder ausgeschaltet?"; abfrage3 ="BidCos-RF.KEQ0XXXXX1:1"; schalt1 = "Heizung"; sbeschr1 = "Heizung ein- oder ausschalten"; sschalt1 ="BidCos-RF.KEQ0XXXXX1:1"; schalt2 = "Alarmanlage"; sbeschr2 = "Alarmanlage ein- oder ausschalten"; sschalt2 ="BidCos-RF.KEQ0XXXXX1:1";
. - Einbindung einer Webcam als Screenshot, direkt ohne Umwege von der Kamera geholt und versendet
Webcam <TAN> würde dann (z.B.) das Bild versenden.
. - Das Zeitintervall im Raspberry Pi im stabilen Rahmen so weit wie möglich herunterschrauben
Hierfür wird es aber noch eine weitere Variable in der CCU geben, damit der Raspi das Script nicht noch einmal triggert während dieser schon läuft und die Meldung bereits verarbeitet.