{"id":2617,"date":"2025-12-30T14:58:22","date_gmt":"2025-12-30T13:58:22","guid":{"rendered":"https:\/\/labalec.fr\/erwan\/?p=2617"},"modified":"2025-12-30T15:53:15","modified_gmt":"2025-12-30T14:53:15","slug":"bandwidth-usage-on-my-ddwrt-router","status":"publish","type":"post","link":"https:\/\/labalec.fr\/erwan\/?p=2617","title":{"rendered":"Bandwidth usage on my DDWRT router"},"content":{"rendered":"\n<p>8 years ago I had come with a dirty solution to graph my router bandwidth usage (<a href=\"https:\/\/labalec.fr\/erwan\/?p=1766\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>).<\/p>\n\n\n\n<p>Now, we have AI (chatgpt here) and with a single prompt (\u00ab\u00a0over ssh, retrieve my ddwrt bandwidth usage\u00a0\u00bb), it provided me with a neat script\/solution.<\/p>\n\n\n\n<p>I then get a web page, refreshed hourly.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\nset -e\n\n# ==================================================\n# Configuration\n# ==================================================\nROUTER_IP=\"192.168.1.250\"\nROUTER_USER=\"root\"\nSSH_KEY=\"$HOME\/.ssh\/id_ed25519\"\nOUTPUT_HTML=\"\/var\/www\/html\/traffic.html\"\n\nMONTH=$(date +%m)\nYEAR=$(date +%Y)\nNVRAM_KEY=\"traff-$MONTH-$YEAR\"\n\nGENERATED_AT=$(date '+%d\/%m\/%Y \u00e0 %H:%M')\n\n# ==================================================\n# R\u00e9cup\u00e9ration des donn\u00e9es depuis le routeur\n# ==================================================\nRAW_DATA=$(ssh -i \"$SSH_KEY\" -q \"$ROUTER_USER@$ROUTER_IP\" \"nvram get $NVRAM_KEY\")\n\n# Total mensuel &#91;incoming:outgoing]\nTOTAL=$(echo \"$RAW_DATA\" | grep -o '\\&#91;.*\\]')\n\n# Donn\u00e9es journali\u00e8res uniquement\nDATA=$(echo \"$RAW_DATA\" | sed 's\/\\&#91;.*\\]\/\/')\n\n# ==================================================\n# G\u00e9n\u00e9ration de la page HTML (\u00e9criture root)\n# ==================================================\nsudo tee \"$OUTPUT_HTML\" > \/dev\/null &lt;&lt;EOF\n&lt;!DOCTYPE html>\n&lt;html lang=\"fr\">\n&lt;head>\n&lt;meta charset=\"UTF-8\">\n&lt;title>Trafic DD-WRT - $MONTH\/$YEAR&lt;\/title>\n&lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\">&lt;\/script>\n&lt;style>\n  body {\n    font-family: Arial, sans-serif;\n    background: #f4f4f9;\n    margin: 40px;\n  }\n  h1 {\n    text-align: center;\n  }\n  #chart-container {\n    max-width: 1100px;\n    margin: auto;\n  }\n  .generated {\n    position: fixed;\n    bottom: 10px;\n    right: 15px;\n    font-size: 0.85em;\n    color: #666;\n  }\n&lt;\/style>\n&lt;\/head>\n&lt;body>\n\n&lt;h1>Trafic journalier DD-WRT \u2013 $MONTH\/$YEAR&lt;\/h1>\n\n&lt;div id=\"chart-container\">\n  &lt;canvas id=\"trafficChart\">&lt;\/canvas>\n&lt;\/div>\n\n&lt;p style=\"text-align:center; font-size:1.1em;\">\n  &lt;strong>Total du mois :&lt;\/strong> $TOTAL MB (incoming : outgoing)\n&lt;\/p>\n\n&lt;div class=\"generated\">\n  G\u00e9n\u00e9r\u00e9 le $GENERATED_AT\n&lt;\/div>\n\n&lt;script>\nconst rawData = \"$DATA\";\n\nconst incoming = &#91;];\nconst outgoing = &#91;];\n\nrawData.trim().split(' ').forEach(pair => {\n  const &#91;inVal, outVal] = pair.split(':').map(Number);\n  incoming.push(inVal);\n  outgoing.push(outVal);\n});\n\nconst labels = Array.from({ length: incoming.length }, (_, i) => 'Jour ' + (i + 1));\n\nconst ctx = document.getElementById('trafficChart').getContext('2d');\nnew Chart(ctx, {\n  type: 'bar',\n  data: {\n    labels: labels,\n    datasets: &#91;\n      {\n        label: 'Incoming (MB)',\n        data: incoming,\n        backgroundColor: 'rgba(54, 162, 235, 0.7)'\n      },\n      {\n        label: 'Outgoing (MB)',\n        data: outgoing,\n        backgroundColor: 'rgba(255, 99, 132, 0.7)'\n      }\n    ]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      legend: { position: 'top' },\n      tooltip: {\n        callbacks: {\n          label: ctx =>\n            ctx.dataset.label + ': ' + ctx.raw.toLocaleString() + ' MB'\n        }\n      }\n    },\n    scales: {\n      x: { stacked: true },\n      y: {\n        stacked: true,\n        beginAtZero: true,\n        title: {\n          display: true,\n          text: 'MB'\n        }\n      }\n    }\n  }\n});\n&lt;\/script>\n\n&lt;\/body>\n&lt;\/html>\nEOF\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"602\" src=\"https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1-1024x602.png\" alt=\"\" class=\"wp-image-2620\" srcset=\"https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1-1024x602.png 1024w, https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1-300x176.png 300w, https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1-768x452.png 768w, https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1-1536x903.png 1536w, https:\/\/labalec.fr\/erwan\/wp-content\/uploads\/2025\/12\/image-1.png 1758w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>8 years ago I had come with a dirty solution to graph my router bandwidth usage (here). Now, we have AI (chatgpt here) and with a single prompt (\u00ab\u00a0over ssh, retrieve my ddwrt bandwidth usage\u00a0\u00bb), it provided me with a neat script\/solution. I then get a web page, refreshed hourly.<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[148],"tags":[],"class_list":["post-2617","post","type-post","status-publish","format-standard","hentry","category-ddwrt","category-148-id","post-seq-1","post-parity-odd","meta-position-corners","fix"],"_links":{"self":[{"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=\/wp\/v2\/posts\/2617","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2617"}],"version-history":[{"count":2,"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=\/wp\/v2\/posts\/2617\/revisions"}],"predecessor-version":[{"id":2621,"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=\/wp\/v2\/posts\/2617\/revisions\/2621"}],"wp:attachment":[{"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2617"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2617"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/labalec.fr\/erwan\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2617"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}