Add more stats

This commit is contained in:
Anton Roslund
2025-08-09 12:20:54 +02:00
parent 968d7613f4
commit 1e9b5aad1f
3 changed files with 181 additions and 1 deletions
+17 -1
View File
@@ -79,6 +79,20 @@ Antalet enheter av firmware version som synts i meshet de senaste 30 dagarna. In
<canvas id="firmwareVersionChart"></canvas>
</div>
## Unmessagable
Visar antalet noder som är markerade som "unmessagable" respektive "messagable" baserat på de senaste 30 dagarnas aktivitet. Inställningen "unmessagable" används för att identifiera oövervakade infrastrukturnoder så att meddelanden inte skickas till noder som aldrig kommer att svara. Noder som inte har denna inställning definierad räknas som "messagable" eftersom denna egenskap infördes i version 2.6.8.
<div id="isUnmessagableContainer" class="stats-chart-container">
<canvas id="isUnmessagableChart"></canvas>
</div>
## OK to MQTT
Visar antalet noder som har "ok_to_mqtt" aktiverad eller avstängd under de senaste 30 dagarna. Flaggan är en begäran om att paket inte ska vidarebefordras till MQTT-servrar och karttjänster som sthlm-mesh. Begäran behöver dock inte respekteras. För att undvika exponering på karttjänster bör man använda krypterade kanaler istället för att dela information publikt.
Äldre firmware skickar inte denna flagga och behandlas som falskt värde.
<div id="isOkToMqttContainer" class="stats-chart-container">
<canvas id="isOkToMqttChart"></canvas>
</div>
## Batteri
Visar genomsnittlig batterinivå av de noder som rapporterat batteri nivå och inte har fast strömförsörjning.
@@ -122,4 +136,6 @@ Graferna baseras på data från [map.sthlm-mesh.se](https://map.sthlm-mesh.se),
<script src="/js/status/nodes-seen.js"></script>
<script src="/js/status/firmware-versions.js"></script>
<script src="/js/status/battery-stats.js"></script>
<script src="/js/status/channel-utilization.js"></script>
<script src="/js/status/channel-utilization.js"></script>
<script src="/js/status/is-unmessagable-chart.js"></script>
<script src="/js/status/is-ok-to-mqtt-chart.js"></script>
+80
View File
@@ -0,0 +1,80 @@
async function isOkToMqttGraph() {
const canvas = document.getElementById('isOkToMqttChart');
const ctx = canvas.getContext('2d');
// Show "Loading..." message
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = '16px Arial';
ctx.fillStyle = 'gray';
ctx.textAlign = 'center';
ctx.fillText('Loading data...', canvas.width / 2, canvas.height / 2);
try {
await fetchNodes();
// Filter nodes to only include those updated within the last 30 days
const recentNodes = nodes.filter(node => new Date(node.updated_at) >= new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
let countTrue = 0;
let countFalse = 0;
let countNull = 0;
for (const node of recentNodes) {
const val = node.ok_to_mqtt;
if (val === true) countTrue++;
else if (val === false) countFalse++;
else countNull++;
}
const container = canvas.parentElement;
if (container) container.style.height = `${2 * 35 + 50}px`;
ctx.clearRect(0, 0, canvas.width, canvas.height);
new Chart(ctx, {
type: 'bar',
data: {
labels: ['True', 'False'],
datasets: [
{
label: 'true',
data: [countTrue, 0,],
backgroundColor: '#7EB26D', // blue
},
{
label: 'false',
data: [0, countFalse],
backgroundColor: '#BF1B00', // green
},
{
label: 'unset',
data: [0, countNull],
backgroundColor: '#808080', // grey
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
plugins: {
legend: { display: false }
},
scales: {
x: { beginAtZero: true, stacked: true },
y: { stacked: true }
}
}
});
} catch (error) {
console.error('Error building ok_to_mqtt chart:', error);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'red';
ctx.fillText('Error loading data', canvas.width / 2, canvas.height / 2);
}
}
isOkToMqttGraph();
+84
View File
@@ -0,0 +1,84 @@
async function isUnmessagableGraph() {
const canvas = document.getElementById('isUnmessagableChart');
const ctx = canvas.getContext('2d');
// Show "Loading..." message
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = '16px Arial';
ctx.fillStyle = 'gray';
ctx.textAlign = 'center';
ctx.fillText('Loading data...', canvas.width / 2, canvas.height / 2);
try {
await fetchNodes();
// Filter nodes to only include those updated within the last 30 days
const recentNodes = nodes.filter(node => new Date(node.updated_at) >= new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
let countTrue = 0;
let countFalse = 0;
let countNull = 0;
for (const node of recentNodes) {
const val = node.is_unmessagable;
if (val === true) countTrue++;
else if (val === false) countFalse++;
else countNull++;
}
const container = canvas.parentElement;
if (container) container.style.height = `${2 * 35 + 50}px`;
ctx.clearRect(0, 0, canvas.width, canvas.height);
new Chart(ctx, {
type: 'bar',
data: {
labels: ['unmessagable', 'messagable'],
datasets: [
{
label: 'true',
data: [countTrue, 0,],
backgroundColor: '#0d6efd', // blue
},
{
label: 'false',
data: [0, countFalse],
backgroundColor: '#7EB26D', // green
},
{
label: 'unset',
data: [0, countNull],
backgroundColor: '#808080', // grey
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
plugins: {
legend: { display: false }
},
scales: {
x: {
beginAtZero: true,
stacked: true,
},
y: {
stacked: true,
}
}
}
});
} catch (error) {
console.error('Error building is_unmessagable chart:', error);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'red';
ctx.fillText('Error loading data', canvas.width / 2, canvas.height / 2);
}
}
isUnmessagableGraph();