Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. [Script] Kamera Alarm ohne Synology oder motion nutzen.

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    14
    1
    337

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    603

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    1.8k

[Script] Kamera Alarm ohne Synology oder motion nutzen.

Geplant Angeheftet Gesperrt Verschoben JavaScript
3 Beiträge 1 Kommentatoren 436 Aufrufe 2 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • jmeister79J Offline
    jmeister79J Offline
    jmeister79
    schrieb am zuletzt editiert von jmeister79
    #1

    Hi,
    ich wollte auch mal was beisteuern hier anstatt immer nur zu fragen
    vielleicht hat ja jemand von Euch Interesse/Verwendung an und für meiner Lösung oder lässt sich inspirieren

    Und zwar wollte ich meine Reolink Kamera zur Bewegungserkennung und Kontrolle auf dem ioBroker für den Raspberry Pi benutzen ohne externe Lösungen wie Synology Surveillance Station oder motion zu verwenden.
    Auch wollte ich die Aufnahmen AUSSERHALB der Kamera speichern, damit ein Bösswicht sie nicht einfach klaut.

    1. Vorraussetzungen
    • Die Kamera muss Zugang zum Netzwerk haben
    • Die Kamera braucht eine eigene Bewegungserkennung
    • Die Kamera sendet die Bilder per FTP
    • FTP Server im Netz. (Ich habe aus meinem pi gleich einen Server gemacht dann hab ich alles an Bord(proFTP), alternativ den FTP via fStab auf dem System Mounten, natürlich kann man sich auch woanders einloggen aber dann braucht ihr andere Befehle fürs Dateisystem. Wichtig ist allerdings, dass ioBroker Lese und Schreibzugriff auf die Ordner hat.
    1. Prinzip
    • Kamera Erkennt Bewegung und erzeugt ein MP4 Film sowie ein jpg Bild
    • Kamera schickt das auf einen FTP Server
    • ioBroker überwacht das Verzeichnis mit Chokidar und erkennt neue Dateien.
    • Tagesverzeichnis wird dann durchgezählt und sollten es mehr Dateien geworden sein gibt es einen Alarm in der vis und das aktuellste Bild per Telegram.
    • letztes jpg wird ausgelesen und in den VIS Ordner gespeichert, damit es angezeigt wird.
    • Ein Sweeper Script löscht alte Verzeichnisse nach Datum

    Dazu brauche ich ein Script.

    [update] Script zum löschen stark optimiert, so dass jetzt nach dateiinfo gelöscht wird und nicht mehr nach namen, somit kompatibel mit allen kameras

    var fs = require('fs');             //Dateisystemoperationen
    const path = require('path')        //Pfadobjekte
    var chokidar = require('chokidar');     //Watcher für Dateisystemveränderungen
    
    //DPs erzeugen
    createState("javascript.0.Alerts.latestSnapshot", "leer", {type: 'string',name: 'latesSnapshot'});
    createState("javascript.0.Alerts.CameraFilesCount_Last", "leer", {type: 'number',name: 'Anzahl Alarme'});
    createState("javascript.0.Alerts.CamMotionDetected", "leer", {type: 'boolean',name: 'Bewegung Kamera'});
    createState("javascript.0.Parameter.enableTelegramCamAlert", "false", {type: 'boolean',name: 'Alarm Bewegung'});
    createState("javascript.0.Parameter.CamStorageDuration", 10, {type: 'number',name: 'Speicherdauer Kameradaten'});
    createState("javascript.0.Helper.CamDeleteOldFiles", false, {type: 'boolean',name: 'Alte Kameradaten löschen'});
    
    
    //String aus Datum berechnen um in die verzeichnisse zu kommen
    function stringofdate(date){
        var d = date.getDate();
        var m = date.getMonth() + 1;
        var y = date.getFullYear();
        var datestring = y + '/' + (m <= 9 ? '0' + m : m) + '/' + (d <= 9 ? '0' + d : d);
        return datestring;
    }
    
    // Verzeichnisstruktur ist zb
    // /watchpath/Kameraname/YYYY/MM/dd/blbala.jpg
    
    var watchpath = '/home/reolink/Kameras';   //Kameraordner darf nicht = home Ordner sein
    var dirlist = fs.readdirSync(watchpath);   //Liste der Kameras holen
    
    
    var watcher = chokidar.watch(watchpath+'/**/*.jpg', {ignored: /^\./, persistent: true,awaitWriteFinish: true, ignoreInitial:true});
    //Was passiert wenn er was findet
    watcher.on('add', function(pathfound) {
        var dirlist = fs.readdirSync(watchpath);   //Liste der Kameras updaten   
        setState("javascript.0.Alerts.CamMotionDetected", true);
        //log("Neue Datei erkannt")    
        for (let i of dirlist) { 
            //DPs updaten
            createState("javascript.0.Alerts.latestSnapshot"+i, "leer", {type: 'string',name: 'latestnapshot'});
            createState("javascript.0.Alerts.CamMotionDetected"+i, "leer", {type: 'boolean',name: 'Bewegung Kamera'});
            var re = new RegExp(i, 'g');        
            // allgmeines Alarmbit setzen und neuen Zählerstand in DP speichern
            setState("javascript.0.Alerts.CamMotionDetected", true);
            if (pathfound.match(re)){  //wenn es sich um dieses element handelt
                //log('javascript.0.Alerts.latestSnapshot'+i+' = '+pathfound);
                setState("javascript.0.Alerts.latestSnapshot"+i, pathfound); //den gefunden Pfad in einen Datenpunkt fürs VIS kopieren
                setState("javascript.0.Alerts.CamMotionDetected"+i, true);  //individuelles alarmbit setzen
                var logtext = 'Bewegung Kamera '+i+' erkannt';
                setState("javascript.0.Logbuch.array_LogMsg", ['move',logtext]);
                //wenn gewollt per telegram verschicken
                if(getState("javascript.0.Parameter.enableTelegramCamAlert").val===true){  
                    sendTo("telegram.0",  {
                        text: fs.readFileSync(pathfound),
                        type: 'photo',
                        caption: logtext
                    }); 
                }  
                try { 
                    //fünf sekunden warten,damit die datei Zeit hat fertig gespeichert zu werden    
                    //log('Schreibe '+pathfound +' auf /vis.0/latest_'+i+'.jpg');  ACHTUNG! dateien ohne Leerzeichen verwenden
                    setTimeout(function(){},5000);exec( 'iobroker file write '+pathfound+' /vis.0/latest_'+i+'.jpg');  
                }catch(err) {
                    console.error("Cam Monitor: Fehler beim Kopieren der Kameradaten "+i);
                }   
            }
        }
    })
    
    //Wichtig beim Beenden des Skriptes den watcher killen, sonst läuft der ewig weiter und es entstehen mit jedem neustart des skripts neue Instanzen
    onStop(() => {
        watcher.close()
            .then(log('Watcher gestoppt', 'info'))
    })
    
    
    //Bewegung erkennen und wert zurücksetzen
    on({id: 'javascript.0.Alerts.CamMotionDetected', val: true}, async function (obj) {
        setTimeout(function () {
            setState("javascript.0.Alerts.CamMotionDetected", false);
        for (let i of dirlist) { 
            setState("javascript.0.Alerts.CamMotionDetected"+i, false);  //individuelles alarmbit setzen
        }
        }, 60000);
    });
    
    //Deklaration der Löschfunktion alter Daten
    function ThroughDirectory(Directory,deletedate) {
        var files;
        fs.readdirSync(Directory).forEach(File => {
            const Absolute = path.join(Directory, File);
            //Fallunterscheidung Verzeichnis oder Datei
            if (fs.statSync(Absolute).isDirectory()){      
                ThroughDirectory(Absolute, deletedate);  //recursiver Aufruf der Funktion, da es ein Verzeichnis ist   
                //checken ob das verzeichnis nun evtl leer ist
                if(fs.readdirSync(Absolute).length === 0){//Leeres Verzeichnis gefunden
                    log("Leeres Verzeichnis "+Absolute+" wird gelöscht");  
                    setState("javascript.0.Logbuch.array_LogMsg", ['info', 'Verz. '+Absolute+' wird gelöscht']);
                    fs.rmdirSync(Absolute);
                }
                return
            }else{
                const info = fs.statSync(Absolute);  //Dateiinfo holen
                files = files + "/n"+info.birthtime        
                if(info.birthtime < deletedate){    // ist die datei älter als der Parameter?
                    fs.unlink(Absolute, (err => {
                    if (err) console.log(err);
                    }));
                }
            }
        })
    }
    
    on({id: 'javascript.0.Parameter.CamStorageDuration', change: "ne"}, async function (obj) {
            setState("javascript.0.Helper.CamDeleteOldFiles", true);  
        //javascript.0.Helper.CamDeleteOldFiles
    });
    
    //Löschen der alten Daten
    schedule("0 0 * * *", function () {//everyday at 00:00
            setState("javascript.0.Helper.CamDeleteOldFiles", true);  
    });
    on({id: 'javascript.0.Helper.CamDeleteOldFiles', val: true}, async function (obj) {
            setState("javascript.0.Helper.CamDeleteOldFiles", false);  
    //schedule("* * * * *", function () {//everyday at 00:00
        var storageduration = getState("javascript.0.Parameter.CamStorageDuration").val;    
        //Datum holen
        var deletedate = new Date()
        deletedate.setDate(deletedate.getDate() - storageduration);//altes datum erzeugen
        log("Deleting older than "+deletedate)
        //setState("javascript.0.Logbuch.array_LogMsg", ['info', 'Deleting older than '+deletedate]);
        ThroughDirectory(watchpath,deletedate); 
        
    
    });
    

    Ich habe so gut es geht versucht zu kommentieren wo nötig.
    Bin für Verbesserungen offen.

    Known Bugs / ToDo:

    • Filme per Telegram?
    • Code ist z.Zt. gewachsen und ist jetzt auf einem für mich funktionierenden Stand.

    LG
    Nils

    jmeister79J 1 Antwort Letzte Antwort
    1
    • jmeister79J jmeister79

      Hi,
      ich wollte auch mal was beisteuern hier anstatt immer nur zu fragen
      vielleicht hat ja jemand von Euch Interesse/Verwendung an und für meiner Lösung oder lässt sich inspirieren

      Und zwar wollte ich meine Reolink Kamera zur Bewegungserkennung und Kontrolle auf dem ioBroker für den Raspberry Pi benutzen ohne externe Lösungen wie Synology Surveillance Station oder motion zu verwenden.
      Auch wollte ich die Aufnahmen AUSSERHALB der Kamera speichern, damit ein Bösswicht sie nicht einfach klaut.

      1. Vorraussetzungen
      • Die Kamera muss Zugang zum Netzwerk haben
      • Die Kamera braucht eine eigene Bewegungserkennung
      • Die Kamera sendet die Bilder per FTP
      • FTP Server im Netz. (Ich habe aus meinem pi gleich einen Server gemacht dann hab ich alles an Bord(proFTP), alternativ den FTP via fStab auf dem System Mounten, natürlich kann man sich auch woanders einloggen aber dann braucht ihr andere Befehle fürs Dateisystem. Wichtig ist allerdings, dass ioBroker Lese und Schreibzugriff auf die Ordner hat.
      1. Prinzip
      • Kamera Erkennt Bewegung und erzeugt ein MP4 Film sowie ein jpg Bild
      • Kamera schickt das auf einen FTP Server
      • ioBroker überwacht das Verzeichnis mit Chokidar und erkennt neue Dateien.
      • Tagesverzeichnis wird dann durchgezählt und sollten es mehr Dateien geworden sein gibt es einen Alarm in der vis und das aktuellste Bild per Telegram.
      • letztes jpg wird ausgelesen und in den VIS Ordner gespeichert, damit es angezeigt wird.
      • Ein Sweeper Script löscht alte Verzeichnisse nach Datum

      Dazu brauche ich ein Script.

      [update] Script zum löschen stark optimiert, so dass jetzt nach dateiinfo gelöscht wird und nicht mehr nach namen, somit kompatibel mit allen kameras

      var fs = require('fs');             //Dateisystemoperationen
      const path = require('path')        //Pfadobjekte
      var chokidar = require('chokidar');     //Watcher für Dateisystemveränderungen
      
      //DPs erzeugen
      createState("javascript.0.Alerts.latestSnapshot", "leer", {type: 'string',name: 'latesSnapshot'});
      createState("javascript.0.Alerts.CameraFilesCount_Last", "leer", {type: 'number',name: 'Anzahl Alarme'});
      createState("javascript.0.Alerts.CamMotionDetected", "leer", {type: 'boolean',name: 'Bewegung Kamera'});
      createState("javascript.0.Parameter.enableTelegramCamAlert", "false", {type: 'boolean',name: 'Alarm Bewegung'});
      createState("javascript.0.Parameter.CamStorageDuration", 10, {type: 'number',name: 'Speicherdauer Kameradaten'});
      createState("javascript.0.Helper.CamDeleteOldFiles", false, {type: 'boolean',name: 'Alte Kameradaten löschen'});
      
      
      //String aus Datum berechnen um in die verzeichnisse zu kommen
      function stringofdate(date){
          var d = date.getDate();
          var m = date.getMonth() + 1;
          var y = date.getFullYear();
          var datestring = y + '/' + (m <= 9 ? '0' + m : m) + '/' + (d <= 9 ? '0' + d : d);
          return datestring;
      }
      
      // Verzeichnisstruktur ist zb
      // /watchpath/Kameraname/YYYY/MM/dd/blbala.jpg
      
      var watchpath = '/home/reolink/Kameras';   //Kameraordner darf nicht = home Ordner sein
      var dirlist = fs.readdirSync(watchpath);   //Liste der Kameras holen
      
      
      var watcher = chokidar.watch(watchpath+'/**/*.jpg', {ignored: /^\./, persistent: true,awaitWriteFinish: true, ignoreInitial:true});
      //Was passiert wenn er was findet
      watcher.on('add', function(pathfound) {
          var dirlist = fs.readdirSync(watchpath);   //Liste der Kameras updaten   
          setState("javascript.0.Alerts.CamMotionDetected", true);
          //log("Neue Datei erkannt")    
          for (let i of dirlist) { 
              //DPs updaten
              createState("javascript.0.Alerts.latestSnapshot"+i, "leer", {type: 'string',name: 'latestnapshot'});
              createState("javascript.0.Alerts.CamMotionDetected"+i, "leer", {type: 'boolean',name: 'Bewegung Kamera'});
              var re = new RegExp(i, 'g');        
              // allgmeines Alarmbit setzen und neuen Zählerstand in DP speichern
              setState("javascript.0.Alerts.CamMotionDetected", true);
              if (pathfound.match(re)){  //wenn es sich um dieses element handelt
                  //log('javascript.0.Alerts.latestSnapshot'+i+' = '+pathfound);
                  setState("javascript.0.Alerts.latestSnapshot"+i, pathfound); //den gefunden Pfad in einen Datenpunkt fürs VIS kopieren
                  setState("javascript.0.Alerts.CamMotionDetected"+i, true);  //individuelles alarmbit setzen
                  var logtext = 'Bewegung Kamera '+i+' erkannt';
                  setState("javascript.0.Logbuch.array_LogMsg", ['move',logtext]);
                  //wenn gewollt per telegram verschicken
                  if(getState("javascript.0.Parameter.enableTelegramCamAlert").val===true){  
                      sendTo("telegram.0",  {
                          text: fs.readFileSync(pathfound),
                          type: 'photo',
                          caption: logtext
                      }); 
                  }  
                  try { 
                      //fünf sekunden warten,damit die datei Zeit hat fertig gespeichert zu werden    
                      //log('Schreibe '+pathfound +' auf /vis.0/latest_'+i+'.jpg');  ACHTUNG! dateien ohne Leerzeichen verwenden
                      setTimeout(function(){},5000);exec( 'iobroker file write '+pathfound+' /vis.0/latest_'+i+'.jpg');  
                  }catch(err) {
                      console.error("Cam Monitor: Fehler beim Kopieren der Kameradaten "+i);
                  }   
              }
          }
      })
      
      //Wichtig beim Beenden des Skriptes den watcher killen, sonst läuft der ewig weiter und es entstehen mit jedem neustart des skripts neue Instanzen
      onStop(() => {
          watcher.close()
              .then(log('Watcher gestoppt', 'info'))
      })
      
      
      //Bewegung erkennen und wert zurücksetzen
      on({id: 'javascript.0.Alerts.CamMotionDetected', val: true}, async function (obj) {
          setTimeout(function () {
              setState("javascript.0.Alerts.CamMotionDetected", false);
          for (let i of dirlist) { 
              setState("javascript.0.Alerts.CamMotionDetected"+i, false);  //individuelles alarmbit setzen
          }
          }, 60000);
      });
      
      //Deklaration der Löschfunktion alter Daten
      function ThroughDirectory(Directory,deletedate) {
          var files;
          fs.readdirSync(Directory).forEach(File => {
              const Absolute = path.join(Directory, File);
              //Fallunterscheidung Verzeichnis oder Datei
              if (fs.statSync(Absolute).isDirectory()){      
                  ThroughDirectory(Absolute, deletedate);  //recursiver Aufruf der Funktion, da es ein Verzeichnis ist   
                  //checken ob das verzeichnis nun evtl leer ist
                  if(fs.readdirSync(Absolute).length === 0){//Leeres Verzeichnis gefunden
                      log("Leeres Verzeichnis "+Absolute+" wird gelöscht");  
                      setState("javascript.0.Logbuch.array_LogMsg", ['info', 'Verz. '+Absolute+' wird gelöscht']);
                      fs.rmdirSync(Absolute);
                  }
                  return
              }else{
                  const info = fs.statSync(Absolute);  //Dateiinfo holen
                  files = files + "/n"+info.birthtime        
                  if(info.birthtime < deletedate){    // ist die datei älter als der Parameter?
                      fs.unlink(Absolute, (err => {
                      if (err) console.log(err);
                      }));
                  }
              }
          })
      }
      
      on({id: 'javascript.0.Parameter.CamStorageDuration', change: "ne"}, async function (obj) {
              setState("javascript.0.Helper.CamDeleteOldFiles", true);  
          //javascript.0.Helper.CamDeleteOldFiles
      });
      
      //Löschen der alten Daten
      schedule("0 0 * * *", function () {//everyday at 00:00
              setState("javascript.0.Helper.CamDeleteOldFiles", true);  
      });
      on({id: 'javascript.0.Helper.CamDeleteOldFiles', val: true}, async function (obj) {
              setState("javascript.0.Helper.CamDeleteOldFiles", false);  
      //schedule("* * * * *", function () {//everyday at 00:00
          var storageduration = getState("javascript.0.Parameter.CamStorageDuration").val;    
          //Datum holen
          var deletedate = new Date()
          deletedate.setDate(deletedate.getDate() - storageduration);//altes datum erzeugen
          log("Deleting older than "+deletedate)
          //setState("javascript.0.Logbuch.array_LogMsg", ['info', 'Deleting older than '+deletedate]);
          ThroughDirectory(watchpath,deletedate); 
          
      
      });
      

      Ich habe so gut es geht versucht zu kommentieren wo nötig.
      Bin für Verbesserungen offen.

      Known Bugs / ToDo:

      • Filme per Telegram?
      • Code ist z.Zt. gewachsen und ist jetzt auf einem für mich funktionierenden Stand.

      LG
      Nils

      jmeister79J Offline
      jmeister79J Offline
      jmeister79
      schrieb am zuletzt editiert von jmeister79
      #2

      [update] neues script im ersten post

      jmeister79J 1 Antwort Letzte Antwort
      0
      • jmeister79J jmeister79

        [update] neues script im ersten post

        jmeister79J Offline
        jmeister79J Offline
        jmeister79
        schrieb am zuletzt editiert von
        #3

        @jmeister79 ursprungspost angepasst und script stark optimiert

        1 Antwort Letzte Antwort
        0
        Antworten
        • In einem neuen Thema antworten
        Anmelden zum Antworten
        • Älteste zuerst
        • Neuste zuerst
        • Meiste Stimmen


        Support us

        ioBroker
        Community Adapters
        Donate

        1.0k

        Online

        32.6k

        Benutzer

        81.9k

        Themen

        1.3m

        Beiträge
        Community
        Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
        ioBroker Community 2014-2025
        logo
        • Anmelden

        • Du hast noch kein Konto? Registrieren

        • Anmelden oder registrieren, um zu suchen
        • Erster Beitrag
          Letzter Beitrag
        0
        • Home
        • Aktuell
        • Tags
        • Ungelesen 0
        • Kategorien
        • Unreplied
        • Beliebt
        • GitHub
        • Docu
        • Hilfe