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 (« over ssh, retrieve my ddwrt bandwidth usage »), it provided me with a neat script/solution.
I then get a web page, refreshed hourly.
#!/bin/bash
set -e
# ==================================================
# Configuration
# ==================================================
ROUTER_IP="192.168.1.250"
ROUTER_USER="root"
SSH_KEY="$HOME/.ssh/id_ed25519"
OUTPUT_HTML="/var/www/html/traffic.html"
MONTH=$(date +%m)
YEAR=$(date +%Y)
NVRAM_KEY="traff-$MONTH-$YEAR"
GENERATED_AT=$(date '+%d/%m/%Y à %H:%M')
# ==================================================
# Récupération des données depuis le routeur
# ==================================================
RAW_DATA=$(ssh -i "$SSH_KEY" -q "$ROUTER_USER@$ROUTER_IP" "nvram get $NVRAM_KEY")
# Total mensuel [incoming:outgoing]
TOTAL=$(echo "$RAW_DATA" | grep -o '\[.*\]')
# Données journalières uniquement
DATA=$(echo "$RAW_DATA" | sed 's/\[.*\]//')
# ==================================================
# Génération de la page HTML (écriture root)
# ==================================================
sudo tee "$OUTPUT_HTML" > /dev/null <<EOF
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Trafic DD-WRT - $MONTH/$YEAR</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background: #f4f4f9;
margin: 40px;
}
h1 {
text-align: center;
}
#chart-container {
max-width: 1100px;
margin: auto;
}
.generated {
position: fixed;
bottom: 10px;
right: 15px;
font-size: 0.85em;
color: #666;
}
</style>
</head>
<body>
<h1>Trafic journalier DD-WRT – $MONTH/$YEAR</h1>
<div id="chart-container">
<canvas id="trafficChart"></canvas>
</div>
<p style="text-align:center; font-size:1.1em;">
<strong>Total du mois :</strong> $TOTAL MB (incoming : outgoing)
</p>
<div class="generated">
Généré le $GENERATED_AT
</div>
<script>
const rawData = "$DATA";
const incoming = [];
const outgoing = [];
rawData.trim().split(' ').forEach(pair => {
const [inVal, outVal] = pair.split(':').map(Number);
incoming.push(inVal);
outgoing.push(outVal);
});
const labels = Array.from({ length: incoming.length }, (_, i) => 'Jour ' + (i + 1));
const ctx = document.getElementById('trafficChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [
{
label: 'Incoming (MB)',
data: incoming,
backgroundColor: 'rgba(54, 162, 235, 0.7)'
},
{
label: 'Outgoing (MB)',
data: outgoing,
backgroundColor: 'rgba(255, 99, 132, 0.7)'
}
]
},
options: {
responsive: true,
plugins: {
legend: { position: 'top' },
tooltip: {
callbacks: {
label: ctx =>
ctx.dataset.label + ': ' + ctx.raw.toLocaleString() + ' MB'
}
}
},
scales: {
x: { stacked: true },
y: {
stacked: true,
beginAtZero: true,
title: {
display: true,
text: 'MB'
}
}
}
}
});
</script>
</body>
</html>
EOF
