☠ ThermReaper

CPU Thermal Control – Live-Daten vom Server aggregiert

Worker nicht erreichbar (offline oder ThermReaper laeuft nicht)
let currentDevice = ''; const chartData = []; async function fetchJSON(url) { const r = await fetch(url); if (!r.ok) throw new Error(r.status); return r.json(); } function loadDeviceList() { fetchJSON('/dashboard/thermreaper/api/devices').then(d => { const sel = document.getElementById('device-select'); sel.innerHTML = ''; d.devices.forEach(dev => { const o = document.createElement('option'); o.value = dev.device_id; o.textContent = `${dev.hostname||dev.device_id} (${dev.ip})`; sel.appendChild(o); }); }); } function loadDevice() { currentDevice = document.getElementById('device-select').value; refreshStatus(); } async function refreshStatus() { if (!currentDevice) return; try { const d = await fetchJSON(`/dashboard/thermreaper/api/${currentDevice}/status`); const dot = document.getElementById('dot'), msg = document.getElementById('offline-msg'), grid = document.getElementById('main-grid'); if (!d.online || !d.data) { dot.className='online-dot off'; msg.style.display='block'; grid.style.display='none'; return; } dot.className='online-dot on'; msg.style.display='none'; grid.style.display='grid'; const s = d.data; document.getElementById('v-temp').textContent = s.current_temp!=null ? s.current_temp.toFixed(1)+' °C':'–'; document.getElementById('v-target').textContent = s.target_temp!=null ? `Ziel: ${s.target_temp.toFixed(1)} °C`:''; const tc = document.getElementById('temp-card'); tc.className = 'card '+(s.error>5?'temp-ok':s.error>-5?'temp-warn':'temp-hot'); document.getElementById('v-freq').textContent = s.current_khz ? Math.round(s.current_khz/1000)+' MHz':'–'; document.getElementById('v-mode').textContent = s.mode||'–'; document.getElementById('v-mode').className = 'mode-badge mode-'+(s.mode||''); if (s.ram && s.ram.percent_used != null) document.getElementById('v-ram').textContent = s.ram.percent_used+' %'; else if (s.system && s.system.ram && s.system.ram.percent_used != null) document.getElementById('v-ram').textContent = s.system.ram.percent_used+' %'; document.getElementById('v-uptime').textContent = s.system_uptime||'–'; fetchJSON(`/dashboard/thermreaper/api/${currentDevice}/config`).then(c => { [['cfg-temp','target_temp'],['cfg-base-step','base_step'],['cfg-max-step','max_step'],['cfg-safety','safety_margin']].forEach(([id,k]) => { const el=document.getElementById(id); if(el&&c[k]!==undefined&&document.activeElement!==el) el.value=c[k]; }); }); document.getElementById('poll-info').textContent=`Letzter Poll: ${d.last_poll}`; } catch(e) {} } async function saveConfig() { if(!currentDevice)return; const d={}; ['cfg-temp','cfg-base-step','cfg-max-step','cfg-safety'].forEach(id=>{ const el=document.getElementById(id); if(el.value!=='')d[id.replace('cfg-','').replace('-','_')]=parseFloat(el.value); }); try{await fetch(`/dashboard/thermreaper/api/${currentDevice}/config`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(d)});}catch(e){} } async function resetFreq() { if(!currentDevice)return; try{await fetch(`/dashboard/thermreaper/api/${currentDevice}/reset_freq`,{method:'POST'});alert('CPU-Freq zurueckgesetzt');}catch(e){alert('Fehler: '+e);} } loadDeviceList(); setInterval(refreshStatus,3000); const ctx=document.getElementById('chart').getContext('2d'); const samples=[]; function drawChart(){ ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); if(samples.length<2)return; const W=ctx.canvas.width,H=ctx.canvas.height,P=30; const temps=samples.map(s=>s[0]),freqs=samples.map(s=>s[1]); const tMin=Math.min(...temps)-2,tMax=Math.max(...temps)+2; const fMin=Math.min(...freqs)-200,fMax=Math.max(...freqs)+200; function draw(data,c,min,max){ ctx.strokeStyle=c;ctx.lineWidth=2;ctx.beginPath(); data.forEach((v,i)=>{const x=P+(i/(data.length-1))*(W-2*P),y=H-P-((v-min)/(max-min))*(H-2*P);i===0?ctx.moveTo(x,y):ctx.lineTo(x,y);}); ctx.stroke(); } draw(temps,'#ff6b6b',tMin,tMax); draw(freqs.map(f=>f/1000),'#42a5f5',fMin/1000,fMax/1000); ctx.fillStyle='#888';ctx.font='10px sans-serif'; ctx.fillText(`${tMin}°C`,2,H-P);ctx.fillText(`${tMax}°C`,2,P); } setInterval(()=>{ if(!currentDevice)return; fetchJSON(`/dashboard/thermreaper/api/${currentDevice}/status`).then(d=>{ if(!d.data||d.data.current_temp==null)return; samples.push([d.data.current_temp,d.data.current_khz||0]); if(samples.length>100)samples.shift(); drawChart(); }).catch(()=>{}); },3000);