Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. ioBroker Allgemein
    4. RTSP Stream Kamera Aufnahme beenden oder Zeitlich begrenzen.

    NEWS

    • Neues Video "KI im Smart Home" - ioBroker plus n8n

    • Neues Video über Aliase, virtuelle Geräte und Kategorien

    • Wir empfehlen: Node.js 22.x

    RTSP Stream Kamera Aufnahme beenden oder Zeitlich begrenzen.

    This topic has been deleted. Only users with topic management privileges can see it.
    • Yoda
      Yoda last edited by Yoda

      Hallo zusammen,

      das Thema hatte ich schon mal, leider aber keine Lösung - vielleicht hat ja nun jemand einen Tip für mich.

      ich verwende in einem Blocky einen RTSP Befehl der nach Trigger mein Video auf dem Raspi für 60 sec. speichert.
      Dieses klappt auch meistens so wie es soll.

      Nur hin und wieder wird die Videoaufnahme nicht nach 60 sec. beendet und läuft und läuft... unkontrolliert weiter.
      Teilweise laufen auch mehrerer Aufnahmen (Dateien) gleichzeitig...

      Das der Code mehrfach getriggert wird habe ich lange beobachtet und kann es ausschließen.

      Hierdurch habe ich teilweise sehr Große Video Dateien in giga Bereich, die sich auch nicht abspielen lassen.
      Eine Ursache für dieses Verhalten konnte ich noch nicht herausfinden.

      Der Kamera Code zur Aufnahme lautet:

      openRTSP -w 1280 -h 960 -4 -d 45 -t -b 100000 rtsp://Nuter:Paswort@192.168.xxx.xxx:xxxx/11> /home/verzeichnisname/xx/Hof-date +%Y%m%d%H%M.mp4
      

      Der wert Teil -t -b 100000 im Code sagt aus, das das Video nach 1 Minute beendet wird.

      Unteres habe ich im Netz gefunden, wüsste nun aber nicht wie ich es im Code einsetzten könnte.
      Vielleicht findet sich ja jemand der sich damit auskennt?

      Angenommen, wir möchten täglich um 21:00 Uhr mit der Aufnahme beginnen und um 8:00 Uhr enden, kann dies unten jeweils in cron definiert werden.
      0 21 * * * <enter full path to the script here>
      0 8 * * * killall -9 ffmpeg]

      Hat da wer eine Idee für mich?
      LG Yoda

      hydrotec 1 Reply Last reply Reply Quote 0
      • hydrotec
        hydrotec @Yoda last edited by

        @yoda

        Hallo Yoda,
        ein ähnliches Problem hatte ich auch schon einmal, bei zwei Kameras.
        Beide Kameras reagierten auf unterschiedliche Trigger,
        und der Stream sollte dementsprechend bei wiederholtem Trigger automatisch verlängert werden.
        Hatte es dann so gelöst, das wenn ein Stream gestartet wurde, die zugehörige PID des Programms (ffmpg)
        in einen Parameter weggeschrieben wird, und mit dieser PID konnte ich dann den Stream verlängern, oder stoppen,
        ohne das der Stream der zweiten Kamera berührt wurde.

        Wenn du möchtest, kann ich dir den Programmcode zukommen lassen.
        Der ganze Code ist allerdings noch aus meinen FHEM Zeiten.
        (heißt, FHEM und System Befehle in Perl geschrieben)

        Gruß, Karsten

        Yoda 1 Reply Last reply Reply Quote 0
        • Yoda
          Yoda @hydrotec last edited by

          @hydrotec

          Hallo Hydrotec,
          Danke für deine Antwort.
          Aber die mögliche Umsetzung hört sich dann doch ein wenig zu kompliziert für mich an.
          LG
          Yoda

          hydrotec 1 Reply Last reply Reply Quote 0
          • hydrotec
            hydrotec @Yoda last edited by

            @yoda

            jeder fängt einmal an 😉
            Hört sich komplizierter an, wie es ist.

            Hier mal ein kurzer Auszug, wie ich es, in dem anderen Forum, beschrieben hatte.
            Eventuell kannst du ja das Grundprinzip in blockly umsetzen.


            !!! Den hier gezeigten Code keinenfalls in ioBroker verwenden. !!!
            (dient nur zur Veranschaulichung)

            Ok, dann versuche ich mal die Funktion zu beschreiben.
            Sollte etwas unklar sein, Rückfragen sind erwünscht.
            
            Zuerst braucht man ein device welches das motion der Kamera erkennt, um darauf reagieren zu können.
            Vermutlich wird es bei den meisten Kameras über MQTT/MQTT2 umgesetzt.
            Prinzipiell ist es egal, man benötigt ja nur das event.
            
            Danach noch ein DOIF, welches auf das event reagiert, mit dem Funktionsaufruf erstellen.
            (Der trigger muss natürlich auf das vorher angelegte device der Kamera angepasst werden.
            ([mq_XXX_01:"^motion:.ON$"]) {camera_ffmpeg_XX("$SELF", 'address stream', 'camera name', 'destination', 'duration')}
            
            Beschreibung des Funktionaufrufes:
            {camera_ffmpeg_XX(...)} ->  Funktionsbezeichnung
                                                         - Muss einmalig sein, da jede Kamera ihre eigene Funktionsbezeichnung benötigt.
                                                         - Bspl.: camera_ffmpeg_01, camera_ffmpeg_02, ...
            "$SELF"                            ->  Damit die readings in das aufrufende Device geschrieben werden.
            'address stream'              ->  Die Adresse unter welcher der rtsp-stream der Kamera aufgerufen werden kann.
                                                         - Bspl.: 'rtsp://192.168.178.186:8554/unicast' oder 'rtsp://{USERNAME}:{PASSWORD}\@192.168.178.186:8554/unicast'
            'camera name'                 ->  Eine einmalige Bezeichnung
                                                        - Am einfachsten den Namen der Kamera welche motion triggert verwenden
                                                        - Bspl.: 'mq_XXX_01'
            'destination'                     ->  Verzeichnispfad wo die Dateien später landen sollen
                                                         - kann ein lokales oder entferntes Verzeichnis sein
            'duration'                          ->  Aufnahmedauer in Sekunden
            
            
            
            Anschließend noch die im Anhang befindliche "99_myUtils_camera.pm" in das Verzeichnis,
            in dem auch die anderen "99_myUtils" Dateien angelegt sind, kopieren.
            Entweder direkt kopieren, oder den Inhalt der Datei über die FHEM Oberfläche (Edit files) in eine "99_myUtils" einfügen.
            https://wiki.fhem.de/wiki/99_myUtils_anlegen
            
            Jetzt ist noch etwas eigene Arbeit angesagt.
            In der angelegten "99_myUtils_camera.pm" MUSS für jede Kamera eine Funktion vorhanden sein.
            Heißt, man kopiert den ganzen Block einer Kamera, und passt die Funktion dementsprechend an.
            Beispiel:
            sub camera_ffmpeg_01 {...}
            sub camera_ffmpeg_02 {...}
            sub camera_ffmpeg_03 {...}
            usw.
            
            Genauso wird für jede Kamera ein DOIF mit dem jeweiligen Funktionsaufruf benötigt.
            Beispiel:
            {camera_ffmpeg_01(...)}
            {camera_ffmpeg_02(...)}
            {camera_ffmpeg_03(...)}
            usw.
            
            Das war es im Prinzip schon.
            
            
            Kurzbeschreibung:
            Die Kamera triggert das DOIF, ffmpeg wird gestartet, und die PID an fhem übergeben.
            Nachdem die eingestellte Dauer abgelaufen ist, wird ffmpeg mit der zugewiesenen PID wieder gestoppt.
            Sollte während der laufenden Aufnahme die Kamera erneut triggern, wird die aktuell noch laufende Aufnahme um die eingestellte Dauer verlängert.
            Die zugehörige PID bleibt erhalten, auch wenn ein anderes Gerät ffmpeg startet.
            
            

            ##############################################
            # $Id: 99_myUtils_camera.pm 00011 2020-12-24 11:30:00Z kst $
            #
            #************************************************************************
            # 2020-12-19 - Datei erstellt
            # 2020-12-20 - ffmpeg direkt zu starten, anstatt ueber script
            # 2020-12-21 - ffmpeg wird gestartet, und die zugehoerige PID im reading angelegt
            #            - Funktion mit DOIF erfolgreich getestet
            # 2020-12-22 - Funktion nur noch mit DOIF getestet
            #            - ffmpeg wird mit der entsprechenden PID gestoppt
            #            - wenn wiederholt geriggert wird, waehrend eine Aufnahme laeuft,
            #              wird die Aufnahme um den Wert der Dauer verlaengert
            # 2020-12-23 - mit einer sub fuer alle Kameras geht nicht
            #            - fuer jede Kamera eine sub funktioniert
            # 2020-12-24 - Funktion das die Aufnahmen nach 2 Tagen geloescht werden
            #            - Aufruf durch at
            #            - 
            #            - 
            #            - 
            #            - 
            #            - 
            #
            #------------------------------------------------------------------------
            
            
            package main;
            
            use strict;
            use warnings;
            
            sub
            myUtils_camera_Initialize($$)
            {
             my ($hash) = @_;
            }
            
            # Enter you functions below _this_ line.
            
            
            #************************************************************************
            # camera ffmpeg mq_dafang_01
            #************************************************************************
            sub camera_ffmpeg_dafang_01 {
            
            
                                                       # Pflicht-Uebernahmeparameter (Name des Geraetes von dem die sub aufgerufen wird), mit Ueberpruefung
               my $kst_name_device             =   shift // return '!!! Error: minimum call {camera_ffmpeg_dafang_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Uebernahmeparameter (Adresse der Kamera unter welcher der stream aufgerufen werden kann), mit Ueberpruefung
                                                       # Beispiel: 'rtsp://192.168.78.86:8554/unicast'
                                                       # Beispiel: 'rtsp://{USERNAME}:{PASSWORD}\@192.168.78.86:8554/unicast'
               my $kst_stream_camera           =   shift // return '!!! Error: minimum call {camera_ffmpeg_dafang_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Uebernahmeparameter (Name der Kamera (Device NAME) welche triggert), mit Ueberpruefung
                                                       # Beispiel: 'mq_dafang_01'
               my $kst_name_camera             =   shift // return '!!! Error: minimum call {camera_ffmpeg_dafang_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Parameter (Zielverzeichnis in welchem die Ordner und Dateien angelegt werden), mit Ueberpruefung
                                                       # Beispiel: '/mnt/ipcam/dafang_01/movie'
               my $kst_folder_path             =   shift // return '!!! Error: minimum call {camera_ffmpeg_dafang_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Parameter (Dauer der Aufnahme wenn nur einmal ausgeloest wird), mit Ueberpruefung
                                                       # Beispiel: '120' fuer zwei Minuten
               my $kst_ffmpeg_duration         =   shift // return '!!! Error: minimum call {camera_ffmpeg_dafang_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Benoetigte Ordner und Dateinamen
               my $kst_folder_sub1             =   strftime("%Y-%m", localtime);
               my $kst_folder_sub2             =   strftime("%d", localtime);
               my $kst_file_name               =   strftime("%Y_%m_%d__%H_%M_%S.mp4", localtime);
               my $kst_folder_destination      =   "$kst_folder_path/$kst_folder_sub1/$kst_folder_sub2";
               my $kst_file_destination        =   "$kst_folder_path/$kst_folder_sub1/$kst_folder_sub2/$kst_file_name";
               my $kst_folder_log              =   "$kst_folder_path/log";
               my $kst_file_log                =   "$kst_name_camera.log";
               my $kst_log_destination         =   "$kst_folder_log/$kst_file_log";
            
                                                       # Meta Daten, die spaeter in der Aufnahme hinterlegt sind (koennen an eigene Beduerfnisse angepasst werden)
                                                       # Moechte man die Meta Daten nicht verwenden, muss der Aufruf von ffmpeg angepasst werden.
                                                       # Beispiel: Ohne Titel einfach "-metadata title=$kst_meta_title" entfernen
               my $kst_meta_title              =   strftime("%Y_%m_%d__%H_%M_%S", localtime);
               my $kst_meta_artist             =   'kst';
               my $kst_meta_comment            =   'Livestream';
               my $kst_meta_date               =   strftime("%Y", localtime);
               my $kst_meta_copyright          =   "'(c) $kst_meta_date by kst'";
            
            
                   if (defined "$kst_name_device:007_ffmpeg_active" && ReadingsNum("$kst_name_device","007_ffmpeg_active",0)==1)
                       {
                       goto continue_rec;
                       }
                   else
                       {
                       fhem("setreading $kst_name_device 007_ffmpeg_active 0");
                       }
            
            
                                                       # Readings fuer eine Uebersicht im verwendeten Geraet
               										# Readings werden nur in einem DOIF geschrieben
               fhem("setreading $kst_name_device 001_name_device $kst_name_device");
               fhem("setreading $kst_name_device 002_name_camera $kst_name_camera");
               fhem("setreading $kst_name_device 003_file_destination $kst_file_destination");
               fhem("setreading $kst_name_device 004_log_destination $kst_log_destination");
                                                       # setreading 005_ffmpeg_pid weiter unten im code
               fhem("setreading $kst_name_device 006_ffmpeg_duration $kst_ffmpeg_duration");
                                                       # setreading 007_ffmpeg_active oben und weiter unten im code
            
            
                   if (! -d "$kst_folder_log")
                       { system("mkdir -p $kst_folder_log && wait \$!");
               		Log3 $kst_name_device, 3, "$kst_name_device: $kst_folder_log wurde erstellt";
               		}
            
            
                   if (! -e "$kst_log_destination")
                       { system("touch $kst_log_destination && wait \$!");
               		Log3 $kst_name_device, 3, "$kst_name_device: $kst_log_destination wurde erstellt";
               		}
            
            
                   if (! -d "$kst_folder_destination")
                       { system("mkdir -p $kst_folder_destination && wait \$!");
               		Log3 $kst_name_device, 3, "$kst_name_device: $kst_folder_destination wurde erstellt";
               		}
            
            
               system("ffmpeg -loglevel quiet -rtsp_transport tcp -y -i $kst_stream_camera -vcodec copy -metadata title=$kst_meta_title -metadata comment=$kst_meta_comment -metadata artist=$kst_meta_artist -metadata copyright=$kst_meta_copyright -metadata date=$kst_meta_date $kst_file_destination&");
               fhem("setreading $kst_name_device 007_ffmpeg_active 1");
               system("wait \$! && ps -fC ffmpeg | awk '/ffmpeg/ {print \$2}' | tail -n 1 >$kst_log_destination && wait \$!");
            
            
            continue_rec:
               my ($error, @content) = FileRead({FileName => "$kst_log_destination", ForceType => "file"});
               return $error if ($error);
               my $kst_ffmpeg_pid = $content[0];
               fhem ("sleep 0.5; setreading $kst_name_device 005_ffmpeg_pid $kst_ffmpeg_pid");
               Log3 $kst_name_device, 3, "$kst_name_device:ffmpeg mit PID: $kst_ffmpeg_pid gestartet";
            
               fhem ("sleep ".($kst_ffmpeg_duration)." ".($kst_name_camera)." quiet; {system('kill -15 \"$kst_ffmpeg_pid\"')}; {Log3 \"$kst_name_device\", 3, \"$kst_name_device:ffmpeg mit PID: $kst_ffmpeg_pid gestoppt\"}; setreading $kst_name_device 007_ffmpeg_active 0");
            
            
            }
            
            
            #************************************************************************
            # camera ffmpeg mq_yi_home_01
            #************************************************************************
            sub camera_ffmpeg_yi_home_01 {
            
            
                                                       # Pflicht-Uebernahmeparameter (Name des Geraetes von dem die sub aufgerufen wird), mit Ueberpruefung
               my $kst_name_device             =   shift // return '!!! Error: minimum call {camera_ffmpeg_yi_home_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Uebernahmeparameter (Adresse der Kamera unter welcher der stream aufgerufen werden kann), mit Ueberpruefung
                                                       # Beispiel: 'rtsp://192.168.78.86:8554/unicast'
                                                       # Beispiel: 'rtsp://{USERNAME}:{PASSWORD}\@192.168.78.86:8554/unicast'
               my $kst_stream_camera           =   shift // return '!!! Error: minimum call {camera_ffmpeg_yi_home_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Uebernahmeparameter (Name der Kamera (Device NAME) welche triggert), mit Ueberpruefung
                                                       # Beispiel: 'mq_dafang_01'
               my $kst_name_camera             =   shift // return '!!! Error: minimum call {camera_ffmpeg_yi_home_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Parameter (Zielverzeichnis in welchem die Ordner und Dateien angelegt werden), mit Ueberpruefung
                                                       # Beispiel: '/mnt/ipcam/dafang_01/movie'
               my $kst_folder_path             =   shift // return '!!! Error: minimum call {camera_ffmpeg_yi_home_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Pflicht-Parameter (Dauer der Aufnahme wenn nur einmal ausgeloest wird), mit Ueberpruefung
                                                       # Beispiel: '120' fuer zwei Minuten
               my $kst_ffmpeg_duration         =   shift // return '!!! Error: minimum call {camera_ffmpeg_yi_home_01(\"$SELF\", \'address stream\', \'camera name\', \'destination\', \'duration\')} !!!';
            
                                                       # Benoetigte Ordner und Dateinamen
               my $kst_folder_sub1             =   strftime("%Y-%m", localtime);
               my $kst_folder_sub2             =   strftime("%d", localtime);
               my $kst_file_name               =   strftime("%Y_%m_%d__%H_%M_%S.mp4", localtime);
               my $kst_folder_destination      =   "$kst_folder_path/$kst_folder_sub1/$kst_folder_sub2";
               my $kst_file_destination        =   "$kst_folder_path/$kst_folder_sub1/$kst_folder_sub2/$kst_file_name";
               my $kst_folder_log              =   "$kst_folder_path/log";
               my $kst_file_log                =   "$kst_name_camera.log";
               my $kst_log_destination         =   "$kst_folder_log/$kst_file_log";
            
                                                       # Meta Daten, die spaeter in der Aufnahme hinterlegt sind (koennen an eigene Beduerfnisse angepasst werden)
                                                       # Moechte man die Meta Daten nicht verwenden, muss der Aufruf von ffmpeg angepasst werden.
                                                       # Beispiel: Ohne Titel einfach "-metadata title=$kst_meta_title" entfernen
               my $kst_meta_title              =   strftime("%Y_%m_%d__%H_%M_%S", localtime);
               my $kst_meta_artist             =   'kst';
               my $kst_meta_comment            =   'Livestream';
               my $kst_meta_date               =   strftime("%Y", localtime);
               my $kst_meta_copyright          =   "'(c) $kst_meta_date by kst'";
            
            
                   if (defined "$kst_name_device:007_ffmpeg_active" && ReadingsNum("$kst_name_device","007_ffmpeg_active",0)==1)
                       {
                       goto continue_rec;
                       }
                   else
                       {
                       fhem("setreading $kst_name_device 007_ffmpeg_active 0");
                       }
            
            
                                                       # Readings fuer eine Uebersicht im verwendeten Geraet
               										# Readings werden nur in einem DOIF geschrieben
               fhem("setreading $kst_name_device 001_name_device $kst_name_device");
               fhem("setreading $kst_name_device 002_name_camera $kst_name_camera");
               fhem("setreading $kst_name_device 003_file_destination $kst_file_destination");
               fhem("setreading $kst_name_device 004_log_destination $kst_log_destination");
                                                       # setreading 005_ffmpeg_pid weiter unten im code
               fhem("setreading $kst_name_device 006_ffmpeg_duration $kst_ffmpeg_duration");
                                                       # setreading 007_ffmpeg_active oben und weiter unten im code
            
            
                   if (! -d "$kst_folder_log")
                       { system("mkdir -p $kst_folder_log && wait \$!");
               		Log3 $kst_name_device, 3, "$kst_name_device: $kst_folder_log wurde erstellt";
               		}
            
            
                   if (! -e "$kst_log_destination")
                       { system("touch $kst_log_destination && wait \$!");
               		Log3 $kst_name_device, 3, "$kst_name_device: $kst_log_destination wurde erstellt";
               		}
            
            
                   if (! -d "$kst_folder_destination")
                       { system("mkdir -p $kst_folder_destination && wait \$!");
               		Log3 $kst_name_device, 3, "$kst_name_device: $kst_folder_destination wurde erstellt";
               		}
            
            
               system("ffmpeg -loglevel quiet -rtsp_transport tcp -y -i $kst_stream_camera -vcodec copy -metadata title=$kst_meta_title -metadata comment=$kst_meta_comment -metadata artist=$kst_meta_artist -metadata copyright=$kst_meta_copyright -metadata date=$kst_meta_date $kst_file_destination&");
               fhem("setreading $kst_name_device 007_ffmpeg_active 1");
               system("wait \$! && ps -fC ffmpeg | awk '/ffmpeg/ {print \$2}' | tail -n 1 >$kst_log_destination && wait \$!");
            
            
            continue_rec:
               my ($error, @content) = FileRead({FileName => "$kst_log_destination", ForceType => "file"});
               return $error if ($error);
               my $kst_ffmpeg_pid = $content[0];
               fhem ("sleep 0.5; setreading $kst_name_device 005_ffmpeg_pid $kst_ffmpeg_pid");
               Log3 $kst_name_device, 3, "$kst_name_device:ffmpeg mit PID: $kst_ffmpeg_pid gestartet";
            
               fhem ("sleep ".($kst_ffmpeg_duration)." ".($kst_name_camera)." quiet; {system('kill -15 \"$kst_ffmpeg_pid\"')}; {Log3 \"$kst_name_device\", 3, \"$kst_name_device:ffmpeg mit PID: $kst_ffmpeg_pid gestoppt\"}; setreading $kst_name_device 007_ffmpeg_active 0");
            
            
            }
            
            
            #************************************************************************
            # camera ffmpeg files deleted after 2 days
            #************************************************************************
            sub camera_ffmpeg_file_delete {
            
            
               system("find /mnt/ipcam/dafang_01/movie /mnt/ipcam/yi_01/movie -daystart -mtime +2  -delete && wait \$!");
               system("find /mnt/ipcam/dafang_01/movie /mnt/ipcam/yi_01/movie -empty -delete &");
            
            
            }
            
            
            #************************************************************************
            # Temporaer
            #************************************************************************
            #
            #FFMPEG loggen muss folgende Zeile vor dem ffmpeg befehl stehen
            #FFREPORT=file=\$kst_folder_log/ffreport.log:level=32
            #Beispiel: system("FFREPORT=file=\$kst_folder_log/ffreport.log:level=32 ffmpeg -loglevel quiet ...")
            #
            #
            #
            
            
            1;
            


            Werde das bestimmt auch noch zu ioBroker umsetzen, doch dazu fehlt mir gerade die Zeit und Muse.
            Fang einfach mal an, und wenn du nicht weiterkommst, kann ich, oder jemand, der den Code lesen kann, weiterhelfen.

            1 Reply Last reply Reply Quote 1
            • First post
              Last post

            Support us

            ioBroker
            Community Adapters
            Donate
            FAQ Cloud / IOT
            HowTo: Node.js-Update
            HowTo: Backup/Restore
            Downloads
            BLOG

            974
            Online

            32.1k
            Users

            80.7k
            Topics

            1.3m
            Posts

            2
            4
            423
            Loading More Posts
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes
            Reply
            • Reply as topic
            Log in to reply
            Community
            Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
            The ioBroker Community 2014-2023
            logo