File Browser in Dashboard

I wanted an easier way to access my files on my PI and created this File Browser in dashboard. Main features:

  • Show files in local folders: size, dates
  • Download files
  • Navigate to any folder
  • Delete files
  • Show/Hide hidden files
  • Open CSV files on a Graph

Check the comment node on the top which explains how to set up the flow.

For more info you can check my Youtube video which explains this flow: https://youtu.be/3QgK4IAAqcQ

File lister node: node-red-contrib-fs

[{"id":"4fa73dd9.83cca4","type":"comment","z":"74f191ff.db063","name":"File Browser","info":"1) Update the default folder in the Init node\n2) Update the default folder in the Reset node as well\n3) You can duplicate the Reset nodes and use them as saved shortcuts\n4) Check the Convert Timestamps function node if you want to see your dates in a different format. I just used the Javascript toISOString format.\n5) The Graph button can read any CSV file which have any number of values, but the first column always contains a timestamp column with javascript timestamp of the data point.","x":110,"y":2940,"wires":[]},{"id":"993d7272.843ae","type":"fs-file-lister","z":"74f191ff.db063","name":"Files","start":"/home/pi","pattern":"*.*","folders":"*","hidden":false,"lstype":"files","path":true,"single":true,"depth":0,"stat":true,"showWarnings":false,"x":510,"y":3040,"wires":[["dd88bd23.37cde","3e094f82.15d6e"]]},{"id":"d185a45.2327a58","type":"inject","z":"74f191ff.db063","name":"Init","topic":"change","payload":"/home/pi","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":3000,"wires":[["3d4e5e51.bdf952"]]},{"id":"a715f7a4.a398a8","type":"ui_button","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":1,"width":"2","height":"1","passthru":false,"label":"Refresh","tooltip":"","color":"","bgcolor":"","icon":"refresh","payload":"","payloadType":"str","topic":"refresh","x":100,"y":3040,"wires":[["3d4e5e51.bdf952"]]},{"id":"eb17c7ee.e61988","type":"ui_dropdown","z":"74f191ff.db063","name":"File Selector","label":"","tooltip":"","place":"Select a file","group":"160e81fb.f1c86e","order":9,"width":"5","height":"1","passthru":false,"options":[{"label":"","value":"","type":"str"}],"payload":"","topic":"","x":910,"y":3040,"wires":[["be4830df.5775d"]]},{"id":"dd88bd23.37cde","type":"function","z":"74f191ff.db063","name":"Format data","func":"// format the data for the dropdown\nmsg.options = [];\nfor (var i=0; i<msg.payload.length; i++) {\n    // This is a file\n    obj = {};\n    obj [msg.payload[i].name.replace(/^.*(\\\\|\\/|\\:)/, '')]=msg.payload[i].name;\n    msg.options.push(obj);\n}\nmsg.payload={};\nreturn msg;","outputs":1,"noerr":0,"x":710,"y":3040,"wires":[["eb17c7ee.e61988"]]},{"id":"638728aa.cd0b08","type":"ui_template","z":"74f191ff.db063","group":"160e81fb.f1c86e","name":"","order":6,"width":"18","height":"6","format":"<div ng-bind-html=\"msg.payload\" height=\"400\" style=\"height: 400px;\"><br/>\n</div>\n\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":1100,"y":3100,"wires":[[]]},{"id":"9d27e846.b31db8","type":"template","z":"74f191ff.db063","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<table width=\"100%\">\n    <tr><th>File Name</th><th>Size</th><th>Created</th><th>Changed</th></tr>\n    {{#payload}}\n        <tr>\n            <td><a href=\"/download?filename={{name}}\" target=\"blank\">{{fname}}</a></td>\n            <td>{{stat.size}}</td>\n            <td>{{stat.created}}</td>\n            <td>{{stat.changed}}</td>\n        </tr>\n    {{/payload}}\n</table>\n","output":"str","x":940,"y":3100,"wires":[["638728aa.cd0b08"]]},{"id":"3e094f82.15d6e","type":"function","z":"74f191ff.db063","name":"Convert timestamps","func":"for (var i=0; i<msg.payload.length; i++) {\n    msg.payload[i].stat.created = msg.payload[i].stat.created.toISOString().slice(0, 19).replace('T', ' ');\n    msg.payload[i].stat.changed = msg.payload[i].stat.changed.toISOString().slice(0, 19).replace('T', ' ');\n    msg.payload[i].stat.accessed = msg.payload[i].stat.accessed.toISOString().slice(0, 19).replace('T', ' ');\n    msg.payload[i].stat.statusChanged = msg.payload[i].stat.statusChanged.toISOString().slice(0, 19).replace('T', ' ');\n    msg.payload[i].fname = msg.payload[i].name.replace(/^.*(\\\\|\\/|\\:)/, '');\n}\nreturn msg;","outputs":1,"noerr":0,"x":740,"y":3100,"wires":[["9d27e846.b31db8"]]},{"id":"7b7e0d58.05e244","type":"ui_button","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":10,"width":"2","height":"1","passthru":false,"label":"Delete","tooltip":"","color":"","bgcolor":"","icon":"delete","payload":"","payloadType":"str","topic":"","x":130,"y":3200,"wires":[["8da8147a.999af8"]]},{"id":"7d490dd1.8458b4","type":"function","z":"74f191ff.db063","name":"Get filename","func":"// Get the filename from the flow context\nlet filename = flow.get(\"fileselected\");\n\n// check, if the filename is undefined that means it does not exist yet, nothing is selected yet\n// return: do not output anything\nif (filename===undefined) {\n    return;\n}\n\n// return the filename to the file-in node to delete\nmsg.filename = filename;\n\nif (msg.filename.replace(/^.*(\\\\|\\/|\\:)/, '')[0]!==\".\") {\n    // Only do this if this is a file, we don't delete folders\n    // and delete the context/selection as we are deleting the file as well\n    flow.set(\"fileselected\");\n    return msg;\n}","outputs":1,"noerr":0,"x":330,"y":3260,"wires":[["372ee262.fc94de"]]},{"id":"372ee262.fc94de","type":"file","z":"74f191ff.db063","name":"Delete file","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":540,"y":3260,"wires":[["993d7272.843ae","5219875b.c070d8"]]},{"id":"aa8c482.93734b8","type":"http in","z":"74f191ff.db063","name":"","url":"/download","method":"get","upload":false,"swaggerDoc":"","x":140,"y":3400,"wires":[["d969ba04.e24028"]]},{"id":"d19cc7d8.646328","type":"http response","z":"74f191ff.db063","name":"","statusCode":"","headers":{},"x":930,"y":3400,"wires":[]},{"id":"d969ba04.e24028","type":"function","z":"74f191ff.db063","name":"Get the file name","func":"msg.filename = msg.req.query.filename;\nmsg.contentdisposition = \"attachment; filename=\\\"\" + msg.req.query.filename.replace(/^.*(\\\\|\\/|\\:)/, '') + \"\\\"\";\nreturn msg;\n","outputs":1,"noerr":0,"x":390,"y":3400,"wires":[["e92381c3.c4cd2"]],"outputLabels":["Folder selected","File selected"]},{"id":"e92381c3.c4cd2","type":"file in","z":"74f191ff.db063","name":"","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":580,"y":3400,"wires":[["99ff4953.d0d5c8"]]},{"id":"99ff4953.d0d5c8","type":"change","z":"74f191ff.db063","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"text/csv","tot":"str"},{"t":"set","p":"headers.Content-Disposition","pt":"msg","to":"contentdisposition","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":3400,"wires":[["d19cc7d8.646328"]]},{"id":"2519dd0d.54d6b2","type":"ui_button","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":11,"width":"2","height":"1","passthru":false,"label":"Graph","tooltip":"","color":"","bgcolor":"","icon":"show_chart","payload":"","payloadType":"str","topic":"","x":90,"y":3520,"wires":[["30f41c01.358a54"]]},{"id":"30f41c01.358a54","type":"function","z":"74f191ff.db063","name":"Get filename","func":"// Get the filename from the flow context\nlet filename = flow.get(\"fileselected\");\n\n// check, if the filename is undefined that means it does not exist yet, nothing is selected yet\n// return: do not output anything\nif (filename===undefined) {\n    return;\n}\n\n// return the filename to the file-in node to delete\nmsg.filename = filename;\n\nif (msg.filename.replace(/^.*(\\\\|\\/|\\:)/, '')[0]!==\".\") {\n    // Only do this if this is a file, we don't delete folders\n    return msg;\n}","outputs":1,"noerr":0,"x":260,"y":3520,"wires":[["4f4072b6.0c320c"]]},{"id":"4f4072b6.0c320c","type":"file in","z":"74f191ff.db063","name":"","filename":"","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":440,"y":3520,"wires":[["4d254665.d508e8"]]},{"id":"4d254665.d508e8","type":"csv","z":"74f191ff.db063","name":"","sep":",","hdrin":true,"hdrout":"","multi":"mult","ret":"\\n","temp":"","skip":"0","strings":true,"x":590,"y":3520,"wires":[["7dcec769.580718"]]},{"id":"84810f0e.43f4e","type":"debug","z":"74f191ff.db063","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1010,"y":3480,"wires":[]},{"id":"7dcec769.580718","type":"function","z":"74f191ff.db063","name":"Format data for chart","func":"var chart = [{\n    \"series\":[],\n    \"data\":[],\n    \"labels\":[msg.filename]\n}];\n\n\n/*\nvar pressure = [];\nvar out2 = [];\n\nfor (var i=0; i<msg.payload.length; i++) {\n    pressure.push({\"x\":msg.payload[i].timestamp, \"y\":msg.payload[i].pressure});\n    out2.push({\"x\":msg.payload[i].timestamp, \"y\":msg.payload[i].out2*200});\n\n}\nchart[0].data.push(pressure);\nchart[0].data.push(out2);\n*/\n\nlet columns = 0;\n\nfor(var series in msg.payload[0]) {\n    if(series!==\"timestamp\") {\n        chart[0].series.push(series);\n        chart[0].data.push([]);\n        columns++;\n    }\n}\n\n\nfor (var j=0; j<msg.payload.length; j++) {\n   for(var i=0;i<columns;i++) {\n       chart[0].data[i].push({\"x\":msg.payload[j].timestamp, \"y\":msg.payload[j][chart[0].series[i]]});\n   } \n}\n\n\n\n\nmsg.payload = chart;\n\nreturn msg;","outputs":1,"noerr":0,"x":800,"y":3520,"wires":[["5022cb53.8adc44","84810f0e.43f4e"]]},{"id":"5022cb53.8adc44","type":"ui_chart","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":12,"width":"18","height":"6","label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":1000,"y":3520,"wires":[[]]},{"id":"554f0095.ac1b2","type":"ui_toast","z":"74f191ff.db063","position":"dialog","displayTime":"3","highlight":"","sendall":false,"outputs":1,"ok":"Yes","cancel":"No","raw":false,"topic":"","name":"Confirmation","x":510,"y":3200,"wires":[["24df84fe.ebf45c"]]},{"id":"8da8147a.999af8","type":"change","z":"74f191ff.db063","name":"Set message","rules":[{"t":"set","p":"topic","pt":"msg","to":"Delete confirmation","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"Are you sure you want to delete this file?","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":3200,"wires":[["554f0095.ac1b2"]]},{"id":"24df84fe.ebf45c","type":"switch","z":"74f191ff.db063","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"Yes","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":710,"y":3200,"wires":[["7d490dd1.8458b4"]]},{"id":"3d4e5e51.bdf952","type":"function","z":"74f191ff.db063","name":"Folder handling","func":"let folder = context.get(\"folder\");\nif (folder===undefined) {\n    folder=\"/\";\n    context.set(\"folder\", folder);\n}\n\nlet hidden = context.get(\"hidden\");\nif (hidden===undefined) {\n    hidden=false;\n    context.set(\"hidden\", hidden);\n}\n\nif (msg.topic===\"up\") {\n    var the_arr = folder.split('/');\n    the_arr.pop();\n    folder=the_arr.join('/'); \n    context.set(\"folder\", folder);\n}\nif (msg.topic===\"change\") {\n    folder=msg.payload;    \n    context.set(\"folder\", folder);\n}\nif (msg.topic===\"hidden\") {\n    hidden=msg.payload;    \n    context.set(\"hidden\", hidden);\n}\n\nmsg.payload = {\"start\":folder, \"hidden\": hidden};\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":3100,"wires":[["993d7272.843ae","399ac341.7d43bc","5219875b.c070d8"]]},{"id":"399ac341.7d43bc","type":"ui_text","z":"74f191ff.db063","group":"160e81fb.f1c86e","order":4,"width":"9","height":"1","name":"","label":"Folder:","format":"{{msg.payload.start}}","layout":"row-left","x":500,"y":2980,"wires":[]},{"id":"b40ea1d8.c700a","type":"ui_button","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":3,"width":"2","height":"1","passthru":false,"label":"Up","tooltip":"","color":"","bgcolor":"","icon":"arrow_upwards","payload":"","payloadType":"str","topic":"up","x":110,"y":3120,"wires":[["3d4e5e51.bdf952"]]},{"id":"80940e39.5035b","type":"ui_button","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":8,"width":"2","height":"1","passthru":false,"label":"Open","tooltip":"","color":"","bgcolor":"","icon":"folder_open","payload":"","payloadType":"str","topic":"","x":130,"y":3320,"wires":[["8c1dfaac.979588"]]},{"id":"8c1dfaac.979588","type":"function","z":"74f191ff.db063","name":"Change folder","func":"// Get the filename from the flow context\nlet folderselected = flow.get(\"folderselected\");\n\n// check, if the filename is undefined that means it does not exist yet, nothing is selected yet\n// return: do not output anything\nif (folderselected===undefined) {\n    return;\n}\n\nmsg.topic = \"change\";\nmsg.payload = folderselected;\n\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":3320,"wires":[["3d4e5e51.bdf952"]]},{"id":"58414ec4.c716e","type":"ui_button","z":"74f191ff.db063","name":"","group":"160e81fb.f1c86e","order":2,"width":"2","height":"1","passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"autorenew","payload":"/home/pi","payloadType":"str","topic":"change","x":110,"y":3080,"wires":[["3d4e5e51.bdf952"]]},{"id":"5219875b.c070d8","type":"fs-file-lister","z":"74f191ff.db063","name":"Folders","start":"/home/pi","pattern":"*.*","folders":"*","hidden":false,"lstype":"directories","path":true,"single":true,"depth":0,"stat":true,"showWarnings":false,"x":520,"y":3140,"wires":[["ebc234be.d53fe8"]]},{"id":"37485c24.212054","type":"ui_dropdown","z":"74f191ff.db063","name":"Folder Selector","label":"","tooltip":"","place":"Select a folder","group":"160e81fb.f1c86e","order":7,"width":"5","height":"1","passthru":false,"options":[{"label":"","value":"","type":"str"}],"payload":"","topic":"","x":920,"y":3140,"wires":[["2875c79f.531558"]]},{"id":"ebc234be.d53fe8","type":"function","z":"74f191ff.db063","name":"Format data","func":"// format the data for the dropdown\nmsg.options = [];\nfor (var i=0; i<msg.payload.length; i++) {\n    // This is a foler\n    obj = {};\n    obj [\"[\"+msg.payload[i].name.replace(/^.*(\\\\|\\/|\\:)/, '')+\"]\"]=msg.payload[i].name;\n    msg.options.push(obj);\n}\nmsg.payload={};\nreturn msg;","outputs":1,"noerr":0,"x":710,"y":3140,"wires":[["37485c24.212054"]]},{"id":"be4830df.5775d","type":"change","z":"74f191ff.db063","name":"Save selection","rules":[{"t":"set","p":"fileselected","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1120,"y":3040,"wires":[[]]},{"id":"2875c79f.531558","type":"change","z":"74f191ff.db063","name":"Save selection","rules":[{"t":"set","p":"folderselected","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1120,"y":3140,"wires":[[]]},{"id":"928e60a7.994be","type":"ui_switch","z":"74f191ff.db063","name":"","label":"Hidden","tooltip":"Show hidden files or not","group":"160e81fb.f1c86e","order":5,"width":"2","height":"1","passthru":false,"decouple":"false","topic":"hidden","style":"","onvalue":"true","onvalueType":"bool","onicon":"check_box","oncolor":"#000000","offvalue":"false","offvalueType":"bool","officon":"check_box_outline_blank","offcolor":"#000000","x":100,"y":3160,"wires":[["3d4e5e51.bdf952"]]},{"id":"160e81fb.f1c86e","type":"ui_group","z":"","name":"File Browser","tab":"b63d1f91.68095","order":1,"disp":true,"width":"18","collapse":false},{"id":"b63d1f91.68095","type":"ui_tab","z":"","name":"Files","icon":"dashboard","disabled":false,"hidden":false}]

Flow Info

Created 2 years, 5 months ago
Updated 2 years, 3 months ago
Rating: 4.888888888888889 9

Owner

Actions

Rate:

Node Types

Core
  • change (x4)
  • comment (x1)
  • csv (x1)
  • debug (x1)
  • file (x1)
  • file in (x2)
  • function (x9)
  • http in (x1)
  • http response (x1)
  • inject (x1)
  • switch (x1)
  • template (x1)
Other
  • fs-file-lister (x2)
  • ui_button (x6)
  • ui_chart (x1)
  • ui_dropdown (x2)
  • ui_group (x1)
  • ui_switch (x1)
  • ui_tab (x1)
  • ui_template (x1)
  • ui_text (x1)
  • ui_toast (x1)

Tags

  • browser
  • dashboard
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option