Author SHA1 Message Date
Pablo Revilla fbbc5a73d6 Revert "feat(node): add power metrics chart (#190)"
This reverts commit a298347f14.
2026-11-09 13:57:08 -08:00
4 changed files with 24 additions and 139 deletions

No files matched your search

-5
View File
@@ -244,7 +244,6 @@
"battery_voltage": "Battery & Voltage",
"air_channel": "Air & Channel Utilization",
"environment": "Environment Metrics",
"power": "Power Metrics",
"neighbors_chart": "Neighbors (Signal-to-Noise)",
"neighbors_chart_subtitle": "Last {days} days — {n} neighbors",
"neighbors_chart_subtitle_capped": "Last {days} days — the {n} neighbors with the most data points, of {total}",
@@ -445,8 +444,6 @@
"Channel:": "Channel:",
"Chat messages API": "Chat messages API",
"Chat uses": "Chat uses",
"Ch{n} current (mA)": "Ch{n} current (mA)",
"Ch{n} voltage (V)": "Ch{n} voltage (V)",
"Click to view text packets": "Click to view text packets",
"Close": "Close",
"Close expanded chart": "Close expanded chart",
@@ -474,8 +471,6 @@
"Drag nodes to rearrange · Scroll to zoom": "Drag nodes to rearrange · Scroll to zoom",
"Elevated": "Elevated",
"Enter a valid node ID or select a node.": "Enter a valid node ID or select a node.",
"Environment current (mA)": "Environment current (mA)",
"Environment voltage (V)": "Environment voltage (V)",
"Error": "Error",
"Error Details": "Error Details",
"Error loading nodes": "Error loading nodes",
-5
View File
@@ -252,7 +252,6 @@
"battery_voltage": "Batería y voltaje",
"air_channel": "Utilización del aire y del canal",
"environment": "Métricas Ambientales",
"power": "Métricas de potencia",
"neighbors_chart": "Vecinos (Relación Señal/Ruido)",
"neighbors_chart_subtitle": "Últimos {days} días — {n} vecinos",
"neighbors_chart_subtitle_capped": "Últimos {days} días — los {n} vecinos con más datos, de {total}",
@@ -453,8 +452,6 @@
"Channel:": "Canal:",
"Chat messages API": "API de mensajes de chat",
"Chat uses": "El chat usa",
"Ch{n} current (mA)": "Corriente Ch{n} (mA)",
"Ch{n} voltage (V)": "Voltaje Ch{n} (V)",
"Click to view text packets": "Pulse para ver los paquetes de texto",
"Close": "Cerrar",
"Close expanded chart": "Cerrar gráfico ampliado",
@@ -482,8 +479,6 @@
"Drag nodes to rearrange · Scroll to zoom": "Arrastre los nodos para reorganizar · Desplace para ampliar",
"Elevated": "Elevada",
"Enter a valid node ID or select a node.": "Introduzca un ID válido o seleccione un nodo.",
"Environment current (mA)": "Corriente ambiental (mA)",
"Environment voltage (V)": "Voltaje ambiental (V)",
"Error": "Error",
"Error Details": "Detalles del error",
"Error loading nodes": "Error al cargar nodos",
-5
View File
@@ -244,7 +244,6 @@
"battery_voltage": "Батарея и напряжение",
"air_channel": "Использование сети и каналов",
"environment": "Метрики среды",
"power": "Метрики питания",
"neighbors_chart": "Соседи (Соотношение сигнал/шум)",
"neighbors_chart_subtitle": "Последние {days} дн. — {n} соседей",
"neighbors_chart_subtitle_capped": "Последние {days} дн. — {n} соседей с наибольшим числом точек, из {total}",
@@ -445,8 +444,6 @@
"Channel:": "Канал:",
"Chat messages API": "API сообщений чата",
"Chat uses": "Чат использует",
"Ch{n} current (mA)": "Ток Ch{n} (мА)",
"Ch{n} voltage (V)": "Напряжение Ch{n} (В)",
"Click to view text packets": "Нажмите для просмотра текстовых пакетов",
"Close": "Закрыть",
"Close expanded chart": "Закрыть развёрнутый график",
@@ -474,8 +471,6 @@
"Drag nodes to rearrange · Scroll to zoom": "Перетаскивайте узлы · Масштабируйте прокруткой",
"Elevated": "Повышенная",
"Enter a valid node ID or select a node.": "Введите корректный ID или выберите узел.",
"Environment current (mA)": "Ток (среда) (мА)",
"Environment voltage (V)": "Напряжение (среда) (В)",
"Error": "Ошибка",
"Error Details": "Подробности ошибки",
"Error loading nodes": "Ошибка загрузки нод",
+24 -124
View File
@@ -139,18 +139,6 @@
<div id="chart_environment" style="height:380px;"></div>
</div>
<!-- Power Metrics -->
<div id="power_chart_container" class="chart-container" style="display:none;">
<div class="chart-header">
⚡ <span data-translate-lang="power">Power Metrics</span>
<div class="chart-actions">
<button onclick="expandChart('power')" data-translate-lang="expand">Expand</button>
<button onclick="exportCSV('power')" data-translate-lang="export_csv">Export CSV</button>
</div>
</div>
<div id="chart_power" style="height:380px;"></div>
</div>
<!-- Neighbor chart -->
<!-- Neighbor Time-Series Chart -->
<div id="neighbor_chart_container" class="chart-container">
@@ -886,30 +874,7 @@ async function loadTelemetryCharts(){
times: [],
battery: [], voltage: [],
airUtil: [], chanUtil: [],
temperature: [], humidity: [], pressure: [],
// power_metrics channels (ch1..ch8) plus the legacy voltage/current
// that INA sensors report inside environment_metrics.
power: {}, powerSeries: []
};
const POWER_CHANNELS = [1, 2, 3, 4, 5, 6, 7, 8];
for (const ch of POWER_CHANNELS) {
chartData.power[`ch${ch}_voltage`] = [];
chartData.power[`ch${ch}_current`] = [];
}
chartData.power.env_voltage = [];
chartData.power.env_current = [];
// Return the body of a "name { ... }" block from the text-format payload.
// Several telemetry variants share field names (voltage, temperature...),
// so each value must be read only from its own block.
const metricsBlock = (pl, name) =>
pl.match(new RegExp(`(?:^|\\n)\\s*${name}\\s*\\{([^}]*)\\}`))?.[1] || "";
// Read a numeric field by its exact name (line-anchored, so "voltage"
// does not match "ch1_voltage").
const metricField = (block, name) => {
const m = block.match(new RegExp(`(?:^|\\n)\\s*${name}:\\s*(-?[\\d.]+(?:[eE][-+]?\\d+)?)`));
return m ? parseFloat(m[1]) : NaN;
temperature: [], humidity: [], pressure: []
};
for (const pkt of packets.reverse()) {
@@ -919,24 +884,28 @@ async function loadTelemetryCharts(){
t.toLocaleString(MeshI18n.language, { month:"2-digit", day:"2-digit", hour:"2-digit", minute:"2-digit" })
);
const dev = metricsBlock(pl, "device_metrics");
const env = metricsBlock(pl, "environment_metrics");
const pwr = metricsBlock(pl, "power_metrics");
for (const ch of POWER_CHANNELS) {
chartData.power[`ch${ch}_voltage`].push(metricField(pwr, `ch${ch}_voltage`));
chartData.power[`ch${ch}_current`].push(metricField(pwr, `ch${ch}_current`));
}
chartData.power.env_voltage.push(metricField(env, "voltage"));
chartData.power.env_current.push(metricField(env, "current"));
chartData.battery.push(metricField(dev, "battery_level"));
chartData.voltage.push(metricField(dev, "voltage"));
chartData.airUtil.push(metricField(dev, "air_util_tx"));
chartData.chanUtil.push(metricField(dev, "channel_utilization"));
chartData.temperature.push(metricField(env, "temperature"));
chartData.humidity.push(metricField(env, "relative_humidity"));
chartData.pressure.push(metricField(env, "barometric_pressure"));
// Matches your payload exactly (inside device_metrics {})
chartData.battery.push(
parseFloat(pl.match(/battery_level:\s*([\d.]+)/)?.[1] || NaN)
);
chartData.voltage.push(
parseFloat(pl.match(/voltage:\s*([\d.]+)/)?.[1] || NaN)
);
chartData.airUtil.push(
parseFloat(pl.match(/air_util_tx:\s*([\d.]+)/)?.[1] || NaN)
);
chartData.chanUtil.push(
parseFloat(pl.match(/channel_utilization:\s*([\d.]+)/)?.[1] || NaN)
);
chartData.temperature.push(
parseFloat(pl.match(/temperature:\s*([\d.]+)/)?.[1] || NaN)
);
chartData.humidity.push(
parseFloat(pl.match(/relative_humidity:\s*([\d.]+)/)?.[1] || NaN)
);
chartData.pressure.push(
parseFloat(pl.match(/barometric_pressure:\s*([\d.]+)/)?.[1] || NaN)
);
}
const hasBattery = chartData.battery.some(v => !isNaN(v));
@@ -1046,73 +1015,9 @@ async function loadTelemetryCharts(){
envContainer.style.display = "none";
}
// Power chart: one voltage (left axis) and one current (right axis) series
// per channel that reported data. Current is dashed and unfilled so up to
// 18 series stay readable.
const POWER_COLORS = [
'rgba(79,155,255,1)', 'rgba(255,214,82,1)', 'rgba(138,255,108,1)',
'rgba(255,102,204,1)', 'rgba(255,138,82,1)', 'rgba(0,212,212,1)',
'rgba(178,132,255,1)', 'rgba(200,200,200,1)'
];
const ENV_POWER_COLOR = 'rgba(255,80,80,1)';
const powerSources = [
...POWER_CHANNELS.map(ch => ({
key: `ch${ch}`,
color: POWER_COLORS[ch - 1],
voltageLabel: MeshI18n.t("Ch{n} voltage (V)", {n: ch}),
currentLabel: MeshI18n.t("Ch{n} current (mA)", {n: ch})
})),
{
key: "env",
color: ENV_POWER_COLOR,
voltageLabel: MeshI18n.t("Environment voltage (V)"),
currentLabel: MeshI18n.t("Environment current (mA)")
}
];
const hasData = values => values.some(v => !isNaN(v));
for (const src of powerSources) {
const voltage = chartData.power[`${src.key}_voltage`];
const current = chartData.power[`${src.key}_current`];
if (hasData(voltage))
chartData.powerSeries.push({ label: src.voltageLabel, color: src.color, data: voltage, axis: 0 });
if (hasData(current))
chartData.powerSeries.push({ label: src.currentLabel, color: src.color, data: current, axis: 1 });
}
const powerContainer = document.getElementById("power_chart_container");
let chart4 = null;
if (chartData.powerSeries.length) {
powerContainer.style.display = "block";
chart4 = initMeshChart(document.getElementById('chart_power'));
chart4.setOption({
tooltip: { trigger:'axis' },
legend: {
type: 'scroll',
data: chartData.powerSeries.map(s => s.label),
textStyle:{ color:'#64748b' }
},
xAxis: { type:'category', data:chartData.times, axisLabel:{ color:'#64748b' } },
yAxis: [
{ type:'value', name:'Voltage (V)', axisLabel:{ color:'#64748b' } },
{ type:'value', name:'Current (mA)', axisLabel:{ color:'#64748b' } }
],
series: chartData.powerSeries.map(s => {
const line = makeLine(s.label, s.color, s.data, s.axis);
if (s.axis === 1) {
delete line.areaStyle;
line.lineStyle = { ...line.lineStyle, type: 'dashed' };
line.symbol = 'triangle';
}
return line;
})
});
} else {
powerContainer.style.display = "none";
}
// Resize charts that exist
window.addEventListener("resize", () => {
[chart1, chart2, chart3, chart4].forEach(c => { if (c) c.resize(); });
[chart1, chart2, chart3].forEach(c => { if (c) c.resize(); });
});
}
@@ -1485,11 +1390,6 @@ function exportCSV(type){
chartData.pressure[i]
]);
}
else if (type === "power") {
rows[0].push(...chartData.powerSeries.map(s => s.label));
for (let i = 0; i < chartData.times.length; i++)
rows.push([chartData.times[i], ...chartData.powerSeries.map(s => isNaN(s.data[i]) ? null : s.data[i])]);
}
else if (type === "neighbors") {
rows[0] = [MeshI18n.t("Neighbor Node ID"), MeshI18n.t("Neighbor Name"), "SNR (dB)"];
for (let i = 0; i < neighborData.ids.length; i++) {