diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py
index f21813b..960a9a7 100644
--- a/kobrax_moonraker_bridge.py
+++ b/kobrax_moonraker_bridge.py
@@ -2333,27 +2333,8 @@ nav.bottom-nav{display:none;position:fixed;bottom:0;left:0;right:0;
-
-
♨ ACE Trocknung
-
- Status: Aus
-
-
- Humidity:
- -
-
-
- Current Temp:
- -
-
-
-
-
-
-
-
-
-
+
@@ -2459,6 +2440,7 @@ var S={nozzle_temp:0,nozzle_target:0,bed_temp:0,bed_target:0,
camera_url:'',fan_speed:0,print_speed_mode:2,light_on:false,light_brightness:80,
ams_slots:[],filament_mode:'toolhead',ace_units:[],ace_drying:{status:0,target_temp:0,duration:0,remain_time:0,humidity:null,current_temp:null,units:[]}};
var tempHistory={n:[],b:[]};
+var aceDryHistory={0:{t:[],h:[]},1:{t:[],h:[]},2:{t:[],h:[]},3:{t:[],h:[]}};
var camOn=false;
var currentStep=1;
var currentPanel='dashboard';
@@ -2479,7 +2461,7 @@ var LANG_DE={
card_progress:'Fortschritt',card_temps:'Temperaturen',card_light_fan:'Lüfter',card_speed:'Druckgeschwindigkeit',card_cam:'Kamera',lbl_elapsed:'Verstrichen:',lbl_remaining:'Restzeit:',lbl_slicer_time:'Slicer-Schätzung:',lbl_layers:'Layer',
speed_silent:'🐢 Leise',speed_normal:'⚡ Normal',speed_sport:'🚀 Sport',
lbl_light:'💡 Licht',lbl_feed:'Einziehen',lbl_unload:'Ausziehen',
- card_ace_dry:'ACE Trocknung',ace_dry_status_off:'Status: Aus',ace_dry_status_on:'Status: Aktiv',ace_dry_status_remaining:'Rest',ace_dry_humidity:'Luftfeuchte',ace_dry_current_temp:'Aktuelle Temperatur',ace_dry_temp:'Temperatur (°C)',ace_dry_duration:'Dauer (Min)',ace_dry_start:'▶ Start',ace_dry_stop:'■ Stop',
+ card_ace_dry:'ACE Trocknung',ace_dry_dryer:'Trockner',ace_dry_status_off:'Status: Aus',ace_dry_status_on:'Status: Aktiv',ace_dry_status_remaining:'Rest',ace_dry_humidity:'Luftfeuchte',ace_dry_current_temp:'Aktuelle Temperatur',ace_dry_chart:'Verlauf (Temp/Feuchte)',ace_dry_temp:'Temperatur (°C)',ace_dry_duration:'Dauer (Min)',ace_dry_start:'▶ Start',ace_dry_stop:'■ Stop',
cam_placeholder:'📷 Kamera nicht gestartet',btn_cam_start:'▶ Kamera',btn_cam_stop:'◼ Kamera',
btn_pause:'⏸ Pause',btn_resume:'▶ Weiter',btn_cancel:'✕ Stopp',
label_nozzle:'Nozzle',label_bed:'Bett',label_fan:'🌀 Lüfter',label_light:'💡 Licht',label_on_off:'Ein / Aus',label_speed:'Geschwindigkeit',
@@ -2542,7 +2524,7 @@ var LANG_EN={
card_progress:'Progress',card_temps:'Temperatures',card_light_fan:'Fan',card_speed:'Print Speed',card_cam:'Camera',lbl_elapsed:'Elapsed:',lbl_remaining:'Remaining:',lbl_slicer_time:'Slicer estimate:',lbl_layers:'Layer',
speed_silent:'🐢 Silent',speed_normal:'⚡ Normal',speed_sport:'🚀 Sport',
lbl_light:'💡 Light',lbl_feed:'Load',lbl_unload:'Unload',
- card_ace_dry:'ACE Drying',ace_dry_status_off:'Status: Off',ace_dry_status_on:'Status: Active',ace_dry_status_remaining:'Remaining',ace_dry_humidity:'Humidity',ace_dry_current_temp:'Current Temp',ace_dry_temp:'Temperature (°C)',ace_dry_duration:'Duration (min)',ace_dry_start:'▶ Start',ace_dry_stop:'■ Stop',
+ card_ace_dry:'ACE Drying',ace_dry_dryer:'Dryer',ace_dry_status_off:'Status: Off',ace_dry_status_on:'Status: Active',ace_dry_status_remaining:'Remaining',ace_dry_humidity:'Humidity',ace_dry_current_temp:'Current Temp',ace_dry_chart:'History (Temp/Humidity)',ace_dry_temp:'Temperature (°C)',ace_dry_duration:'Duration (min)',ace_dry_start:'▶ Start',ace_dry_stop:'■ Stop',
cam_placeholder:'📷 Camera not started',btn_cam_start:'▶ Camera',btn_cam_stop:'◼ Camera',
btn_pause:'⏸ Pause',btn_resume:'▶ Resume',btn_cancel:'✕ Stop',
label_nozzle:'Nozzle',label_bed:'Bed',label_fan:'🌀 Fan',label_light:'💡 Light',label_on_off:'On / Off',label_speed:'Speed',
@@ -2668,6 +2650,7 @@ function toggleLang(){
applyLang();
}
function applyLang(){
+ ensureAceDryCards();
// Nav
var nb=document.getElementById('nb-dashboard');if(nb)nb.querySelector('.nav-text').textContent=T.nav_dashboard;
nb=document.getElementById('nb-console');if(nb)nb.querySelector('.nav-text').textContent=T.nav_console;
@@ -2697,7 +2680,6 @@ function applyLang(){
setText('d-card-progress',T.card_progress);
setText('d-card-temps',T.card_temps);
setText('d-card-lightfan',T.card_light_fan);
- setText('d-card-ace-dry',T.card_ace_dry);
setText('d-card-speed',T.card_speed);
setText('d-card-cam',T.card_cam);
setText('d-card-ams',T.panel_ams_title);
@@ -2755,12 +2737,16 @@ function applyLang(){
// AMS feed/unload
document.querySelectorAll('.lbl-feed').forEach(e=>e.textContent=T.lbl_feed);
document.querySelectorAll('.lbl-unload').forEach(e=>e.textContent=T.lbl_unload);
- setText('ace-dry-start',T.ace_dry_start);
- setText('ace-dry-stop',T.ace_dry_stop);
- setText('d-ace-dry-humidity-label',(T.ace_dry_humidity||'Humidity')+':');
- setText('d-ace-dry-current-temp-label',(T.ace_dry_current_temp||'Current Temp')+':');
- var adTemp=document.getElementById('ace-dry-temp');if(adTemp)adTemp.setAttribute('placeholder',T.ace_dry_temp);
- var adDur=document.getElementById('ace-dry-duration');if(adDur)adDur.setAttribute('placeholder',T.ace_dry_duration);
+ for(var i=0;i<4;i++){
+ setText('d-card-ace-dry-'+i,'ACE '+(i+1)+' - '+(T.ace_dry_dryer||'Dryer'));
+ setText('ace-dry-start-'+i,T.ace_dry_start);
+ setText('ace-dry-stop-'+i,T.ace_dry_stop);
+ setText('d-ace-dry-humidity-label-'+i,(T.ace_dry_humidity||'Humidity')+':');
+ setText('d-ace-dry-current-temp-label-'+i,(T.ace_dry_current_temp||'Current Temp')+':');
+ setText('d-ace-dry-chart-label-'+i,T.ace_dry_chart||'History (Temp/Humidity)');
+ var adTemp=document.getElementById('ace-dry-temp-'+i);if(adTemp)adTemp.setAttribute('placeholder',T.ace_dry_temp);
+ var adDur=document.getElementById('ace-dry-duration-'+i);if(adDur)adDur.setAttribute('placeholder',T.ace_dry_duration);
+ }
// conn-btn text (nur wenn nicht im Übergangszustand)
updateConnBtn();
// Slot-Edit-Dialog
@@ -2776,6 +2762,34 @@ function applyLang(){
setText('file-cancel-btn',T.file_cancel_btn);
}
function setText(id,txt){var el=document.getElementById(id);if(el)el.textContent=txt;}
+
+function ensureAceDryCards(){
+ var grid=document.getElementById('d-ace-dry-grid');
+ if(!grid||grid.getAttribute('data-init')==='1')return;
+ var html='';
+ for(var i=0;i<4;i++){
+ html+='
'
+ +'
♨ ACE '+(i+1)+' - Dryer
'
+ +'
Status: Off
'
+ +'
Humidity: -
'
+ +'
Current Temp: -
'
+ +'
'
+ +''
+ +''
+ +'
'
+ +'
'
+ +''
+ +''
+ +'
'
+ +'
'
+ +'
History (Temp/Humidity)
'
+ +'
'
+ +'
'
+ +'
';
+ }
+ grid.innerHTML=html;
+ grid.setAttribute('data-init','1');
+}
(function(){
var l=localStorage.getItem('lang')||'de';
currentLang=l;T=l==='de'?LANG_DE:LANG_EN;
@@ -2999,35 +3013,52 @@ function applyState(){
amsTitle.textContent=modeTxt?(baseTitle+' - '+modeTxt):baseTitle;
}
- var acePresent=((s.ace_units||[]).length>0)
- ||(s.filament_mode&&s.filament_mode!=='toolhead')
- ||((s.ams_slots||[]).some(function(sl){return (sl.box_id||-1)>=0;}));
- var aceCard=document.getElementById('d-ace-dry-card');
- if(aceCard)aceCard.style.display=acePresent?'':'none';
- var dry=s.ace_drying||{status:0,target_temp:0,duration:0,remain_time:0,humidity:null,current_temp:null};
- var dryStatus=document.getElementById('d-ace-dry-status');
- if(dryStatus){
- if(dry.status){
- var rem=(dry.remain_time||0);
- dryStatus.textContent=(T.ace_dry_status_on||'Status: Active')+(rem>0?(' - '+(T.ace_dry_status_remaining||'Remaining')+': '+rem+' min'):'');
- }else{
- dryStatus.textContent=T.ace_dry_status_off||'Status: Off';
+ ensureAceDryCards();
+ var dry=s.ace_drying||{status:0,target_temp:0,duration:0,remain_time:0,humidity:null,current_temp:null,units:[]};
+ var units=(dry.units||[]);
+ var unitMap={};
+ units.forEach(function(u){var id=Number(u.id);if(id>=0&&id<=3)unitMap[id]=u;});
+ var detected=(s.ace_units||[]).filter(function(id){return id>=0&&id<=3;});
+ if(!detected.length){
+ Object.keys(unitMap).forEach(function(k){detected.push(Number(k));});
+ }
+ if(!detected.length){
+ (s.ams_slots||[]).forEach(function(sl){var id=Number(sl.box_id);if(id>=0&&id<=3&&detected.indexOf(id)<0)detected.push(id);});
+ }
+ detected.sort(function(a,b){return a-b;});
+ var aceWrap=document.getElementById('d-ace-dry-wrap');
+ if(aceWrap)aceWrap.style.display=detected.length?'contents':'none';
+ for(var i=0;i<4;i++){
+ var card=document.getElementById('d-ace-dry-card-'+i);
+ if(!card)continue;
+ var show=detected.indexOf(i)>=0;
+ card.style.display=show?'':'none';
+ if(!show)continue;
+ var ud=unitMap[i]||dry;
+ var st=document.getElementById('d-ace-dry-status-'+i);
+ if(st){
+ if(Number(ud.status||0)){
+ var rem=(Number(ud.remain_time)||0);
+ st.textContent=(T.ace_dry_status_on||'Status: Active')+(rem>0?(' - '+(T.ace_dry_status_remaining||'Remaining')+': '+rem+' min'):'' );
+ }else{
+ st.textContent=T.ace_dry_status_off||'Status: Off';
+ }
}
+ var hh=document.getElementById('d-ace-dry-humidity-'+i);
+ if(hh){
+ var hv=(ud.humidity===null||ud.humidity===undefined||ud.humidity==='')?null:Number(ud.humidity);
+ hh.textContent=(hv===null||Number.isNaN(hv))?'-':(Math.round(hv)+'%');
+ }
+ var ht=document.getElementById('d-ace-dry-current-temp-'+i);
+ if(ht){
+ var ct=(ud.current_temp===null||ud.current_temp===undefined||ud.current_temp==='')?null:Number(ud.current_temp);
+ ht.textContent=(ct===null||Number.isNaN(ct))?'-':(ct.toFixed(1)+'°C');
+ }
+ var dryTemp=document.getElementById('ace-dry-temp-'+i);
+ if(dryTemp&&Number(ud.target_temp)>0&&String(dryTemp.value||'')==='55')dryTemp.value=Number(ud.target_temp);
+ var dryDur=document.getElementById('ace-dry-duration-'+i);
+ if(dryDur&&Number(ud.duration)>0&&String(dryDur.value||'')==='240')dryDur.value=Number(ud.duration);
}
- var dryHumidity=document.getElementById('d-ace-dry-humidity');
- if(dryHumidity){
- var hv=(dry.humidity===null||dry.humidity===undefined||dry.humidity==='')?null:Number(dry.humidity);
- dryHumidity.textContent=(hv===null||Number.isNaN(hv))?'-':(Math.round(hv)+'%');
- }
- var dryCurrentTemp=document.getElementById('d-ace-dry-current-temp');
- if(dryCurrentTemp){
- var ct=(dry.current_temp===null||dry.current_temp===undefined||dry.current_temp==='')?null:Number(dry.current_temp);
- dryCurrentTemp.textContent=(ct===null||Number.isNaN(ct))?'-':(ct.toFixed(1)+'°C');
- }
- var dryTemp=document.getElementById('ace-dry-temp');
- if(dryTemp&&dry.target_temp>0&&String(dryTemp.value||'')==='55')dryTemp.value=dry.target_temp;
- var dryDur=document.getElementById('ace-dry-duration');
- if(dryDur&&dry.duration>0&&String(dryDur.value||'')==='240')dryDur.value=dry.duration;
// AMS
if(s.ams_slots&&s.ams_slots.length){
@@ -3127,6 +3158,25 @@ function updateHistory(){
if(tempHistory.n.length>60)tempHistory.n.shift();
if(tempHistory.b.length>60)tempHistory.b.shift();
drawChart('d-chart',tempHistory,[{data:tempHistory.n,color:'#00c8ff',max:300},{data:tempHistory.b,color:'#ff6b35',max:120}]);
+
+ var dry=S.ace_drying||{};
+ var units=(dry.units||[]);
+ var unitMap={};
+ units.forEach(function(u){var id=Number(u.id);if(id>=0&&id<=3)unitMap[id]=u;});
+ for(var i=0;i<4;i++){
+ var u=unitMap[i];
+ var t=u&&u.current_temp!=null?Number(u.current_temp):null;
+ var h=u&&u.humidity!=null?Number(u.humidity):null;
+ if((t===null||Number.isNaN(t))&&(h===null||Number.isNaN(h)))continue;
+ if(t!==null&&!Number.isNaN(t))aceDryHistory[i].t.push(t);
+ if(h!==null&&!Number.isNaN(h))aceDryHistory[i].h.push(h);
+ if(aceDryHistory[i].t.length>60)aceDryHistory[i].t.shift();
+ if(aceDryHistory[i].h.length>60)aceDryHistory[i].h.shift();
+ drawChart('d-ace-dry-chart-'+i,aceDryHistory[i],[
+ {data:aceDryHistory[i].t,color:'#ff8c2f',max:100},
+ {data:aceDryHistory[i].h,color:'#3aa8ff',max:100}
+ ]);
+ }
}
function drawChart(id,_,series){
var canvas=document.getElementById(id);if(!canvas)return;
@@ -3481,32 +3531,6 @@ function amsFeed(type,slotIndex){
.catch(function(e){clog('AMS-Fehler: '+e,'msg-err');throw e;});
}
-function aceDryStart(){
- var t=parseInt(document.getElementById('ace-dry-temp').value||55);
- var d=parseInt(document.getElementById('ace-dry-duration').value||240);
- t=Math.max(30,Math.min(80,t));
- d=Math.max(10,Math.min(1440,d));
- return post('/api/ace/dry',{action:'start',target_temp:t,duration:d})
- .then(function(r){return r.json();})
- .then(function(r){
- if(r.error){throw new Error(r.error);}
- clog((T.card_ace_dry||'ACE Drying')+': '+(T.ace_dry_start||'start')+' ('+t+'°C, '+d+' min)','msg-ok');
- poll();
- })
- .catch(function(e){clog('ACE-Fehler: '+e,'msg-err');});
-}
-
-function aceDryStop(){
- return post('/api/ace/dry',{action:'stop'})
- .then(function(r){return r.json();})
- .then(function(r){
- if(r.error){throw new Error(r.error);}
- clog((T.card_ace_dry||'ACE Drying')+': '+(T.ace_dry_stop||'stop'),'msg-ok');
- poll();
- })
- .catch(function(e){clog('ACE-Fehler: '+e,'msg-err');});
-}
-
// ── Camera ──
function camStart(){
var img=document.getElementById('cam-img');
@@ -3539,22 +3563,47 @@ function camStart(){
clog((T.log_error||'Fehler:')+' '+e,'msg-err');
});
}
+
function camStop(){
- post('/api/camera/stop',{}).then(function(){
- var img=document.getElementById('cam-img');
- img.src='';
- img.style.display='none';
- document.getElementById('cam-spinner').style.display='none';
- document.getElementById('cam-placeholder').style.display='flex';
- camOn=false;
- document.getElementById('cam-toggle-btn').textContent=T.btn_cam_start||'▶ Kamera';
- clog(T.log_cam_stop||'Kamera gestoppt','msg-ok');
- }).catch(function(e){clog((T.log_error||'Fehler:')+' '+e,'msg-err')});
+ var img=document.getElementById('cam-img');
+ post('/api/camera/stop',{}).catch(function(){});
+ img.src='';
+ img.style.display='none';
+ document.getElementById('cam-placeholder').style.display='flex';
+ camOn=false;
+ document.getElementById('cam-toggle-btn').textContent=T.btn_cam_start||'▶ Kamera';
+ clog((T.log_cam_stop||'Kamera gestoppt'),'msg-ok');
}
+
+function aceDryStart(aceId){
+ aceId=(typeof aceId==='number'&&aceId>=0)?aceId:0;
+ var t=parseInt((document.getElementById('ace-dry-temp-'+aceId)||{}).value||55);
+ var d=parseInt((document.getElementById('ace-dry-duration-'+aceId)||{}).value||240);
+ t=Math.max(30,Math.min(80,t));
+ d=Math.max(10,Math.min(1440,d));
+ return post('/api/ace/dry',{action:'start',target_temp:t,duration:d,ace_id:aceId})
+ .then(function(r){return r.json();})
+ .then(function(r){
+ if(r.error){throw new Error(r.error);}
+ clog('ACE '+(aceId+1)+' - '+(T.ace_dry_dryer||'Dryer')+': '+(T.ace_dry_start||'start')+' ('+t+'°C, '+d+' min)','msg-ok');
+ poll();
+ })
+ .catch(function(e){clog('ACE-Fehler: '+e,'msg-err');});
+}
+
function toggleCam(){if(camOn)camStop();else camStart()}
-// ── GCode Store ──
-var storeFiles=[];
+function aceDryStop(aceId){
+ aceId=(typeof aceId==='number'&&aceId>=0)?aceId:0;
+ return post('/api/ace/dry',{action:'stop',ace_id:aceId})
+ .then(function(r){return r.json();})
+ .then(function(r){
+ if(r.error){throw new Error(r.error);}
+ clog('ACE '+(aceId+1)+' - '+(T.ace_dry_dryer||'Dryer')+': '+(T.ace_dry_stop||'stop'),'msg-ok');
+ poll();
+ })
+ .catch(function(e){clog('ACE-Fehler: '+e,'msg-err');});
+}
function loadStore(){
fetch(_apiUrl('/kx/files')).then(function(r){return r.json()}).then(function(d){
@@ -4299,6 +4348,16 @@ function loadPrinterTab(){
if not ace_ids:
return web.json_response({"error": "ACE not detected"}, status=400)
+ ace_id_raw = body.get("ace_id", None)
+ if ace_id_raw is not None:
+ try:
+ ace_id = int(ace_id_raw)
+ except Exception:
+ return web.json_response({"error": "ace_id must be an integer"}, status=400)
+ if ace_id not in ace_ids:
+ return web.json_response({"error": f"ACE {ace_id + 1} not detected"}, status=400)
+ ace_ids = [ace_id]
+
if action == "start":
target_temp = int(body.get("target_temp", 45))
duration = int(body.get("duration", 240))