NEWS
[Script] Kamera Alarm ohne Synology oder motion nutzen.
-
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 inspirierenUnd 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.- 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.
- 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 -
[update] neues script im ersten post
-
@jmeister79 ursprungspost angepasst und script stark optimiert