ioBroker im Docker auf der Synology DiskStation im gleichen Subnet

eingetragen in: Docker, ioBroker, Synology 3

Um Docker auf der Synology NAS zu installieren gibt es von Andre Buanet ein Docker Paket. Die Installation habe ich wie hier auf https://buanet.de/2017/09/iobroker-unter-docker-auf-der-synology-diskstation/ beschrieben vorgenommen.  Ich habe aber ein paar kleine Änderungen vorgenommen damit ich den Bonjour Dienst im Docker Container parallel zu dem auf der Synology NAS laufen lassen kann.  Auch kann man mit der Lösung unten, mehrere ioBroker Instanzen laufen lassen, die alle dann unter Apple Homekit sichtbar sind.

Dafür darf der ioBroker Container nicht im Host Modus gestartet werden. Der NET (Bridge) Modus geht leider auch nicht, da die vergebene IP Adresse aus einem anderen Subnet ist (172.17.0.0) ist. Damit funktioniert Apple Homekit / Bonjour nicht mehr.
Aus diesem Grund muss ein MACVLAN angelegt werden (Mit Macvlan ist es möglich einer Netzwerkschnittstelle mehrere virtuelle Schnittstellen mit einer eigenen MAC Adresse zuzuweisen). Leider bietet Synology diese Option über die Docker Oberfläche nicht an.
Also mit SSH auf der NAS als root anmelden und folgenden Befehl ausführen:

docker network create -d macvlan --subnet=192.168.20.0/24 --gateway=192.168.20.40 --ip-range=192.168.20.80/28 --aux-address 'host=192.168.20.80' -o parent=eth0 mac0

Die IP Adresse meiner NAS ist 192.168.20.30 das dazugehörige Subnetz ist 192.168.20.0/24. Mit dem Befehl oben habe ich eine „MACVLAN Schnittstelle“ in Docker im Subnet 192.168.20.80/28 angelegt. In diesem Subnet werden IP Adressen von 192.168.20.80 bis 192.168.20.95 vergeben (http://www.subnet-calculator.com/cidr.php).

Dieser Bereich sollte beim eigenen DHCP Server ausgeschlossen werden um nicht doppelte IP Adressen zu vergeben.
In Docker sehe ich jetzt nun den Eintrag mac0 unter Netzwerk.

 

Also nächstes den Docker Container buanet/iobroker installieren und anders als in der Anleitung beschrieben in den erweiterten Einstellungen unter Netzwerk den Netzwerknamen mac0 auswählen.

Jetzt kann der Container gestartet werden und sollte eine IP Adressen zwischen 192.168.20.81 bis 192.168.20.95 erhalten. (Die IP Adresse 192.168.20.80 wurde mit dem Parameter –aux-address ‚host=192.168.20.80‘ ausgeschlossen).
Nun solle man ioBroker mit http://192.168.20.x:8081 von außen erreichen. Jetzt gibt es leider noch 2 Probleme. Einmal ist der DNS Server im Docker Container verkehrt und keine Namen auflösen und man kann aus dem Docker Container nicht die NAS erreichen und umgekehrt.
Folgendermaßen kann man das Problem lösen! Dafür wieder mit SSH auf der NAS als root anmelden und folgende Befehle ausführen:

# Virtuelles Netzwerk Device mac1 anlegen
ip link add mac1 link eth0 type macvlan mode bridge
ip addr add 192.168.20.80/32 dev mac1
ip link set mac1 up
ip route add 192.168.20.80/28 dev mac1

Somit ist der Docker ioBroker Container nun auch von der NAS aus ansprechbar. Aus dem Docker Container ist die NAS über die zusätzliche IP Adresse 192.168.20.80 zu erreichen. Leider sind die Einstellungen nach jedem reboot der NAS weg und müssen neu angelegt werden. Ich habe mir ein kleines sh Skript erstellt, welches bei jedem Start der NAS über den Synology Aufgabenplaner ausgeführt wird.

Nun noch das Problem mit dem DNS Server bereinigen. Dafür im ioBroker Docker Container ein Terminal öffnen. Nun als erstes einmal folgenden Befehl durchführen:

echo "nameserver 8.8.8.8 "> /etc/resolv.conf

Damit wurde der DNS Server mit der IP-Adresse 8.8.8.8 eingerichtet. Leider wird der Eintrag beim jedem reboot des ioBroker Docker Containers überschrieben. Nun schnell noch folgende Befehle ausführen um das System auf den neusten Stand zu bringen und den Editor vi zu installieren.

apt-get update
apt-get upgrade
# Editor VI installieren
apt-get install vim
apt-get install net-tools
apt-get install dnsutils
# Für Webcam Einbindung bei iobroker.yahka
apt-get install ffmpeg

# Zeitzone geradeziehen
cp /usr/share/zoneinfo/Europe/Berlin /etc/localtime

So jetzt nun noch das Startskript anpassen, damit der Nameserver bei jedem Neustart des Container überschrieben wird. Dafür im Docker Container mit dem Editor vi das Skript /opt/scripts/iobroker_startup.sh um folgende Einträge am Anfang ergänzen:

#!/bin/sh

# Ergänzung - Anfang
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "192.168.20.80 nas nas.mydomain.local" >> /etc/hosts
# Ergänzung - Ende

cd /opt/iobroker

# ...

Jetzt den Container neu starten. Übrigens die Migration der „alten“ ioBroker Docker Installation in den neuen Container war total unproblematisch. Dafür habe ich das /opt/iobroker Verzeichnis kopiert. Vorher hatte ich die ioBroker / ioBroker Admin Versionen auf die gleiche Versionsnummern hochgezogen.

Falls Ihr ein Bond am Laufen habt oder Open vSwitch nutzt dann müsst Ihr als Device bondX (X = 0,1,2,…), oder ovs_ethX (X = 0,1,2,…) statt ethX nutzen.

Leider muss das MACVLAN bei jedem Neustart der Synology NAS neu eingerichtet werden. Aus diesem Grund habe ich eine kleines Skript erstellt, Dieses rufe ich im Aufgabenplaner (Systemsteuerung -> Aufgabenplaner) alle 5 Minuten als Benutzer root auf:

#!/bin/sh

if ip link | grep "mac1@eth0" > /dev/null; then

 echo "Device mac1 existiert"

else

 echo "Device mac1 anlegen"
 ip link del mac1
 ip link add mac1 link eth0 type macvlan mode bridge
 ip addr add 192.168.20.80/32 dev mac1
 ip link set mac1 up
 ip route add 192.168.20.80/28 dev mac1

fi

 

Internet of Things – ioBroker

eingetragen in: ioBroker 3

Wozu benötige ich etwas wie Internet of Things (IoT)

Plan war es unser Haus mit Heizungsthermostaten die ich online per App über das Smartphone steuern kann aufzurüsten. Da ich die Lupusec XT2+ Alarmanlage besitze, in der man auch Smarthomekomponenten schalten kann, habe ich mir passend Steckdosen und Heizungsthermostaten von Lupus bestellt. Die Steckdosen sind super, aber mit den Thermostaten war ich nicht so zufrieden und habe diese zurückgeschickt. Aber was nun, Thermostaten von anderen Herstellen bestellen und diese mit einer weiteren App schalten? Solange die Fenster offen sind sollen die Heizungsthermostaten die Temperatur automatisch runterregeln.  Aber wie soll das funktionieren mit Türkontakten von Lupusec und Heizungsthermostaten von einem anderen Hersteller? Wie sollen die Thermostaten wissen, wann die Fenster offen sind? Ich hatte kurz mit dem Gedanken gespielt, die Lupusec Alarmanlage zu verkaufen und stattdessen mir das Smarthomesystem von Homematic zuzulegen. Diese hat tolle Smarthomekomponenten für einen guten Preis. Aber als Alarmanlage kommt die Homematic für mich überhaupt nicht in Frage.

Jetzt stand ich vor dem Problem, Lupusec als super Alarmanlage behalten und Homematic mit tollen Smarthomekomponenten zuzulegen. Aber wie kombiniere ich Beides miteinander ohne mit mehren Apps zu arbeiten.

Durch Zufall bin ich über das Thema IoT (Internet of Things) gestolpert. IoT Lösungen wie FHEM, OpenHAB und ioBroker können Smarthomekomponenten verschiedener Anbieter miteinander verknüpfen. Ich habe mir alle drei Lösungen angeschaut. Dabei gefiel mir ioBroker am besten, da es auf nodejs aufbaut und Betriebssystem unabhängig ist. ioBroker kann man auf einem Raspberry, Windows, Mac, Unix oder in einem Docker Container auf einer Synology NAS installieren. Die ioBroker Oberfläche wird über einen Webbrowser wie z.b. Chrome aufgerufen.

Da ich noch einen Raspberry PI3 für ca. 35 EUR herumliegen hatte, habe ich ioBroker dort testweise installiert. Linux Kenntnisse sind hilfreich.

Ein ioBroker Testsystem zum auszuprobieren kann man über http://iobroker.click/ aufrufen.

Erste Schritte mit ioBroker

Eine Installationsanweisung wie man ioBroker installiert findet man für die verschiedenen Plattformen hier. Ist die Installation abgeschlossen, dann ruft man die Admininstrationsoberfläche über den Webbrowser mit http://<IP-raspbbery>:8081 auf (Bsp.: http://192.168.20.82:8081).

Die drei wichtigsten Bereiche in der ioBroker Adminoberfläche sind:

  • Adapter (verschieden Komponenten von Herstellern, wie z.B. Phillips HUE, Lupusec, Homematic, …., Stand Juni 2018 gibt es über 200 Adapter)
  • Instanzen (Instanz eines Adapters, erst dann kann man mit einem Adapter arbeiten)
  • Objekte (Objekte sind Geräte (Devices) von den Adpater-Instanzen und Datenpunkte wie z.B. Soll Temperatur vom Wohnzimmer Heizungsthermostaten. Der Status der Datenpunkte können gelesen und geändert werden.)

Hier ein Beispiel mit dem Lupusec Adapter. Auf dem Reiter Objekte kann mit status_ex die Lupusec Funksteckdose an und ausgestellt  bzw. den Status der Steckdose abgefragt werden, auch wenn diese über die Lupsusec App und nicht über ioBroker geschaltet wurde.

Jetzt kann man verschiedene Objekte voneinander abhängig schalten. Dieses geht für den der programmieren kann mit Javascript. Die die nicht programmieren können oder wollen steht die Blocksprache Blocky zur Verfügung.

In dem Beispiel unten, wird die Steckdose angeschaltet (true), sobald die Haustür aufgeht (Türkontaktstatus true). Sobald die Haustür geschlossen wird (Türkontaktstatus false) wird die Steckdose ausgeschaltet (false).

Wer ein iPhone besitzt kann seine ganzen Komponenten auch darüber steuern. Dafür muss der Apple Homekit Adapter installiert werden.

ioBroker bietet auch einen grafischen Editor an um eigene Oberflächen zum Steuern von Geräten zu erstellen. Programmierkenntnisse sind dafür nicht notwendig.

Synology Surveillance Station Videostream in MJPEG wandeln

eingetragen in: Synology 54

Gerade die günstigen Webcams bieten häufig keinen MJPEG Stream an. Da kann man sich glücklich schätzen, wenn man Besitzer eine Synology NAS ist. Als erstes sind die Pakete  „Surveillance Station“, „Web Station“ und „PHP 5.6 oder höher“ auf der NAS zu installieren. Anschließend ist die Webcam in der Surveillance Station einzubinden.

Bevor es aber los geht, hier kurz erklärt was MJPEG ist:

Bei Motion JPEG (MJPEG) handelt es sich um ein Videocodec, bei dem jedes Einzelbild des Videostreams separat als JPEG-Bild komprimiert im MJPEG gespeichert wird. Mann kann sich das als aneinander gereihte JPEGs in einer nicht endenden Datei vorstellen.

Sobald man die Webcam in der Surveillance Station konfiguriert hat, ist das PHP Skript auf der Webstation in dem Verzeichnis /volume1/web/ mit dem Namen webcam.php zu kopieren. Alternativ kann das PHP Script auch auf jedem anderen Webserver im Internet bzw. Intranet liegen, vorausgesetzt es besteht eine Internetverbindung zur NAS.

Nur die ersten Zeilen im Skript sind anzupassen, unter $ip ist die IP-Adresse oder der Hostname sowie unter $port der Port der NAS einzutragen. Diese Informationen werden aus Sicherheitsgründen nicht beim Aufruf des Skriptes mitgeben.

Das PHP Skript greift über die Surveillance Station WebAPI v2.0 auf den Webcam Stream der Surveillance Station zu und wandelt diesen in das MJPEG Format um und zeigt dann das MJPEG im Webbrowser. Das Bild erneuert sich alle paar Sekunden.

Das Skript folgendermaßen aufrufen:

http://webserver/webcam.php?usr=<benutzername>&pwd=<passwort>&cam=<1|2|…>&format=<mjpeg|jpeg>

 

 

Die Variablen sind wie folgt zu füllen:

Variable Wert
usr Benutzername
(am besten einen Benutzer in der Surveillance Station mit wenig Berechtigungen anlegen)
pwd Passwort (Passwort des Benutzers)
cam 1,2,3,4 oder … (die ersten konfigurierte Webam ist 1 die zweite 2 usw.)
format jpeg für eine einzelnes Bild oder mjpeg für einen jpeg Stream

 

Beispiel:

http://nas-stueben.goip.de/webcam.php?usr=thorsti&pwd=geheim&cam=2&format=mjpeg

Wer möchte kann aber auch den Variablen $user, $pass und $cameraID im Skript direkt Werte zuweisen. Dann ist das Passwort in der URL nicht sichtbar! Download

<?php

// **********************************************************************************
// Diese Beiden Werte sind ggf. anzupassen. Sonst ist nichts zu tun!
// **********************************************************************************
$ip   = "localhost"; // IP-Adresse eures Synology-NAS
$port = "5000";      // default Port der Surveillance Station
                    
// **********************************************************************************
// Anmelden an der Surveillance Station und sid zurückgeben
// **********************************************************************************
function getSid($user, $pass, $ip, $port) {
	$link_login = 'http://' . $ip . ':' . $port . '/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=3&account=' . $user . '&passwd=' . $pass . '&session=SurveillanceStation&format=sid';
	
	// Eventuell müsst ihr die URLs von HTTP auf HTTPS anpassen, sofern ihr HTTPS aktiviert habt!
	// Authentifizierung an Synology Surveillance Station WebAPI und auslesen der SID
	$json = file_get_contents ( $link_login );
	$obj = json_decode ( $json, true );
	@$sid = $obj ["data"] ["sid"];
	return $sid;
}

// **********************************************************************************
// Link für MJPEG von Surveillance Station lesen
// **********************************************************************************
function getStreamLink($user, $pass, $ip, $port, $cameraID) {
	$sid = getSid ( $user, $pass, $ip, $port );
	$link_stream = 'http://' . $ip . ':' . $port . '/webapi/SurveillanceStation/videoStreaming.cgi?api=SYNO.SurveillanceStation.VideoStream&version=1&method=Stream&cameraId=' . $cameraID . '&format=mjpeg&_sid=' . $sid;
	
	return $link_stream;
}

// **********************************************************************************
// Link für JPEG von Surveillance Station lesen
// **********************************************************************************
function getImageLink($user, $pass, $ip, $port, $cameraID) {
	$sid = getSid ( $user, $pass, $ip, $port );
	$link_image = 'http://' . $ip . ':' . $port . '/webapi/entry.cgi?camStm=1&version=8&cameraId=' . $cameraID . '&api="SYNO.SurveillanceStation.Camera"&preview=true&method=GetSnapshot&_sid=' . $sid;

	return $link_image;
}

// **********************************************************************************
// MJPEG Stream von Surveillance Station ausgeben
// **********************************************************************************
function printStream($user, $pass, $ip, $port, $cameraID) {
	$link_stream = getStreamLink ( $user, $pass, $ip, $port, $cameraID );
	
	set_time_limit ( 60 );
	
	$r = "";
	$i = 0;
	$boundary = "\n--myboundary";
	$new_boundary = "newboundary";
	
	$f = fopen ( $link_stream, "r" );
	
	if (! $f) {
		// **** cannot open
		print "error";
	} else {
		// **** URL OK
		header ( "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0" );
		header ( "Cache-Control: private" );
		header ( "Pragma: no-cache" );
		header ( "Expires: -1" );
		header ( "Content-type: multipart/x-mixed-replace;boundary={$new_boundary}" );
		
		while ( true ) {
			
			while ( substr_count ( $r, "Content-Length:" ) != 2 ) {
				$r .= fread ( $f, 32 );
			}
			
			$pattern = "/Content-Length\:\s([0-9]+)\s\n(.+)/i";
			preg_match ( $pattern, $r, $matches, PREG_OFFSET_CAPTURE );
			$start = $matches [2] [1];
			$len = $matches [1] [0];
			$end = strpos ( $r, $boundary, $start ) - 1;
			$frame = substr ( "$r", $start + 2, $len );
			
			print "--{$new_boundary}\n";
			print "Content-type: image/jpeg\n";
			print "Content-Length: ${len}\n\n";
			print $frame;
			usleep ( 40 * 1000 );
			$r = substr ( "$r", $start + 2 + $len );
		}
	}
	
	fclose ( $f );
}

// **********************************************************************************
// JPEG Image von Surveillance Station ausgeben
// **********************************************************************************
function printImage($user, $pass, $ip, $port, $cameraID) {
	$link_image = getImageLink ( $user, $pass, $ip, $port, $cameraID );
	$image = file_get_contents ( $link_image );
	
	header ( "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0" );
	header ( "Cache-Control: private" );
	header ( "Pragma: no-cache" );
	header ( "Expires: -1" );
	header ( "Content-type: image/jpeg" );
	header ( "Content-Length: " . ( string ) (filesize ( $image )) );
	print $image;
}

// **********************************************************************************
// Stream als MJPEG ausgeben
// http://nas-stueben.goip.de/webcam.php?usr=<benutzername>&pwd=<passwort>&cam=<1|2|...>&format=<mjpeg|jpeg>
// **********************************************************************************
$user     = $_GET ["usr"]; // Synology Benutzer mit Berechtigung die Kamera anzuzeigen
$pass     = $_GET ["pwd"]; // Passwort zu eben eingegebenem Benutzer
$cameraID = $_GET ["cam"]; // ID der Kamera, welche angezeigt werden soll
$format   = $_GET ["format"]; // format = jpeg für Image oder mjpeg für Stream

if (strtolower ( $format ) == "jpeg") {
	
	printImage ( $user, $pass, $ip, $port, $cameraID );
} else {
	
	printStream ( $user, $pass, $ip, $port, $cameraID );
}

?>

Jetzt kann man noch in dem Webstation Verzeichnis /volume1/web/ ein Unterverzeichnis webcam anlegen (/volume1/web/webcam) und dort die Datei .httaccess anlegen. Da das Passwort für den Webam Benutzer in der Datei hinterlegt ist, sollten die entsprechenden Berechtigungen für die Datei .httaccess gesetzt sein.

bis Apache 2.2:

# ********************************************************
# .htaccess bis Apache 2.2
# ********************************************************

# Nur im lokalem Netz gilt die Regel
order deny,allow 
deny from all
allow from 192.168.20.0/24 

# Regel jpg -> php bzw. mjpg -> php
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^cam(.*)\.jpg$ /webcam.php?usr=<user>&pwd=<password>&cam=$1&format=jpeg [L,NC]
RewriteRule ^cam(.*)\.mjpg$ /webcam.php?usr=<user>&pwd=<password>&cam=$1&format=mjpeg [L,NC]

ab Apache 2.4:

# ********************************************************
# .htaccess ab Apache 2.4 
# ********************************************************

# Nur im lokalem Netz gilt die Regel
Require ip 192.168.20.0/24 

# Regel jpg -> php bzw. mjpg -> php
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^cam(.*)\.jpg$ /webcam.php?usr=<user>&pwd=<password>&cam=$1&format=jpeg [L,NC]
RewriteRule ^cam(.*)\.mjpg$ /webcam.php?usr=<user>&pwd=<password>&cam=$1&format=mjpeg [L,NC]

 

Jetzt könnt Ihr die Webcam wie folgt aufrufen:

# Webcam 1 / Einzelbild
http://webserver/webcam/cam1.jpg
 
# Webcam 2 / Einzelbild
http://webserver/webcam/cam2.jpg 

# Webcam 1 / MJPEG Stream 
http://webserver/webcam/cam1.mjpg 

# Webcam 2 / MJPEG Stream
http://webserver/webcam/cam2.mjpg
1 2 3 4 5