Skip to content

Instantly share code, notes, and snippets.

@nygma2004
Created September 28, 2017 20:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save nygma2004/0da4d606575df13700461400178aca4e to your computer and use it in GitHub Desktop.
Save nygma2004/0da4d606575df13700461400178aca4e to your computer and use it in GitHub Desktop.
NVR: capture, store, email pictures/videos from IP camera streams

This video is my simple NVR implementation for Node Red. It just shows how easy it is to capture pictures, video clips from RTSP streams server by IP cameras. This flow offers an entire framework for capturing footage, storing in database, simple reporting, sending images over email etc.

There is a longer video which explains the details of the flow: https://youtu.be/ihZWrJmbGFY

[{"id":"9efa042c.1ed728","type":"tab","label":"NVR","disabled":false,"info":""},{"id":"a92f1b2f.277878","type":"debug","z":"9efa042c.1ed728","name":"","active":false,"console":"false","complete":"true","x":770,"y":160,"wires":[]},{"id":"161395bd.44fcda","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":100,"y":140,"wires":[["fbd47d60.0e2dc"]]},{"id":"59d467aa.79d638","type":"exec","z":"9efa042c.1ed728","command":"avconv -i rtsp://user:password@192.168.1.71:554/ch01.264 -frames 1 -qscale 1 -f image2 ","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Grab a frame","x":530,"y":80,"wires":[["ccefbf92.43434","5181ecea.300284","2664e0ab.3ccb"],[],["a92f1b2f.277878","37e6ad7b.6e58e2"]]},{"id":"ccefbf92.43434","type":"ui_template","z":"9efa042c.1ed728","group":"675036dd.603328","name":"Static image","order":2,"width":0,"height":0,"format":"<div height=\"210\" style=\"height: 210px;\">\n<img src=\"/grab.jpg\"/ width=\"280\"><br/>\n<a href=\"/grab.jpg\" target=\"_blank\">Full screen</a>\n</div>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":790,"y":40,"wires":[[]]},{"id":"5181ecea.300284","type":"debug","z":"9efa042c.1ed728","name":"","active":false,"console":"false","complete":"true","x":770,"y":80,"wires":[]},{"id":"efab5927.df2fe8","type":"file","z":"9efa042c.1ed728","name":"","filename":"/home/pi/avconv.out","appendNewline":true,"createDir":false,"overwriteFile":"true","x":820,"y":120,"wires":[]},{"id":"37e6ad7b.6e58e2","type":"function","z":"9efa042c.1ed728","name":"Statistics","func":"var now = new Date();\nvar stat = context.get(\"stat\");\nif (stat===undefined) {\n // Initialize the object in case NR restart\n stat = { \"count\": 0, \"success\": 0, \"rate\": 0.0, \"last\": now};\n}\nif (msg.topic===\"reset\") {\n // Reset message was received: reset statistics\n stat = { \"count\": 0, \"success\": 0, \"rate\": 0.0, \"last\": now};\n} else {\n // Update statistics\n stat.count++;\n if (msg.payload.code===0) {\n stat.success++;\n } \n stat.rate=stat.success/stat.count;\n stat.last=now;\n}\n\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\nmsg.formattedtime = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss;\nmsg.success = stat.success;\nmsg.rate = Math.floor(stat.rate*100);\n\nnode.status({fill:\"blue\",shape:\"ring\",text:\"Frames: \"+msg.success+\" | \"+msg.rate+\"% | Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n\n// Saving data in the context\ncontext.set(\"stat\",stat);\n\nreturn msg;\n\n\n","outputs":1,"noerr":0,"x":680,"y":280,"wires":[["e8b0a469.8ba128","b06a46c8.57af78","df71ef74.0f8d2"]]},{"id":"ae82c778.306d48","type":"inject","z":"9efa042c.1ed728","name":"Reset stat","topic":"reset","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":480,"y":280,"wires":[["37e6ad7b.6e58e2"]]},{"id":"e8b0a469.8ba128","type":"ui_text","z":"9efa042c.1ed728","group":"e16e06ca.f38438","order":1,"width":0,"height":0,"name":"Last time","label":"Last grab","format":"{{msg.formattedtime}}","layout":"row-spread","x":881.0000839233398,"y":200.39999198913574,"wires":[]},{"id":"b06a46c8.57af78","type":"ui_text","z":"9efa042c.1ed728","group":"e16e06ca.f38438","order":2,"width":0,"height":0,"name":"Frame count","label":"Frames grabbed","format":"{{msg.success}}","layout":"row-spread","x":890.8999900817871,"y":236.1999807357788,"wires":[]},{"id":"df71ef74.0f8d2","type":"ui_text","z":"9efa042c.1ed728","group":"e16e06ca.f38438","order":3,"width":0,"height":0,"name":"Success rate","label":"Success rate","format":"{{msg.rate}} %","layout":"row-spread","x":891.8999633789062,"y":274.1999740600586,"wires":[]},{"id":"92f3cf3b.51ac8","type":"ui_button","z":"9efa042c.1ed728","name":"Refresh","group":"675036dd.603328","order":1,"width":0,"height":0,"passthru":false,"label":"Refresh image","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":84.59999084472656,"y":94.13333797454834,"wires":[["fbd47d60.0e2dc"]]},{"id":"67d53350.bb09dc","type":"debug","z":"9efa042c.1ed728","name":"","active":true,"console":"false","complete":"true","x":778.0000152587891,"y":1040.0000228881836,"wires":[]},{"id":"848f3283.66a9a","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":128.00001525878906,"y":1000.0000228881836,"wires":[["db6e6672.8e0358"]]},{"id":"a5aeed17.49a44","type":"exec","z":"9efa042c.1ed728","command":"avconv -i rtsp://user:password@192.168.1.71:554/ch01.264 -r 10 -t 30 -y -vcodec copy -an ","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Capture video","x":548.0000152587891,"y":960.0000228881836,"wires":[["a9b89709.b65498","677574cb.bbb1fc","2664e0ab.3ccb"],["8a971c90.ebe9b"],["67d53350.bb09dc","1c4d41e8.c597be"]]},{"id":"a9b89709.b65498","type":"ui_template","z":"9efa042c.1ed728","group":"cb67921.cf4df7","name":"Video link","order":2,"width":0,"height":0,"format":"<div height=\"70\" style=\"height: 70px;\">\n<a href=\"/video.mp4\" target=\"_blank\">Open video</a>\n</div>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":788.0000152587891,"y":920.0000228881836,"wires":[[]]},{"id":"677574cb.bbb1fc","type":"debug","z":"9efa042c.1ed728","name":"","active":true,"console":"false","complete":"true","x":778.0000152587891,"y":960.0000228881836,"wires":[]},{"id":"8a971c90.ebe9b","type":"file","z":"9efa042c.1ed728","name":"","filename":"/home/pi/avconv.out","appendNewline":true,"createDir":false,"overwriteFile":"true","x":828.0000152587891,"y":1000.0000228881836,"wires":[]},{"id":"1c4d41e8.c597be","type":"function","z":"9efa042c.1ed728","name":"Statistics","func":"var now = new Date();\nvar stat = context.get(\"stat\");\nif (stat===undefined) {\n // Initialize the object in case NR restart\n stat = { \"count\": 0, \"success\": 0, \"rate\": 0.0, \"last\": now};\n}\nif (msg.topic===\"reset\") {\n // Reset message was received: reset statistics\n stat = { \"count\": 0, \"success\": 0, \"rate\": 0.0, \"last\": now};\n} else {\n // Update statistics\n stat.count++;\n if (msg.payload.code===0) {\n stat.success++;\n } \n stat.rate=stat.success/stat.count;\n stat.last=now;\n}\n\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\nmsg.formattedtime = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss;\nmsg.success = stat.success;\nmsg.rate = Math.floor(stat.rate*100);\n\nnode.status({fill:\"blue\",shape:\"ring\",text:\"Videos: \"+msg.success+\" | \"+msg.rate+\"% | Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n\n// Saving data in the context\ncontext.set(\"stat\",stat);\n\nreturn msg;\n\n\n","outputs":1,"noerr":0,"x":668.0000152587891,"y":1160.0000228881836,"wires":[["c7d29921.7b3a88","dec88d14.74771","9131b705.d7da98"]]},{"id":"4d857e74.bf6e6","type":"inject","z":"9efa042c.1ed728","name":"Reset stat","topic":"reset","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":468.00001525878906,"y":1160.0000228881836,"wires":[["1c4d41e8.c597be"]]},{"id":"c7d29921.7b3a88","type":"ui_text","z":"9efa042c.1ed728","group":"984422e9.d6d96","order":1,"width":0,"height":0,"name":"Video Last time","label":"Last grab","format":"{{msg.formattedtime}}","layout":"row-spread","x":888.0000152587891,"y":1080.0000228881836,"wires":[]},{"id":"dec88d14.74771","type":"ui_text","z":"9efa042c.1ed728","group":"984422e9.d6d96","order":2,"width":0,"height":0,"name":"Video count","label":"Video files","format":"{{msg.success}}","layout":"row-spread","x":878.0000152587891,"y":1120.0000228881836,"wires":[]},{"id":"9131b705.d7da98","type":"ui_text","z":"9efa042c.1ed728","group":"984422e9.d6d96","order":3,"width":0,"height":0,"name":"Video Success rate","label":"Success rate","format":"{{msg.rate}} %","layout":"row-spread","x":898.0000152587891,"y":1160.0000228881836,"wires":[]},{"id":"60807950.8a1308","type":"ui_button","z":"9efa042c.1ed728","name":"Capture","group":"cb67921.cf4df7","order":1,"width":0,"height":0,"passthru":false,"label":"Capture video","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":112.60000610351562,"y":954.1333608627319,"wires":[["db6e6672.8e0358"]]},{"id":"71c22335.23098c","type":"comment","z":"9efa042c.1ed728","name":"Frame grabber","info":"This section of the flow is responsible for \ngrabbing a single out of the RTSP feed of the IP\nCamera. It uses avconv to do that which is part\nof the libav-tools for raspberry pi.\n\nThe trigger can be an inject, or a UI button.\nThe statistic node keeps a track of the number of\ngrabbed frames and the success rate (when the\nvideo conversion/grabbing was successful). The \nStatistic node also has a reset input which can \nbe used to periodically reset the stats (e.g.\ndaily, weekly).\n\nI directed the second output of the Exec node to\na file, as the output of the avconv is usually \nquite long and if there are errors you don't\nsee the entire output in the debug window, so in\nthat case just open to output and see what the issue\nis.","x":125.625,"y":36.5000057220459,"wires":[]},{"id":"b3106a10.da69e8","type":"comment","z":"9efa042c.1ed728","name":"Video capture","info":"This section of the flow captures a fixed length \nvideo of the RTSP feed.\nIt works very similar to the frame capture above,\nthe only main difference is the parameters in the\ncommmand line.","x":118.00001525878906,"y":900.0000228881836,"wires":[]},{"id":"fbd47d60.0e2dc","type":"change","z":"9efa042c.1ed728","name":"Set filename","rules":[{"t":"set","p":"payload","pt":"msg","to":"/home/pi/node-red-static/grab.jpg","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":80,"wires":[["59d467aa.79d638"]]},{"id":"db6e6672.8e0358","type":"change","z":"9efa042c.1ed728","name":"Set filename","rules":[{"t":"set","p":"payload","pt":"msg","to":"/home/pi/node-red-static/video.mp4","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":298.00001525878906,"y":960.0000228881836,"wires":[["a5aeed17.49a44"]]},{"id":"f44069e.c6a9998","type":"function","z":"9efa042c.1ed728","name":"Frame grab","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"frame_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"store\"; // Flag to store this image in the DB\nmsg.type = \"timelapse\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":380,"wires":[["59d467aa.79d638"]]},{"id":"6bd4711d.97ae","type":"inject","z":"9efa042c.1ed728","name":"Grab a frame","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":130,"y":380,"wires":[["f44069e.c6a9998"]]},{"id":"6626a216.c8071c","type":"function","z":"9efa042c.1ed728","name":"Save to DB","func":"// only run this if the topic contains 'store', so the manual frame grabs do not get\n// stored in the database\nif (msg.topic.indexOf(\"store\") !== -1) {\n\n msg.topic = \"INSERT INTO nvr (type,path,wwwpath,filename,epoch) \" +\n \"VALUES ('\"+msg.type+\"','\"+msg.path+\"','\"+msg.wwwpath+\"','\"+msg.file+\"',\"+msg.epoch+\");\";\n \n node.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+msg.formatteddate});\n \n return msg;\n}","outputs":1,"noerr":0,"x":990,"y":340,"wires":[["1862d096.0aa7ef"]]},{"id":"1862d096.0aa7ef","type":"sqlite","z":"9efa042c.1ed728","mydb":"1c25415d.b8427f","name":"Node Red DB","x":1180,"y":340,"wires":[[]]},{"id":"3fc38f78.17dab","type":"function","z":"9efa042c.1ed728","name":"Video capture","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"video_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".mp4\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"store\"; // Flag to store this image in the DB\nmsg.type = \"videotest\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":328.00001525878906,"y":1080.0000228881836,"wires":[["a5aeed17.49a44"]]},{"id":"f752e943.e325d8","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":128.00001525878906,"y":1080.0000228881836,"wires":[["3fc38f78.17dab"]]},{"id":"7f436228.923ccc","type":"function","z":"9efa042c.1ed728","name":"Email image","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"frame_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"store|email\"; // Flag to store this image in the DB\nmsg.type = \"front\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":420,"wires":[["59d467aa.79d638"]]},{"id":"3802963b.c35a2a","type":"inject","z":"9efa042c.1ed728","name":"Email a pic","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":120,"y":420,"wires":[["7f436228.923ccc"]]},{"id":"2664e0ab.3ccb","type":"function","z":"9efa042c.1ed728","name":"Dummy","func":"\nreturn msg;","outputs":1,"noerr":0,"x":780,"y":380,"wires":[["6626a216.c8071c","cb390310.7a97e","83fd6a62.ba5898","606e90cf.e660c","e85ffa62.1a8b98"]]},{"id":"cb390310.7a97e","type":"function","z":"9efa042c.1ed728","name":"Compose email","func":"// only run this if the topic contains 'email', so other types do not get emailed\nif (msg.topic.indexOf(\"email\") !== -1) {\n\n msg.filename = msg.path + msg.file;\n \n node.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+msg.formatteddate});\n \n return msg;\n}","outputs":1,"noerr":0,"x":1000,"y":400,"wires":[["7159813e.2bdb4"]]},{"id":"50e67922.ab82c8","type":"e-mail","z":"9efa042c.1ed728","server":"smtp.gmail.com","port":"465","secure":true,"name":"youremail@gmail.com","dname":"Email notification","x":1390,"y":400,"wires":[]},{"id":"7159813e.2bdb4","type":"change","z":"9efa042c.1ed728","name":"Set up email","rules":[{"t":"set","p":"attachments","pt":"msg","to":"[{\t \"filename\": msg.file, \t \"path\": msg.filename,\t \"content\": $$.payload\t}]","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"Front door motion","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"Dear,<br><br>Motion has been detected at the front door<br>In attachment you can find a snapshot.<br><br>Kind regards,<br>Your Node-Red flow","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1190,"y":400,"wires":[["50e67922.ab82c8"]]},{"id":"db1675ae.6608a8","type":"comment","z":"9efa042c.1ed728","name":"Image / Video Log","info":"","x":138.00000762939453,"y":1664.0000324249268,"wires":[]},{"id":"6eed45ad.3a9f4c","type":"template","z":"9efa042c.1ed728","name":"Formatting","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<table>\n <tr><th>ID</th><th>Type</th><th>Timestamp</th><th>File</th></tr>\n {{#payload}}\n <tr class=\"\">\n <td>{{id}}</td>\n <td>{{type}}</td>\n <td>{{timestamp}}</td>\n <td><a href=\"{{wwwpath}}{{filename}}\" target=\"_blank\">{{filename}}</a></td>\n </tr>\n {{/payload}}\n</table>\n","x":737.0000076293945,"y":2039.0000324249268,"wires":[["e60dddf1.8029c"]]},{"id":"46d1c098.824e5","type":"function","z":"9efa042c.1ed728","name":"SQL","func":"context.set(msg.topic,msg.payload);\n\nvar d = new Date();\nvar epoch = d.getTime();\nvar fromdate = 0;\nvar enddate = 0;\n\nmsg.topic = \"SELECT * FROM nvr WHERE id>0 \";\n\nif (context.get(\"type\")!==undefined) {\n if (context.get(\"type\")!==\"*\") {\n msg.topic = msg.topic + \" AND type = '\"+context.get(\"type\")+\"'\";\n }\n}\n\nif (context.get(\"time\")!==undefined) {\n switch (context.get(\"time\")) {\n case 0:\n fromdate = epoch - 1000*60*60*24;\n msg.topic = msg.topic + \" AND epoch > \"+fromdate;\n break;\n case 1:\n fromdate = epoch - 1000*60*60*24*7;\n msg.topic = msg.topic + \" AND epoch > \"+fromdate;\n break;\n case 2:\n fromdate = epoch - 1000*60*60*24*30;\n msg.topic = msg.topic + \" AND epoch > \"+fromdate;\n break;\n }\n}\n\n// msg.topic = msg.topic.substring(0,msg.topic.length-2);\nmsg.topic = msg.topic+ \" ORDER BY id\";\nreturn msg;","outputs":1,"noerr":0,"x":324.00000762939453,"y":2038.0000324249268,"wires":[["3f0bae61.7c1962"]]},{"id":"a67b5ea9.1f61","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":157.00000762939453,"y":2038.0000324249268,"wires":[["46d1c098.824e5"]]},{"id":"bf2db74c.7ebda8","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"x":278.00000762939453,"y":1904.0000324249268,"wires":[["e022fd92.f69d2"]]},{"id":"e022fd92.f69d2","type":"change","z":"9efa042c.1ed728","name":"Initial value","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":458.00000762939453,"y":1904.0000324249268,"wires":[["c997c751.b0e9e8"]]},{"id":"82ca9c8d.fa89b","type":"function","z":"9efa042c.1ed728","name":"Process systems","func":"var output = [];\noutput.push({\"All systems\":\"*\"});\nfor (var i = 0; i < msg.payload.length; i++) {\n obj = {};\n obj [msg.payload[i].type]=msg.payload[i].type;\n output.push(obj);\n}\nmsg.options = output;\nreturn msg;","outputs":1,"noerr":0,"x":478.00000762939453,"y":1784.0000324249268,"wires":[["968f6573.a694c8"]]},{"id":"b0228a6d.84f3e8","type":"ui_button","z":"9efa042c.1ed728","name":"","group":"a06d1d6b.bdef2","order":1,"width":"3","height":"1","passthru":false,"label":"Refresh","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"refresh","x":668.0000076293945,"y":1844.0000324249268,"wires":[["46d1c098.824e5"]]},{"id":"c997c751.b0e9e8","type":"ui_dropdown","z":"9efa042c.1ed728","name":"Time filter","label":"","place":"","group":"a06d1d6b.bdef2","order":3,"width":"4","height":"1","passthru":true,"options":[{"label":"Last 24 hrs","value":0,"type":"num"},{"label":"Last 7 days","value":1,"type":"num"},{"label":"Last 30 days","value":2,"type":"num"},{"label":"All","value":3,"type":"num"}],"payload":"","topic":"time","x":668.0000076293945,"y":1904.0000324249268,"wires":[["46d1c098.824e5"]]},{"id":"968f6573.a694c8","type":"ui_dropdown","z":"9efa042c.1ed728","name":"Type","label":"","place":"","group":"a06d1d6b.bdef2","order":5,"width":"5","height":"1","passthru":true,"options":[{"label":"All","value":"*","type":"str"},{"label":"timelapse","value":"timelapse","type":"str"},{"label":"front","value":"front","type":"str"},{"label":"videotest","value":"videotest","type":"str"}],"payload":"","topic":"type","x":678.0000076293945,"y":1784.0000324249268,"wires":[["46d1c098.824e5"]]},{"id":"e60dddf1.8029c","type":"ui_template","z":"9efa042c.1ed728","group":"a06d1d6b.bdef2","name":"Log output","order":6,"width":0,"height":0,"format":"<div ng-bind-html=\"msg.payload\" height=\"600\" style=\"height: 600px;\"><br/></div>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":958.0000076293945,"y":2039.0000324249268,"wires":[[]]},{"id":"3f0bae61.7c1962","type":"sqlite","z":"9efa042c.1ed728","mydb":"1c25415d.b8427f","name":"Node Red DB","x":532.0000076293945,"y":2038.0000324249268,"wires":[["6eed45ad.3a9f4c"]]},{"id":"68a84ff2.caa05","type":"inject","z":"9efa042c.1ed728","name":"Email 3 pics","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":130,"y":460,"wires":[["ce7424a5.0105c8"]]},{"id":"ce7424a5.0105c8","type":"function","z":"9efa042c.1ed728","name":"1st img","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"frame_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"3mail/1|delete\"; // Flag to store this image in the DB\nmsg.type = \"temp\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":460,"wires":[["9b2e9667.e1a578","59d467aa.79d638"]]},{"id":"94e6544f.7a4998","type":"function","z":"9efa042c.1ed728","name":"2st img","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"frame_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"3mail/2|delete\"; // Flag to store this image in the DB\nmsg.type = \"temp\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":520,"wires":[["910a4663.888b28","59d467aa.79d638"]]},{"id":"8082b36d.9fca8","type":"function","z":"9efa042c.1ed728","name":"3rd img","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"frame_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"3mail/3|delete\"; // Flag to store this image in the DB\nmsg.type = \"temp\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":580,"wires":[["59d467aa.79d638"]]},{"id":"9b2e9667.e1a578","type":"delay","z":"9efa042c.1ed728","name":"","pauseType":"delay","timeout":"15","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":140,"y":520,"wires":[["94e6544f.7a4998"]]},{"id":"910a4663.888b28","type":"delay","z":"9efa042c.1ed728","name":"","pauseType":"delay","timeout":"15","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":140,"y":580,"wires":[["8082b36d.9fca8"]]},{"id":"83fd6a62.ba5898","type":"function","z":"9efa042c.1ed728","name":"Compose email 3","func":"// only run this if the topic contains 'email', so other types do not get emailed\nif (msg.topic.indexOf(\"3mail\") !== -1) {\n\n if (msg.topic.indexOf(\"/1\") !== -1) {\n context.set(\"file1\", msg.path + msg.file);\n }\n if (msg.topic.indexOf(\"/2\") !== -1) {\n context.set(\"file2\", msg.path + msg.file);\n }\n if (msg.topic.indexOf(\"/3\") !== -1) {\n msg.file3 = msg.path + msg.file;\n msg.file1 = context.get(\"file1\");\n msg.file2 = context.get(\"file2\");\n msg.payload = msg.file1 + \" \" + msg.file2 + \" \" + msg.file3;\n node.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+msg.formatteddate});\n return msg;\n }\n \n}","outputs":1,"noerr":0,"x":1010,"y":460,"wires":[["ac788639.9a1f38"]]},{"id":"ac788639.9a1f38","type":"change","z":"9efa042c.1ed728","name":"Set up email","rules":[{"t":"set","p":"attachments","pt":"msg","to":"[{\t \"filename\": \"Image1.jpg\", \t \"path\": msg.file1,\t \"content\": $$.payload\t},\t{\t \"filename\": \"Image2.jpg\", \t \"path\": msg.file2,\t \"content\": $$.payload\t},\t{\t \"filename\": \"Image3.jpg\", \t \"path\": msg.file3,\t \"content\": $$.payload\t}]","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"Front door motion","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"Dear,<br><br>Motion has been detected at the front door<br>In attachment you can find a 3 snapshots.<br><br>Kind regards,<br>Your Node-Red flow","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1190,"y":460,"wires":[["50e67922.ab82c8"]]},{"id":"6e97a4dc.7c8a0c","type":"delay","z":"9efa042c.1ed728","name":"","pauseType":"delay","timeout":"15","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1200,"y":520,"wires":[["d8a70733.d24de8"]]},{"id":"d8a70733.d24de8","type":"exec","z":"9efa042c.1ed728","command":"sudo rm ","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Delete images","x":1380,"y":520,"wires":[[],[],[]]},{"id":"1d2feed2.02ee51","type":"sqlite","z":"9efa042c.1ed728","mydb":"1c25415d.b8427f","name":"Node Red DB","x":508.00000762939453,"y":1724.0000324249268,"wires":[["82ca9c8d.fa89b"]]},{"id":"4cdfebb3.4cdd34","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"00 3 * * *","once":false,"x":138.00000762939453,"y":1724.0000324249268,"wires":[["b4336ef9.06d2e"]]},{"id":"b4336ef9.06d2e","type":"change","z":"9efa042c.1ed728","name":"Set SQL","rules":[{"t":"set","p":"topic","pt":"msg","to":"SELECT DISTINCT type from nvr","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":308.00000762939453,"y":1724.0000324249268,"wires":[["1d2feed2.02ee51"]]},{"id":"606e90cf.e660c","type":"function","z":"9efa042c.1ed728","name":"Delete pictures","func":"// only run this if the topic contains 'email', so other types do not get emailed\nvar send = false;\nif (msg.topic.indexOf(\"delete\") !== -1) {\n\n msg.payload = msg.path + msg.file;\n send = true;\n if (msg.topic.indexOf(\"/1\") !== -1) {\n context.set(\"file1\", msg.path + msg.file);\n send = false;\n }\n if (msg.topic.indexOf(\"/2\") !== -1) {\n context.set(\"file2\", msg.path + msg.file);\n send = false;\n }\n if (msg.topic.indexOf(\"/3\") !== -1) {\n msg.file3 = msg.path + msg.file;\n msg.file1 = context.get(\"file1\");\n msg.file2 = context.get(\"file2\");\n msg.payload = msg.file1 + \" \" + msg.file2 + \" \" + msg.file3;\n send = true;\n } \n \n if (send) {\n node.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+msg.formatteddate});\n return msg;\n }\n\n \n}","outputs":1,"noerr":0,"x":1000,"y":520,"wires":[["6e97a4dc.7c8a0c"]]},{"id":"e85ffa62.1a8b98","type":"function","z":"9efa042c.1ed728","name":"Tweet image","func":"// only run this if the topic contains 'email', so other types do not get emailed\nif (msg.topic.indexOf(\"tweet\") !== -1) {\n\n msg.filename = msg.path + msg.file;\n \n node.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+msg.formatteddate});\n \n return msg;\n}","outputs":1,"noerr":0,"x":990,"y":580,"wires":[["3606c596.2f6c8a"]]},{"id":"53ed43f.f6b91bc","type":"twitter out","z":"9efa042c.1ed728","twitter":"","name":"Tweet","x":1510,"y":580,"wires":[]},{"id":"fe5e05f6.dc5d88","type":"change","z":"9efa042c.1ed728","name":"Set up tweet","rules":[{"t":"set","p":"media","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"Image from the front door","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1350,"y":580,"wires":[["53ed43f.f6b91bc","52573496.bc77ac"]]},{"id":"3606c596.2f6c8a","type":"file in","z":"9efa042c.1ed728","name":"Read image","filename":"","format":"utf8","chunk":false,"sendError":false,"x":1170,"y":580,"wires":[["fe5e05f6.dc5d88","afd0c8a3.9d63b8"]]},{"id":"3fe6a4d4.b4df1c","type":"inject","z":"9efa042c.1ed728","name":"Tweet a frame","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":130,"y":620,"wires":[["c51cf57f.0a85b8"]]},{"id":"c51cf57f.0a85b8","type":"function","z":"9efa042c.1ed728","name":"Frame grab","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"frame_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"tweet|delete\"; // Flag to store this image in the DB\nmsg.type = \"timelapse\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":620,"wires":[["59d467aa.79d638"]]},{"id":"48dac303.25c24c","type":"inject","z":"9efa042c.1ed728","name":"Grab a frame","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":810,"y":640,"wires":[["e27eca44.184fa8"]]},{"id":"e27eca44.184fa8","type":"change","z":"9efa042c.1ed728","name":"Set filename","rules":[{"t":"set","p":"filename","pt":"msg","to":"/home/pi/node-red-static/grab.jpg","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":640,"wires":[["3606c596.2f6c8a"]]},{"id":"afd0c8a3.9d63b8","type":"debug","z":"9efa042c.1ed728","name":"","active":true,"console":"false","complete":"true","x":1365.8571515764509,"y":650.5714198521205,"wires":[]},{"id":"52573496.bc77ac","type":"debug","z":"9efa042c.1ed728","name":"","active":true,"console":"false","complete":"true","x":1550,"y":640,"wires":[]},{"id":"866e0069.da033","type":"comment","z":"9efa042c.1ed728","name":"Delete old pictures","info":"","x":138.00000762939453,"y":2124.0000324249268,"wires":[]},{"id":"eaa06376.68fe4","type":"inject","z":"9efa042c.1ed728","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":128.00000762939453,"y":2184.0000324249268,"wires":[["1264fe76.2b8012"]]},{"id":"1264fe76.2b8012","type":"function","z":"9efa042c.1ed728","name":"SQL (older than 30 days)","func":"var d = new Date();\nvar epoch = d.getTime();\n\n// today - 30 days\nvar fromdate = epoch - 1000*60*60*24*30;\n\nmsg.topic = \"SELECT * FROM nvr WHERE epoch < \"+fromdate;\n\nreturn msg;","outputs":1,"noerr":0,"x":358.00000762939453,"y":2184.0000324249268,"wires":[["4a90a27f.4f1a5c"]]},{"id":"4a90a27f.4f1a5c","type":"sqlite","z":"9efa042c.1ed728","mydb":"1c25415d.b8427f","name":"Node Red DB","x":608.0000076293945,"y":2184.0000324249268,"wires":[["16f8cd9.c442a32"]]},{"id":"f43773ab.04fb9","type":"exec","z":"9efa042c.1ed728","command":"sudo rm ","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Delete images","x":1348.0000076293945,"y":2184.0000324249268,"wires":[[],[],[]]},{"id":"16f8cd9.c442a32","type":"split","z":"9efa042c.1ed728","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":798.0000076293945,"y":2184.0000324249268,"wires":[["58a0e5b7.6a59dc"]]},{"id":"58a0e5b7.6a59dc","type":"delay","z":"9efa042c.1ed728","name":"","pauseType":"random","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"50","randomLast":"500","randomUnits":"milliseconds","drop":false,"x":948.0000076293945,"y":2184.0000324249268,"wires":[["ac43dfad.5b253","c2bd4748.fce518"]]},{"id":"cc750eb0.b57a7","type":"sqlite","z":"9efa042c.1ed728","mydb":"1c25415d.b8427f","name":"Node Red DB","x":1348.0000076293945,"y":2244.0000324249268,"wires":[[]]},{"id":"ac43dfad.5b253","type":"change","z":"9efa042c.1ed728","name":"Set SQL","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"DELETE * FROM nvr where filename='\" & payload.filename & \"'\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1128.0000076293945,"y":2244.0000324249268,"wires":[["cc750eb0.b57a7"]]},{"id":"c2bd4748.fce518","type":"change","z":"9efa042c.1ed728","name":"Set filename","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.path & payload.filename","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1138.0000076293945,"y":2184.0000324249268,"wires":[["f43773ab.04fb9"]]},{"id":"fa52360b.8fd1d8","type":"inject","z":"9efa042c.1ed728","name":"Noon timelapse","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":138,"y":706,"wires":[["7f834a77.89c694"]]},{"id":"7f834a77.89c694","type":"function","z":"9efa042c.1ed728","name":"Frame grab","func":"var now = new Date();\n// Create formatted time\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\n\n// Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss});\n\n// file path with / at the end\nvar path = \"/home/pi/node-red-static/\"; // This is the path\nvar filename = \"noon_\"+yyyy+mm+dd+\"-\"+hh+mm+ss+\".jpg\"; // file name\nmsg.payload = path + filename; // pass the full path to payload for the exec node to add to the end of the command\nmsg.file = filename; // To be used later to store the information in the DB\nmsg.path = path; // Same as above\nmsg.wwwpath = \"/\"; // Same as above\nmsg.topic = \"store\"; // Flag to store this image in the DB\nmsg.type = \"Noon timelapse\"; // Image type e.g. Front camera, etc.\nmsg.epoch = now.getTime(); // Current timestamp\nmsg.formatteddate = dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss; // Formatted timestamp to be used later\n\nreturn msg;","outputs":1,"noerr":0,"x":308,"y":706,"wires":[["59d467aa.79d638"]]},{"id":"675036dd.603328","type":"ui_group","z":"","name":"Frame Grab","tab":"7af2d9c8.0a9148","order":1,"disp":true,"width":"6"},{"id":"e16e06ca.f38438","type":"ui_group","z":"","name":"Frame Statistics","tab":"7af2d9c8.0a9148","order":2,"disp":true,"width":"6"},{"id":"cb67921.cf4df7","type":"ui_group","z":"","name":"Video Capture","tab":"7af2d9c8.0a9148","order":3,"disp":true,"width":"6"},{"id":"984422e9.d6d96","type":"ui_group","z":"","name":"Video Statistics","tab":"7af2d9c8.0a9148","order":4,"disp":true,"width":"6"},{"id":"1c25415d.b8427f","type":"sqlitedb","z":0,"db":"/home/pi/sqlite/nodered"},{"id":"a06d1d6b.bdef2","type":"ui_group","z":"","name":"Report","tab":"7af2d9c8.0a9148","order":5,"disp":true,"width":"12"},{"id":"7af2d9c8.0a9148","type":"ui_tab","z":"","name":"NVR","icon":"dashboard","order":13}]
@roeidalm
Copy link

Dear friend,
first amazing good ty!
sec, I find 2 major adding for your flow:

  1. In the capcure image you need to add -y to the command for overwrite otherwise it didn't work because it wait for you answer.
    avconv -i rtsp://:@192.168.1.119:554/onvif1 -frames 1 -qscale 1 -f image2 -y
  2. you need to install first libav-tools and ffmpeg.
    here it the flow to do this in one click:
    [{"id":"e86415be.490378","type":"inject","z":"976663a6.4462b","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":173.07991218566895,"y":2498.9515810012817,"wires":[["2e533e91.3a09c2"]]},{"id":"2e533e91.3a09c2","type":"exec","z":"976663a6.4462b","command":"apt-get install libav-tools ","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":429.0695056915283,"y":2509.61127948761,"wires":[["cc959f0b.8f921","4450db18.841a04"],[],["d631f88a.092e58"]]},{"id":"cc959f0b.8f921","type":"exec","z":"976663a6.4462b","command":"apt-get install ffmpeg","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":715.0764217376709,"y":2505.618525505066,"wires":[["365728a6.474108"],[],["e7d3209e.1e6dd"]]},{"id":"365728a6.474108","type":"debug","z":"976663a6.4462b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":938.0764293670654,"y":2453.896082878113,"wires":[]},{"id":"d631f88a.092e58","type":"debug","z":"976663a6.4462b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":590.0173206329346,"y":2566.121838569641,"wires":[]},{"id":"e7d3209e.1e6dd","type":"debug","z":"976663a6.4462b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":900.0173110961914,"y":2543.121729850769,"wires":[]},{"id":"30fa2e40.e60972","type":"comment","z":"976663a6.4462b","name":"installation","info":"first use anycam.io to scan and get the rstp address\nsec, run the commands","x":229.01735496520996,"y":2407.2328271865845,"wires":[]},{"id":"4450db18.841a04","type":"debug","z":"976663a6.4462b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":594.0173206329346,"y":2464.010775566101,"wires":[]}]

again. ty amazing job!

@daxliniere
Copy link

daxliniere commented Mar 1, 2021

Hey @roeidalm Thanks for your addition. I wasn't able to get your install flow to run, I just get Error 100 for both Exec nodes. Node is running as the user pi.

I also have never been able to get avconv to work. I found a flow which is similar, but uses ffmpeg and this works well, but doesn't have all the nice SQL and email features. @nygma2004 do you have any idea on how I could substitute the 100% working ffmpeg frame grabber into your flow, please?

This is the working command:
ffmpeg -y -i rtsp://USER:PASSWORD@IPADDRESS:PORT/live0.264 -vframes 1 -f image2pipe -vcodec png -

It comes from this flow which is based on Csongor's flow:
https://flows.nodered.org/flow/127b038961f873d1babeecaf5578959e

@nygma2004
Copy link
Author

@nygma2004 do you have any idea on how I could substitute the 100% working ffmpeg frame grabber into your flow, please?
There is an exec node in the flow which says grab a frame. You just put your ffmpeg command there. My flow will put the filename in the msg.payload which will get appended to the end of the command. I assume this is how you specify the output filename to ffmpeg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment