diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 3d0a26f..6c35d06 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -30,11 +30,11 @@
-
+

History

-

Temperature & humidity over time

+

@@ -191,25 +191,86 @@ function renderMessages() { `).join(''); } +const chartPresets = { + Temperature: { + subtitle: 'Temperature & humidity over time', + datasets: (msgs) => [ + makeDs('Temperature °C', msgs.map(m => m.decoded?.temperature_c ?? null), '#2563eb', 'y'), + makeDs('Humidity %', msgs.map(m => m.decoded?.humidity_pct ?? null), '#059669', 'y1'), + ], + }, + Light: { + subtitle: 'Brightness over time', + datasets: (msgs) => [ + makeDs('Brightness (lux)', msgs.map(m => m.decoded?.brightness_lux ?? null), '#d97706', 'y'), + ], + }, + Door: { + subtitle: 'Cumulative openings', + datasets: (msgs) => [ + makeDs('Event count', msgs.map(m => m.decoded?.event_count ?? null), '#7c3aed', 'y'), + ], + }, + Vibration: { + subtitle: 'Cumulative vibration events', + datasets: (msgs) => [ + makeDs('Event count', msgs.map(m => m.decoded?.event_count ?? null), '#e11d48', 'y'), + ], + }, + Move: { + subtitle: 'Cumulative movement events', + datasets: (msgs) => [ + makeDs('Move events', msgs.map(m => m.decoded?.move_events ?? null), '#e11d48', 'y'), + ], + }, + Magnet: { + subtitle: 'Cumulative magnet events', + datasets: (msgs) => [ + makeDs('Event count', msgs.map(m => m.decoded?.event_count ?? null), '#7c3aed', 'y'), + ], + }, + 'Reed Switch': { + subtitle: 'Cumulative reed switch events', + datasets: (msgs) => [ + makeDs('Reed events', msgs.map(m => m.decoded?.reed_events ?? null), '#7c3aed', 'y'), + ], + }, +}; + +function makeDs(label, data, color, yID) { + return { + label, data, borderColor: color, backgroundColor: color + '14', + yAxisID: yID, tension: 0.35, borderWidth: 2, fill: true, + spanGaps: true, pointRadius: 2, pointHoverRadius: 4, + }; +} + function renderChart() { const dev = state.selectedDevice || document.getElementById('chart-device').value; - if (!dev) return; + const section = document.getElementById('chart-section'); + const subtitle = document.getElementById('chart-subtitle'); + if (!dev) { section.style.display = 'none'; return; } + const msgs = (state.messagesByDevice[dev] || []).slice().reverse(); + const latestMode = msgs.length ? msgs[msgs.length - 1].decoded?.mode : null; + const preset = chartPresets[latestMode]; + + if (!preset) { + section.style.display = 'none'; + return; + } + section.style.display = ''; + subtitle.textContent = preset.subtitle; + const labels = msgs.map(m => fmtShortTime(m.ts)); - const temps = msgs.map(m => m.decoded?.temperature_c ?? null); - const humi = msgs.map(m => m.decoded?.humidity_pct ?? null); + const datasets = preset.datasets(msgs); + const hasSecondAxis = datasets.some(ds => ds.yAxisID === 'y1'); const ctx = document.getElementById('chart').getContext('2d'); if (state.chart) state.chart.destroy(); state.chart = new Chart(ctx, { type: 'line', - data: { - labels, - datasets: [ - { label: 'Temperature °C', data: temps, borderColor: '#2563eb', backgroundColor: 'rgba(37,99,235,0.08)', yAxisID: 'y', tension: 0.35, borderWidth: 2, fill: true, spanGaps: true, pointRadius: 2, pointHoverRadius: 4 }, - { label: 'Humidity %', data: humi, borderColor: '#059669', backgroundColor: 'rgba(5,150,105,0.08)', yAxisID: 'y1', tension: 0.35, borderWidth: 2, fill: true, spanGaps: true, pointRadius: 2, pointHoverRadius: 4 }, - ] - }, + data: { labels, datasets }, options: { responsive: true, interaction: { mode: 'index', intersect: false }, @@ -219,8 +280,8 @@ function renderChart() { }, scales: { x: { ticks: { color: '#94a3b8', font: { size: 11 } }, grid: { color: '#f1f5f9' } }, - y: { ticks: { color: '#2563eb', font: { size: 11 } }, grid: { color: '#f1f5f9' }, position: 'left', border: { display: false } }, - y1: { ticks: { color: '#059669', font: { size: 11 } }, grid: { drawOnChartArea: false }, position: 'right', border: { display: false } }, + y: { ticks: { color: datasets[0].borderColor, font: { size: 11 } }, grid: { color: '#f1f5f9' }, position: 'left', border: { display: false } }, + ...(hasSecondAxis ? { y1: { ticks: { color: datasets[1].borderColor, font: { size: 11 } }, grid: { drawOnChartArea: false }, position: 'right', border: { display: false } } } : {}), } } });