Bestimmte Mobotix T25 Keypad Eingaben an IP-Symcon weiterleiten

In der letzten Zeit waren wir viel in unserem Garten unterwegs und haben den Pool wieder aufgestellt. Nun braucht dieser Pool fast tägliche Pflege (Chlor, Anti-Algenmittel etc.). Diese ganzen Chemikalien haben wir in unserer Garage gelagert, welche natürlich die meiste Zeit geschlossen ist. Oftmals merkt man das erst wenn man vor verschlossenem Tor steht, und da natürlich weder Handy noch Schlüssel am Mann / der Frau ist, kam mir die Idee das ich das Tor auch mit der Eingabe einer Zahlenkombination an der Klingel öffnen / schließen könnte.

Heute habe ich mich mit dieser Thematik etwas weiter befasst und möchte allen Interessierten gerne an einem Beispiel zeigen, wie ihr eure T25 samt Keypad mit IP-Symcon reden lassen könnt.

Dazu benötigt ihr als erstes das T24/T25 PHP Modul für IP-Symcon welches ich programmiert habe und Öffentlich zur Verfügung stelle. Alle weiteren Informationen und eine Anleitung dazu findet ihr hier:

Link zum Forumsbeitrag
Link zum Github Projekt

Nachdem ihr das PHP Modul installiert und konfiguriert habt geht es folgendermaßen weiter:

Als erstes muss man im Adminmenü unter dem Punkt ‚Nachrichtenkonfiguration der MxBus-Module‘ eine neue Nachricht anlegen, dies ist auch gleichzeitig der Eventname welcher später an IP-Symcon übermittelt wird. Wichtig ist die Gültigkeit dieser Nachricht auf ‚Lokal‘ oder ‚Intern‘ zu setzen, denn dann verlässt diese Nachricht nicht die Klingelanlage. In der Einstellung ‚Global‘ könnte diese Nachricht über das Netzwerk übermittelt und ggfs. mitgelesen werden. :
2016-08-02 18_06_29-T25 MxMessageSystem der MxBus-Module
Diese Nachricht steht dann innerhalb des Keypads zur Verfügung, dort muss als nächstes unter der Rubrik ‚Keypad‘ ein neuer Eintrag angelegt (1), die gewünschte Zahlenkombination eingetragen werden (2) und mit der Nachricht welche wir zuvor erstellt haben verheiratet werden (3). Zu guter letzt muss die Konfiguration noch auf die Bus-Module zurück geschrieben werden (4):
2016-08-02 18_09_55-T25 MxMessageSystem der MxBus-Module

 

Nun haben wir unsere Nachricht erfolgreich konfiguriert und müssen nun noch der Klingel beibringen diese auch an IP-Symcon zu melden. Dazu bewegen wir uns aus dem Adminmenü heraus in das Setupmenü. Dort rufen wir als erstes die ‚Ereignisübersicht‘ auf. Dort finden wir unter der Rubrik Nachrichtenereignisse, hier muss über den Button ‚Bearbeiten…‘ ein neues Nachrichtenereignis in folgender Form angelegt werden:2016-08-02 18_20_34-T25 Nachrichtenereignisse

Haben wir diese letzte Hürde genommen sind wir endlich in der Lage diese Nachricht an IP-Symcon weiter zu leiten indem wir im Setupmenü die ‚Aktionsgruppen-Übersicht‘ aufrufen und die (hoffentlich) bei der PHP Modulkonfiguration angelegte Aktionsgruppe ‚IPSymcon‘ um die Nachricht ‚Keypad_Garagentor‘ erweitern:
2016-08-02 18_25_52-T25 Aktionsgruppen-Details

Nach einem kurzen Test sollte nun eine neue Variable mit der Bezeichnung ‚Ereignis: Keypad_Garagentor‘ in eurer T25 Instanz innerhalb IP-Symcon auftauchen:
2016-08-02 18_27_38-IP-Symcon Management Console [ipsymcon.seitingen.lan]

Gaszähler BK-G4 an IP-Symcon anbinden

Wie schon im letzten Post zum Thema Hausautomatisierung angekündigt folgt nun der erste Basteltip. Nachdem ich bereits unseren intelligenten Stromzähler erfolgreich an IP-Symcon angebunden habe (Basteltip folgt) wollte ich mehr. Mehr Daten, mehr Transparenz. Wichtig zu erfassen sind meiner Meinung nach primär folgende drei Messwerte:

  • Energieverbrauch / Energie Einspeisung
  • Gasverbrauch
  • Wasserverbrauch

Entsprechend war der nächste Punkt klar, ich möchte meinen Gaszähler überwachen. Ich hatte zunächst angenommen, dass dies eine Aufgabe sein wird die durchaus komplizierter sein könnte. Doch ich hatte Glück, und unser Gaszähler vom Typ BK-G4 bietet im unteren Bereich des Zählers auf der rechten Seite eine Einbuchtung. An dieser Einbuchtung kann man einen Reed-Kontakt (Magnetschalter) platzieren. Charakterisierend für einen einfachen Reed-Kontakt ist es, dass dieser einen Kreislauf schließt sobald etwas Magnetisches in seine Nähe kommt. Bei dem Gaszähler ist das der Fall wenn sich die letzte Stelle des Zählers von 9 auf 0 ändert, also 0,01 cbm verbraucht wurde, weil genau an dieser Stelle des Segmentes ein Magnet innerhalb des Zählers eingebaut wurde, der genau für solche Einsatzzwecke gedacht ist. So ist es recht einfach Möglich den Verbrauch exakt zu speichern und später auszuwerten.

Was braucht man also ? Eine kleine Einkaufsliste:

Ihr fragt euch bestimmt, warum ein Raspberry Pi benötigt wird ? Nun der Gaszähler hat ja einen bestimmten Zählerstand (in der Regel wird dieser bei den wenigsten Lesern 0 betragen) und der Reed-Kontakt meldet uns nur einen Wechsel von 0 auf 1 (Kreislauf offen / geschlossen, low / high) nicht aber den Gesamtzählerstand welchen wir u.a. aber haben möchten. Um diese Daten im IP-Symcon halten zu können benötigen wir eine Schnittstelle, diese Schnittstelle stellt aus Hardware Sicht der Raspberry Pi dar.
Auf ihm läuft ein Bash Skript welches permanent auf den GPIO Pins lauscht und mitbekommt wenn der Kreislauf auf zwei vorher definierten Pins von low auf high wechselt und somit einen Stromkreis schließt. Wenn das der Fall ist, wird ein JSON Request an den IPSymcon Server abgesendet, diese Schnittstelle werden wir im weiteren Verlauf noch einrichten.

Das Skript für den Raspberry Pi sieht folgendermaßen aus:

[bash highlight=“4″]#!/bin/bash

# URL zur Ipsymcon JSON Schnittstelle
IPSURL=“http://<ipsymcon-server-ip-adresse>:9998″
# Identifier auf welchen dann die Ipsymvon JSON Schnittstelle reagiert
IDENTIFIER=“GAS_BKG4″
# GPIO Input Pin (auf welchen reagiert wird)
GPIO_PIN_IN=17
# Impulseinheit in Double (z.B. 1 Impuls entspricht 10.0 Einheiten oder 0.1 Einheiten)
UNIT=“0.01″
# DO NOT CHANGE
SCRIPTNAME=basename "$0"

if [[ ps aux | grep $SCRIPTNAME | wc -l -gt 4 ]]; then
exit 0
fi

if [[ ls -la /sys/class/gpio | grep gpio$GPIO_PIN_IN | wc -l -eq 0 ]]; then
echo „$GPIO_PIN_IN“ > /sys/class/gpio/export
sleep 0.5
echo „in“ > /sys/class/gpio/gpio$GPIO_PIN_IN/direction
sleep 0.5
fi
CURVAL=0
while true; do
PREVVAL=$CURVAL
CURVAL=cat /sys/class/gpio/gpio$GPIO_PIN_IN/value
if [[ $CURVAL -gt $PREVVAL ]]; then
JSON=“{\“Name\“:\““$IDENTIFIER“\“, \“Timestamp\“:\“date +%s\“, \“Values\“: [“
JSON=$JSON“{ \“Name\“:\“impulse\“, \“Value\“:\“1\“, \“Unit\“:\““$UNIT“\“}“
JSON=$JSON“] }“

curl -0 -i -X POST -d „json=$JSON“ $IPSURL &> /dev/null
echo $JSON
fi
sleep 0.1
done[/bash]

Das Skript setzt automatisch den definierten Eingangspin, sodass nichts weiter getan werden muss. Das Skript könnt ihr einfach jede Minute durch den Cron ausführen lassen:

[bash]# Gaszähler auslesen
*/1 * * * * root /opt/datacollector_bkg4.sh &> /dev/null[/bash]

Das Skript selbst gestattet nur eine Ausführung von sich selbst, somit ist der Aufruf über den Cron problemlos möglich.

Nun zur Verkabelung. Pin 1 dient hierbei als Stromversorgung mit 3V Ausgangsspannung, Pin 17 ist der Eingang wo bei Schließung des Stromkreises (wenn der Reed-Kontakt schließt) wieder die 3V Ausgangsspannung anliegen und somit ein Impuls erzeugt wird:

HM-GAS11

Pin 1 = 3V, Pin 17 = Eingang

Ein weiterer wichtiger Bestandteil der Lösung ist auf IP-Symcon Seite die vorhin angesprochene JSON Schnittstelle. JSON ist eine definierte Notation um Informationen zwischen verschiedensten Systemen auszutauschen. Es handelt sich hierbei konkret um ein PHP Skript welches mittels einer Register Variablen an einen Server Socket gebunden wird.

Doch zunächst erstellen wir das PHP Skript welches die eigentliche JSON Schnittstelle enthält und den JSON Request vom Raspberry Pi versteht und auch verarbeitet. Der Inhalt dieses Skriptes ist folgender:

[php collapse=“true“ highlight=“28″]
<?
// JSON Schnittstelle
// Schnittstellentyp: POST
// Schnittstelle nimmt POST Daten im JSON Format entgegen und speichert diese nach Definition in Variablen ab.
// Die Schnittstelle legt außerdem benötigte Variablen automatisch an.
//
// von: Daniel Schäfer (@gotteshand)
// Version: 1.1

if ($_IPS[‚SENDER‘] == „RegisterVariable“) {
$Handler = new IPSServerDataHandler($_IPS[‚INSTANCE‘], $_IPS[‚VALUE‘]);

$ok = $Handler->CheckData(function ($event) {
$data = ‚-1′;
$content = $event[‚content‘];

$result = null;
switch ($event[‚method‘]) {
case ‚GET':
break;
case ‚POST':
if (isset($content[‚json‘])) {
$json = json_decode($content[‚json‘]);

// Gaszähler
if (strtolower($json->Name) == „gas_bkg4″) {
foreach ($json->Values as $value) {
$variable_id = 4711; // !!!!!!!!!! ID ANPASSSEN !!!!!!!!!!

$ipsvalue = GetValue($variable_id);
$ipsvalue += ($value->Value*$value->Unit);
SetValue($variable_id, $ipsvalue);
}
}
}

break;
case ‚NOTIFY':
break;
case ‚CALL':
break;
}
return $data;
});
}

class IPSServerDataHandler
{
private $INSTANCE_ID = 0;
private $CALLBACKURL = “;
private $LAST_EVENT = null;
private $headLines = [];

function __construct($InstanceId, $value)
{
$this->INSTANCE_ID = $InstanceId;
$this->DATA = RegVar_GetBuffer($InstanceId).$value;
$this->DEBUG = $this->__read(‚DEBUG‘, 0);
if ($this->DEBUG) Debug(‚GET :‘.$this->DATA);
}

function CheckData($CallBack = null)
{
while ($ev = $this->__SplittEventData()) {
$sendData = ($CallBack) ? $CallBack($ev) : array(‚status‘ => ‚200 OK‘, ‚content‘ => “);
$this->__SendHttpResponse($sendData);
$this->LAST_EVENT = $ev;
}
$this->__write(‚LAST_REQUEST‘, „{$this->LAST_EVENT[‚method‘]}: „.ArrayToStr($this->LAST_EVENT[‚content‘]));
$this->__write(‚LAST_RESPONSE‘, ArrayToStr($sendData, 0, “));
RegVar_SetBuffer($this->INSTANCE_ID, $this->DATA);
return ($CallBack) ? (bool)$this->LAST_EVENT : $this->LAST_EVENT;
}

public function LastEvent()
{
return $this->LAST_EVENT;
}

private function __SplittEventData()
{
$methods = array(‚GET‘, ‚POST‘, ‚NOTIFY‘);
$content = $head = [];
$event = false;
$method = $mdata = “;
$dpos = $contlen = 0;
$lines = explode(„\n“, $this->DATA);
foreach ($lines as $lnum => $line) {
$dpos += strlen($line) + 1;
$line = trim($line);
if ($line) {
if (!$method) { // wenn noch keine methode gefunden wurde
foreach ($methods as $m) { // Zeile auf methoden prüfen
$find = preg_match(‚/‘.$m.‘ \/(.+)HTTP\//‘, $line, $matches);
if ($find) {
$method = $m;
$mdata = $matches[1];
break;
}
}
if ($method) {
$this->headLines = [];
continue;
}
}
$this->headLines[] = $line;
$m = explode(‚:‘, $line);
if (isSet($m[1])) {
$m[0] = strtoupper(trim($m[0]));
$head[$m[0]] = trim($m[1]);
if ($m[0] == ‚CONTENT-LENGTH‘) $contlen = (int)$m[1];
if ($m[0] == ‚CALLBACK‘) $this->CALLBACKURL = $m[1];
}
} else {
if ($contlen > 0) {
$content = substr($this->DATA, $dpos, $contlen);
$this->DATA = trim(substr($this->DATA, $dpos + $contlen));
} else {
$this->DATA = trim(substr($this->DATA, $dpos));
$content = $mdata;
}
break;
}
}
if ($method) {
$event[‚method‘] = $method;
$event[‚head‘] = $head;
$event[‚content‘] = ($content) ? $content : $mdata;
}
if ($event) $this->__prepareReadingData($event);
return $event;
}

// private function __prepareReadingData(&$Event){
public function __prepareReadingData(&$Event)
{
$data =& $Event[‚content‘];
$pa = null;
if (!$data || ($Event[‚method‘] == ‚NOTIFY‘)) return true;
if (($data[0] == ‚?‘) || ($data[0] == ‚&‘)) {
$pa = explode(‚&‘, substr($data, 1));
} else if ($Event[‚method‘] == ‚POST‘) {
$pa = explode(‚&‘, $data);
} else if ($Event[‚method‘] == ‚GET‘) {
if (preg_match(‚/(.*)\((.*)\)/‘, $data, $matches)) {
array_shift($matches);
$Event[‚method‘] = ‚CALL';
$da[‚name‘] = array_shift($matches);
$aa = array_shift($matches);
if ($aa != “) {
$da[‚arguments‘] = explode(‚,‘, $aa);
foreach ($da[‚arguments‘] as $k => $v) {
if (is_bool($v) === true)
$v = (bool)$v;
else if (is_numeric($v) === true) {
if (is_double($v) === true) $v = (double)$v;
else $v = (integer)$v;
}
$da[‚arguments‘][$k] = $v;
}
} else $da[‚arguments‘] = [];
$data = $da;
}
}
if (!$pa) return true;
$data = null;
foreach ($pa as $pl) {
$da = explode(‚=‘, $pl);
if (isSet($da[1])) {
$dn = array_shift($da);
if (strtoupper($dn) == ‚CALLBACK‘) {
$this->CALLBACKURL = implode(‚=‘, $da);
} else {
$data[$dn] = implode(‚=‘, $da);
}
}
}
return true;
}

private function __SendHttpResponse($Data = null)
{
$id = @IPS_GetInstance($this->INSTANCE_ID)[‚ConnectionID‘];
if (!$id) return false;
if (!$Data) $Data = array(‚status‘ => ‚200 OK‘);
if (!is_array($Data)) $Data = array(‚content‘ => $Data);
if (!isSet($Data[‚status‘])) $Data[‚status‘] = ‚200 OK';
if (!isSet($Data[‚content‘])) $Data[‚content‘] = “;
$res[] = „HTTP/1.1 {$Data[‚status‘]}“;
if ($Data[‚content‘]) {
if (!isSet($Data[‚size‘])) $Data[‚size‘] = strlen($Data[‚content‘]);
if (!isSet($Data[‚type‘])) $Data[‚type‘] = ‚text/plain';
$res[] = „CONTENT-TYPE: {$Data[‚type‘]}“;
$res[] = „CONTENT-LENGTH: {$Data[‚size‘]}“;
$res[] = „“;
$res[] = $Data[‚content‘];
}
$content = implode(„\r\n“, $res).PHP_EOL;
$this->__write(‚LAST_STATE‘, $Data[‚status‘]);
if ($this->DEBUG) Debug(‚SEND: ‚.$content);
return SSCK_SendText($id, $content);
}

private function __write($VarIdent, $VarData, $VarType = 3)
{
$id = @IPS_GetObjectIdByIdent($VarIdent, $this->INSTANCE_ID);
if (!$id) {
$id = IPS_CreateVariable($VarType);
IPS_SetParent($id, $this->INSTANCE_ID);
IPS_SetIdent($id, $VarIdent);
IPS_SetName($id, $VarIdent);
}
SetValue($id, $VarData);
}

private function __read($VarIdent, $VarType = 3)
{
$id = @IPS_GetObjectIdByIdent($VarIdent, $this->INSTANCE_ID);
if (!$id) {
$id = IPS_CreateVariable($VarType);
IPS_SetParent($id, $this->INSTANCE_ID);
IPS_SetIdent($id, $VarIdent);
IPS_SetName($id, $VarIdent);
}
return GetValue($id);
}

}

function ArrayToStr($a, $step = 0, $cr = „\n“)
{
if (!$a) return “; else if (!is_array($a)) return $a;
$r = “;
$sp = str_repeat(‚ ‚, $step);
foreach ($a as $k => $v) $r .= „$sp’$k’=“.(is_array($v) ? „[$cr{$sp}“.ArrayToStr($v, $step + 2).“$sp$sp]“ : „$v“).“$cr“;
return $r;
}

?>
[/php]

Der JSON Request enhält neben einigen Rumpfdaten immer auch einen Wert und dessen Multiplikator. Der Wert ist immer 1, der Multiplikator kann im Bash Skript angepasst werden – je nachdem an welcher Stelle der Reed-Kontakt ausgelöst wird.
Die Schnittstelle Multipliziert zunächst den Wert 1 mit dem Multiplikator und addiert dies dann zum eigentlichen Zählerstand hinzu. Der Zählerstand wird in einer Variablen gespeichert, welche manuell angelegt werden muss (als Typ Float). Dessen Variablen ID muss noch in Zeile 28 eingesetzt werden. Möchte man seinen aktuellen Zählerstand als Basiswert festlegen, ändert man einfach den Wert dieser Variablen ab.

Anschließend wollen wir einen neuen ‚Server Socket‘ unter dem Knoten IO-Instanzen erstellen (Insofern nicht schon vorhanden) und geben ihr den Namen ‚JSON Socket Listener':

2015-07-05 22_53_29-Instanz hinzufügen

Außerdem brauchen wir noch eine Register Variable welche wir unter dem PHP Skript anlegen welches wir vorhin erstellt und mit Leben gefüllt haben:

2015-07-05 22_54_27-Instanz hinzufügen

Als letztes müssen wir nur noch den Server Socket mit dem PHP Skript verbinden. Als Bindeglied dient uns die Register Variable. Hierzu öffnen wir die Eigenschaften der Register Variable und legen im folgenden Dialog im oberen Bereich die JSON Schnittstelle (PHP Skript) und im unteren Bereich den Server Socket fest:

2015-07-05 22_56_18-IP-Symcon Verwaltungskonsole

Nun haben wir Skript und Socket über die Register Variable miteinander verheiratet.
Zum Abschluss müssen wir die Schnittstelle noch aktivieren. Das tun wir indem wir die Eigenschaften vom Server Socket aufrufen:

2015-07-05 23_04_00-IP-Symcon Verwaltungskonsole

Eventuell müsst ihr den Port 9998 in eurer Umgebung anpassen und ggfs. auch in der Windows Firewall des Servers freigeben.

Zum Schluss noch ein paar Bilder die während der Installation geknipst wurden. Bei mir sitzt der Raspberry Pi direkt im Zählerschrank. Dorthin wurde auch über bestehende Leerrohre der Klingeldraht gezogen: