USB Devices im ioBroker Docker Container nutzen

Wie bekomme ich im ioBroker Container Zugriff auf ein USB-Device am Docker Host?

Eine Frage die mir in jüngster Vergangenheit öfter gestellt wurde und eigentlich gar nicht so kompliziert zu beantworten ist. 

In Bezug auf mein ioBroker-Docker-Image habe ich bereits in Version 4.1.0 eine Umgebungsvariable eingeführt, die dafür sorgt, dass zumindest die Rechte innerhalb des Containers immer passen sollten und letztendlich auch „reboot-sicher“ sind. Dazu aber gleich mehr.

Zuvor von mir noch ein kleiner „Best Practice“-Hinweis, über den ihr zumindest einmal nachgedacht haben solltet. 🙂

Bei mir verhält es sich nämlich so, dass meine DiskStation (also mein Docker Host) nicht gerade optimal platziert ist um USB-Devices wie einen Zigbee-Stick oder einen rflink zu betreiben. Daher habe ich schon früh eine andere Lösung gesucht und diese im Multihost-Modus von ioBroker gefunden.

Dabei nutze ich heute einen Raspberry Pi 2B als Multihost Slave und habe dort nur Adapter installiert, die zum Betrieb meiner USB-Geräte notwendig sind. In Sachen Leistung ist der Pi dafür absolut ausreichend und ich habe den Vorteil dass ich den kleinen Freund direkt mittig im Haus, unter der Treppe, also optimal für Sende- und Empfangsaufgaben, platzieren konnte.

Im Falle einer DiskStation als Docker Host kommt außerdem hinzu, dass leider nicht alle USB Devices problemlos an den DiskStations laufen. Aber das ist eine andere Geschichte. 

Nun gut, soweit dies als kleinen Denkanstoß. Kommen wir also nun dazu unser USB Device in den Container durch zu reichen.

Schritt 1 - Device auf dem Host identifizieren

Damit wir das Device in den Container einbinden können müssen wir erst einmal identifizieren unter welchem Pfad es auf unserem Host zur Verfügung steht und angesprochen werden kann.

Die einfachste Lösung das zu erledigen ist das Ausführen des Befehls „dmesg“ unmittelbar nach dem Anstecken. Ziemlich am Ende sollte sich erkennen lassen, dass ein USB-Device angesteckt worden ist. Bei mir sieht das so aus:

An der Ausgabe ist zu erkennen, dass mein USB-Device als ttyACM0 auf dem Host zur Verfügung steht.

Eine andere Möglichkeit wäre die Ausführung des Befehls lsusb. Das sieht bei mir dann so aus:

Die Auflistung zeigt alle meine USB-Devices. Da ich weiß, das mein Zigbee-Stick mit der Kennung „Texas Instruments…“ kommt können wir der Ausgabe entnehmen, dass er an den USB-Port „usb2“, genauer sogar an „2-1“ gesteckt wurde. Mit einem ll /sys/class/tty | grep usb2 oder ll /sys/class/tty | grep 2-1 können wir nun ebenfalls ermitteln dass mein Stick unter dem Alias ttyACM0 zu finden ist:

Aus diesen Erkenntnissen lässt sich schlussfolgern, dass unser Device auf dem Host unter /dev/ttyACM0 verfügbar ist.

Es geht aber auch anders...

Die beiden bisher genannten Wege zur Ermittlung des Pfades unter dem das Device ansprechbar ist, sind Beispiele von einer DiskStation. Auf anderen Systemen, wie z.B. einem Rasopberry Pi, gibt es noch eine Andere Möglichkeit. Hier ist es nämlich möglich ein Device auch „by-id“ an zu sprechen. Dazu kann man per ls -al /dev/serial/by-id/ so etwas auslesen:

Auch hier ist wieder zu erkennen, dass das Device usb-Texas_Instruments…  als tty/ACM0 zur Verfügung steht. Doch was viel besser ist ist die Erkenntnis, dass das Device auch über den Pfad /dev/serial/by-id/usb-Texas_Instruments_[...] ansprechbar ist. Der Voreil dieser Variante ist, dass es sich bei dem Pfad um den echten Namen (ID) und nicht um einen Alias wie ttyACM0 handelt. Das wird vor allem dann interessant wenn man, wie ich, nicht nur ein USB Device durchreichen möchte. Denn es kann durchaus passieren, dass nach einem Reboot des Hosts der Alias plötzlich einem anderen USB device zugeordnet wird weil die verschiedenen Devices vom Betriebssystem in einer anderen Reihenfolge erkannt wurden. 

Kurzum: Es empfiehlt sich bei mehreren USB-Devices am Docker Host den „by-id“-Pfad zu verwenden und nicht den Alias.   

Es gibt natürlich noch andere Wege an die Information zum Device-Pfad zu kommen. Die Möglichkeiten können sich von System zu System unterscheiden. Google spuckt da echt eine Menge aus. Für uns soll das hier aber erstmal genügen.

Schritt 2 - Erforderliche Konfigurationseinstellungen im Container (via Portainer)

Damit unser USB-Device später im Container zur Verfügung steht müssen wir bei der Erstellung des Containers zwei Konfigurationen berücksichtigen. Zum Einen müssen wir das Device in den Container durchreichen, zum Anderen müssen wir dafür Sorge tragen dass unser ioBroker später auch auf das Device zugreifen kann bzw. darf.

Zuerst reichen wir also das Device in den Container durch. Dazu werfen wir beim Erstellen des Containers über Portainer (siehe dazu auch das Tutorial zum Erstellen des Containers) einen Blick auf die „Advanced container settings“, genauer in den Bereich „Runtime & Resources“. Dort gibt es einen Punkt „Devices“. Über „add device“ können wir ein Device hinzufügen

Einzugeben ist hier unter „host“ der zuvor ermittelte Device-Pfad. Bei Verwendung des Alias-Pfads (also z.B. /dev/ttyACM0) empfehle ich das Device auch innerhalb des Container unter dem gleichen Pfad verfügbar zu machen. Das Ganze sieht dann in etwa so aus:

Für den Fall, dass wir den eindeutigen, langen „by-id“-Pfad verwenden wollen, empfiehlt es sich im Container einen Alias zu verwenden. Möglich wäre z.B. /dev/serial/by-id/zigbee für einen Zigbee-Stick.

Wenn unser Device in einen ioBroker Container durchgereicht wird, sollten wir nun noch sicherstellen, dass unser ioBroker später auch auf das Device zugreifen darf. Dazu habe ich, wie eingangs schon angesprochen, seit der Version 4.1.0 des Docker Images eine Umgebungsvariable (ENV, mehr Infos in der Readme auf Github) eingeführt. Die Variable heißt „USBDEVICES“ und wird mit dem Pfad gefüllt, unter dem das Device innerhalb des Containers erreichbar ist. In meinen Beispielen wäre das also /dev/ttyACM0 oder /dev/serial/by-id/zigbee. Das Ganze passiert ebenfalls unter den „Advanced container settings“ im Punkt „Env“ und sieht dann in etwa so aus:

Schritt 3 - Überprüfung

Nach dem Start des Containers sollte das USB-Device nun innerhalb des Containers zur Verfügung stehen. Prüfen lässt sich das recht einfach über ein ls -al /dev/* bzw. ls -al /dev/serial/by-id/*
Hier muss nun unser Device auftauchen und die Berechtigungen entsprechend für die Gruppe „dialout“ gesetzt sein:

Das sollte es dann auch schon gewesen sein. Jetzt müsst ihr nur noch den entsprechenden Adapter installieren und euer Device in der Adapter-Konfiguration entsprechend angeben.

Das solle es zu diesem Thema erst einmal gewesen sein. Bei Fragen und Anregungen nutzt gerne die Kommentare oder kontaktiert mich über einen der öffentlichen Kanäle wie z.B. über das ioBroker Forum oder den ioBroker Discord Channel.

Grundsätzlich biete ich keinen persönlichen Support per Messenger bzw. E-Mail an. Fragen sollten meiner Meinung nach immer öffentlich gestellt und beantwortet werden, damit auch andere User mit der selben Frage Zugriff auf die Antworten bekommen. 🙂 Falls ihr mal irgendwo keine Antwort bekommt, nutzt gerne das Kontaktformular und macht mich auf euren Kommentar, Post, Beitrag oder Thread aufmerksam! Danke.

MfG,
André

Änderungshistorie

2020-09-15 Überarbeitung und Umzug von buanet.de/tutorials zu smarthome.buanet.de. Ergänzung um Möglichkeit zur Einbindung über /serial/by-id/* (Nicht möglich bei Synology DiskStations).