v0.14.2-connectivity

This commit is contained in:
Mateusz Gruszczyński
2026-09-15 10:02:49 +02:00
parent 2fd7e47eb9
commit c3f6e773f8
41 changed files with 1295 additions and 108 deletions
+23 -8
View File
@@ -106,6 +106,11 @@ function renderHistoryNavigation() {
const intervalSelect = $('#historyEnergyInterval'); if (intervalSelect) intervalSelect.value = app.historyEnergyInterval;
const compareSelect = $('#historyEnergyCompare'); if (compareSelect) compareSelect.value = app.historyEnergyCompare;
if (energyPickerWasOpen && $('#historyEnergyTargetPicker')) $('#historyEnergyTargetPicker').open = true;
} else if (app.historyTab === 'network') {
const targets = app.historyNetworkTargets || [];
const targetOptions = targets.map(target => `<option value="${esc(target.id)}">${esc(target.name)}</option>`).join('');
host.innerHTML = `<label><span>${esc(tr('history.networkTarget'))}</span><select id="historyNetworkSelect"><option value="all">${esc(tr('history.allNetworkTargets'))}</option>${targetOptions}</select></label>`;
const select = $('#historyNetworkSelect'); if ([...select.options].some(option => option.value === app.historyNetworkTarget)) select.value = app.historyNetworkTarget;
} else if (app.historyTab === 'sensors') {
host.innerHTML = `<label><span>${esc(tr('history.haSensor'))}</span><select id="historySensorSelect"><option value="all">${esc(tr('history.allSensors'))}</option>${options.sensors}</select></label>`;
const select = $('#historySensorSelect'); if ([...select.options].some(option => option.value === app.historySensor)) select.value = app.historySensor;
@@ -129,6 +134,16 @@ async function loadHistory() {
if (app.currentView === 'history') updateBrowserUrl(currentHistoryPath(), true);
return;
}
if (app.historyTab === 'network') {
const target = app.historyNetworkTarget && app.historyNetworkTarget !== 'all' ? `&target_id=${encodeURIComponent(app.historyNetworkTarget)}` : '';
const data = await api(`/api/history/network?hours=${encodeURIComponent(hours)}&limit=20000${target}`);
app.historyNetwork = data.readings || [];
app.historyNetworkTargets = data.targets || [];
renderHistoryNavigation();
renderHistoryPage();
if (app.currentView === 'history') updateBrowserUrl(currentHistoryPath(), true);
return;
}
const data = await api(`/api/history?scope=overview&hours=${encodeURIComponent(hours)}&limit=20000`);
app.historyData = { zones: data.zones || [], devices: data.devices || [], sensors: data.sensors || [] };
app.historyCounts = data.counts || {};
@@ -323,11 +338,11 @@ function nearestPoint(points, targetTs) {
return Math.abs(before.ts - targetTs) <= Math.abs(after.ts - targetTs) ? before : after;
}
function formatChartTooltipValue(item, point, binaryLabels) {
function formatChartTooltipValue(item, point, binaryLabels, tooltipUnit = ' °C') {
if (!point || !Number.isFinite(point.value)) return '—';
if (typeof item.tooltipValue === 'function') return String(item.tooltipValue(point.value, point.row));
const formatted = Number(point.value).toLocaleString(locale(), { minimumFractionDigits: 1, maximumFractionDigits: 2 });
return binaryLabels ? formatted : `${formatted} °C`;
return binaryLabels ? formatted : `${formatted}${tooltipUnit}`;
}
function bindChartTooltip(canvas, series, sortedRows, geometry) {
@@ -337,7 +352,7 @@ function bindChartTooltip(canvas, series, sortedRows, geometry) {
const selection = wrap?.querySelector('.chart-selection');
if (!canvas || !wrap || !tooltip || !line) return;
const { pad, width, height, firstTs, lastTs, binaryLabels } = geometry;
const { pad, width, height, firstTs, lastTs, binaryLabels, tooltipUnit } = geometry;
const plotWidth = width - pad.left - pad.right;
const timestamps = [...new Set(sortedRows.map(row => new Date(row.timestamp).getTime()).filter(Number.isFinite))].sort((a, b) => a - b);
const timestampPoints = timestamps.map(ts => ({ ts, value: ts }));
@@ -367,7 +382,7 @@ function bindChartTooltip(canvas, series, sortedRows, geometry) {
$$('.chart-hover-line').forEach(node => { if (node !== line) node.hidden = true; });
tooltip.innerHTML = `<strong class="chart-tooltip-time">${esc(chartPreciseTime(snapped))}</strong><div class="chart-tooltip-values">${values.map(({ item, point }) => {
const sampleTime = Math.abs(point.ts - snapped) > 1000 ? `<small class="chart-tooltip-sample-time">${esc(chartPreciseTime(point.ts))}</small>` : '';
return `<div class="chart-tooltip-row"><span class="chart-tooltip-label"><i style="--tooltip-color:${esc(item.color)}"></i><span>${esc(item.label)}</span></span><span class="chart-tooltip-value">${esc(formatChartTooltipValue(item, point, binaryLabels))}${sampleTime}</span></div>`;
return `<div class="chart-tooltip-row"><span class="chart-tooltip-label"><i style="--tooltip-color:${esc(item.color)}"></i><span>${esc(item.label)}</span></span><span class="chart-tooltip-value">${esc(formatChartTooltipValue(item, point, binaryLabels, tooltipUnit))}${sampleTime}</span></div>`;
}).join('')}</div>`;
tooltip.hidden = false;
line.hidden = false;
@@ -454,9 +469,9 @@ function bindChartTooltip(canvas, series, sortedRows, geometry) {
};
}
function drawLineChart(canvas, series, rows, { height = 340, minValue = null, maxValue = null, binaryLabels = false } = {}) {
function drawLineChart(canvas, series, rows, { height = 340, minValue = null, maxValue = null, binaryLabels = false, axisSuffix = '°', tooltipUnit = ' °C', axisDigits = 1 } = {}) {
if (!canvas) return;
const options = { height, minValue, maxValue, binaryLabels };
const options = { height, minValue, maxValue, binaryLabels, axisSuffix, tooltipUnit, axisDigits };
if (canvas.id) chartRuntime.set(canvas.id, { series, rows, options });
const visibleSeries = series.filter((item, index) => !isChartSeriesHidden(canvas.id, item, index));
if (!rows.length || !visibleSeries.length) return drawEmptyChart(canvas, height);
@@ -482,7 +497,7 @@ function drawLineChart(canvas, series, rows, { height = 340, minValue = null, ma
for (let i = 0; i <= 5; i++) {
const value = min + (max - min) * i / 5, py = y(value);
ctx.beginPath(); ctx.moveTo(pad.left, py); ctx.lineTo(width - pad.right, py); ctx.stroke();
ctx.textAlign = 'right'; ctx.fillText(binaryLabels ? value.toFixed(0) : `${value.toFixed(1)}°`, pad.left - 8, py + 3);
ctx.textAlign = 'right'; ctx.fillText(binaryLabels ? value.toFixed(0) : `${value.toFixed(axisDigits)}${axisSuffix}`, pad.left - 8, py + 3);
}
const ticks = width < 420 ? 2 : width < 620 ? 3 : 5;
for (let i = 0; i <= ticks; i++) {
@@ -501,7 +516,7 @@ function drawLineChart(canvas, series, rows, { height = 340, minValue = null, ma
});
ctx.stroke(); ctx.setLineDash([]);
});
bindChartTooltip(canvas, visibleSeries, sortedRows, { pad, width, height, firstTs, lastTs, binaryLabels });
bindChartTooltip(canvas, visibleSeries, sortedRows, { pad, width, height, firstTs, lastTs, binaryLabels, tooltipUnit });
}
function energyBucketLabel(timestamp, interval) {