Mit dem neuen Raspberry Pi 3 Model B+ oder kurz RPI3B+ steht nun ein potenter Rechner zur Verfügung, der bereits in seiner Vorgängerversion Anwendung in der neuen Homematic Zentrale CCU3 findet. Somit hat er nicht nur genügend Power, um FHEM zu beherbergen, sondern auch andere Dienste wie Alexa-FHEM, Homebridge, Reverse-Proxy (NGINX) und vieles mehr. 

Aber keine Angst, dieser Beitrag soll nicht zu technisch werden, sondern gezielt zeigen, wie Du in relativ kurzer Zeit Deinen RPI 3B+ mit Docker, FHEM und den Busware SCCs stabil und sicher installieren kannst.

Wer sich etwas mit Software-Entwicklung auskennt, der weiß die Vorzüge von Docker zu schätzen. Ich selbst lerne diese gerade selbst kennen und dachte, dass es ein guter Grund wäre, diesen Ansatz für die Hausautomation auszuprobieren.

Was ist Docker?

Docker bietet eine Umgebung mit der so genannte Container erstellt werden können. Diese Container beinhalten alle für das Ausführen der Anwendung erforderlichen Bestandteile wie Code, Systemtools, Bibliotheken und Einstellungen. Container isolieren quasi Software von ihrer Umgebung und stellen sicher, dass sie trotz Unterschieden, z. B. zwischen Entwicklung und Staging, einheitlich funktioniert.

Wird ein Container geschlossen, so steht er nach dem Neustart wieder in seinem ursprünglichen Zustand wieder zur Verfügung – sofern keine Maßnahmen der Speicherung der Änderungen getroffen werden.

Dabei ist zwischen zwei Dingen zu unterscheiden: docker-compose.yml und Dockerfile.
Mittels docker-compose.yml kann eine komplette Systemumgebung bestehend aus mehreren Containern, Netzwerken und persistenten Datenspeichern erstellt werden. Das Dockerfile bestimmt was in einem Container zur Verfügung steht, also das Linux-Betriebssystem, die Bibliotheken, der Anwendungscode und die Einstellungen.

Auf den ersten Blick erscheint es etwas kompliziert, aber mit ein paar grundlegenden Kenntnissen wie Pakete im Linux-Umfeld installiert werden, ist es prinzipiell sehr einfach. Darüber hinaus gibt es bereits eine Vielzahl von fertigen Docker-Containern, auf die man zugreifen kann, wie z. B. Portainer oder nginx.

Es gibt auch eine Vielzahl von FHEM-Containern, Alexa-FHEM und dergleichen, auf die Du auch zugreifen kannst. In diesem Beitrag will ich Dir aber zeigen wie einfach Du das selbst machen kannst, und wie flexibel Du die Container auf Deine Bedürfnisse anpassen kannst.

Hypriot – Eine Docker-Umgebung für den RaspberryPi (RPI)

Seit Anfang 2015 bietet Hypriot das gleichnamige Betriebssystem HypriotOS an. Ein speziell für den RaspberryPi optimiertes Betriebssystem zur Verwaltung von Docker-Containern.

Das aktuelle Image findest Du hier:
https://github.com/hypriot/image-builder-rpi/releases

Die Installation ist einfach:

  • Image als ZIP laden und entpacken
  • Image mit Win32 Disk ImagerApplePiBaker oder Balea Etcher auf eine SD-Karte übertragen
  • Optional kann auch noch eine Datei /boot/user-data angepasst werden, damit vor dem ersten Start von hypriot entsprechende Systemeinstellungen vorgenommen werden

Nachdem die SD-Karte vorbereitet wurde, kann der RPI gestartet werden. Hierfür am besten den RPI mit dem LAN-Kabel verbinden. So kann sichergestellt werden, dass der RPI auch erreichbar ist. WLAN-Einstellungen und sonstige Anpassungen können anschließend erfolgen.

Hinweis: Eine Installation auf einer externen mSata SSD war mit bisher nicht möglich. Ich zeige Dir im dritten Teil, wie Du die SD-Karte auf eine mSata SSD (oder sonstiges USB-Medium) übertragen kannst. Das war für mich der einfachste Weg.

Beispiel für die Datei /boot/user-data
Bitte entsprechend an Deine Bedürfnisse anpassen. Du kannst aber auch die Standard-Datei verwenden. Die weitere Beschreibung setzt diese Datei nicht voraus, sondern basiert auf dem Standard.

#cloud-config
# vim: syntax=yaml
#
# Documentation: http://cloudinit.readthedocs.io/en/0.7.9/index.html

# Set your hostname here, the manage_etc_hosts will update the hosts file entries as well
hostname: black-pearl
manage_etc_hosts: true
resize_rootfs: true

# You could modify this for your own user information
users:
  - name: pirate
    gecos: "Hypriot Pirate"
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    groups: users,docker,video,input
    plain_text_passwd: Mein-geheimes-Passwort
    lock_passwd: false
    ssh_pwauth: true
    chpasswd: { expire: false }

# Set the locale of the system
locale: "de_DE.UTF-8"

# Set the timezone
# Value of 'timezone' must exist in /usr/share/zoneinfo
timezone: "Europe/Berlin"

# Update apt packages on first boot
package_update: true
package_upgrade: true
package_reboot_if_required: true
#package_upgrade: false

# # Install any additional apt packages you need here
# packages:
#  - ntp

# WiFi connect to HotSpot
# - use `wpa_passphrase SSID PASSWORD` to encrypt the psk
write_files:
   - content: |
      hostname
      persistent
      option rapid_commit
      option interface_mtu
      require dhcp_server_identifier
      slaac private
      interface wlan0
      static ip_address=192.168.1.XXX/24
      static routers=192.168.1.1
      static domain_name_servers=192.168.1.1
    path: /etc/dhcpcd.conf
 - content: |
     allow-hotplug wlan0
     iface wlan0 inet dhcp
     wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
     iface default inet dhcp
    path: /etc/network/interfaces.d/wlan0
  - content: |
      country=de
      ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
      update_config=1
      network={
        ssid="Mein-WLAN-SSID"
        psk="Mein-WLAN-Passwort"
        proto=RSN
        key_mgmt=WPA-PSK
        pairwise=CCMP
        auth_alg=OPEN
      }
    path: /etc/wpa_supplicant/wpa_supplicant.conf
  - content: |
      hdmi_force_hotplug=1
      enable_uart=1
      start_x=0
      disable_camera_led=1
      gpu_mem=1
      dtparam=audio=on
      dtoverlay=pi3-miniuart-bt
    path: /boot/config.txt
  - content: |
      dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 cgroup_enable=cpuset cgroup_enable=memory swapaccount=1 elevator=deadline fsck.repair=yes rootwait logo.nologo vt.global_cursor_default=0
    path: /boot/cmdline.txt
  - content: |
      #!/bin/sh -e
      /usr/bin/tvservice -o
      sudo sh -c 'echo none > /sys/class/leds/led0/trigger'
      sudo sh -c 'echo none > /sys/class/leds/led1/trigger'
      sudo sh -c 'echo 0 > /sys/class/leds/led0/brightness'
      sudo sh -c 'echo 0 > /sys/class/leds/led1/brightness'
      exit 0
    path: /etc/rc.local

# These commands will be ran once on first boot only
runcmd:
  # Pickup the hostname changes
  - 'systemctl restart avahi-daemon'
  - 'systemctl mask serial-getty@ttyAMA0.service'
  
  # Activate WiFi interface
  - 'ifup wlan0'
  
  # Run portainer, so we can see our logs and control stuff from a UI
  - [
      docker, service, create,
      "--detach=false",
      "--name", "portainer",
      "--publish", "9000:9000",
      "--mount", "type=volume,src=portainer_data,dst=/data",
      "--mount", "type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock",
      "portainer/portainer", "-H", "unix:///var/run/docker.sock", "--no-auth"
    ]

Hypriot für FHEM vorbereiten

Mit den folgenden Anpassungen richtest Du eine WLAN-Verbindung ein, setzt eine festet IP-Adresse und schaltest die LEDs des Raspberry Pi aus und deaktiviert den HDMI-Port. Letzteres spart etwas Strom.

vi /etc/dhcpcd.conf
In dieser Datei wird die statische IP-Adresse eingerichtet. In meinem Beispiel ist es das On-Board WIFI-Modul des RaspberryPi (RPI 3 B+).

hostname
clientid
persistent
option rapid_commit
option interface_mtu
require dhcp_server_identifier
slaac private
interface wlan0
# HIER DEINE ANPASSUNGEN VORNEHMEN
static ip_address=192.168.1.xxx/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1

vi /etc/wpa_supplicant/wpa_supplicant.conf
In dieser Datei werden die Zugangsdaten für das WLAN gespeichert.

country=DE
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
ssid="Meine-WLAN-SSID"
psk="Mein-WLAN-Passwort"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}

vi /etc/rc.local
In dieser Datei werden die LEDs des RaspberryPi ausgeschaltet.

#!/bin/sh -e
sudo /usr/bin/tvservice -o
sudo sh -c 'echo none > /sys/class/leds/led0/trigger'
sudo sh -c 'echo none > /sys/class/leds/led1/trigger'
sudo sh -c 'echo 0 > /sys/class/leds/led0/brightness'
sudo sh -c 'echo 0 > /sys/class/leds/led1/brightness'
sudo iwconfig wlan0 power off
exit 0

Spezielle Einstellungen für den Busware SCC

Wenn Du einen oder mehrere SCC der Firma Busware verwenden willst, dann musst Du noch die folgenden Einstellungen vornehmen, damit Du ihn in FHEM verwenden kannst.

vi /etc/inittab
In dieser Datei den folgenden Eintrag löschen oder wie im Beispiel auskommentieren.

#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

vi /boot/config.txt
In dieser Datei werden entsprechende Einstellungen vorgenommen, so dass der Busware SCC mit FHEM verwendet werden kann. Da ich weder Monitor noch graphische Benutzungsoberfläche verwenden will, habe ich den Grafikspeicher auf 1MB herunter gesetzt.

hdmi_force_hotplug=1
enable_uart=1
start_x=0
disable_camera_led=1
gpu_mem=1
dtparam=audio=on
dtoverlay=pi3-miniuart-bt

vi /boot/cmdline.txt
Auch in dieser Datei müssen entsprechende Anpassungen vorgenommen werden, damit der Busware SCC mit FHEM und dem RaspberryPi funktioniert. Darüber hinaus habe ich noch ein paar Anpassungen gemacht, so dass Logo under dergleichen nicht mehr angezeigt werden.

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 cgroup_enable=cpuset cgroup_enable=memory swapaccount=1 elevator=deadline fsck.repair=yes rootwait logo.nologo vt.global_cursor_default=0

Zum Schluss muss noch der ttyAMA0-Dienst abgeschaltet werden:

systemctl mask serial-getty@ttyAMA0.service

Container für FHEM-Umgebung einrichten

In der Datei docker-compose.yml werden alle Container verwaltet, die wir für die FHEM-Umgebung verwenden wollen. Darüber hinaus können wir diese in Beziehung zueinander setzen, z. B. in Form von Abhängigkeiten, Netzwerkzugriffe und Datenspeicherung außerhalb der Container. Letzteres ist besonders wichtig, denn bei jedem Start werden die Container wieder auf ihren Ursprung zurückgesetzt.

Die folgenden Container habe ich in Verwendung. Du kannst einige natürlich deaktivieren (auskommentieren), wenn Du diese nicht verwenden möchtest.

Mein Ziel der Docker-Umgebung war es, sie so schlank wie möglich zu halten. Viele andere Docker-Container, die man im Netz finden kann, schleppen unnötigen Ballast mit sich. Das kann u. U. Performance kosten, aber auf jeden Fall auch Speicherplatz.

Meine Docker-Container

  • Portainer: Verwaltung der Docker-Umgebung über Port 9000
    http://black-pearl.local:9000
  • Adminer: Verwaltung der MariaDB über Port 8080
    http://black-pearl.local:8080
  • Reverseproxy: Einsatz von nginx, so dass FHEM über Port 80 (intern) und 8088 (extern) erreichbar ist und zudem ein Schutz gegen Angriffe von außen besteht.
  • FHEM: Hausautomation über Port 8088 (extern) erreichbar, intern nur über den Reverseproxy. Dieser Container wird über eine eigene Dockerfile auf Basis von Alpine Linux erstellt.
  • MariaDB: Datenbank zur Speicherung der Daten für die Ausgabe von Graphen. Dieser Container wird über eine eigene Dockerfile auf Basis von Alpine Linux erstellt.
  • Alexa-FHEM: Verknüpfung von FHEM und Alexa. Wie das genau geht, findest Du in dem Beitrag FHEM hört jetzt auf Alexa Dieser Container wird über eine eigene Dockerfile auf Basis von Alpine Linux erstellt.

Du findest alle Dateien auch auf GitHub:
https://github.com/krannich/dkDockerFHEM

Meine Ordnerstruktur

  • alexa
    • config Zertifikat, Public-Key und Konfigurationsdatei
    • core Quellcode von Alexa-FHEM
  • fhem
    • core Quellcode von FHEM (außerhalb vom Container)
    • fonts Meteofonts für Statusanzeige auf Kindle
  • mariadb
    • data Speicherort der Daten (außerhalb vom Container)
    • files Konfigurationsdateien und Scripts
  • reverseproxy
    • conf Konfigurationsdatei

docker-compose.yml

Das Ganze sieht dann in der docker-compose.yml wie folgt aus:

version: '3'

services:
    
    ## GENERAL
    portainer:
        restart: always
        image: portainer/portainer:latest
        command: -H unix:///var/run/docker.sock --no-auth
        ports:
            - "9000:9000"
        environment:
            - REGISTRY_HTTP_TLS_CERTIFICATE=/certs/portainer.crt
            - REGISTRY_HTTP_TLS_KEY=/certs/portainer.key
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - portainer_data:/data
            - /home/pirate/certs/portainer.key:/certs/portainer.key
            - /home/pirate/certs/portainer.crt:/certs/portainer.crt
        networks:
            - fhem-network
    
    adminer:
        image: adminer
        restart: always
        ports:
            - 8080:8080
        networks:
            - fhem-network
            
    reverseproxy:
        restart: always
        image: nginx:latest
        volumes:
            - ./reverseproxy/conf/nginx.conf:/etc/nginx/nginx.conf
        ports:
            - 80:80
            #- 443:443
            - 8088:8088
        networks:
            - fhem-network
        depends_on:
            - "fhem"    
            
    ## FHEM
    fhem:
        restart: always
        expose:
            - "8088"
        ports:
            - "8083:8083"
            - "7072:7072"
        build: ./fhem
        privileged: true
        devices:
            - "/dev/ttyAMA0:/dev/ttyAMA0"
            #- "/dev/ttyUSB0:/dev/ttyUSB0"
        volumes:
            - ./fhem/core/:/opt/fhem/
            - /etc/localtime:/etc/localtime:ro
            - /sys/class/gpio:/sys/class/gpio
            #- /dev/serial/by-id:/dev/serial/by-id
        entrypoint:
            - /opt/fhem/start-fhem.sh
        networks:
            - fhem-network
    
    ## SERVICES
    mariadb:
        restart: always
        build: ./mariadb
        environment:
            MYSQL_ROOT_PASSWORD: fhem1234
        ports:
            - "3306:3306"
        volumes:
            - ./mariadb/data:/var/lib/mysql
            - ./mariadb/files/init.sql:/docker-entrypoint-initdb.d/fhem-init.sql
        networks:
            - fhem-network
            
    alexa:
        restart: always
        ports:
            - "3000:3000"
        build: ./alexa
        volumes:
            - ./alexa/config/config.json:/alexa-fhem/config.json
            - ./alexa/config/cert.pem:/root/.alexa/cert.pem
            - ./alexa/config/key.pem:/root/.alexa/key.pem
        networks:
            - fhem-network
        depends_on:
            - "fhem"
    
volumes:
    portainer_data:
        
networks:
    fhem-network:
        driver: bridge