document.addEventListener('DOMContentLoaded', function() { // Initialize Chart const ctx = document.getElementById('hashrateChart').getContext('2d'); const hashrateChart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'Hashrate (MH/s)', data: [], borderColor: '#0d6efd', backgroundColor: 'rgba(13, 110, 253, 0.1)', borderWidth: 2, fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { color: '#333' }, ticks: { color: '#aaa' } }, y: { grid: { color: '#333' }, ticks: { color: '#aaa' }, beginAtZero: true } } } }); // Helper to format large numbers function formatNumber(num) { return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); } function updateDashboard() { fetch('/api/data') .then(response => response.json()) .then(data => { const summary = data.summary; const devs = data.devs; const pools = data.pools; // Update Top Stats if (summary) { const mhs = summary['MHS 5s'] || summary['MHS av'] || 0; document.getElementById('stat-mhs').innerText = parseFloat(mhs).toFixed(2); document.getElementById('stat-accepted').innerText = formatNumber(summary['Accepted'] || 0); document.getElementById('stat-hw').innerText = formatNumber(summary['Hardware Errors'] || 0); } // Update Device Count if (devs) { document.getElementById('stat-devices').innerText = devs.length; } // Helper to find temp in stats if missing in devs // Cgminer 'stats' command returns specific driver info. // Structure: stats ->STATS -> list of dicts. usually one dict per device with ID keys like 'MM ID 0', etc. // Or simplified list. function getTempFallback(devId, statsData) { // Try to find matching ID in stats // This is heuristic as structure varies wildy between forks return 0; } // Update Devs Table & Max Temp let maxTemp = 0; const tbody = document.getElementById('devs-table-body'); tbody.innerHTML = ''; if (devs && devs.length > 0) { devs.forEach(dev => { let temp = dev['Temperature'] || 0; // Fallback logic could go here if we understood the STATS structure for this specific fork // For now we rely on standard API. if (temp > maxTemp) maxTemp = temp; const tr = document.createElement('tr'); tr.innerHTML = ` ${dev['ID']} ${dev['Name'] || 'Gridseed'} ${dev['Enabled']} ${dev['Status']} ${temp > 0 ? temp.toFixed(1) : '-'} ${parseFloat(dev['MHS 5s'] || 0).toFixed(2)} ${parseFloat(dev['MHS av'] || 0).toFixed(2)} ${dev['Accepted']} ${dev['Rejected']} ${dev['Hardware Errors']} `; tbody.appendChild(tr); }); } else { tbody.innerHTML = 'Keine Geräte gefunden...'; } document.getElementById('stat-temp').innerText = maxTemp.toFixed(1); // Update Chart const now = new Date(); const timeLabel = now.getHours() + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0'); if (summary) { const currentMhs = summary['MHS 5s'] || summary['MHS av'] || 0; if (hashrateChart.data.labels.length > 60) { // Keep last 60 points hashrateChart.data.labels.shift(); hashrateChart.data.datasets[0].data.shift(); } hashrateChart.data.labels.push(timeLabel); hashrateChart.data.datasets[0].data.push(currentMhs); hashrateChart.update(); } // Update Devs Table const tbody = document.getElementById('devs-table-body'); tbody.innerHTML = ''; if (devs && devs.length > 0) { devs.forEach(dev => { const tr = document.createElement('tr'); tr.innerHTML = ` ${dev['ID']} ${dev['Name'] || 'Gridseed'} ${dev['Enabled']} ${dev['Status']} ${dev['Temperature']} ${parseFloat(dev['MHS 5s'] || 0).toFixed(2)} ${parseFloat(dev['MHS av'] || 0).toFixed(2)} ${dev['Accepted']} ${dev['Rejected']} ${dev['Hardware Errors']} `; tbody.appendChild(tr); }); } else { tbody.innerHTML = 'Keine Geräte gefunden...'; } // Update Pools const poolList = document.getElementById('pool-list'); poolList.innerHTML = ''; if (pools && pools.length > 0) { pools.forEach(pool => { const div = document.createElement('div'); div.className = 'list-group-item bg-dark text-white border-secondary d-flex justify-content-between align-items-center'; div.innerHTML = `
Pool ${pool['POOL']}
${pool['URL']}
${pool['Status']} `; poolList.appendChild(div); }); } else { poolList.innerHTML = '
Keine Pools konfiguriert...
'; } }) .catch(err => console.error('Error fetching data:', err)); } // Update Log function updateLog() { fetch('/api/log') .then(response => response.json()) .then(data => { const logDiv = document.getElementById('miner-log'); if (data.log) { const isScrolledToBottom = logDiv.scrollHeight - logDiv.clientHeight <= logDiv.scrollTop + 1; logDiv.innerText = data.log; if (isScrolledToBottom) { logDiv.scrollTop = logDiv.scrollHeight; } } }) .catch(err => console.error('Error fetching log:', err)); } // Update every 3 seconds setInterval(updateDashboard, 3000); setInterval(updateLog, 5000); updateDashboard(); // Initial call updateLog(); });