Dashboard web FastAPI + Chart.js

- 4 vues : spectre temps reel, historique detections, background, timeline CPS
- API REST : /api/status, /api/spectrum/current, /api/spectrum/difference,
  /api/background, /api/background/spectrum, /api/history, /api/cps/timeline
- Frontend vanilla JS + Chart.js (pas de Node.js, leger pour Pi 4)
- Moniteur modifie pour exporter son etat dans /data/monitor_state.json
  et le CPS dans /data/cps_log.jsonl chaque cycle
- Nouveau conteneur Docker 'web' sur port 8080
- Theme sombre, calibration energie (E = 0.33 + 2.97 * canal)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jacquin Antoine
2026-05-19 13:33:07 +02:00
parent 27ef0727e8
commit 1e0c1a5ea5
22 changed files with 1031 additions and 2 deletions

51
web/static/js/app.js Normal file
View File

@ -0,0 +1,51 @@
const API_BASE = '';
let refreshInterval = null;
const REFRESH_MS = 30000; // 30 seconds
// Tab navigation
document.querySelectorAll('nav a').forEach(link => {
link.addEventListener('click', e => {
e.preventDefault();
const tab = link.dataset.tab;
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
link.classList.add('active');
document.getElementById(`tab-${tab}`).classList.add('active');
// Refresh the active tab
if (tab === 'spectrum') refreshSpectrum();
if (tab === 'background') refreshBackground();
if (tab === 'cps') loadCps(24);
});
});
// Status bar refresh
async function refreshStatus() {
try {
const resp = await fetch(`${API_BASE}/api/status`);
if (!resp.ok) {
document.getElementById('status-connected').className = 'status-dot';
return;
}
const data = await resp.json();
const dot = document.getElementById('status-connected');
dot.className = data.connected && !data.stale ? 'status-dot connected' : 'status-dot';
document.getElementById('status-cps').textContent = `${data.cps.toFixed(1)} CPS`;
document.getElementById('status-live-time').textContent = `${data.cumulated_live_time_h.toFixed(1)} h`;
} catch {
document.getElementById('status-connected').className = 'status-dot';
}
}
// Auto-refresh
function startRefresh() {
refreshStatus();
refreshSpectrum();
refreshInterval = setInterval(() => {
refreshStatus();
const activeTab = document.querySelector('.tab.active')?.dataset.tab;
if (activeTab === 'spectrum') refreshSpectrum();
}, REFRESH_MS);
}
// Initialize
startRefresh();