Leben, Essen, Schlafen

Schnipsel, Anregungen, Haltbares

Projekte, Aufgaben, Einfaches

Autor: Ole

RGB+W Bulb in openHAB integerieren und für HomeKit und Alexa ereichbar machen

RGB+W Bulb in openHAB integerieren und für HomeKit und Alexa ereichbar machen

Um die zuvor geflashte Lampe in openHAB einzubinden sind das MQTT Binding Version 2.5 und der MQTT Broker Moquette (unter Misc) über die PaperUI zu installieren. Über die Inbox kann jetzt der MQTT Broker als Thing hinzugefügt werden. Unter Configuration -> Services können noch ein paar Einstellungen vorgenommen werden (Username, Password). Um zu prüfen ob alles läuft habe ich das Programm MQTT.fx genutzt, um als unabhängiger Client im MQTT Netzwerk aktiv zu sein. Ist es geöffnet, kann über das Zahnrad und das “+” ein neuer MQTT Broker eingepflegt werden.

Profilname: mqtt embeddet broker
IP: <openHAB-IP>

Unter user Credentials noch den eventuell vergebenen Usernamen und das Passwort eintragen.Speichern und danach im Hauptfenster “Connect” drücken um eine Verbindung aufzubauen. Oben rechts sollte die Lampe grün werden, dann ist die Verbindung aufgebaut. In dem Reiter “Subscribe” eine “#” eintragen und “Subscribe” drücken um allen anfallenden Datenverkehr abzufangen.

Jetzt ist es an der Zeit die Lampe in openHAB einzubinden. Wichtig dabei ist, dass die Lampe ein eindeutiges Topic hat und sich mit dem eben angelegten Broker verbindet. >IP der Lampe> in Browser -> Configuration -> Configure MQTT aufrufen und folgende Daten speichern.

Host: <openHAB-IP>
Port: 1883 (es sei denn in openHAB ist ein anderer eingetragen worden)
Client: [so lassen]
User: <in openHAB vergebener Username angeben>
Password: <in openHAB vergebenes Passwort angeben und Haken markieren>
Topic: <eindeutigen Namen vergeben>
Full Topic: [so lassen]

In openHAB in der Inbox auf “+, dann auf MQTT Binding, “manually add” und auf generic MQTT Thing drücken. Dort einen eindeutigen Namen vergeben, gegebenenfalls die ID anpassen und die embedded bridge auswählen, speichern.

Unter Configuration -> Things das soeben erstelle Thing öffnen und und einen Channel erstellen.

Color Value als Channel Type auswählen, eindeutige Channel ID vergeben, Label zu Erklärung anpassen. Als MQTT State Topic ist “stat/$topic/Color” und als MQTT Command Topic ist ” cmnd/$topic/Color” einzutragen. $topic entspricht dem auf der Lampe vergebenen Topic weiter oben. Falls diese Topiczusammensetzung nicht klar ist, lässt sich diese entweder in MQTT.fx unter Subscribe oder in der Console der Lampe nach einem Schaltvorgang einsehen. “stat”, beziehungsweise “cmnd” bleibt immer gleich, $topic wird selbst vergeben und die einstellenden Größen sind dann eben zu sehen. Beispiel:

{"POWER":"ON","Dimmer":100,"Color":"89,122,255,0","HSBColor":"228,65,100","Channel":[35,48,100,0]}

Jetzt ist das Thing angelegt, ein Item muss erstellt werden, also eine *.items anlegen:

olor  item_name   "Beschreibung"     <colorpicker> (Gruppenname)  [ "Lighting" ]  {channel="Channelname zu finden in der PaperUI -> Configuration -> Things -> Lampe unter dem Channel"}

Für die Homekit Integration ist der Tag [ “Lighting” ] schon eingepflegt, um die Lampe über Alexa steuern zu können muss das Thing an myOpenhab.org übergeben werden. Eine Möglichkeit: Unter Configuration -> Services -> IO ->openHAB Cloud ->Items to Expose das angelegte Thing auswählen. Fertig, fröhliches schalten!

Tuya RGB+W Bulb mit Tasmota flashen

Tuya RGB+W Bulb mit Tasmota flashen

Bei Amazon gab es günstige RGB Lampen, die sich mittels Wifi in das Smart Home einbinden lassen. Allerdings waren diese noch mit Software von Tuya versehen, die aufgrund der Kommunikation zu chinesischen Servern in der Kritik stehen. Also muss der Quatsch runter und Tasmota rauf.

Das kann ganz einfach mit einem Raspberry Pi gemacht werden. Ich habe dazu schnell ein neues Image raufgeschmissen um Konflikten aus dem Weg zu gehen und den Pi über LAN ans Netz angeschlossen, es klappt auch mit einem bestehenden Image, dann müssen aber weitere Punkte beachtet werden.

Nach einem Update des Systems muss die eigentliche Flashsoftware installiert werden und alle abhängigen Pakete ebenfalls, eventuell ist dazu vorher noch git zu installieren.

sudo apt-get install git
git clone https://github.com/ct-Open-Source/tuya-convert
cd tuya-convert
sudo ./install_prereq.sh

Der eigentliche Flashvorgang muss anschließend gestartet werden, dieser stellt ein Wifi “vtrust-flash” her, in das man sich mit einem weiteren einwählen muss.

sudo ./start_flash.sh

Das Gerät selbst muss sich jetzt im Einrichtungsmodus befinden, bei den Lampen erreicht man diesen durch eine bestimmte Ein- und Ausschalt-Kombination, zur Bestätigung blinkt die Lampe mehrmals pro Sekunde auf. Dies wird am Pi bestätigt und das Flashen läuft.

Im nöchsten Schritt muss die neue Software konfiguriert werden. Dazu verbindet man sich mit dem neuen Wifi “Tasmota-XXXX”, und trägt die eigenen Wifi Zugangsdaten ein. Ist die Lampe mit dem eigenen Wifi verbunden, kann man mit der IP über einen Browser darauf zugreifen. Unter “Configuration” -> “Configure Other” sind noch Template Daten einzutragen und MQTT zu aktiveren. Für die Template Daten sind hier allerhand Vorgaben zu finden, für die RBG+W Bulb ist also folgendes einzutragen:

{“NAME”:”Gosund RGB+W”,”GPIO”:[0,0,0,0,40,0,0,0,37,38,39,0,0],”FLAG”:0,”BASE”:18}

Anschließend im Browser ein Firmware Update durchführen und fertig ist das flashen.

Roborock in openHAB integrieren mit iOS

Roborock in openHAB integrieren mit iOS

Roborock über die Mi Home App anmelden. Backup (nicht verschlüsselt!) des iOS Geräts in iTunes machen. Das Backup öffnen (für Win zum Beispiel http://www.imactools.com/iphonebackupviewer/ nutzen). Backup auswählen und Raw Bild (Verzeichnisbaum) anklicken. Links unter AppDomain nach com.xiaomi.home suchen und auswählen. Rechts nach xxxxxxxxxx_mihome.sqlite suchen und auswählen, anschließend diese Datenbank exportieren. Textdatei in einem Editor öffnen zwischen dem ganzen Buchstaben- und Zeichensalat ist der benötigte Token direkt nach dem Namen des Wlan Netzes verborgen und besitzt 96 Zeichen. Diese kopieren und zum Beispiel hier umwandeln lassen.

Input type: Text
Input text: <kopierter Token>
Function: AES
Mode: ECB
Key: 00000000000000000000000000000000 (32 Nullen!), Hex auswählen
> Decrypt!

Der Token ist auf der rechten Seite des decrypted Textes enstanden. Kopieren und ohne Leerzeichen einsetzen.

Backup erstellen Plex Server auf Raspberry Pi

Backup erstellen Plex Server auf Raspberry Pi

Backup, Restore oder Klon vom Plex Server

Backing up your PMS data involves the same steps you’ll need if you were to clone your PMS and take to a new computer, reinstall the operating system, or just want a backup for safe keeping.

Ein Backup machen:

  • Plex Server stoppen
  • als root in der Shell anmelden
  • Backup Image erstellen
  • Das Backup Image kopieren

Ein Backup wieder einspielen:

  • Plex Server neu aufsetzen
  • Den Plex Server einmal starten und stoppen
  • Das Backup einspielen
  • Plex Server starten

Das Backup Image erstellen:

Auf dem System folgendes durchführen:

[chuck@lizum ~.74]$ sudo sh
sh-4.4# cd /var/lib/plexmediaserver
sh-4.4# tar cfz /nas/tmp/PlexBackup.tar.gz ./Library
sh-4.4# ls -la /nas/tmp/PlexBackup.tar.gz 
-rw-r--r--. 1 root root 864050144 Oct 29 15:29 /nas/tmp/PlexBackup.tar.gz
sh-4.4# 

Das Backup Image einspielen:

sh-4.4# sudo sh
sh-4.4# systemctl start plexmediaserver
sh-4.4# systemctl stop plexmediaserver
sh-4.4# cd /var/lib/plexmediaserver
sh-4.4# rm -rf Library
sh-4.4# tar xf /nas/tmp/PlexBackup.tar.gz
sh-4.4# chown -R plex:plex ./Library
sh-4.4# systemctl start plexmediaserver

Ein System klonen:

  • Ein Backup Image erstellen
  • Auf das neue System kopieren
  • Das Backup Image einspielen
  • Die alte Preferences.xml löschen, ansonsten existieren zwei identische Server

Quelle: https://forums.plex.tv/t/linux-tips/276247/7

TRÅDFRI mit openHAB nach Update auf 1.8.25

TRÅDFRI mit openHAB nach Update auf 1.8.25

Ikea hat dem TRÅDFRI  System ein umfangreiches Update verpasst. Dadurch ist leider die bisherige Konfiguration hinfällig, da jetzt eine Identifikation nicht mehr nur über den Sicherheitscode auf der Rückseite des TRÅDFRI Gateways erfolgt. Um dennoch wieder lauffähig zu sein und das ganze als .things im System zu integrieren ist folgendes zu tun.

  1. Aktuelles Thing des Gateways in der PaperUI löschen.
  2. Neustart von openHAB (sudo systemctl restart openhab2.service)
  3. Über die Inbox in der PaperUI das Gateway wieder hinzufügen, eventuell ist die Suche nach TRÅDFRI Geräten nochmal anzuschubsen.
  4. In der PaperUI: Configuration->Things wird der Code von der Rückseite des Gateways beim Thing eingetragen und gespeichert. Die Verbindung ist aufgebaut.
  5. Die neuen Parameter identity und preSharedKey findet man über die Rest API <IP_openHAB_Server>:<Port>/rest/things
  6. Dort einmal nach preSharedKey suchen und die beiden Werte in die tradfri.things mit folgendem neuen Format kopieren:
Bridge tradfri:gateway:gwb07xxxxxxxx [ host=“xxx.xxx.xxx.xxx”, identity=“xxxxxxxxxxxxxxxxxxxxx”, preSharedKey=“xxxxxxxxx” ] {
}

Im Vergleich zur alten tradfri.things fällt sofort auf, dass der Code der Rückseite nicht mehr gebraucht wird.

Bridge tradfri:gateway:gw1 [ host=“xxx.xxx.xxx.xxx”, code=“xxxxxxxxxx” ] {
}

Mit OpenHAB Cloud items über Alexa erreichen (Debug)

Mit OpenHAB Cloud items über Alexa erreichen (Debug)

Mit dem OpenHAB Cloud Connector Addon lassen sich Items über myopenhab.org für weitere Dienste erreichbar machen. Die Einrichtung ist im Addon beschrieben. Um beispielsweise einen Schalter einzupflegen sind folgende Schritte notwendig.

Der Simple Mode unter PaperUI -> Configuration –> System ausstellen. Die ausgewählten Items sind mit einem Tag zu versehen. Welche Tags möglich sind kann im HomeKit Addon geprüft werden.

Switch  item_name  "Name" <switch> (Gruppe, Untergruppe) [ "Switchable" ] {channel="Adresse"}

Die Konfiguration erfolgt am besten in der Dabei conf/services/openhabcloud.cfg wie folgt:

############################## openHAB Cloud Connector #############################

# The URL of the openHAB Cloud service to connect to.
# Optional, default is set to the service offered by the openHAB Foundation
# (https://myopenhab.org/)
baseURL=https://myopenhab.org/

# Defines the mode in which you want to operate the connector.
# Possible values are:
# - notification: Only push notifications are enabled, no remote access is allowed.
# - remote: Push notifications and remote access are enabled.
# Optional, default is 'remote'.
mode=remote

# A comma-separated list of items to be exposed to external services like IFTTT. 
# Events of those items are pushed to the openHAB Cloud and commands received for
# these items from the openHAB Cloud service are accepted and sent to the local bus.
# Optional, default is an empty list.
expose=item_name,item_name2

Auf myopenhab.org/items prüfen, ob Items online sind. Vorher einmal schalten, da sonst kein Update erfolgt. Ist dort alles vorhanden kann der Alexa Skill installiert werden. Dabei ist eine Kontoverknüpfung mit myopenhab.org notwendig. Bei der anschließenden Suche werden hoffentlich alle Items gefunden.

Sollte das nicht klappen, liegt es sehr wahrscheinlich an Umlauten in den Items und deren aktuellen Zuständen. Grundsätzlich sollte in den Bezeichnungen der Items auf ö,ä,ü,ß und weitere Sonderzeichen verzichtet werden. In meinem Fall kommen aber noch weitere Dinge hinzu. Ich greife mit dem Amazon Echo Control Binding zum Beispiel die letzten Kommandos ab. Da kommen, genauso wie im Astro Binding, ebenfalls Umlaute vor. Sobald ich diese Items ausblende (ich benenne einfach die Dateiendung um) läuft es. Überprüfen kann man das mittels der Rest API unter https://myopenhab.org/rest/items/. Da müssen auch die angelegten Tags auffindbar sein.

Weiterer Lesestoff: https://community.openhab.org/t/alexa-smart-home-skill-no-devices-found-solution/31953

Serverstatus in WordPress ausgeben

Serverstatus in WordPress ausgeben

Um ein paar Informationen über meine lokalen Server auszuspucken habe ich folgendes Idee: Ein bash-Skript fragt die Informationen lokal ab und macht einen Upload zum Blog. Abzufragen sind erstmal grundsätzlich

ping
uname -a
cat /sys/class/thermal/thermal_zone0/temp
cat /proc/uptime

um Onlinestatus, Hostnamen, Kernel, Release, Architektur der CPU, CPU Temperatur und Laufzeit zu erfahren.

Als erstes ist es ganz hilfreich überhaupt zu prüfen, ob der abzufragende Server vorhanden ist.

ping -q -c1 $ip > /dev/null
if [ $? -eq 0 ]
then
    status1="✓"
else
    status1="✘"
fi

Anschließend kann auch ein möglicher Fernzugriff erfolgen, um mit ‘uname‘ grundlegende Informationen abzugreifen.

host1=$(uname -a)
kernelname1="$(cut -d' ' -f1 <<<$host1)"
release1="$(cut -d' ' -f3 <<<$host1)"
machine1="$(cut -d' ' -f11 <<<$host1)"
host1="$(cut -d' ' -f2 <<<$host1)"

Die Ausgabe des Temperaturwertes ist in Tausendstel °C, also ist eine kurze Rechnung zur Anpassung nötig.

temp1=$(cat /sys/class/thermal/thermal_zone0/temp)
temp1="$(($temp1/1000))°C"

Die Ausgabe der Laufzeit gibt zunächst zwei Werte aus, einmal die eigentliche Laufzeit in Sekunden und der Anteil daran im Idle. Also ist nur der erste Wert zur Berechnung notwendig. Jetzt muss nur noch das Sekundenwirrwarr in eine sinnvolle Darstellung umgerechnet werden (Tage, Stunden, Minuten, Sekunden). Die Sekunden berechnen sich aus der Gesamtzahl der Sekunden mit dem Modulo 60. Für die Minuten wird die Gesamtzahl der Sekunden mit ‘/60‘ in Minuten umgerechnet (Ganzzahl!!, der “Überschuss” fällt weg und ist ja auch schon zuvor in den Sekunden berechnet worden) und anschließend mit ‘%60‘ der Rest ausgegeben, der keine ganze Stunde mehr ergibt. Ähnliches erfolgt bei der Stunden- und Tagesberechnung. In der Ausgabe gibt es noch eine kleinere Spielerei, um Tage auszublenden, wenn keine vorhanden sind.

uptime1=$(cat /proc/uptime)
uptime1=${uptime1%%.*};
secs=$((${uptime1}%60))
mins=$((${uptime1}/60%60))
hours=$((${uptime1}/3600%24))
days=$((${uptime1}/86400))
if [ "${days}" -eq "0" ]
then
    uptime="${hours}h${mins}m${secs}s"
else
    uptime="${days}d${hours}h${mins}m${secs}s"
fi

Die bereitgestellten Informationen speichere ich im aktuellen Verzeichnis (Dateiname besteht aus dem Hostanteil der IP Adresse, eventuell vorehandene Leerzeichen werden noch entfernt) und lade diese hoch.

workdir=$(pwd);
workname=$(hostname -I)
worknameshort=$(cut -d'.' -f4 <<<$workname)
worknameshort=`echo "$worknameshort" | sed "s/ //g"`
echo $host > $workdir/$worknameshort.js
echo $status >> $workdir/$worknameshort.js
echo $kernelname >> $workdir/$worknameshort.js
echo $release >> $workdir/$worknameshort.js
echo $machine >> $workdir/$worknameshort.js
echo $temp >> $workdir/$worknameshort.js
echo $uptime >> $workdir/$worknameshort.js
# upload $data.js to webspace
curl -T $worknameshort.js -u user:pass ftp://serveradress.net/
# clean up the mess
rm $workdir/$worknameshort.js

Jetzt tummeln sich etliche Geräte im Netzwerk, das Skript kann alle nach einander abarbeiten. Dazu ist ein kleines Array mit IP Adressen notwendig.

ip[1]=192.168.178.5  #raspberry
ip[2]=192.168.178.17 #berrycam
ip[3]=192.168.178.28 #plexberry
ip[4]=192.168.178.39 #kitchentable
ip[5]=192.168.178.74 #openhab
ip[6]=192.186.178.80 #pihole
ip[7]=192.168.178.99 #automotiverocketlauncher

Daraus kann dann mittels ‘ ${ip[@]} ‘ innerhalb einer Schleife die jeweilige IP genutzt werden. Natürlich muss zunächst mal der ECDSA Key akzeptiert werden (Alternativ kann auch nach ‘sshpass -p ${passwd} ssh‘ mit ‘-o StrictHostKeyChecking=no‘ dieses umgangen werden). Um eine Anmeldung am Remote ohne Passworteingabe zu bewerkstelligen installiert man das Paket sshpass. Die bash ruft ein Subskript ‘remote.sh‘ auf in der alles aufbereitet wird. Falls kein Zugriff erfolgen kann (ping erfolglos) schreibt das Skript trotzdem einen Eintrag. Daten dazu auf Git.

for adress in ${ip[@]} ; do
    ping -q -c1 ${adress} > /dev/null
    if [ $? -eq 0 ]
    then
        sshpass -p ${passwd} ssh -o StrictHostKeyChecking=no ${user}@${adress} 'bash -s' < /home/pi/statuspi/remote.sh
    status="✘";
    workdir=$(pwd);
    workname=${adress};
    worknameshort=$(cut -d'.' -f4<<<$workname)
    worknameshort=`echo "$worknameshort" | sed "s/ //g"`
    echo $adress > $workdir/$worknameshort.js
    echo $status >> $workdir/$worknameshort.js
    # upload $data.js to webspace
    curl -T $worknameshort.js -u user:pass ftp://serveradress.net/
    # clean up the mess
    rm $workdir/$worknameshort.js
    fi
done

Das Skript selbst muss alle x Minuten ausgeführt werden, um einen ständigen Upload zu garantieren. Für mich reicht da ein Abstand von 60 Minuten. Also muss folgendes mit crontab /e als cronjob eingetragen werden.

1 * * * * /home/pi/statuspi/statuspi.sh

Um das ganze in WordPress anzuzeigen ist folgender Code nötig. Ich habe die Dateinamen einfach in einem Array übernommen, das geht natürlich anders. Mit dem Plugin WP H-PHP Widget lässt sich das ganze problemlos einfügen.

<?php
$name = array("5.js","17.js","28.js","39.js","74.js","80.js","9.js");
$dir = "http://serveradress.net/";
$text = array("Name","Status","System","Release","Architektur","Temperatur","Laufzeit");

for($k=0;$k < count($name); $k++){
    $data = file($dir . $name[$k]);
    echo substr($data[0], 0, -1);
    echo " ist ";
    echo $data[1];
    echo "<br />";
    for($i=2;$i < count($data); $i++){
        echo $text[$i];
        echo ": ";
        echo $data[$i];
        echo "<br />";
    }
    echo "<hr>";
}
?>
Pan-Tilt Servos am Raspberry Pi mit motionEye

Pan-Tilt Servos am Raspberry Pi mit motionEye

Bauteile:

Anschluss der Servos: Vertikal an 0, horizontal an 1. Ground (braun) an Außenseite der Platine

motionEye auflegen: Entweder inklusive Homekit oder die einfache und schnelle Variante (Kamera aktivieren, wenn Raspberry Camera Modul verwendet wird!):

sudo apt-get install ffmpeg
sudo modprobe bcm2835-v4l2 
sudo apt-get install libmariadbclient18 libpq5
wget https://github.com/Motion-Project/motion/releases/download/release-4.1.1/pi_stretch_motion_4.1.1-1_armhf.deb
sudo dpkg -i pi_stretch_motion_4.1.1-1_armhf.deb
sudo apt-get install python-pip python-dev libssl-dev libcurl4-openssl-dev libjpeg-dev libz-dev
sudo pip install motioneye
sudo mkdir -p /etc/motioneye
sudo cp /usr/local/share/motioneye/extra/motioneye.conf.sample /etc/motioneye/motioneye.conf
sudo mkdir -p /var/lib/motioneye
sudo cp /usr/local/share/motioneye/extra/motioneye.systemd-unit-local /etc/systemd/system/motioneye.service
sudo systemctl daemon-reload
sudo systemctl enable motioneye
sudo systemctl start motioneye

I2C in raspi-config aktivieren

sudo apt-get install python-smbus
sudo apt-get install i2c-tools
sudo i2cdetect -y 1

Adafruit Python PCA9685 installieren

sudo apt-get install git build-essential python-dev
git clone https://github.com/adafruit/Adafruit_Python_PCA9685.git
cd Adafruit_Python_PCA9685
sudo python setup.py install

Sollte es zu Problemen bei ausführen von setup.py kommen (“File is not a zip file“) liegt das am Inhalt “301 moved permanently”. Das ganze mit wget
https://pypi.python.org/packages/source/s/setuptools/setuptools-3.5.1.zip direkt in das erstellte Verzeichnis Adafruit_Python_PCA9685 laden hilft.
Bei weiteren Problemen folgende Pakete händisch installieren: spidev, adafruit-pureio, Adafruit-GPIO, adafruit-pca9685

sudo pip3 install spidev
sudo pip3 install Adafruit-PureIO
sudo pip3 install Adafruit-GPIO
sudo pip3 install Adafruit-PCA9685

oder auf

sudo pip install adafruit-pca9685

zurückgreifen.

Die passenden Skripte für die Steuerung der Tilt-Shift Servos gibt es hier:

git clone https://github.com/luetzel/motion_eye_servo_action.git

Die Dateien einmal in /etc/motioneye kopieren und ausführbar machen. Beispiel:

sudo chmod 755 /etc/motioneye/up_1

Die ‘1‘ in den Dateien bezieht sich auf die Kamera. Sollten mehrere in motionEye vorhanden sein, bitte anpassen.
Die Einträge ‘servo_min‘ und ‘servo_max‘  in left_1, right_1, down_1 und up_1 sind noch anzupassen an die gegebenen Endpositionen der beiden Servos (vertikal von 225 bis 575, horizontal von 150 bis 625 bei mir).

In motionEye (http://ipdesraspberry:8765, Login: admin oder user ohne Passwort) Kamera als Typ Local MMAL Camera mit VideoCore Camera anlegen. Gute und flüssige Übertragung klappt zum Beispiel bei einer Auflösung von 1024×600.

Quelle: https://github.com/luetzel/motion_eye_servo_action, https://raspberryblog.de/?p=2209 https://github.com/luetzel/motion_eye_servo_action

Die Plexdatenbank exportieren

Die Plexdatenbank exportieren

Um die Datenbank von Plex auszulesen gibt es verschiedene Wege. Mit dem ExportTools Plugin lassen sich fix csv beziehungsweise xls Dateien erstellen. Man muss nur darauf achten dem Ausgabeverzeichnis die richtigen Rechte zu geben, damit der User plex auch zugreifen kann.

Um gleich eine webbasierte Darstellung zu erhalten kann momentan noch das nicht mehr weiterentwickelte Plex Export genutzt werden. Durch lokalen Zugriff (oder auch auf dem Plexserver selbst) kann ein kompletter Export erfolgen.

Durch ein paar schnelle Anpassungen in der cli.php, plex.js und index.html ist eine eingedeutschte Darstellung möglich.

Das Skript ist auf einem lokalen Web-Server mit ‘php /pfad/zum/www/Verzeichnis/cli.php -plex-url=http://plexIP:32400 -token=deinToken‘ auszuführen. Den eigenen Token findet man so.

Raspberry Pi Camera in Homekit einbinden

Raspberry Pi Camera in Homekit einbinden

UPDATE 19.01.2020:
Das Internet vergisst nie und doch fehlt irgendwann mal was. In unzähligen Anleitungen rund um MotionEye wird für ältere Raspberry Pis mit armv6 (erste Generation, Zero und Zero W) auf eine angepasste Version von NodeJS verwiesen. Diese lag lange Zeit auf http://node-arm.herokuapp.com/node_latest_armhf.deb bereit, heute aber leider nicht mehr. Danke Daniel für diesen Hinweis und auch für die Anmerkung, dass das Paket python-pillow noch fehlte. Um in das Vergnügen der Kamera in Homekit zu kommen, habe ich das Script angepasst, es läuft jetzt auch wieder für armv6 Geräte trotz Warnmeldungen durch und läuft. Ein weiterer Hinweis: Der beschriebene Ablauf ist für Raspbian Stretch und für Buster noch nicht getestet, bzw. übertragen worden. Vielleicht hat ja mal jemand eine Rückmeldung ob es da durchläuft?

Der Weg zur Tür ist etwas weit, um mal eben zu schauen was auf dem Hof los ist, habe ich die Idee einen Raspberry mit eigener Kamera einzusetzen. Viele andere Ansätze haben leider nicht wirklich zum Erfolg geführt. Und ganz nebenbei ist auch noch ein Abgreifen des Signals über den Browser möglich. Das ganze läuft mit HAP-NodJS Framework und motionEye.

Notwendig sind folgende Sachen:

  • Raspberry Pi
  • SD Karte mit einem aktuellen Raspbian
  • Netzteil
  • Raspberry Pi Camera Modul mit Flachbandkabel

Schritt 1

Raspberry Pi einrichten:
ssh, wifi, Kamera aktivieren, Memory Split auf mindestens 128MB

sudo modprobe bcm2835-v4l2
sudo reboot

Schritt 2

Installation:
Folgende Befehle abarbeiten:

cd /home/pi
sudo apt-get update
sudo apt-get remove node nodejs nodejs-legacy -y
sudo apt-get install git-core libnss-mdns libavahi-compat-libdnssd-dev -y
uname -m # bei armv6l folgendes
    wget https://nodejs.org/dist/v6.2.1/node-v6.2.1-linux-armv6l.tar.gz
    sudo mv node-v6.2.1-linux-armv6l.tar.gz /opt
    cd /opt
    sudo tar -xzf node-v6.2.1-linux-armv6l.tar.gz
    sudo mv node-v6.2.1-linux-armv6l nodejs
    sudo rm node-v6.2.1-linux-armv6l.tar.gz
    sudo ln -s /opt/nodejs/bin/node /usr/bin/node
    sudo ln -s /opt/nodejs/bin/npm /usr/bin/npm
# ansonsten
    sudo curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
    sudo apt-get install nodejs -y
    sudo npm install -g n
    sudo rm /usr/local/bin/node
    sudo rm -rf /usr/local/lib/node_modules/npm
    sudo n prune
    sudo n 8.2.1
# weiter im Text
sudo apt-get install gcc g++ make -y
sudo npm install -g node-gyp
sudo git clone --branch v0.4.27 https://github.com/KhaosT/HAP-NodeJS.git
cd HAP-NodeJS/
sudo rm -rf accessories/AirConditioner_accessory.js
sudo npm install

Schritt 3

motionEye installieren, dazu folgendes ausführen:

curl -sSL goo.gl/68K6gS | bash

Oder folgende Befehle abarbeiten:

cd /home/pi
sudo apt-get remove libavcodec-extra-57 libavformat57 libavresample2 libavutil55
sudo apt-get install libavutil55 libavformat57 libswscale3 -y
sudo apt-get install python-pip python-dev curl libssl-dev libcurl4-openssl-dev libmariadbclient18 libpq5 libjpeg8-dev mysql-common ffmpeg -y
sudo apt-get install python-pillow -y
sudo wget "https://github.com/Motion-Project/motion/releases/download/release-4.1/pi_stretch_motion_4.1-1_armhf.deb" #für Raspbian Stretch!
sudo dpkg -i pi_stretch_motion_4.1-1_armhf.deb
sudo rm -rf pi_stretch_motion_4.1-1_armhf.deb
sudo pip install motioneye
sudo mkdir -p /etc/motioneye
sudo cp /usr/local/share/motioneye/extra/motioneye.conf.sample /etc/motioneye/motioneye.conf
sudo mkdir -p /var/lib/motioneye
sudo cp /usr/local/share/motioneye/extra/motioneye.systemd-unit-local /etc/systemd/system/motioneye.service
sudo systemctl daemon-reload
sudo systemctl enable motioneye
sudo systemctl start motioneye
cd /home/pi/HAP-NodeJS
sudo npm install
sudo npm install node-cmd
sudo systemctl restart motioneye
cd /home/pi/HAP-NodeJS/lib/
sudo mv Camera.js OriginalCamera.bak
sudo wget https://goo.gl/VZZMYV -O Camera.js

Schritt 4

Kamera einrichten:

Im Browser die url des Raspberry Pis mit dem Port 8765 aufrufen, Anmeldung erfolgt als admin, ohne Passwort (Kann später geändert werden). Die Einrichtung der neuen Kamera erfolgt mit folgenden Parametern:

Camera type: Local MMAL Camera
Camera: VideoCore Camera

Schritt 5

Um das ganze zu automatisieren ist ein Autostart einzurichten:

cd ~
sudo npm install forever -g
sudo nano /start

Dort folgende Zeilen einsetzen und speichern:

sudo forever stopall
cd /home/pi/HAP-NodeJS
sudo forever start CameraCore.js

Anschließend folgendes ausführen:

sudo chmod +x /start
sudo nano /etc/rc.local

Und dort vor exit 0 folgendes einsetzen und speichern:

sudo sh /start

Abschließend einmal durchstarten. Jetzt kann die Kamera in Homekit eingebunden werden. Dazu in der Home App auf ‘+‘, ‘Gerät hinzufügen‘, ‘Code fehlt bzw. kann nicht gescannt werden?‘, Kamera auswählen und mit dem Code 031-45-154 einbinden. Fertig!

Quelle:  https://raspberrytips.nl/raspberry-pi-homekit-camera/

Update von motionEye durch:

sudo pip install motioneye --upgrade
sudo systemctl restart motioneye

Als Anhang die modifizierte Camera.js (als Download im Skript oben enthalten):

'use strict';

var debug = require('debug')('Camera');
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var clone = require('./util/clone').clone;
var uuid = require('./util/uuid');
var Service = require('./Service').Service;
var Characteristic = require('./Characteristic').Characteristic;
var StreamController = require('./StreamController').StreamController;
var HomeKitTypes = require('./gen/HomeKitTypes');
var fs = require('fs');
var ip = require('ip');
var spawn = require('child_process').spawn;

module.exports = {
  Camera: Camera
};

function Camera() {
  this.services = [];
  this.streamControllers = [];

  this.pendingSessions = {};
  this.ongoingSessions = {};

  let options = {
    proxy: false, // Requires RTP/RTCP MUX Proxy
    disable_audio_proxy: false, // If proxy = true, you can opt out audio proxy via this
    srtp: true, // Supports SRTP AES_CM_128_HMAC_SHA1_80 encryption
    video: {
      resolutions: [
        [1280, 720, 30],
        [320, 240, 15]
      ],
      codec: {
        profiles: [0, 1, 2], // Enum, please refer StreamController.VideoCodecParamProfileIDTypes
        levels: [0, 1, 2] // Enum, please refer StreamController.VideoCodecParamLevelTypes
      }
    },
    audio: {
      comfort_noise: false,
      codecs: [
        {
          type: "OPUS", // Audio Codec
          samplerate: 24 // 8, 16, 24 KHz
        },
        {
          type: "AAC-eld",
          samplerate: 16
        }
      ]
    }
  }

  this.createCameraControlService();
  this._createStreamControllers(2, options);
}

Camera.prototype.handleSnapshotRequest = function(request, callback) {
  // Image request: {width: number, height: number}
  // Please override this and invoke callback(error, image buffer) when the snapshot is ready

  let resolution = request.width + 'x' + request.height;
  var ffmpegImageSource = "-i http://localhost:8765/picture/1/current/"
  var imageSource = ffmpegImageSource !== undefined ? ffmpegImageSource : this.ffmpegSource;
  let ffmpeg = spawn('ffmpeg', (imageSource + ' -t 1 -s '+ '1280x720' + ' -f image2 -').split(' '), {env: process.env});
  var imageBuffer = Buffer(0);

  ffmpeg.stdout.on('data', function(data) {
    imageBuffer = Buffer.concat([imageBuffer, data]);
  });
  ffmpeg.on('close', function(code) {
    callback(undefined, imageBuffer);
  });
}

Camera.prototype.handleCloseConnection = function(connectionID) {
  this.streamControllers.forEach(function(controller) {
    controller.handleCloseConnection(connectionID);
  });
}

Camera.prototype.prepareStream = function(request, callback) {
  // Invoked when iOS device requires stream

  var sessionInfo = {};

  let sessionID = request["sessionID"];
  let targetAddress = request["targetAddress"];

  sessionInfo["address"] = targetAddress;

  var response = {};

  let
videoInfo = request["video"];
  if (videoInfo) {
    let targetPort = videoInfo["port"];
    let srtp_key = videoInfo["srtp_key"];
    let srtp_salt = videoInfo["srtp_salt"];

    let videoResp = {
      port: targetPort,
      ssrc: 1,
      srtp_key: srtp_key,
      srtp_salt: srtp_salt
    };

    response["video"] = videoResp;

    sessionInfo["video_port"] = targetPort;
    sessionInfo["video_srtp"] = Buffer.concat([srtp_key, srtp_salt]);
    sessionInfo["video_ssrc"] = 1;
  }

  let audioInfo = request["audio"];
  if (audioInfo) {
    let targetPort = audioInfo["port"];
    let srtp_key = audioInfo["srtp_key"];
    let srtp_salt = audioInfo["srtp_salt"];

    let audioResp = {
      port: targetPort,
      ssrc: 1,
      srtp_key: srtp_key,
      srtp_salt: srtp_salt
    };

    response["audio"] = audioResp;

    sessionInfo["audio_port"] = targetPort;
    sessionInfo["audio_srtp"] = Buffer.concat([srtp_key, srtp_salt]);
    sessionInfo["audio_ssrc"] = 1;
  }

  let currentAddress = ip.address();
  var addressResp = {
    address: currentAddress
  };

  if (ip.isV4Format(currentAddress)) {
    addressResp["type"] = "v4";
  } else {
    addressResp["type"] = "v6";
  }

  response["address"] = addressResp;
  this.pendingSessions[uuid.unparse(sessionID)] = sessionInfo;

  callback(response);
}

Camera.prototype.handleStreamRequest = function(request) {
  // Invoked when iOS device asks stream to start/stop/reconfigure
  var sessionID = request["sessionID"];
  var requestType = request["type"];
  if (sessionID) {
    let sessionIdentifier = uuid.unparse(sessionID);

    if (requestType == "start") {
      var sessionInfo = this.pendingSessions[sessionIdentifier];
      if (sessionInfo) {
        var width = 1024;
        var height = 600;
        var fps = 30;
        var bitrate = 250;

        let videoInfo = request["video"];
        if (videoInfo) {
          width = videoInfo["width"];
          height = videoInfo["height"];

          let expectedFPS = videoInfo["fps"];
          if (expectedFPS < fps) {
            fps = expectedFPS;
          }

//          bitrate = videoInfo["max_bit_rate"];
        }

        let targetAddress = sessionInfo["address"];
        let targetVideoPort = sessionInfo["video_port"];
        let videoKey = sessionInfo["video_srtp"];

        let ffmpegCommand = '-re -i http://localhost:8081 -threads 0 -vcodec h264_omx -an -pix_fmt yuv420p -r '+ fps +' -f rawvideo -tune zerolatency -vf scale='+ width +':'+ height +' -b:v '+ bitrate +'k -bufsize '+ bitrate +'k -payload_type 99 -ssrc 1 -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params '+videoKey.toString('base64')+' srtp://'+targetAddress+':'+targetVideoPort+'?rtcpport='+targetVideoPort+'&localrtcpport='+targetVideoPort+'&pkt_size=1378';
        let ffmpeg = spawn('ffmpeg', ffmpegCommand.split(' '), {env: process.env});
        this.ongoingSessions[sessionIdentifier] = ffmpeg;
      }

      delete this.pendingSessions[sessionIdentifier];
    } else if (requestType == "stop") {
      var ffmpegProcess = this.ongoingSessions[sessionIdentifier];
      if (ffmpegProcess) {
        ffmpegProcess.kill('SIGKILL');
      }

      delete this.ongoingSessions[sessionIdentifier];
    }
  }
}

Camera.prototype.createCameraControlService = function() {
  var controlService = new Service.CameraControl();

  // Developer can add control characteristics like rotation, night vision at here.

  this.services.push(controlService);
}

// Private

Camera.prototype._createStreamControllers = function(maxStreams, options) {
  let self = this;

  for (var i = 0; i < maxStreams; i++) {
    var streamController = new StreamController(i, options, self);

    self.services.push(streamController.service);
    self.streamControllers.push(streamController);
  }
}